天天看點

c++提高學習筆記——05-c++STLday13_貪吃蛇案例

在學習c++提高-STL總結了筆記,并分享出來。有問題請及時聯系部落客:​​Alliswell_WP​​,轉載請注明出處。

05-c++STLday13_貪吃蛇案例

目錄:一、上節作業——綜合案例(學校演講比賽)二、貪食蛇案例

一、上節作業——綜合案例(學校演講比賽)

》比賽規則:某市舉行一場演講比賽( speech_contest ),共有24個人參加。比賽共三輪,前兩輪為淘汰賽,第三輪為決賽。

》比賽方式:分組比賽,每組6個人;選手每次要随機分組,進行比賽;

>第一輪分為4個小組,每組6個人。比如編号為: 100-123.  整體進行抽簽(draw)後順序演講。當小組演講完後,淘汰組内排名最後的三個選手,然後繼續下一個小組的比賽。

>第二輪分為2個小組,每組6人。比賽完畢,淘汰組内排名最後的三個選手,然後繼續下一個小組的比賽。

>第三輪隻剩下1組6個人,本輪為決賽,選出前三名。

》比賽評分:10個評委打分,去除最低、最高分,求平均分每個選手演講完由10個評委分别打分。該選手的最終得分是去掉一個最高分和一個最低分,求得剩下的8個成績的平均分。選手的名次按得分降序排列。用STL程式設計,求解這個問題1)請列印出所有選手的名字與參賽号,并以參賽号的升序排列。2)列印每一輪比賽後,小組比賽成績和小組晉級名單

需求分析:

1) 産生選手 ( ABCDEFGHIJKLMNOPQRSTUVWX ) 姓名、得分;選手編号

2) 第1輪    選手抽簽 選手比賽 檢視比賽結果

3) 第2輪    選手抽簽 選手比賽 檢視比賽結果

4) 第3輪    選手抽簽 選手比賽 檢視比賽結果實作思路:

需要把選手資訊、選手得分資訊、選手比賽抽簽資訊、選手的晉級資訊儲存在容器中,需要涉及到各個容器的選型。

選手可以設計一個類Speaker(姓名和得分)

所有選手的編号可以單獨放在一個vector容器中,做抽簽用

所有選手編号和選手資訊,可以放在容器内:map<int, Speaker>

所有選手的編号名單,可以放在容器:vecter<int> v1中

第1輪晉級編号名單,可以放在容器vecter<int> v2中

第2輪晉級編号名單,可以放在容器vecter<int> v3中

第3輪前三名名單,可以放在容器vecter<int> v4中

每個小組的比賽得分資訊,按照從大到小的順序放在multimap<成績, 編号, greater<int>>中

每個選手的得分,可以放在容器deque<int> dscore; 友善去除最低最高分.

代碼如下:

