中国矿业大学信控学院算法
1、问题描述数据结构
以数据结构思想设计实现贪吃蛇小游戏。函数
2、需求分析字体
首先须要考虑如何设计一个win运行窗口来实时显示结果spa
而后考虑到蛇的身子是一节一节的,此时最容易联想到的数据结构就是顺序表,链表,若是把蛇比作顺序表或者链表,在以后吃到食物的时候,身子确定会变长,这就涉及到插入的操做,因此为了更高的效率,咱们用链表实现咱们的蛇的部分,最初咱们把蛇身子按照四个结点打印在屏幕。.net
对于蛇的移动,在屏幕上面蛇的移动看起来是整个身子向前方平移一个单位,可是其原理是咱们在屏幕的另外一个地方把蛇重新打印一遍,又把以前的蛇身子去除掉。设计
对于食物的产生,随机的在地图中产生一个节点,在蛇的头坐标和食物的坐标重复的时候,食物消失,蛇的身子加长,也就是蛇的节点数增长一个。指针
蛇在其中的几种状态,正常状态:蛇头节点的坐标没有和墙的坐标以及本身身子的坐标重合,code
被本身杀死:蛇头的坐标和蛇身子的坐标重合,blog
撞墙:蛇头的坐标和墙的坐标重合。
3、算法设计
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 }