中國礦業大學信控學院算法
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 }