1 #define _CRT_SECURE_NO_WARNINGS
  2 #include<iostream>
  3 using namespace std;
  4 #include<vector>
  5 #include<map>
  6 #include<string>
  7 #include<algorithm>
  8 #include<deque>
  9 #include<numric>
 10 #include<functional>
 11 #inlcude<ctime>
 12 
 13 /*
 14 需求分析:
 15 1) 産生選手 ( ABCDEFGHIJKLMNOPQRSTUVWX ) 姓名、得分;選手編号
 16 
 17 2) 第1輪    選手抽簽 選手比賽 檢視比賽結果 
 18 3) 第2輪    選手抽簽 選手比賽 檢視比賽結果
 19 4) 第3輪    選手抽簽 選手比賽 檢視比賽結果
 20 
 21 */
 22 class Speaker
 23 {
 24 public:
 25     
 26     string m_Name;//姓名
 27     int m_Score[3];//得分數組
 28 };
 29 
 30 void createSpeaker(vector<int>& v, map<int, Speaker>& m)
 31 {
 32     string nameSeed = "ABCDEFGHIJKLMNOPQRSTUVWX";
 33     for(int i = 0; i < nameSeed.size(); i++)
 34     {
 35         string name = "選手";
 36         name += nameSeed[i];
 37         
 38         Speaker sp;
 39         sp.m_Name = name;
 40         for(int j = 0; j < 3; j++)
 41         {
 42             sp.m_Score[j] = 0;
 43         }
 44         
 45         v.push_back(i + 100);//編号100~123
 46         m.insert(make_pair(i + 100, sp));
 47     }
 48     
 49 }
 50 
 51 //抽簽
 52 void speechDraw(vector<int>v)
 53 {
 54     //洗牌
 55     random_shuffle(v.begin(), v.end());
 56 }
 57 
 58 //index存放第幾輪,v1存放比賽選手編号,m是選手編号和具體選手,v2存放晉級選手編号容器
 59 void speechContest(int index,vector<int>& v1, map<int, Speaker>& m, vector<int>& v2)
 60 {
 61     //臨時容器:key 分數,value 編号
 62     multimap<int, int, greater<int>>groupMap;
 63     int num = 0;
 64     for(vector<int>::iterator it = v1.begin(); it != v1.end(); it++)
 65     {
 66         num++;
 67         deque<int>d;
 68         for(int i = 0; i < 10; i++)
 69         {
 70             int score = rand() % 41 + 60;//60~100
 71             d.push_back(score);
 72         }
 73         //排序
 74         sort(d.begin(), d.end());
 75         //去除最高最低分
 76         d.pop_back();
 77         d.pop_front();
 78         //累積分數
 79         int sum = accumulate(d.begin(), d.end(), 0);
 80         int avg = sum / d.size();
 81         
 82         //将平均分放入到m容器中
 83         m[*it].m_Score[index - 1] = avg;
 84         
 85         //每6個人,取前三名晉級
 86         //臨時容器,儲存6個人
 87         //臨時容器,存入資料
 88         groupMap.insert(make_pair(avg, *it));
 89         
 90         if(num % 6 == 0)
 91         {
 92             /*
 93             cout << "各個小組比賽成績如下:" << endl;
 94             for(multimap<int, int, greater>:: iterator mit = groupMap.begin(); mit != groupMap.end(); mit++)
 95             {
 96                 cout << "選手編号:" << mit->second << "  姓名:" << m[mit->second].m_Name << "  得分:" << m[mit->second].m_Score[index-1] << endl;
 97             }
 98             */
 99             
100             //取前三名
101             int count = 0;
102             for(multimap<int, int, greater>:: iterator mit = groupMap.begin(); mit != groupMap.end(), count < 3; mit++, count++)
103             {
104                 //晉級容器,擷取資料
105                 v2.push_back(mit->second);
106             }
107             
108             groupMap.clear();//清空臨時容器
109         }
110     }
111     
112     
113     
114 }
115 
116 
117 void showScore(int index, vector<int>& v, map<int, Speaker>& m)
118 {
119     cout << "第" << index << "輪,比賽成績如下:" << endl;
120     
121     for(map<int, Speaker>::iterator it = m.begin(); it != m.end(); it++)
122     {
123         cout << "選手編号:" << it->first << "姓名:" << it->second.m_Name << "分數:" << it->second.m_Score[index-1] << endl;
124     }
125     
126     cout << "晉級選手編号" << endl;
127     for(vector<int>::iterator it = v.begin(); it != v.end(); it++)
128     {
129         cout << *it << endl;
130     }
131     
132 }
133 
134 
135 
136 void test01()
137 {
138     //最後添加随機數種子
139     srand((unsigned int)time(NULL));
140     
141     vector<int>v1;//選手編号
142     
143     map<int, Speaker>m;//存放選手編号和對應的具體的選手
144     
145     //建立選手
146     createSpeaker(v1, m);
147     
148     /*
149     //測試
150     for(map<int, Speaker>::iterator it = m.begin(); it!= m.end(); it++)
151     {
152         cout << "選手編号:" << it->first << "姓名:" << it->second.m_Name << endl;
153     }
154     */
155     
156     //抽簽
157     speechDraw(v1);
158     
159     vector<int>v2;//進入下一輪比賽的人員編号
160     
161     //第一輪比賽
162     speechContest(1, v1, m, v2);
163     
164     //顯示比賽結果
165     showScore(1, v2, m);//輪數 晉級編号 具體人員資訊
166     
167     //第二輪比賽
168     speechDraw(v2);
169     vector<int>v3;//進入下一輪比賽的人員編号
170     speechContest(2, v2, m, v3);
171     showScore(2, v3, m);
172     
173     //第三輪比賽
174     speechDraw(v3);
175     vector<int>v4;//進入下一輪比賽的人員編号
176     speechContest(3, v3, m, v4);
177     showScore(3, v4, m);
178     
179 }
180 
181 int main()
182 {
183     test01();
184     
185     system("pause");
186     return EXIT_SUCCESS;
187 }      

