欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

《数据结构与算法分析》课程设计——贪吃蛇问题

程序员文章站 2022-07-10 21:43:39
中国矿业大学信控学院 /*文献参考*/ https://blog.csdn.net/Fdog_/article/details/102625969 https://blog.csdn.net/DY_1024/article/details/78841757 一、问题描述 以数据结构思想设计实现贪吃蛇 ......

中国矿业大学信控学院

 

 

/*文献参考*/

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 }