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

来源:https://www.cnblogs.com/zhangzhangzhang624531420/archive/2020/01/12/12181744.html
-Advertisement-
Play Games

中國礦業大學信控學院 /*文獻參考*/ 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); //設置控制台游標位置
	   

您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 米娜桑,哦哈喲~ 個人製作,該文章主要講解最近基於 "uni app" 框架編寫的集圖文拖拽等多方位編輯、油墨電子簽名、開放式海報於一體的小程式的製作思路和實現代碼。 目錄 1、完整源碼鏈接 2、實現思路 3、核心代碼 3 1、圖文多方位編輯 3 2、油墨電子簽名 3 3、開放式海報 3 4、小結 ...
  • 內聯樣式表 內部樣式表 外部樣式表 創建一個cssTest.css的css文件 使用外部樣式表 完整測試代碼 css文件 css三種導入方式的優先順序 內聯樣式表 內部樣式表 外部樣式表 ...
  • let和var區別: 1 for(var i=0;i<5;i++){ 2 setTimeout(()=>{ 3 console.log(i);//5個5 4 },100) 5 } 6 console.log(i);//5 7 console.log(' ') 8 9 for(let j=0;j<5; ...
  • 6)靜態方法和prototype(難)例 3.6.1<head> <meta http-equiv="content-type" content="text/html; charset=utf-8"/></head><script> /*note that 馬克-to-win: static var ...
  • 2020-01-11 ant-Design ,Input , onPressEnter 和 onChange 的區別 onChange 輸入內容的回調 onPressEnter 按下回車的回調 需求:看下圖,右邊欄配置開關組件的內容。輸入內容,不想要左邊實時更改 原來的代碼:<Input defau ...
  • 效果圖 index.html <!DOCTYPE html> <html> <head> <title></title> <link rel="stylesheet" type="text/css" href="calc.css"> <script type="text/javascript" sr ...
  • 我們在k8s部署服務時,一般來說一個服務會對應一類pod,而pod通過rs實現副本集,而這些pod的日誌一般有控制台stdout和文件的,一般會把這些日誌最終輸出到elasticsearch里,再通過kabana進行分析,而在實現由pod到elasticsearch(es)時有多種方法,下麵我列舉一 ...
  • 1.代碼生成器: [正反雙向](單表、主表、明細表、樹形表,快速開發利器)freemaker模版技術 ,0個代碼不用寫,生成完整的一個模塊,帶頁面、建表sql腳本、處理類、service等完整模塊2.多數據源:(支持同時連接無數個資料庫,可以不同的模塊連接不同數的據庫)支持N個數據源3.阿裡資料庫連 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...