二、貪食蛇案例

總結:

1、玩法介紹

2、具體實作

3、牆子產品

3.1 二維數組維護,遊戲内容

3.2 初始化二維數組:initwall

3.3 畫出牆壁 drawwall

3.4 提供對外接口setwall、getwall

3.5測試

4、蛇子產品

4.1 初始化蛇

4.2 銷毀所有結點

4.3 添加新結點

5、食物子產品

5.1 foodX、foodY位置

5.2 setFood對外接口,可以設定食物

5.3 随機出兩個可以放置的位置,設定#

6、删除結點和移動蛇的封裝

6.1 删除結點,通過兩個臨時結點,删除尾結點

6.2 移動,判斷使用者輸入内容,然後進行移動操作

7、接收使用者輸入

7.1 接收一個字元,讓蛇移動第一步

7.2 使用者輸入按鍵後,進行自動移動

8、解決bug

8.1 按鍵沖突

8.2 180度不可以轉

8.3 死亡撞牆,多走一步

8.4 循環追尾,不要進入死亡判斷

9、輔助玩法

9.1 難度設定,根據蛇身段,産生不同難度

9.2 分數設定

10、優化遊戲

代碼如下:

game.cpp

1 #define _CRT_SECURE_NO_WARNINGS
  2 #include<iostream>
  3 using namespace std;
  4 #include"wall.h"
  5 #include"snake.h"
  6 #include"food.h"
  7 #include<ctime>
  8 #include<conio.h>
  9 #include<window.h>
 10 
 11 void gotoxy(HANDLE hOut, int x, int y)
 12 {
 13     COORD pos;
 14     pos.X = x;             //橫坐标
 15     pos.Y = y;            //縱坐标
 16     SetConsoleCursorPosition(hOut, pos);
 17 }
 18 HANDLE hOut = GetStdHandle(STD_OUTPUT_HANDLE);//定義顯示器句柄變量
 19 
 20 
 21 
 22 void test01()
 23 {
 24     //添加随機種子
 25     srand((unsigned int)time(NULL));
 26     
 27     //是否死亡的辨別
 28     bool isDead = false;
 29     
 30     //上一步的辨別
 31     char preKey = NULL;
 32     
 33     Wall wall;
 34     wall.initWall();
 35     wall.drawWall();
 36     
 37     /*
 38     //測試
 39     wall.setWall(5, 4, '=');
 40     wall.setWall(5, 5, '=');
 41     wall.setWall(5, 6, '@');
 42     
 43     wall.drawWall();
 44     
 45     cout << wall.getWall(0, 0) << endl;
 46     cout << wall.getWall(5, 4) << endl;
 47     cout << wall.getWall(5, 6) << endl;
 48     cout << wall.getWall(1, 1) << endl;
 49     */
 50     Food food(wall);
 51     food.setFood();
 52     
 53     Snake snake(wall, food);
 54     snake.initSnake();
 55     
 56     /*
 57     //測試
 58     snake.move('w');
 59     snake.move('w');
 60     snake.move('a');
 61     */
 62     //snake.delPoint();//測試删結點
 63     
 64     //wall.drawWall();
 65     gotoxy(hOut, 0, Wall::ROW);
 66     
 67     cout << "得分:" << snake.getScore() << "分" << endl;
 68     
 69     //gotoxy(hOut, 10, 5);//坐标系相反;y*2,x
 70     
 71     //接收使用者的輸入
 72     while(!isDead)
 73     {
 74         char key = _getch();
 75         
 76         //判斷,如果是第一次按了左鍵,才不能激活遊戲
 77         //判斷上一次移動方向
 78         if(preKey == NULL && key == snake.LEFT)
 79         {
 80             continue;
 81         }
 82         
 83         do
 84         {
 85             if(key == snake.UP || key == snake.DOWN || key == snake.LEFT || key == snake.RIGHT)
 86             {
 87                 //判斷本次按鍵是否與上次沖突
 88                 if((key == snake.LEFT && preKey == snake.RIGHT)||(key == snake.RIGHT && preKey == snake.LEFT)||(key == snake.UP && preKey == snake.DOWN)||(key == snake.DOWN && preKey == snake.UP))
 89                 {
 90                     key = preKey;
 91                 }
 92                 else
 93                 {
 94                     preKey = key; //不是沖突按鍵,可以更新按鍵
 95                 }
 96                 
 97                 if(snake.move(key) == true)
 98                 {
 99                     //移動成功 代碼
100                     //system("cls");
101                     //wall.drawWall();
102                     gotoxy(hOut, 0, Wall::ROW);
103                     
104                     cout << "得分:" << snake.getScore() << "分" << endl;
105                     Sleep(snake.getSleepTime());
106                 }
107                 else
108                 {
109                     isDead = true;
110                     break;
111                 }
112             }
113             else
114             {
115                 key = preKey;//強制将錯誤按鍵變為上一次移動的方向
116             }
117     
118         }while(!_kbhit());//當沒有鍵盤輸入的時候,傳回0
119         
120 
121     }
122 
123     
124 }
125 
126 int main()
127 {
128     test01();
129     
130     system("pause");
131     return EXIT_SUCCESS;
132 }      

