# snake **Repository Path**: hez2010/snake ## Basic Information - **Project Name**: snake - **Description**: 贪吃蛇 - **Primary Language**: Unknown - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2020-12-30 - **Last Updated**: 2020-12-31 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # 贪吃蛇 2018 软件工程 17364025 贺恩泽 ## 会动的蛇 ### 整体框架 整体流程为: 1. 输出地图 2. 如果游戏未结束 1. 等待用户输入并移动蛇 2. 输出移动后的地图 3. 重复上述过程 3. 否则输出 `Game Over!!` 因此,会动的蛇设计为以下几个函数: - `print_map` 输出地图 - `snake_mode` 移动蛇 - `game_over` 结束游戏 用代码表示为: ```c print_map(); while (!gameOver) { input = getchar(); switch (input) { case 'A': // 向左移动 snake_move(0, -1); break; case 'D': // 向右移动 snake_move(0, 1); break; case 'W': // 向上移动 snake_move(-1, 0); break; case 'S': // 向下移动 snake_move(1, 0); break; default: continue; } print_map(); } ``` ### 输出地图 首先利用打表的方式存储空地图: ```c // 空地图 char map[12][12] = { "************", "* *", "* *", "* *", "* *", "* *", "* *", "* *", "* *", "* *", "* *", "************" }; ``` 为了记录蛇的位置,可以利用数组分别保存蛇的行列坐标: ```c // 蛇身体的行坐标 int snakeX[SNAKE_MAX_LEN] = { 1,1,1,1,1 }; // 蛇身体的列坐标 int snakeY[SNAKE_MAX_LEN] = { 1,2,3,4,5 }; ``` 这样一来,只需要判断坐标点是否记录在 `snakeX` 和 `snakeY` 中即可知道对应位置是不是蛇: ```c if (snakeX[k] == i && snakeY[k] == j) { ch = k == snakeLength - 1 ? SNAKE_HEAD : SNAKE_BODY; } ``` 然后根据位置输出对应的字符即可。 ### 移动蛇 移动蛇的步骤为: 1. 计算蛇头的新位置 ```c int i, x = snakeX[snakeLength - 1] + dx, y = snakeY[snakeLength - 1] + dy; ``` 2. 判断是否撞到墙壁,若撞到则游戏结束 ```c if (x > 10 || x < 1 || y > 10 || y < 1) { game_over(); return; } ``` 3. 判断是否撞到自身,若撞到则游戏结束 ```c for (i = 1; i < snakeLength; i++) { if (snakeX[i] == x && snakeY[i] == y) { game_over(); return; } } ``` 4. 更新蛇的位置 ```c for (i = 0; i < snakeLength - 1; i++) { snakeX[i] = snakeX[i + 1]; snakeY[i] = snakeY[i + 1]; } snakeX[snakeLength - 1] = x; snakeY[snakeLength - 1] = y; ``` ### 结束游戏 程序中利用一个全局变量 `gameOver` 指示游戏是否结束,因此结束游戏只需要将这个变量设置为 `1` 即可。 ```c void game_over(void) { gameOver = 1; } ``` ## 会吃的蛇 ### 整体框架 会吃的蛇相比会动的蛇多了食物的生成和吃的行为,流程如下: 1. 生成食物 2. 输出地图 3. 如果游戏未结束 1. 等待用户输入并移动蛇 2. 如果吃到食物则更新蛇的长度并生成新食物 3. 输出移动后的地图 4. 重复上述过程 4. 否则输出 `Game Over!!` 因此,会吃的蛇设计为以下几个函数: - `print_map` 输出地图 - `snake_mode` 移动蛇 - `game_over` 结束游戏 - `generate_food` 生成食物 用代码表示为: ```c generate_food(); print_map(); while (!gameOver) { input = getchar(); switch (input) { case 'A': // 向左移动 snake_move(0, -1); break; case 'D': // 向右移动 snake_move(0, 1); break; case 'W': // 向上移动 snake_move(-1, 0); break; case 'S': // 向下移动 snake_move(1, 0); break; default: continue; } print_map(); } ``` 由于其他部分和会动的蛇基本一致,接下来只对新增的部分进行说明。 ### 生成食物 生成的食物需要确保食物出现在空白的位置,这个位置不能和蛇所在的位置重叠,因此只需要不断获取随机数直到找到满足要求的位置即可。 ```c void generate_food(void) { int isSnakeBody, i; do { isSnakeBody = 0; foodX = rand() % 10 + 1; foodY = rand() % 10 + 1; for (i = 0; i < snakeLength; i++) { if (snakeX[i] == foodX && snakeY[i] == foodY) { isSnakeBody = 1; break; } } } while (isSnakeBody); } ``` ### 移动蛇 这一部分相比会动的蛇,多了判断是否吃到食物的过程,如果吃到了食物需要加长蛇的身体并重新生成食物。 ```c // 判断是否吃到食物 if (x == foodX && y == foodY) { // 若蛇超过最长长度则游戏结束 if (snakeLength + 1 == SNAKE_MAX_LEN) { game_over(); return; } snakeX[snakeLength] = x; snakeY[snakeLength] = y; snakeLength++; generate_food(); return; } ``` ### 输出地图 相比会动的蛇多了判断是否为食物的逻辑,其余没有改变。 ```c if (i == foodX && j == foodY) { ch = SNAKE_FOOD; } ``` ## 构建方法 ```bash mkdir build && cd build cmake .. make ``` 然后运行 `build` 目录中的 `snake_move` 或 `snake_eat` 程序即可。