《数据结构与算法分析》课程设计——贪吃蛇问题
中国矿业大学信控学院
/*文献参考*/
https://blog.csdn.net/fdog_/article/details/102625969
https://blog.csdn.net/dy_1024/article/details/78841757
一、问题描述
以数据结构思想设计实现贪吃蛇小游戏。
二、需求分析
首先需要考虑如何设计一个win运行窗口来实时显示结果
然后考虑到蛇的身子是一节一节的,此时最容易联想到的数据结构就是顺序表,链表,如果把蛇比做顺序表或者链表,在之后吃到食物的时候,身子肯定会变长,这就涉及到插入的操作,所以为了更高的效率,我们用链表实现我们的蛇的部分,最初我们把蛇身子按照四个结点打印在屏幕。
对于蛇的移动,在屏幕上面蛇的移动看起来是整个身子向前方平移一个单位,但是其原理是我们在屏幕的另一个地方把蛇从新打印一遍,又把之前的蛇身子去除掉。
对于食物的产生,随机的在地图中产生一个节点,在蛇的头坐标和食物的坐标重复的时候,食物消失,蛇的身子加长,也就是蛇的节点数增加一个。
蛇在其中的几种状态,正常状态:蛇头节点的坐标没有和墙的坐标以及自己身子的坐标重合,
被自己杀死:蛇头的坐标和蛇身子的坐标重合,
撞墙:蛇头的坐标和墙的坐标重合。
三、算法设计
1.相关变量。
1 1.相关变量。 2 int judgesum = 0; //判断是否加快 3 int pause = 200000000; //暂停速度(移动速度) 4 int * pj = &judgedirection; //用指针传值判断移动方向 5 nakebody *end = null; //尾节点
2.创建链表 ,贪吃蛇的身体如何保存是游戏的核心,所以我们需要用到链表来保存蛇的身体,这样就可以随时知道蛇身数据。
1 typedef struct snakebody 2 { 3 int x, y; //蛇身的坐标 4 struct snakebody *next;//保存下一个蛇身的地址 5 }snakebody; //通过typedef将 snakebody 替代 struct snakebody
3.记录食物出现的坐标。
1 typedef struct snakexy 2 { 3 int x; 4 int y; 5 }snakexy; //记录食物坐标
4.绘制初始界面和游戏地图,如图所示。
1 #include<windows.h> 2 #define height 20 //设置地图高度 3 #define width 40 //设置地图宽度 4 #define printf printf("■"); 5 #define line printf("\n"); 6 #define empty printf(" "); //因为这三个语句经常用,所以我就定义成了宏 7 void front(); //绘制初始界面 8 void deawmap(); //绘制地图 9 10 void front() 11 { 12 setconsoletextattribute(getstdhandle(std_output_handle), foreground_intensity | foreground_red | foreground_blue);//设置红色和蓝色相加 13 movecursor(18, 15); 14 printf("请等待......"); 15 for (int i = 0; i <= 3000000000; i++) {} 16 system("cls");//清屏处理 17 } 18 void deawmap() 19 { 20 for (int i = 0; i < width; i++)printf line //打印上边框 21 for (int i = 1; i < height - 1; i++) //打印左右边框 22 { 23 for (int j = 0; j < width; j++) 24 { 25 if (j == 0 || j == width - 1 || j == width - 10) 26 { 27 printf 28 if (j == width - 1)line 29 } 30 else empty 31 } 32 } 33 for (int i = 0; i < width; i++)printf line //打印下边框 34 }
setconsoletextattribute()函数是一个api设置字体颜色和背景色的函数。参数表中使用两个属性(属性之间用,隔开),不同于system(),setconsoletextattribute()可以改变界面多种颜色,而system()只能修改为一种!。
5. 初始化蛇身,刚开始蛇不应该只要一个头,所以我们必须创建几个身体。
1 snakebody *phead = null; //存储着整个蛇身 不可更改 2 snakebody *phead_1 = null; //指向蛇身 3 snakebody *pbady = null; //创建节点 4 void isnake(); //初始化蛇身 5 void isnake() 6 { 7 for (int i = 0; i < 5; i++)//初始化蛇身拥有五个长度 8 { 9 pbady = (snakebody*)malloc(sizeof(snakebody));//创建节点 10 pbady->x = 5 - i; 11 pbady->y = 5; 12 if (phead == null) 13 { 14 phead = pbady; 15 } 16 else 17 { 18 end->next = pbady; 19 } 20 pbady->next = null; 21 end = pbady; 22 } 23 phead_1 = phead; 24 while (phead_1->next != null)//打印蛇身 25 { 26 movecursor(phead_1->x, phead_1->y); 27 printf 28 phead_1 = phead_1->next; 29 } 30 }
6.产生食物,随机产生食物,如果和蛇身体重合则再次随机产生食物。
1 #include<time.h> 2 int sum = 0; //计算得分 3 snakexy * food = null; //保存食物位置 4 void foodrand(); //生成食物 5 void foodrand() 6 { 7 srand((int)time(0)); 8 int x = rand() % 27 + 2;//生成随机数 9 int y = rand() % 17 + 2; 10 phead_1 = phead; 11 for (int i = 0; i <= 200; i++) 12 { 13 if (phead_1->x == x && phead_1->y == y) 14 { 15 x = rand() % 27 + 2; 16 y = rand() % 17 + 2; 17 } 18 else 19 { 20 phead_1 = phead_1->next; 21 } 22 if (phead_1->next == null) 23 { 24 break; 25 } 26 } 27 movecursor(x, y); 28 printf 29 food = (snakexy*)malloc(sizeof(snakexy)); 30 food->x = x; 31 food->y = y; 32 movecursor(33, 5); 33 printf(" "); 34 showf(); 35 sum++; 36 setconsoletextattribute(getstdhandle(std_output_handle), foreground_intensity);// 蓝 37 }
rand函数功能为获取一个伪随机数,如要产生[m,n]范围内的随机数num,可用int num=rand()%(n-m+1)+m;
7.游戏刷新和暂停 ,按回车可暂停游戏。
1 int judgedirection = 4; //判断方向 2 void controlmove(); //控制移动和暂停 3 void controlmove() 4 { 5 if (getasynckeystate(vk_up) && 0x8000) 6 { 7 if (judgedirection == 2) 8 { 9 } 10 else 11 { 12 judgedirection = 1; 13 } 14 } 15 if (getasynckeystate(vk_down) && 0x8000) 16 { 17 if (judgedirection == 1) 18 { 19 } 20 else 21 { 22 judgedirection = 2; 23 } 24 } 25 if (getasynckeystate(vk_right) && 0x8000) 26 { 27 if (judgedirection == 3) 28 { 29 } 30 else 31 { 32 judgedirection = 4; 33 } 34 } 35 if (getasynckeystate(vk_left) && 0x8000) 36 { 37 if (judgedirection == 4) 38 { 39 } 40 else 41 { 42 judgedirection = 3; 43 } 44 } 45 if (getasynckeystate(vk_return) && 0x0d)//判断回车 46 { 47 while (1) 48 { 49 if (getasynckeystate(vk_return) && 0x0d)//再次回车退出死循环 50 { 51 break; 52 } 53 } 54 } 55 }
getasynckeystate()确定用户当前是否按下了键盘上的一个键
8.显示分数和难度,更新分数和难度。
1 int sum = 0; //计算得分 2 int hard = 0; //计算难度 3 void showf(); //显分数以及难度 4 void showf() 5 { 6 setconsoletextattribute(getstdhandle(std_output_handle), foreground_blue);// 蓝 7 movecursor(33, 5); 8 printf("得分:%d", sum); 9 movecursor(33, 6); 10 printf("难度:%d", hard); 11 }
9.移动光标 ,游戏不闪的原因就是我们只绘制一次地图 然后用光标定点刷新目标点。
1 void movecursor(int x, int y); //移动光标 2 void movecursor(int x, int y)//设置光标位置(就是输出显示的开始位置) 3 { 4 coord pos = { x * 2,y }; 5 handle output = getstdhandle(std_output_handle);//获得标准输出的句柄 6 setconsolecursorposition(output, pos); //设置光标位置 7 }
coord是windows api中定义的一种结构体
10.检测,检测是否吃到食物,是否撞墙,是否撞到自己。
1 void jfood(); //检测是否吃到食物 2 void jwall(); //检测蛇头是否撞墙 3 void jsnake(); //检测蛇头是否撞到蛇身 4 void jfood() 5 { 6 phead_1 = phead; 7 if (phead_1->x == food->x&&phead_1->y == food->y) 8 { 9 foodrand(); 10 judgesum += 1; 11 if (judgesum == 5) 12 { 13 judgesum = 0;//如果judgesum等于5则从新判断 14 hard += 1; 15 pause -= 20000000;//每成立一次循环减少20000000 16 } 17 while (phead_1->next != null) 18 { 19 phead_1 = phead_1->next; 20 } 21 snakebody *s = (snakebody*)malloc(sizeof(snakebody)); 22 s->x = food->x; 23 s->y = food->y; 24 s->next = null; 25 phead_1->next = s; 26 controlmove(); 27 movecursor(phead_1->x, phead_1->y); 28 printf 29 } 30 //获取食物的坐标和蛇头做对比 31 } 32 void jwall() 33 { 34 if (phead->x == 0 || phead->x == 29 || phead->y == 0 || phead->y == 19) 35 { 36 movecursor(10, 20); 37 setconsoletextattribute(getstdhandle(std_output_handle), foreground_intensity | foreground_red);//设置红色 38 printf("抱歉,你撞到了自己,游戏结束! "); 39 system("pause>nul"); 40 exit(0); 41 } 42 } 43 void jsnake() 44 { 45 phead_1 = phead->next; 46 while (phead_1->next != null) 47 { 48 if ((phead->x == phead_1->x) && (phead->y == phead_1->y)) 49 { 50 movecursor(10, 20); 51 setconsoletextattribute(getstdhandle(std_output_handle), foreground_intensity | foreground_red);//设置红色 52 printf("抱歉,你撞到了自己,游戏结束! "); 53 system("pause>nul"); 54 exit(0); 55 } 56 phead_1 = phead_1->next; 57 } 58 }
11.游戏循环
1 void move(); //游戏运行 2 void move() 3 { 4 while (1) 5 { 6 phead_1 = phead; 7 while (phead_1->next->next != null) 8 { 9 phead_1 = phead_1->next; 10 } 11 phead_1->next = null; 12 for (int i = 0; i < pause; i++) {} 13 controlmove(); 14 movecursor(phead_1->x, phead_1->y); 15 empty 16 //上面为消除尾部 17 snakebody *phead_2 = (snakebody*)malloc(sizeof(snakebody)); 18 if (*pj == 1) 19 { 20 phead_2->x = phead->x; 21 phead_2->y = phead->y - 1; 22 } 23 if (*pj == 2) 24 { 25 phead_2->x = phead->x; 26 phead_2->y = phead->y + 1; 27 } 28 if (*pj == 3) 29 { 30 phead_2->x = phead->x - 1; 31 phead_2->y = phead->y; 32 } 33 if (*pj == 4) 34 { 35 phead_2->x = phead->x + 1; 36 phead_2->y = phead->y; 37 } 38 phead_2->next = phead; 39 phead = phead_2; 40 movecursor(phead_2->x, phead_2->y); 41 printf 42 jfood(); 43 jwall(); 44 jsnake(); 45 movecursor(40, 20); 46 } 47 }
12.释放内存
1 void free(); //释放内存 2 void free() 3 { 4 while (phead->next != null) 5 { 6 phead = phead->next; 7 free(phead); 8 } 9 free(phead); 10 }
附录:完整代码
1 #include<stdio.h> 2 #include<time.h> 3 #include<windows.h> 4 #define height 20 //设置地图高度 5 #define width 40 //设置地图宽度 6 #define printf printf("■"); 7 #define line printf("\n"); 8 #define empty printf(" "); 9 typedef struct snakebody 10 { 11 int x, y;//身体的坐标 12 struct snakebody *next;//结构指针 13 }snakebody;//先来创建保持身体的链表,贪吃蛇的核心代码就是该如何保存蛇的身体 14 typedef struct snakexy 15 { 16 int x; 17 int y; 18 }snakexy; //记录食物坐标 19 int sum = 0; //计算得分 20 int judgesum = 0; //判断是否加快 21 int hard = 0; //计算难度 22 int pause = 200000000; //暂停速度(移动速度) 23 int judgedirection = 4; //判断方向 24 int * pj = &judgedirection; //用指针传值判断移动方向 25 snakebody *phead = null; //存储着整个蛇身 不可更改 26 snakebody *phead_1 = null; //指向蛇身 27 snakebody *pbady = null; //创建节点 28 snakebody *end = null; //尾节点 29 snakexy * food = null; //保存食物位置 30 void front(); //游戏开始页面1 31 void jfood(); //检测是否吃到食物1 32 void jwall(); //检测蛇头是否撞墙1 33 void jsnake(); //检测蛇头是否撞到蛇身1 34 void isnake(); //初始化蛇身1 35 void deawmap(); //绘制地图1 36 void foodrand(); //生成食物1 37 void controlmove(); //控制移动和暂停1 38 void movecursor(int x, int y); //移动光标1 39 void move(); //游戏运行1 40 void showf(); //显分数以及难度1 41 void free(); //释放内存 42 int main() 43 { 44 front(); 45 setconsoletextattribute(getstdhandle(std_output_handle), foreground_green);//绿 46 deawmap(); 47 showf(); 48 setconsoletextattribute(getstdhandle(std_output_handle), foreground_intensity);// 暗白 49 movecursor(34, 10); 50 printf("↑"); 51 movecursor(31, 11); 52 printf("使用←↓→来控制"); 53 movecursor(31, 12); 54 printf("蛇的移动,撞墙游"); 55 movecursor(31, 13); 56 printf("戏结束,每5分增 "); 57 movecursor(31, 14); 58 printf("一个难度(速度)"); 59 isnake(); 60 foodrand(); 61 movecursor(40, 20); 62 move(); 63 return 0; 64 } 65 void front() 66 { 67 setconsoletextattribute(getstdhandle(std_output_handle), foreground_intensity | foreground_red | foreground_blue);//设置红色和蓝色相加 68 movecursor(18, 15); 69 printf("请等待......"); 70 for (int i = 0; i <= 3000000000; i++) {} 71 system("cls"); 72 } 73 void deawmap() 74 { 75 for (int i = 0; i < width; i++)printf line //上边框 76 for (int i = 1; i < height - 1; i++) //打印左右边框 77 { 78 for (int j = 0; j < width; j++) 79 { 80 if (j == 0 || j == width - 1 || j == width - 10) 81 { 82 printf 83 if (j == width - 1)line 84 } 85 else empty 86 } 87 } 88 for (int i = 0; i < width; i++)printf line //下边框 89 } 90 void movecursor(int x, int y)//设置光标位置(就是输出显示的开始位置) 91 { 92 /* coord是windows api中定义的一种结构体 93 * typedef struct _coord 94 * { 95 * short x; 96 * short y; 97 * } coord; 98 * */ 99 coord pos = { x * 2,y }; 100 handle output = getstdhandle(std_output_handle);//获得 标准输出的句柄 101 setconsolecursorposition(output, pos); //设置控制台光标位置 102 } 103 void foodrand() 104 { 105 srand((int)time(0)); 106 int x = rand() % 27 + 2; 107 int y = rand() % 17 + 2; 108 phead_1 = phead; 109 for (int i = 0; i <= 200; i++) 110 { 111 if (phead_1->x == x && phead_1->y == y) 112 { 113 x = rand() % 27 + 2; 114 y = rand() % 17 + 2; 115 } 116 else 117 { 118 phead_1 = phead_1->next; 119 } 120 if (phead_1->next == null) 121 { 122 break; 123 } 124 } 125 movecursor(x, y); 126 printf 127 food = (snakexy*)malloc(sizeof(snakexy)); 128 food->x = x; 129 food->y = y; 130 movecursor(33, 5); 131 printf(" "); 132 showf(); 133 sum++; 134 setconsoletextattribute(getstdhandle(std_output_handle), foreground_intensity);// 蓝 135 } 136 void controlmove() 137 { 138 if (getasynckeystate(vk_up) && 0x8000) 139 { 140 if (judgedirection == 2) 141 { 142 } 143 else 144 { 145 judgedirection = 1; 146 } 147 } 148 if (getasynckeystate(vk_down) && 0x8000) 149 { 150 if (judgedirection == 1) 151 { 152 } 153 else 154 { 155 judgedirection = 2; 156 } 157 } 158 if (getasynckeystate(vk_right) && 0x8000) 159 { 160 if (judgedirection == 3) 161 { 162 } 163 else 164 { 165 judgedirection = 4; 166 } 167 } 168 if (getasynckeystate(vk_left) && 0x8000) 169 { 170 if (judgedirection == 4) 171 { 172 } 173 else 174 { 175 judgedirection = 3; 176 } 177 } 178 if (getasynckeystate(vk_return) && 0x0d) 179 { 180 while (1) 181 { 182 if (getasynckeystate(vk_return) && 0x0d) 183 { 184 break; 185 } 186 } 187 } 188 } 189 void isnake() 190 { 191 for (int i = 0; i < 5; i++) 192 { 193 pbady = (snakebody*)malloc(sizeof(snakebody)); 194 pbady->x = 5 - i; 195 pbady->y = 5; 196 if (phead == null) 197 { 198 phead = pbady; 199 } 200 else 201 { 202 end->next = pbady; 203 } 204 pbady->next = null; 205 end = pbady; 206 } 207 phead_1 = phead; 208 while (phead_1->next != null) 209 { 210 movecursor(phead_1->x, phead_1->y); 211 printf 212 phead_1 = phead_1->next; 213 } 214 } 215 void move() 216 { 217 while (1) 218 { 219 phead_1 = phead; 220 while (phead_1->next->next != null) 221 { 222 phead_1 = phead_1->next; 223 } 224 phead_1->next = null; 225 for (int i = 0; i < pause; i++) {} 226 controlmove(); 227 movecursor(phead_1->x, phead_1->y); 228 empty 229 //上面为消除尾部 230 snakebody *phead_2 = (snakebody*)malloc(sizeof(snakebody)); 231 if (*pj == 1) 232 { 233 phead_2->x = phead->x; 234 phead_2->y = phead->y - 1; 235 } 236 if (*pj == 2) 237 { 238 phead_2->x = phead->x; 239 phead_2->y = phead->y + 1; 240 } 241 if (*pj == 3) 242 { 243 phead_2->x = phead->x - 1; 244 phead_2->y = phead->y; 245 } 246 if (*pj == 4) 247 { 248 phead_2->x = phead->x + 1; 249 phead_2->y = phead->y; 250 } 251 phead_2->next = phead; 252 phead = phead_2; 253 movecursor(phead_2->x, phead_2->y); 254 printf 255 jfood(); 256 jwall(); 257 jsnake(); 258 movecursor(40, 20); 259 } 260 } 261 void jfood() 262 { 263 phead_1 = phead; 264 if (phead_1->x == food->x&&phead_1->y == food->y) 265 { 266 foodrand(); 267 judgesum += 1; 268 if (judgesum == 5) 269 { 270 judgesum = 0; 271 hard += 1; 272 pause -= 20000000; 273 } 274 while (phead_1->next != null) 275 { 276 phead_1 = phead_1->next; 277 } 278 snakebody *s = (snakebody*)malloc(sizeof(snakebody)); 279 s->x = food->x; 280 s->y = food->y; 281 s->next = null; 282 phead_1->next = s; 283 controlmove(); 284 movecursor(phead_1->x, phead_1->y); 285 printf 286 } 287 //获取食物的坐标和蛇头做对比 288 } 289 void jwall() 290 { 291 if (phead->x == 0 || phead->x == 29 || phead->y == 0 || phead->y == 19) 292 { 293 movecursor(10, 20); 294 setconsoletextattribute(getstdhandle(std_output_handle), foreground_intensity | foreground_red);//设置红色 295 printf("抱歉,你撞到了自己,游戏结束! "); 296 system("pause>nul"); 297 exit(0); 298 } 299 } 300 void jsnake() 301 { 302 phead_1 = phead->next; 303 while (phead_1->next != null) 304 { 305 if ((phead->x == phead_1->x) && (phead->y == phead_1->y)) 306 { 307 movecursor(10, 20); 308 setconsoletextattribute(getstdhandle(std_output_handle), foreground_intensity | foreground_red);//设置红色 309 printf("抱歉,你撞到了自己,游戏结束! "); 310 system("pause>nul"); 311 exit(0); 312 } 313 phead_1 = phead_1->next; 314 } 315 } 316 void showf() 317 { 318 setconsoletextattribute(getstdhandle(std_output_handle), foreground_blue);// 蓝 319 movecursor(33, 5); 320 printf("得分:%d", sum); 321 movecursor(33, 6); 322 printf("难度:%d", hard); 323 } 324 void free() 325 { 326 while (phead->next != null) 327 { 328 phead = phead->next; 329 free(phead); 330 } 331 free(phead); 332 }
推荐阅读
-
Python cookbook(数据结构与算法)字典相关计算问题示例
-
Python数据结构与算法之使用队列解决小猫钓鱼问题
-
数据结构与算法分析笔记02:链表
-
Java数据结构与算法:图、图的概念、深度优先搜索DFS、广度优先搜索BFS、思路分析、代码实现
-
《数据结构与算法分析》课程设计——迷宫问题
-
《数据结构与算法分析》课程设计——贪吃蛇问题
-
Java数据结构与算法:数组模拟队列、环形队列、思路分析
-
算法与数据结构 如何分析时间复杂度和空间复杂度
-
JavaScript数据结构与算法之检索算法实例分析【顺序查找、最大最小值、自组织查询】
-
【数据结构与算法Python描述】——Python列表实现原理探究及常用操作时间复杂度分析