wall.cpp

1 #include"wall.h"
 2 
 3 
 4 void Wall::initWall()
 5 {
 6     for(int i = 0; i < ROW; i++)
 7     {
 8         for(int j = 0; j < COL; j++)
 9         {
10             //放牆壁
11             if(i == 0 || j == 0 || i == ROW - 1 || j == COL -1)
12             {
13                 gameArray[i][j] = '*';
14             }
15             else
16             {
17                 gameArray[i][j] = ' ';
18             }
19         }
20         
21     }
22 }
23 
24 
25 void Wall::drawWall()
26 {
27     for(int i = 0; i < ROW; i++)
28     {
29         for(int j = 0; j < COL; j++)
30         {
31             cout << gameArray[i][j] << " ";
32         }
33         if(i == 5)
34         {
35             cout << "create by wp";
36         }
37         if(i == 6)
38         {
39             cout << "a:left";
40         }
41         if(i == 7)
42         {
43             cout << "d:right";
44         }
45         if(i == 8)
46         {
47             cout << "w:up";
48         }
49         if(i == 9)
50         {
51             cout << "s:down";
52         }
53 
54         
55         cout << endl;
56     }
57 }
58 
59 
60 void Wall::setWall(int x, int y, char c)
61 {
62     gameArray[x][y] = c;
63 }
64 
65 char Wall::getWall(int x, int y)
66 {
67     return gameArray[x][y];
68 }      

wall.h

1 #ifndef _WALL_HEAD
 2 #define _WALL_HEAD
 3 #include<iostream>
 4 using namespace std;
 5 
 6 class Wall
 7 {
 8 public:
 9     enum{ROW = 26, COL = 26};
10     
11     //初始化牆壁
12     void initWall();
13     
14     //畫出牆壁
15     void drawWall();
16     
17     //根據索引設定二維數組裡的内容
18     void setWall(int x, int y, char c);
19     
20     //根據索引擷取目前位置的符号
21     char getWall(int x, int y);
22     
23 private:
24     char gameArray[ROW][COL];
25     
26 };
27 
28 
29 
30 
31 
32 
33 #endif      

snake.cpp

