1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <string.h>
4 #include <sys/types.h>
5 #include <sys/wait.h>
6 #include <sys/select.h>
7 #include <time.h>
8 #include <signal.h>
9 #include <unistd.h>
10 #include <ncurses.h>
11
12 /* 雷区的范围 */
13 #define MINEAREA_WIDTH 9
14 #define MINEAREA_LENGTH 9
15 /* 雷的个数 */
16 #define MINE_NUMBER 10
17
18 /*
19 * 将每个方块的状态分类:
20 * 1.初始状态
21 * 2.判定为雷(也就是通常的插旗)
22 * 3.排除(若周边有n个雷则显示为n,0则显示为空,用-1来表示雷)
23 */
24 #define SQUARE_INIT 0
25 #define SQUARE_FLAG 1
26 #define SQUARE_CLEAN 2
27 #define SQUARE_ZERO 0
28 #define SQUARE_MINE -1
29
30 /* 显示图形 */
31 #define GRAPH_INIT '.'
32 #define GRAPH_MINE '@'
33 #define GRAPH_NULL ' '
34 #define GRAPH_FLAG 'F'
35
36 #define NEWLINE addch('\n')
37 #define _W(y) (y * 2 + 3)
38 #define _L(x) (x * 3 + 1)
39 /* 设置光标 */
40 #define SET_CURSOR(y, x) mvchgat(_W(y), _L(x), 2, A_REVERSE, 0, NULL)
41 #define CLEAN_CURSOR(y, x) mvchgat(_W(y), _L(x), 2, A_NORMAL, 0, NULL)
42
43 #define WPRINT_NUMBER(y, x, v) \
44 mvprintw(y, x, "%d", v)
45 #define WPRINT_CHAR(y, x, c) \
46 mvaddch(y, x, c)
47
48 /* 光标的位置 */
49 int g_cur_y = 0;
50 int g_cur_x = 0;
51
52 struct square_t {
53 int type;
54 int mine;
55 };
56
57 /* timer process function */
58 int timer_p();
59 void sig_refresh_time(int signum);
60
61 int init_mine(struct square_t square[MINEAREA_WIDTH][MINEAREA_LENGTH]);
62 int check_yx(int y, int x);
63 int game_loop(struct square_t square[MINEAREA_WIDTH][MINEAREA_LENGTH]);
64 int clean_zero_squares(struct square_t square[MINEAREA_WIDTH][MINEAREA_LENGTH], int cur_y, int cur_x);
65
66 /* window functions */
67 int win_init(int width, int length, int mine_num);
68 int win_refresh(struct square_t square[MINEAREA_WIDTH][MINEAREA_LENGTH], int width, int length, int mines);
69 int win_refresh_remine_mines(int mines);
70 int win_refresh_secs(int secs);
71 int win_mv_cursor(int delta_y, int delta_x, int width, int length);
72 int win_destroy();
73 int win_bang();
74 int win_win();
75 int win_game_over();
76
77 int main()
78 {
79 int pid_timer;
80 int pid_main;
81
82 switch (pid_timer = fork()) {
83 case 0:
84 /* timer进程,用作计时器 */
85 timer_p();
86 _exit(0);
87 case -1:
88 perror("fork() error!");
89 return -1;
90 default:
91 /* main process */
92 break;
93 }
94
95 pid_main = getpid();
96
97 /* SIGUSR1信号用来刷新显示时间 */
98 if (signal(SIGUSR1, sig_refresh_time) == SIG_ERR)
99 return -1;
100
101 struct square_t square[MINEAREA_WIDTH][MINEAREA_LENGTH];
102
103 if (init_mine(square) == -1) return -1;
104
105 win_init(MINEAREA_WIDTH, MINEAREA_LENGTH, MINE_NUMBER);
106
107 /* 主循环 */
108 game_loop(square);
109
110 win_game_over();
111
112 /* 主进程结束前需要结束timer子进程 */
113 kill(pid_timer, SIGKILL);
114
115 int key = -1;
116 do {
117 key = getch();
118 }
119 while (key != 'y' && key != 'Y');
120
121 wait(NULL);
122 win_destroy();
123
124 return 0;
125 }
126
127 /* 初始化雷区信息 */
128 int init_mine(struct square_t square[MINEAREA_WIDTH][MINEAREA_LENGTH])
129 {
130 if (square == NULL)
131 return -1;
132
133 printf("waiting...\n");
134
135 int n,m;
136 for (n = 0; n < 9; ++n) {
137 for (m = 0; m < 9; ++m) {
138 square[n][m].type = 0;
139 square[n][m].mine = 0;
140 }
141 }
142
143 int i;
144 int y, x;
145
146 srandom((int)time(NULL));
147
148 for (i = 0; i < MINE_NUMBER; ++i) {
149 y = random() % MINEAREA_WIDTH;
150 x = random() % MINEAREA_LENGTH;
151
152 if (square[y][x].mine == SQUARE_MINE) {
153 --i;
154 }
155 else {
156 square[y][x].mine = SQUARE_MINE;
157
158 if (check_yx(y-1, x ) == 0 && square[y-1][x ].mine != SQUARE_MINE)
159 ++square[y-1][x ].mine;
160
161 if (check_yx(y+1, x ) == 0 && square[y+1][x ].mine != SQUARE_MINE)
162 ++square[y+1][x ].mine;
163
164 if (check_yx(y , x-1) == 0 && square[y ][x-1].mine != SQUARE_MINE)
165 ++square[y ][x-1].mine;
166
167 if (check_yx(y , x+1) == 0 && square[y ][x+1].mine != SQUARE_MINE)
168 ++square[y ][x+1].mine;
169
170 if (check_yx(y-1, x-1) == 0 && square[y-1][x-1].mine != SQUARE_MINE)
171 ++square[y-1][x-1].mine;
172
173 if (check_yx(y+1, x-1) == 0 && square[y+1][x-1].mine != SQUARE_MINE)
174 ++square[y+1][x-1].mine;
175
176 if (check_yx(y-1, x+1) == 0 && square[y-1][x+1].mine != SQUARE_MINE)
177 ++square[y-1][x+1].mine;
178
179 if (check_yx(y+1, x+1) == 0 && square[y+1][x+1].mine != SQUARE_MINE)
180 ++square[y+1][x+1].mine;
181 }
182 }
183
184 return 0;
185 }
186
187 int check_yx(int y, int x)
188 {
189 if (y >= 0
190 && y < MINEAREA_WIDTH
191 && x >= 0
192 && x < MINEAREA_LENGTH)
193 {
194 return 0;
195 }
196
197 return -1;
198 }
199
200 /* 主循环 */
201 int game_loop(struct square_t square[MINEAREA_WIDTH][MINEAREA_LENGTH])
202 {
203 fd_set rfd;
204 FD_ZERO(&rfd);
205 FD_SET(0, &rfd);
206
207 static int sweeped_mines = 0; /* 判定正确的雷数 */
208
209 int ret;
210 int input;
211 int remain_mines = MINE_NUMBER;
212
213 while (1) {
214 if ((ret = select(1, &rfd, NULL, NULL, NULL)) <= 0) {
215 //return -1; //当程序被信号中断时select可能会返回-1
216 continue;
217 }
218
219 switch (input = getch()) {
220 /* w,s,a,d方向键 */
221 case 'w':
222 case 'W':
223 win_mv_cursor(-1, 0, MINEAREA_WIDTH, MINEAREA_LENGTH);
224 break;
225 case 's':
226 case 'S':
227 win_mv_cursor(+1, 0, MINEAREA_WIDTH, MINEAREA_LENGTH);
228 break;
229 case 'a':
230 case 'A':
231 win_mv_cursor(0, -1, MINEAREA_WIDTH, MINEAREA_LENGTH);
232 break;
233 case 'd':
234 case 'D':
235 win_mv_cursor(0, +1, MINEAREA_WIDTH, MINEAREA_LENGTH);
236 break;
237
238 /* 插旗 */
239 case 'j':
240 case 'J':
241 if (square[g_cur_y][g_cur_x].type == SQUARE_INIT) {
242 square[g_cur_y][g_cur_x].type = SQUARE_FLAG;
243 --remain_mines;
244
245 if (square[g_cur_y][g_cur_x].mine == SQUARE_MINE)
246 ++sweeped_mines;
247 }
248 else if (square[g_cur_y][g_cur_x].type == SQUARE_FLAG) {
249 square[g_cur_y][g_cur_x].type = SQUARE_INIT;
250 ++remain_mines;
251
252 if (square[g_cur_y][g_cur_x].mine == SQUARE_MINE)
253 --sweeped_mines;
254 }
255 else
256 break;
257
258 if (sweeped_mines == MINE_NUMBER) {
259 win_win();
260 goto GAME_OVER;
261 }
262
263 win_refresh(square, MINEAREA_WIDTH, MINEAREA_LENGTH, remain_mines);
264 break;
265
266 /* 打开方块 */
267 case 'k':
268 case 'K':
269 if (square[g_cur_y][g_cur_x].type == SQUARE_CLEAN)
270 break;
271 else if (square[g_cur_y][g_cur_x].mine == SQUARE_MINE) {
272 win_bang();
273
274 int n, m;
275 for (n = 0; n < MINEAREA_WIDTH; ++n) {
276 for (m = 0; m < MINEAREA_LENGTH; ++m) {
277 square[n][m].type = SQUARE_CLEAN;
278 }
279 }
280
281 win_refresh(square, MINEAREA_WIDTH, MINEAREA_LENGTH, remain_mines);
282 goto GAME_OVER;
283 }
284
285 square[g_cur_y][g_cur_x].type = SQUARE_CLEAN;
286
287 if (square[g_cur_y][g_cur_x].mine == SQUARE_ZERO)
288 clean_zero_squares(square, g_cur_y, g_cur_x);
289
290 win_refresh(square, MINEAREA_WIDTH, MINEAREA_LENGTH, remain_mines);
291 break;
292
293 /* 退出 */
294 case 'q':
295 case 'Q':
296 goto GAME_OVER;
297
298 default:
299 break;
300 }
301 }
302
303 GAME_OVER:
304 return 0;
305 }
306
307 /* 如果打开的方块下面是0,则自动打开所有周围为0的方块 */
308 int clean_zero_squares(struct square_t square[MINEAREA_WIDTH][MINEAREA_LENGTH], int cur_y, int cur_x)
309 {
310 if (check_yx(cur_y - 1, cur_x) == 0
311 && square[cur_y - 1][cur_x].mine == SQUARE_ZERO
312 && square[cur_y - 1][cur_x].type != SQUARE_CLEAN)
313 {
314 square[cur_y - 1][cur_x].type = SQUARE_CLEAN;
315 clean_zero_squares(square, cur_y - 1, cur_x);
316 }
317
318 if (check_yx(cur_y + 1, cur_x) == 0
319 && square[cur_y + 1][cur_x].mine == SQUARE_ZERO
320 && square[cur_y + 1][cur_x].type != SQUARE_CLEAN)
321 {
322 square[cur_y + 1][cur_x].type = SQUARE_CLEAN;
323 clean_zero_squares(square, cur_y + 1, cur_x);
324 }
325
326 if (check_yx(cur_y, cur_x - 1) == 0
327 && square[cur_y][cur_x - 1].mine == SQUARE_ZERO
328 && square[cur_y][cur_x - 1].type != SQUARE_CLEAN)
329 {
330 square[cur_y][cur_x - 1].type = SQUARE_CLEAN;
331 clean_zero_squares(square, cur_y, cur_x - 1);
332 }
333
334 if (check_yx(cur_y, cur_x + 1) == 0
335 && square[cur_y][cur_x + 1].mine == SQUARE_ZERO
336 && square[cur_y][cur_x + 1].type != SQUARE_CLEAN)
337 {
338 square[cur_y][cur_x + 1].type = SQUARE_CLEAN;
339 clean_zero_squares(square, cur_y, cur_x + 1);
340 }
341
342 return 0;
343 }
344
345 /*****************************************************************************/
346 /* 初始化显示界面 */
347 int win_init(int width, int length, int mine_num)
348 {
349 initscr();
350 raw();
351 noecho();
352 keypad(stdscr, TRUE);
353 curs_set(0);
354 refresh();
355
356 win_refresh_remine_mines(MINE_NUMBER);
357 win_refresh_secs(0);
358
359 int frame_width = width * 2 + 1;
360 int frame_length = length * 3 + 1;
361 char *line = NULL;
362 line = (char*)malloc((frame_length + 1) * sizeof(char));
363 memset(line, '-', frame_length);
364 *(line + frame_length) = '\0';
365 mvprintw(2, 0, line);NEWLINE;
366
367 int i, j;
368 for (j = 0; j < frame_width - 2; ++j) {
369 addch('|');
370 for (i = 0; i < length * 2 + 1 - 2; ++i) {
371 if (j % 2 == 0) {
372 if (i % 2 == 0) {
373 addch(GRAPH_INIT);addch(' ');
374 }
375 else {
376 addch('|');
377 }
378 }
379 else {
380 if (i % 2 == 0) {
381 addch('-');addch('-');
382 }
383 else {
384 addch('+');
385 }
386 }
387 }
388 addch('|');NEWLINE;
389 }
390
391 printw(line);NEWLINE;
392
393 /* set cursor position */
394 SET_CURSOR(g_cur_y, g_cur_x);
395
396 refresh();
397 return 0;
398 }
399
400 /* 刷新显示界面 */
401 int win_refresh(struct square_t square[MINEAREA_WIDTH][MINEAREA_LENGTH], int width, int length, int mines)
402 {
403 if (square == NULL)
404 return -1;
405
406 win_refresh_remine_mines(mines);
407
408 int j, i;
409 for (j = 0; j < width; ++j) {
410 for (i = 0; i < length; ++i) {
411 switch (square[j][i].type) {
412 case SQUARE_INIT:
413 WPRINT_CHAR(_W(j), _L(i), GRAPH_INIT);
414 break;
415 case SQUARE_FLAG:
416 WPRINT_CHAR(_W(j), _L(i), GRAPH_FLAG);
417 break;
418 case SQUARE_CLEAN:
419 switch (square[j][i].mine) {
420 case SQUARE_MINE:
421 WPRINT_CHAR(_W(j), _L(i), GRAPH_MINE);
422 break;
423 case SQUARE_ZERO:
424 WPRINT_CHAR(_W(j), _L(i), GRAPH_NULL);
425 break;
426 default:
427 WPRINT_NUMBER(_W(j), _L(i), square[j][i].mine);
428 break;
429 }
430 break;
431 default:
432 break;
433 }
434 }
435 }
436
437 refresh();
438
439 return 0;
440 }
441
442 int win_refresh_remine_mines(int mines)
443 {
444 mvprintw(0, 0, "Mines: %d", mines);
445 mvprintw(1, 0, "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~");
446 refresh();
447 return 0;
448 }
449
450 int win_refresh_secs(int secs)
451 {
452 mvprintw(0, 15, "Seconds: %d", secs);
453 refresh();
454 return 0;
455 }
456
457
458 int win_mv_cursor(int delta_y, int delta_x, int width, int length)
459 {
460 CLEAN_CURSOR(g_cur_y, g_cur_x);
461
462 if (g_cur_y + delta_y < width && g_cur_y + delta_y >= 0)
463 g_cur_y += delta_y;
464
465 if (g_cur_x + delta_x < length && g_cur_x + delta_x >= 0)
466 g_cur_x += delta_x;
467
468 SET_CURSOR(g_cur_y, g_cur_x);
469
470 refresh();
471
472 return 0;
473 }
474
475 int win_destroy()
476 {
477 endwin();
478
479 return 0;
480 }
481
482 int win_bang()
483 {
484 mvprintw(0, 0, "BANG!!!!");
485 refresh();
486 return 0;
487 }
488
489 int win_win()
490 {
491 mvprintw(0, 0, "WIN!!!!");
492 refresh();
493 return 0;
494 }
495
496 int win_game_over()
497 {
498 mvprintw(1, 0, "Game Over!");
499 mvprintw(1, 0, "Press 'y' or 'Y' to end.");
500 refresh();
501 return 0;
502 }
503
504 /*****************************************************************************/
505 int timer_p()
506 {
507 /* 每秒钟给主进程发一次信号 */
508 do {
509 sleep(1);
510 kill(getppid(), SIGUSR1);
511 }
512 while (1);
513
514 return 0;
515 }
516
517 void sig_refresh_time(int signum)
518 {
519 static int secs = 0;
520 win_refresh_secs(++secs);
521 }
522