文件概述


- 主要定义变量都是在common. h 以及 common. c文件中
- game_setup. c都是游戏初始化定义
- snake. c是==game loop==,main 函数所在文件
- game. c是游戏主要逻辑实现函数
- test目录下的都是代码测试文件
主要文件代码
common.h
extern int g_game_over; // 1 if game is over, 0 otherwise
extern int g_score; // game score: 1 point for every food eaten
extern enum input_key g_last_input; //record last direction
extern int g_snake_length; // the length of snake
extern int g_name_length; // the lenght of game's name
extern char* g_name; // the name of the game
/** Snake struct. This struct is not needed until part 3!
* Fields:
* - None yet!
*/
typedef struct place
{
size_t x;
size_t y;
} place_t;
typedef struct snake {
// TODO: Define your snake struct! (in ')
// Store any necessary information about your snake here.
node_t *g_snake_head;
} snake_t;
- 我一开始是在 snake 内部定义回那些全局变量的,但是后面重新修改 game. c代码逻辑就重新将common. h的内容页修改一下,让 snake 结构体没怎么臃肿
- 我是将这个 ==bord==看成一个 xoy 坐标的,所以直接定义一个 place 结构体,因此 cells这个数组的值也是要用 x 和 y 来表示特定位置的 FLAG
snake.c
switch (argc) {
case (2):
snake_grows = atoi(argv[1]);
if (snake_grows != 1 && snake_grows != 0) {
printf("snake_grows must be either 1 (grows) or 0 (does not grow)\n");
return 0;
}
status = initialize_game(&cells, &width, &height, &snake, NULL);
break;
case (3):
snake_grows = atoi(argv[1]);
if (snake_grows != 1 && snake_grows != 0) {
printf("snake_grows must be either 1 (grows) or 0 (does not grow)\n");
return 0;
} else if (*argv[2] == '\0') {
status = initialize_game(&cells, &width, &height, &snake, NULL);
break;
}
status = initialize_game(&cells, &width, &height, &snake, argv[2]);
break;
case (1):
default:
printf("usage: snake <GROWS: 0|1> [BOARD STRING]\n");
return 0;
}
switch获取命令行作为参数,从而实现对应选择。
game loop logic
while(!g_game_over)
{
enum input_key input = g_last_input;
usleep(100000);
input = get_input();
update(cells, width, height, &snake,input , snake_grows);
render_game(cells, width, height);
}
这个是对应游戏调用循环逻辑
差不多这个 snake. c 就可以宣告就怎么多了
game.c
place_t new_pos;
new_pos.x = ((place_t*)snake_p->g_snake_head->data)->x;
new_pos.y = ((place_t*)snake_p->g_snake_head->data)->y;
if(input == INPUT_NONE)
{
input = g_last_input;
}
switch (input)
{
case INPUT_RIGHT:
new_pos.x = ((place_t*)snake_p->g_snake_head->data)->x + 1;
g_last_input = INPUT_RIGHT;
break;
case INPUT_LEFT:
new_pos.x = ((place_t*)snake_p->g_snake_head->data)->x - 1;
g_last_input = INPUT_LEFT;
break;
case INPUT_UP:
new_pos.y = ((place_t*)snake_p->g_snake_head->data)->y - 1;
g_last_input = INPUT_UP;
break;
case INPUT_DOWN:
new_pos.y = ((place_t*)snake_p->g_snake_head->data)->y + 1;
g_last_input = INPUT_DOWN;
break;
// case INPUT_NONE:
// new_pos.x = ((place_t*)snake_p->g_snake_head->data)->x + 1;
// g_last_input = INPUT_RIGHT;
// break;
default:
break;
}
==这个就是方向移动选择逻辑,一开始我是没有写 input = g_last_input,因为我是改了 snake. c 里的,但是现在统一弄到 game. c 中==
[!NOTE] 因为头部节点这里的data已经发生变化,已经移动了,所以如果还这样做的话,不能单纯使用遍历链表的方式取data数据然后使用cells来标记蛇的位置
if(new_pos.x == 0 ||
new_pos.x >= width ||
new_pos.y == 0 ||
new_pos.y >= height ||
(cells[new_pos.y * width + new_pos.x] & FLAG_SNAKE) == FLAG_SNAKE)
{
g_game_over = 1;
return;
}
==移动方向后要判断蛇是否已经是可以判断是结束了的条件了(因为我是选择了用 x 和 y 坐标,所以我是判断大小,而不是用那个地方是否标记为 FLAG_SNAKE 还是 FALG_WALL)==
接下来就是主要的蛇的移动和生长逻辑实现:
前提:都是使用链表,因此,要使用到链表中的data这个成员
- 一开始我是打算以如下方式实现的:让蛇后一节代替前一节,data 数据也是,具体如下所示:

但是没有想到如何实现,这个蛇的每一节的 data 一开始存储数据就要先存储初始坐标值的话(会很麻烦也不知道要怎么实心)->比如: 蛇在墙转头吃了 food,然后在蛇尾生长呢?
- 方案二: 移动靠增加头部节点,然后根据情况删除尾部节点(吃了食物后就不用删除,没吃就删除)用
linked_list. c中的函数中的insert_first以及remove_last来实现->因为每一次变化都是根据当时的情况每次更新的,所以后续的只需要考虑头部以及尾部节点就可以了。
bool eaten_food = (cells[new_pos.y * width + new_pos.x] & FLAG_FOOD) == FLAG_FOOD;
insert_first(&(snake_p->g_snake_head), &new_pos, sizeof(place_t));
if(growing)
{
if(eaten_food)
{
cells[new_pos.y * width + new_pos.x] ^= FLAG_FOOD;
g_score+=1;
place_food(cells,width,height);
// 检查是否有食物重置到蛇身上
node_t *curr = snake_p->g_snake_head;
size_t curr_index;
bool is_food = false;
while(curr)
{
curr_index = ((place_t*)curr->data)->x + ((place_t*)curr->data)->y * width;
is_food = (cells[curr_index] & FLAG_FOOD) == FLAG_FOOD;
curr = curr->next;
if(is_food)
{
place_food(cells,width,height);
curr = snake_p->g_snake_head;
}
}
}
else
{
place_t* tail = (remove_last(&(snake_p->g_snake_head)));
size_t tail_index = tail->x + tail->y * width;
cells[tail_index] ^= FLAG_SNAKE;
free(tail);
}
}
else
{
if(eaten_food)
{
cells[new_pos.y * width + new_pos.x] ^= FLAG_FOOD;
g_score+=1;
place_food(cells,width,height);
}
place_t* tail = (remove_last(&(snake_p->g_snake_head)));
size_t tail_index = tail->x + tail->y * width;
cells[tail_index] ^= FLAG_SNAKE;
free(tail);
}
cells[new_pos.y * width + new_pos.x] ^= FLAG_SNAKE;
这样就实现了基本的 updatte
释放内存
void teardown(int* cells, snake_t* snake_p) {
free(cells);
if (snake_p->g_snake_head != NULL) {
node_t* curr = snake_p->g_snake_head;
while(curr->next){
curr = curr->next;
if (curr->prev->data != NULL) {
free(curr->prev->data);
}
free(curr->prev);
}
if (curr->data != NULL) {
free(curr->data);
}
free(curr);
}
}
gameover
==基本 snakeproject 就这样结束了,不过这个只是个大概框架,还有许多地方可以扩展==