1 #include"snake.h"
  2 #include<window.h>
  3 
  4 void gotoxy1(HANDLE hOut1, int x, int y)
  5 {
  6     COORD pos;
  7     pos.X = x;             //橫坐标
  8     pos.Y = y;            //縱坐标
  9     SetConsoleCursorPosition(hOut1, pos);
 10 }
 11 HANDLE hOut1 = GetStdHandle(STD_OUTPUT_HANDLE);//定義顯示器句柄變量
 12 
 13 Snake::Snake(Wall& tempWall, Food& tmpFood):wall(tempWall),food(tmpFood)
 14 {
 15     pHead = NULL;
 16     isRool = false;
 17 }
 18 
 19 void Snake::initSnake()
 20 {
 21     destroyPoint();
 22     addPoint(5, 3);
 23     addPoint(5, 4);
 24     addPoint(5, 5);
 25 }
 26 
 27 void Snake::destroyPoint()
 28 {
 29     Point* pCur = pHead;
 30     
 31     while(pHead != NULL)
 32     {
 33         pCur = pHead->next;
 34         delete pHead;
 35         
 36         pHead = pCur;
 37     }
 38     
 39 }
 40 
 41 void Snake::addPoint(int x, int y)
 42 {
 43     //建立新結點
 44     Point* newPoint = new Point;
 45     newPoint->x = x;
 46     newPoint->y = y;
 47     newPoint->next = NULL;
 48     
 49     //如果原來頭不為空,改為身子
 50     if(pHead != NULL)
 51     {
 52         wall.setWall(pHead->x, pHead->y, '=');
 53         
 54         gotoxy1(hOut1, pHead->y * 2, pHead->x);
 55         cout << "=";
 56     }
 57     
 58     newPoint->next = pHead;
 59     
 60     pHead = newPoint;//更新頭部
 61     
 62     wall.setWall(pHead->x, pHead->y, '@');
 63     
 64     gotoxy1(hOut1, pHead->y * 2, pHead->x);
 65     cout << "@";
 66 }
 67 
 68 void Snake::delPoint()
 69 {
 70     //兩個結點以上,才去做删除操作
 71     if(pHead == NULL || pHead->next == NULL)
 72     {
 73         return;
 74     }
 75     
 76     Point* pCur = pHead->next;
 77     Point* pPre = pHead;
 78     
 79     while(pCur->next != NULL)
 80     {
 81         pPre = pPre->next;
 82         pCur = pCur->next;
 83     }
 84     //删除尾結點
 85     
 86     wall.setWall(pCur->x, pCur->y, ' ');
 87     gotoxy1(hOut1, pCur->y * 2, pCur->x);
 88     cout << " ";
 89     
 90     delete pCur;
 91     pCur = NULL;
 92     pPre->next = NULL;
 93 }
 94 
 95 bool Snake::move(char key)
 96 {
 97     int x = pHead->x;
 98     int y = pHead->y;
 99     
100     switch(key)
101     {
102         case UP:
103             x--;
104             break;
105         case DOWN:
106             x++;
107             break;
108         case LEFT:
109             y--;
110             break;
111         case RIGHT:
112             y++;
113             break;
114         default:
115             break;
116     }
117     
118     //判斷:如果是下一步碰到的尾巴,不應該死亡
119     Point* pCur = pHead->next;
120     Point* pPre = pHead;
121     
122     while(pCur->next != NULL)
123     {
124         pPre = pPre->next;
125         pCur = pCur->next;
126     }
127     if(pCur->x == x && pCur->y == y)
128     {
129         //碰到尾巴,成為循環
130         isRool = true;
131     }
132     else
133     {
134         //判斷使用者到達位置是否成功
135         if(wall.getWall(x, y) == '*' || wall.getWall(x, y) = '=')
136         {
137             addPoint(x,y);//如果是*的話,再多走一步,但是吃掉自己也會出問題,可以更改上面的if分開進行判斷
138             delPoint();
139             system("cls");
140             wall.drawWall();
141             
142             cout << "得分:" << getScore() << "分" << endl;
143             cout << "GAME OVER!!!" << endl;
144             return false;
145         }
146     }
147 
148     
149     //移動成功分兩種
150     //吃到食物 未吃到食物
151     if(wall.getWall(x, y) == '#')
152     {
153         addPoint(x, y);
154         
155         //重新設定食物
156         food.setFood();
157     }
158     else
159     {
160         addPoint(x, y);
161         delPoint();
162         if(isRool == true)
163         {
164             wall.setWall(x, y, '@');
165             gotoxy1(hOut1, y * 2, x);
166             cout << "@";
167         }
168     }
169     return true;
170 }
171 
172 int Snake::getSleepTime()
173 {
174     int sleepTime = 0;
175     int size = countList();
176     if(size < 5)
177     {
178         sleepTime = 300;
179     }
180     else if(size >= 5 && size <= 8)
181     {
182         sleepTime = 200;
183     }
184     else
185     {
186         sleepTime = 100;
187     }
188     return sleepTime;
189 }
190 int Snake::countList()
191 {
192     int size = 0;
193     Point* curPoint = pHead;
194     while(curPoint != NULL)
195     {
196         size++;
197         curPoint = curPoint->next;
198     }
199     return size;
200 }
201 
202 int Snake::getScore()
203 {
204     int size = countList();
205     
206     int score = (size - 3) * 100;
207     
208     return score;
209 }      

