《數據結構與算法分析》課程設計——貪吃蛇問題

中國礦業大學信控學院算法

 

 

/*文獻參考*/

https://blog.csdn.net/Fdog_/article/details/102625969

http://www.javashuo.com/article/p-wsqyakus-bh.html


 

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 }
相關文章
相關標籤/搜索