贪吃蛇C++版

正文

大一学弟发过来的一段贪吃蛇代码,网上找的,实现用的C++,整体写的挺好的。因为发的截图,所以自己照着实现了下,打上了注释,然后有几个小bug的地方修改了下。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
#include <iostream>
#include <windows.h>
#include <stdlib.h>
#include <conio.h>
#include <time.h>
using namespace std;
#define MAX_FANCE 20
enum dir{up, down, l, r}; //方向枚举类型
enum dir;
//定义棋盘类
class Fence{ // 面向对象 类定义方法,C属于面向过程只能直接定义一个二维数组,然后类方法定义子函数
public: // 公有方法
void InitFence(); // 初始化棋盘
void OutputFence(); // 打印棋盘
public:
char game[MAX_FANCE][MAX_FANCE]; //公有属性
}f; //棋盘类的对象; 这里是全局变量,方便其他类方法内部调用
//类方法定义
//初始化棋盘
void Fence::InitFence(){
//原代码这里写死的20x20棋盘,我这里用宏定义方便修改
for(int i=0;i<MAX_FANCE;i++){
for(int j=0;j<MAX_FANCE;j++){
if(i==0||i==MAX_FANCE-1||j==MAX_FANCE-1||j==0){ //四周墙
game[i][j] = '#'; //这里原代码用的*表示墙,我用# *用来表示蛇
}else{
game[i][j] = ' '; //不是墙的地方先用空格代替
}
}
}
}
// 在控制台打印出棋盘
void Fence::OutputFence(){
cout<<"-------------Welcome the snake game--------------"<<endl;
cout<<"please using w s a d to control the direction of the snake"<<endl;
for(int i=0; i<MAX_FANCE; i++){
for(int j=0; j<MAX_FANCE; j++){
cout<<game[i][j]; //c++输出舒服一些,不用管数据类型
}
cout<<endl; // 下一行
}
}

//定义蛇类, 这里利用了线性链表的思想,通过定义坐标结点和前后指针将一个个坐标结点连成了一条蛇
class SnakeNode{
private: // 私有属性
int x,y;
SnakeNode *prior, *next;
public: // 公有方法
//void initSnake(); // 这里加了一个初始化一条蛇
void add_head(int x,int y); //蛇向前移动,所以头部增加坐标结点
int get_x();
int get_y();
void delete_tail(); //蛇向前移动,所以尾部坐标结点被删除 写到这,个人觉得可以用队列来实现这个蛇的结点线性表。蛇的每一次移动就是一次出入队,头部结点入队,尾部结点出队
}*head=NULL, *tail=NULL; // 这里可以通过一对head tail指针确定一条蛇,这里定义全局变量也是方便别的类内部方法调用
int SnakeNode::get_x(){
return x;
}
int SnakeNode::get_y(){
return y;
}
void initSnake(){ // 初始化一条蛇
head->add_head(4,5);
head->add_head(4,6);
head->add_head(4,7);
}
// 插入头结点 哦吼还真是队列入队
void SnakeNode::add_head(int x,int y){
SnakeNode *q = new SnakeNode;
q->x = x;
q->y = y;
q->next = head;
q->prior = NULL;
if(head) head->prior = q;
head = q;
if(!tail) tail = head;
f.game[x][y] = '*'; //更新棋盘类中对应的位置
}
//删除尾结点,出队
void SnakeNode::delete_tail(){
SnakeNode *p = tail;
f.game[p->get_x()][p->get_y()] = ' '; //更新棋盘类中对应的位置
if(tail==head) tail = head = NULL; //这条蛇长度为1时
else{
tail = tail->prior;
tail->next = NULL;
}
delete p;
}

//move 移动
class moved{
public:
dir point; // 枚举类型,控制方向
//食物坐标,蛇吃了长度+1
int food_x;
int food_y;

public:
void moving(); //蛇移动 可以设置每次刷新棋盘移动一下
void change_point(char); //改变移动方向
void get_food();
};
void moved::moving() {
int x, y;
// 获得蛇头结点坐标
x = head->get_x();
y = head->get_y();
switch(point) { // 注意二维坐标,[0][0] 在控制台左上角
case up: --x;break; // up往上走应该是行减少 也就是x--
case down: ++x;break; // down往下走应该是行减少 也就是x++
case l: --y;break;
case r: ++y;break;
}
if(x==0||y==0||x==MAX_FANCE-1||y==MAX_FANCE-1||f.game[x][y]=='*'){// 判断撞墙,同时还要判断是否咬到自己,原代码没判断咬到自己
cout<<"game over"<<endl;
exit(0);
}
if(x==food_x&&y==food_y){ // 判断是否吃到食物
head->add_head(x,y); //头部长度+1
get_food(); // 产生下一个食物
}else{ // 没吃到食物
head->add_head(x,y); //头部+1
head->delete_tail(); // 删除尾部
}
}
void moved::change_point(){char keydown}{ // 通过键盘输入改变运动方向
switch(keydown){
case 'w': point = up; break;
case 's': point = down; break;
case 'a': point = l; break;
case 'd': point = r; break;
default: cout<<"input error"; break;
}
}
void moved::get_food(){
srand((unsigned int)time(NULL)); // 用时间做随机种子
food_x = rand()%(MAX_FANCE-2)+1;
food_y = rand()%(MAX_FANCE-2)+1;
while(f.game[food_x][food_y]!=' '){ // 这里比原代码多加了一个判断,避免生成的食物落到了蛇身上
food_x = rand()%(MAX_FANCE-2)+1;
food_y = rand()%(MAX_FANCE-2)+1;
}
f.game[food_x][food_y] = '$';
}
int main() {
int time = 500; //多久刷新一次棋盘,也就是多久蛇动一下
move m; // 移动类对象
f.InitFence(); //初始化棋盘
initSnake();
m.get_food(); //产生食物
// 这里么看到原代码后续,自己补充写的
while(true){
f.OutputFence(); // 打印棋盘
if(_kbhit()){ //如果有案键按下,则_kbhit()返回真
char keydown = getch(); // 获取输入字符
m.change_point(keydown); // 改变方向
}
Sleep(time);
m.moving(); //移动蛇
system("cls"); //清空控制台输出新的棋盘。
}
return 0;
}


本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!