snake.h

1 #pragma once
 2 #include<iostream>
 3 using namespace std;
 4 #include"wall.h"
 5 #include"food.h"
 6 
 7 class Snake
 8 {
 9 public:
10     
11     Snake(Wall& tempWall, Food& food);
12     
13     enum{ UP = 'w', DOWN = 's', LEFT = 'a', RIGHT = 'd'};
14     
15     
16     //結點
17     struct Point
18     {
19         //資料域
20         int x;
21         int y;
22         
23         //指針域
24         Point* next;
25     };
26     
27     //初始化
28     void initSnake();
29     
30     //銷毀所有結點
31     void destroyPoint();
32     
33     //添加結點
34     void addPoint(int x, int y);
35     
36     //删除結點
37     void delPoint();
38     
39     //移動蛇操作
40     //傳回值代表移動是否成功
41     bool move(char key);
42     
43     //設定難度
44     //設定刷屏時間
45     int getSleepTime();
46     //擷取蛇身段
47     int countList();
48     
49     //擷取分數
50     int getScore();
51     
52     Point* pHead;
53     
54     Wall& wall;
55     
56     Food& food;
57     
58     bool isRool;//判斷碰到尾巴的辨別
59 };      

food.cpp

1 #pragma once
 2 #include<iostream>
 3 using namespace std;
 4 #include"wall.h"
 5 
 6 class Food
 7 {
 8 public:
 9     Food(Wall& tempWall);
10     
11     //設定食物
12     void setFood();
13     
14     int foodX;
15     int foodY;
16     
17     Wall& wall;
18 };      

food.h

1 #include"food.h"
 2 #include<window.h>
 3 
 4 void gotoxy2(HANDLE hOut2, int x, int y)
 5 {
 6     COORD pos;
 7     pos.X = x;             //橫坐标
 8     pos.Y = y;            //縱坐标
 9     SetConsoleCursorPosition(hOut2, pos);
10 }
11 HANDLE hOut2 = GetStdHandle(STD_OUTPUT_HANDLE);//定義顯示器句柄變量
12 
13 Food::Food(Wall& tempWall):wall(tempWall)
14 {
15     
16 }
17 
18 void Food::setFood()
19 {
20     
21     while(true)
22     {
23         foodX = rand() % (Wall::ROW - 2) + 1;
24         foodY = rand() % (Wall::COL - 2) + 1;
25         
26         //如果随機的位置是蛇頭或蛇身,就重新生成随機數
27         if(wall.getWall(foodX, foodY) == ' ')
28         {
29             wall.setWall(foodX, foodY, '#');
30             gotoxy2(hOut2, foodY * 2, foodX);
31             cout << "#";
32             break;
33         }
34     }
35 }