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 }

在学习c++提高-STL总结了笔记,并分享出来。有问题请及时联系博主:Alliswell_WP,转载请注明出处。

原文地址:https://www.cnblogs.com/Alliswell-WP/p/CPlusPlus_ImprovedLearning_04.html