1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <memory.h>
4 #include <time.h>
5 #include <signal.h>
6
7 #define WIDTH 10
8 #define HEIGHT 10
9 #define SUCCESS 0
10 #define FAILURE 1
11 #define EATEN_FOOD 2
12 #define GAME_OVER 3
13
14 const int width = WIDTH;
15 const int height = HEIGHT;
16 static int area[WIDTH][HEIGHT];
17
18 #define CHECK_FALSE_RET(expr, ret) if(!(expr)) return ret;
19
20 #define LOG(format, ...) //printf(format, ##__VA_ARGS__)
21
22 typedef enum Direct {
23 NO_DIRECT,
24 UP,
25 DOWN,
26 LEFT,
27 RIGHT
28 }Direct;
29
30 typedef struct Point {
31 int x;
32 int y;
33 Point *next;
34 }Point;
35
36 typedef struct Snake {
37 Point *head;
38 Point *tail;
39 int length;
40 Direct direct;
41 }Snake;
42
43 Point* NewPoint()
44 {
45 Point *np = (Point*)malloc(sizeof(Point));
46 if(np != NULL)
47 {
48 memset(np, 0, sizeof(Point));
49 }
50 return np;
51 }
52
53 Point* NewPointWithParam(int x, int y)
54 {
55 Point *np = NewPoint();
56 if(np != NULL)
57 {
58 np->x = x;
59 np->y = y;
60 }
61 return np;
62 }
63
64 Direct DecideForwardDirect(Snake *snake, Point *food)
65 {
66 if(snake == NULL || food == NULL)
67 {
68 LOG("[error]: DecideForwardDirect failed. snake=NULL|food=NULL\n");
69 return NO_DIRECT;
70 }
71 Direct dir = NO_DIRECT;
72 Point *head = snake->head;
73 if(head == NULL)
74 {
75 LOG("[error]: DecideForwardDirect failed. head=NULL\n");
76 return NO_DIRECT;
77 }
78
79 switch(snake->direct)
80 {
81 case UP:
82 if(head->x == 0) dir = RIGHT;
83 else if(head->x == width-2) dir = LEFT;
84 break;
85 case DOWN:
86 if(head->x == width - 1)
87 {
88 if(head->y == height - 1) dir = LEFT;
89 else dir = DOWN;
90 }
91 break;
92 case LEFT:
93 if(head->x == 0) dir = UP;
94 else dir = LEFT;
95 break;
96 case RIGHT:
97 if(head->y == 0 && head->x == width - 1) dir = DOWN;
98 else if(head->y != 0 && head->x == width - 2) dir = UP;
99 else dir = RIGHT;
100 break;
101 default:
102 break;
103 }
104 snake->direct = dir;
105 return dir;
106 }
107
108 Point *CreateOneFood(Snake *snake)
109 {
110 if(snake == NULL) return NULL;
111
112 Point *np = NULL;
113 Point *food = NULL;
114 int unusedNum = width*height - snake->length;
115 int randPos;
116 int x, y;
117 int foodX = -1, foodY = -1;
118 memset(area, 0, width*height*sizeof(int));
119 np = snake->head;
120 while(np != NULL)
121 {
122 area[np->x][np->y] = 1;
123 np = np->next;
124 }
125 randPos = rand() % unusedNum;
126 for(x=0; x<width; x++)
127 {
128 if(randPos < 0) break;
129 for(y=0; y<height; y++)
130 {
131 if(area[x][y] == 0) randPos--;
132 if(randPos < 0)
133 {
134 foodX = x;
135 foodY = y;
136 break;
137 }
138 }
139 }
140
141 if((foodX >= 0 && foodX < width) && (foodY >= 0 && foodY < height))
142 {
143 food = NewPointWithParam(foodX, foodY);
144 }
145
146 return food;
147 }
148
149 int FortainEatFood(Snake *snake, Point *food)
150 {
151 if(snake == NULL || food == NULL) return FAILURE;
152 food->next = snake->head;
153 snake->head = food;
154 if(snake->tail == NULL) snake->tail = food;
155 snake->length++;
156 return SUCCESS;
157 }
158
159 Point *GetPreviousTail(Snake *snake)
160 {
161 if(snake == NULL) return NULL;
162 Point *np = snake->head;
163 while(np->next != snake->tail && np->next != NULL) np = np->next;
164 if(np->next == NULL) return NULL;
165 return np;
166 }
167
168
169 int FortainForward(Snake *snake, Direct dir, Point *food)
170 {
171 if(snake == NULL) return FAILURE;
172 if(dir == NO_DIRECT) return FAILURE;
173 if(food == NULL) return FAILURE;
174 Point *preTail;
175 preTail = GetPreviousTail(snake);
176 if(preTail == NULL) return FAILURE;
177 int currX = snake->head->x;
178 int currY = snake->head->y;
179 int nextX = currX;
180 int nextY = currY;
181
182 switch(dir)
183 {
184 case UP:
185 nextY = currY - 1;
186 break;
187 case DOWN:
188 nextY = currY + 1;
189 break;
190 case LEFT:
191 nextX = currX - 1;
192 break;
193 case RIGHT:
194 nextX = currX + 1;
195 default:
196 break;
197 }
198 if(nextX == food->x && nextY == food->y)
199 {
200 FortainEatFood(snake, food);
201 return EATEN_FOOD;
202 }
203 else
204 {
205 snake->tail->x = nextX;
206 snake->tail->y = nextY;
207 snake->tail->next = snake->head;
208 snake->head = snake->tail;
209 snake->tail = preTail;
210 snake->tail->next = NULL;
211 }
212
213 return SUCCESS;
214 }
215
216 int FortainGameInit(Snake *snake)
217 {
218 if(snake == NULL) return FAILURE;
219 Point *np1 = NewPointWithParam(width-1, height-1);
220 Point *np2 = NewPointWithParam(width-2, height-1);
221 Point *np3 = NewPointWithParam(width-3, height-1);
222 if(np1 == NULL || np2 == NULL || np3 == NULL) return FAILURE;
223
224 FortainEatFood(snake, np1);
225 FortainEatFood(snake, np2);
226 FortainEatFood(snake, np3);
227
228 snake->direct = LEFT;
229 return SUCCESS;
230 }
231
232 int FortainGameCheck(Snake *snake)
233 {
234 if(snake == NULL) return FAILURE;
235 memset(area, 0, width*height*sizeof(int));
236 Point *np = snake->head;
237 while(np != NULL)
238 {
239 CHECK_FALSE_RET(np->x >= 0, FAILURE);
240 CHECK_FALSE_RET(np->x < width, FAILURE);
241 CHECK_FALSE_RET(np->y >= 0, FAILURE);
242 CHECK_FALSE_RET(np->y < height, FAILURE);
243
244 CHECK_FALSE_RET(area[np->x][np->y] == 0, GAME_OVER);
245 area[np->x][np->y] = 1;
246 np = np->next;
247 }
248 CHECK_FALSE_RET(snake->length != width*height, GAME_OVER);
249 return SUCCESS;
250 }
251
252 void FortainGameMain()
253 {
254 Snake snake;
255 Point *food;
256 int ret;
257 int count = 0;
258 Direct dir;
259
260 memset(&snake, 0, sizeof(Snake));
261 ret = FortainGameInit(&snake);
262 if(ret == FAILURE)
263 {
264 LOG("[error]: GameInit failed!\n");
265 return;
266 }
267 LOG("[info]: GameInit done! length=%d\n", snake.length);
268
269 Point *np = snake.head;
270 while(np != NULL)
271 {
272 LOG("x=%d,y=%d,next=%x\n", np->x, np->y, np->next);
273 np = np->next;
274 }
275
276 food = CreateOneFood(&snake);
277 if(food == NULL) LOG("[error]: CreateFood failed!\n");
278 else LOG("[info]: CreateFood success! P(%d, %d)\n", food->x, food->y);
279 while(true)
280 {
281 dir = DecideForwardDirect(&snake, food);
282 LOG("[info]: DecideDirect, dir=%d\n", dir);
283 ret = FortainForward(&snake, dir, food);
284 LOG("[info]: Forward, dir=%d, food=(%d,%d), head=(%d,%d), length=%d\n", dir, food->x, food->y, snake.head->x, snake.head->y, snake.length);
285 if(ret == FAILURE)
286 {
287 LOG("[error]: Forward failed.dir|%d,length|%d.\n", dir, snake.length);
288 break;
289 }
290 else if(ret == EATEN_FOOD)
291 {
292 LOG("[info]: Eat food. length|%d\n", snake.length);
293 if(snake.length < width*height)
294 {
295 food = CreateOneFood(&snake);
296 if(food == NULL)
297 {
298 LOG("[error]: CreateFood failed!\n");
299 break;
300 }
301 else LOG("[info]: CreateFood success! P(%d, %d)\n", food->x, food->y);
302 }
303 }
304 ret = FortainGameCheck(&snake);
305 if(ret == FAILURE)
306 {
307 LOG("[error]: GameCheck failed!\n");
308 break;
309 }
310 else if(ret == GAME_OVER)
311 {
312 printf("[info]: Game over!!! length=%d, count=%d\n", snake.length, count);
313 break;
314 }
315 count++;
316 }
317 }
318
319 void SignHandler(int sign)
320 {
321 switch(sign)
322 {
323 case SIGABRT:
324 printf("signal: SIGABRT. \n 程序异常终止。\n");
325 break;
326 case SIGFPE:
327 printf("signal: SIGFPE. \n 算术运算出错,如除数为 0 或溢出(不一定是浮点运算)。\n");
328 break;
329 case SIGILL:
330 printf("signal: SIGILL. \n 非法函数映象,如非法指令,通常是由于代码中的某个变体或者尝试执行数据导致的。\n");
331 break;
332 case SIGINT:
333 printf("signal: SIGINT. \n 中断信号,如 ctrl-C,通常由用户生成。\n");
334 break;
335 case SIGSEGV:
336 printf("signal: SIGSEGV. \n 非法访问存储器,如访问不存在的内存单元。\n");
337 break;
338 case SIGTERM:
339 printf("signal: SIGTERM. \n 发送给本程序的终止请求信号。\n");
340 break;
341 }
342 }
343
344 int main()
345 {
346 signal(SIGABRT, SignHandler);
347 signal(SIGFPE, SignHandler);
348 signal(SIGILL, SignHandler);
349 signal(SIGINT, SignHandler);
350 signal(SIGSEGV, SignHandler);
351 signal(SIGTERM, SignHandler);
352 for(int i=0; i<100; i++)
353 {
354 clock_t start = clock();
355 srand(time(NULL));
356 FortainGameMain();
357 printf("Time: %d ms\n", clock()-start);
358 }
359 return 0;
360 }