# GreedySnake **Repository Path**: fentende/greedy-snake ## Basic Information - **Project Name**: GreedySnake - **Description**: No description available - **Primary Language**: C - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2020-12-31 - **Last Updated**: 2020-12-31 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # 贪吃蛇 ## 1. 任务一:会动的蛇 本次任务要求完成实现贪吃蛇的移动。因此可以将本次的程序分为 - 游戏开始 - 游戏进行 - 游戏结束 ### (1).游戏开始 本次设计游戏较为简单,因此游戏开始阶段并无太大难点,仅仅需要完成初始化数据即可。即设置地图、蛇身长度数据等等,完成准备工作。 ``` //记录游戏地图信息 char map[12][12] = { "************", "*XXXXH *", "* *", "* *", "* *", "* *", "* *", "* *", "* *", "* *", "* *", "************" } //蛇的位置信息与长度, 索引0为蛇头位置。 int snakeX[SNAKE_MAX_LENGTH] = {5, 4, 3, 2, 1}; int snakeY[SNAKE_MAX_LENGTH] = {1, 1, 1, 1, 1}; int snakeLength = 5; ``` ### (2).游戏进行 当游戏进行时,可以分为如下四个动作: - 根据地图信息与贪吃蛇信息打印数据,直观展现游戏状态 - 游戏暂停,等待玩家输入移动命令,并确保命令有效 - 根据玩家命令,贪吃蛇进行相应移动 - 判断游戏是否结束 现在开始依次实现以上功能 > 根据地图信息与贪吃蛇信息打印数据 ``` void output() { //两层循环依次打印二维数组 for(int i = 0; i < 12; i++) { for(int j = 0; j < 12; j++) { putchar(map[i][j]); } putchar('\n'); } } ``` >游戏暂停,等待玩家输入移动命令,并确保命令有效 ``` int over = 0; //判断游戏是否结束的标志: 1为结束, 0为继续 char ch, oldCh = 'D'; //保留玩家输入信息 : ch为这次输入, oldCh为上一次输入 //oldCh初始为'D'确保玩家第一次移动不能左移 output(); while(!over) { scanf("%c", &ch); getchar(); //处理玩家输入后的回车键 //根据ch决定接下来贪吃蛇的行动 switch(ch) { case 'a': case 'A': //防止玩家掉头 if( oldCh == 'D' || oldCh == 'd' ) { continue; } snakeMove(-1, 0); //贪吃蛇左移 break; case 'd': case 'D': if( oldCh == 'A' || oldCh == 'a') { continue; } snakeMove(1, 0); //贪吃蛇右移 break; case 'w': case 'W': if( oldCh == 'S' || oldCh == 's') { continue; } snakeMove(0, -1); //贪吃蛇上移 break; case 's': case 'S': if( oldCh == 'W' || oldCh == 'w') { continue; } snakeMove(0, 1); //贪吃蛇下移 break; default: continue; //防止玩家输入非正常命令 } oldCh = ch; //记录玩家输入信息 } ``` > 根据玩家命令,贪吃蛇进行相应移动 ``` void snakeMove(int dx, int dy) { //更新地图信息中贪吃蛇身体 map[snakeY[snakeLength-1]][snakeX[snakeLength-1]] = BLANK_CELL; map[snakeY[0]][snakeX[0]] = SNAKE_BODY; //更新贪吃蛇身体新位置 for(int i = snakeLength - 1; i > 0; i--) { snakeY[i] = snakeY[i - 1]; snakeX[i] = snakeX[i - 1]; } //更新贪吃蛇头部新位置 snakeY[0] += dy; snakeX[0] += dx; //更新地图信息中贪吃蛇头部 map[snakeY[0]][snakeX[0]] = SNAKE_HEAD; } ``` > 判断游戏是否结束。为了判断游戏结束,可以在移动贪吃蛇时判断,因此对snakeMove()函数添加判断。 ``` void snakeMove(int dx, int dy) { if(map[snakeY[0] + dy][snakeX[0] + dx] != BLANK_CELL ) { over = 1; //设置游戏结束标志 return; } ... ... ... } ``` ### (3).游戏结束 当over为1即游戏结束时,打印Game Over! ``` void gameover() { printf("Game Over!\n"); } ``` ### (4).综合 至此就完成了贪吃蛇的所有设计,将以上代码整合如下 ``` int main() { int dx, dy; //贪吃蛇位置变化参数,根据ch设置 char ch, oldCh = 'D'; //保留玩家输入信息 : ch为这次输入, oldCh为上一次输入 //oldCh初始为'D'确保玩家第一次移动不能左移 output(); while(!over) { scanf("%c", &ch); getchar(); //处理玩家输入后的回车键 //根据ch决定接下来贪吃蛇的行动 switch(ch) { case 'a': case 'A': //防止玩家掉头 if( oldCh == 'D' || oldCh == 'd' ) { continue; } snakeMove(-1, 0); //贪吃蛇左移 break; case 'd': case 'D': if( oldCh == 'A' || oldCh == 'a') { continue; } snakeMove(1, 0); //贪吃蛇右移 break; case 'w': case 'W': if( oldCh == 'S' || oldCh == 's') { continue; } snakeMove(0, -1); //贪吃蛇上移 break; case 's': case 'S': if( oldCh == 'W' || oldCh == 'w') { continue; } snakeMove(0, 1); //贪吃蛇下移 break; default: continue; //防止玩家输入非正常命令 } oldCh = ch; //记录玩家输入信息 output(); } gameover(); } ``` ## 2.任务二:会吃的蛇 会吃的蛇相对于会动的蛇而言也就算在“游戏进行”的阶段有些变化,在原先的基础上增加了行为,即执行如下循环 - 在地图随机生成一个食物 - 根据地图信息与贪吃蛇信息打印数据,直观展现游戏状态 - 游戏暂停,等待玩家输入移动命令,并确保命令有效 - 根据玩家命令,贪吃蛇进行相应移动 - 判断贪吃蛇移动后状态,并处理 - 若吃到食物,则增加长度并生成新食物 - 若碰到障碍物则游戏结束 因此只需增加“在地图随机生成一个食物”动作与修改判断游戏结束的动作即可 ### (1).在地图随机生成食物 ``` void putMoney() { int randomX, randomY; //食物生成位置 srand((int)time(NULL)); while( 1 ) { //不断循环,直到食物成功放置为止 randomX = rand() % 10 + 1; randomY = rand() % 10 + 1; if( map[randomY][randomX] == BLANK_CELL ) { map[randomY][randomX] = SNAKE_FOOD; break; } } } ``` ### (2).改进状态判断 原先的判断语句已经不能满足现在的需求了,因此需要将其修改 ``` void snakeMove(int dx, int dy) { if(map[snakeY[0] + dy][snakeX[0] + dx] != BLANK_CELL ) { over = 1; //设置游戏结束标志 return; } map[snakeY[snakeLength-1]][snakeX[snakeLength-1]] = BLANK_CELL; map[snakeY[0]][snakeX[0]] = SNAKE_BODY; ... ... ... } ``` 修改如下 ``` void snakeMove(int dx, int dy) { int i, setFood = 0; //setFood判断是否放置食物 if(map[snakeY[0] + dy][snakeX[0] + dx] == SNAKE_BODY || map[snakeY[0] + dy][snakeX[0] + dx] == WALL_CELL) { over = 1; //设置游戏结束标志 return; } else if(map[snakeY[0] + dy][snakeX[0] + dx] == SNAKE_FOOD) { //更新贪吃蛇的属性 snakeLength++; //放置新的食物 setFood = 1; } else { //更新地图信息中贪吃蛇身体 map[snakeY[snakeLength-1]][snakeX[snakeLength-1]] = BLANK_CELL; } map[snakeY[0]][snakeX[0]] = SNAKE_BODY; ... ... ... //在完全更新完地图信息后才能开始放置新食物 if(setFood) { putMoney(); } } ``` 至此就完成了此次任务