以前的一篇文章用C写的控制台版简易贪吃蛇中讲述了一个非常不规范的贪吃蛇,整个程序内部的变量等等非常的散乱,是在老师给的程序上改变成的,后来在看了大神写的贪吃蛇贪吃蛇 AI 的实现 snake AI之后我便萌生了自己再重新写一个贪吃蛇程序的念头,这个程序在不断的搭建中,这篇文章主要介绍基本框架的搭建,包括贪吃蛇的界面,行走函数,显示函数等等。
因为尽量使用C语言,所以没有使用类,使用的是结构体来定义基本的“类”。到目前为止,基本的框架中包含三个重要的结构体,点,地图,蛇。首先看“点:结构体。
点:基本代码如下
struct Point
{
int x;
int y;
char symbol;
bool isSafe;
void setPoint(int InputX, int InputY)
{
x = InputX;
y = InputY;
}
};
定义了x,y代表对应在地图上的坐标,symbol表示该点显示的符号,isSafe表示该点是否安全(是否被占据):true表示安全,false表示不安全。setPoint()函数用来设置xy值。
地图:基本代码如下
struct Map
{
Point map[20][20];
Point indexMap[20][20];
void show()
{
system("cls");
for(int i = 0; i <= 19; i++)
{
for(int j = 0; j <= 19; j++)
{
printf("%c",map[i][j].symbol);
}
printf("\n");
}
}
};
map[20][20]是由20×20个点组成的地图,indexMap和map就大小而言是一样的,但是上面储存的xy代表的是map中对应的上一个坐标,在寻路算法中有一种Breadth算法,需要知道当前点对应的上一个点在哪,所以开辟了一个indexMap用来记录真实地图中的对应关系。show()函数用来显示地图。地图中的坐标轴如下图示意
蛇:基本代码如下
struct Snake
{
Point head;
vector body;
Point temp;
void initSnake(Map* map)
{
head.x = 0;
head.y = 3;
for(int i = 2; i >= 0; i--)
{
temp.setPoint(0,i);
temp.symbol = '*';
temp.isSafe = false;
body.push_back(temp);
map->map[body[2-i].x][body[2-i].y].symbol = '*';
map->map[body[2-i].x][body[2-i].y].isSafe = false;
}
map->map[head.x][head.y].symbol = '#';
map->map[head.x][head.y].isSafe = false;
}
void MoveUp(Map* map)
{
map->map[head.x][head.y].symbol = '*';
//body[0] = head;
head.x--;
map->map[body.back().x][body.back().y].symbol = ' ';
map->map[body.back().x][body.back().y].isSafe = true;
/*body.back().symbol = body[body.size()-1].symbol;
body.back().setPoint(body[body.size()-1].x,body[body.size()-1].y);*/
//body.back().setPoint(body[body.size()-2].x,body[body.size()-2].y);
for(int i = 2; i >= 1; i--)
{
body[i] = body[i-1];
}
body[0].setPoint(head.x+1,head.y);
map->map[head.x][head.y].symbol = '#';
map->map[head.x][head.y].isSafe = false;
}
void MoveDown(Map* map)
{
map->map[head.x][head.y].symbol = '*';
//body[0] = head;
head.x++;
map->map[body.back().x][body.back().y].symbol = ' ';
map->map[body.back().x][body.back().y].isSafe = true;
/*body.back().symbol = body[body.size()-1].symbol;
body.back().setPoint(body[body.size()-1].x,body[body.size()-1].y);*/
//body.back().setPoint(body[body.size()-2].x,body[body.size()-2].y);
for(int i = 2; i >= 1; i--)
{
body[i] = body[i-1];
}
body[0].setPoint(head.x-1,head.y);
map->map[head.x][head.y].symbol = '#';
map->map[head.x][head.y].isSafe = false;
}
void MoveLeft(Map* map)
{
map->map[head.x][head.y].symbol = '*';
//body[0] = head;
head.y--;
map->map[body.back().x][body.back().y].symbol = ' ';
map->map[body.back().x][body.back().y].isSafe = true;
/*body.back().symbol = body[body.size()-1].symbol;
body.back().setPoint(body[body.size()-1].x,body[body.size()-1].y);*/
//body.back().setPoint(body[body.size()-2].x,body[body.size()-2].y);
for(int i = 2; i >= 1; i--)
{
body[i] = body[i-1];
}
body[0].setPoint(head.x,head.y+1);
map->map[head.x][head.y].symbol = '#';
map->map[head.x][head.y].isSafe = false;
}
void MoveRight(Map* map)
{
map->map[head.x][head.y].symbol = '*';
//body[0] = head;
head.y++;
map->map[body.back().x][body.back().y].symbol = ' ';
map->map[body.back().x][body.back().y].isSafe = true;
//body.pop_back();
//body.back().setPoint(body[body.size()-2].x,body[body.size()-2].y);
for(int i = 2; i >= 1; i--)
{
body[i] = body[i-1];
}
body[0].setPoint(head.x,head.y-1);
map->map[head.x][head.y].symbol = '#';
map->map[head.x][head.y].isSafe = false;
}
};
一个类别一个类别的看,
Point head;
vector body;
Point temp;
void initSnake(Map* map)
{
head.x = 0;
head.y = 3;
for(int i = 2; i >= 0; i--)
{
temp.setPoint(0,i);
temp.symbol = '*';
temp.isSafe = false;
body.push_back(temp);
map->map[body[2-i].x][body[2-i].y].symbol = '*';
map->map[body[2-i].x][body[2-i].y].isSafe = false;
}
map->map[head.x][head.y].symbol = '#';
map->map[head.x][head.y].isSafe = false;
}
在这段代码里面定义了蛇的头和身体,身体使用vector来记录,在后期加上吃果子会变长的特性后需要加长蛇身,用vector可以方便的管理。定义一个临时点temp,在initSnake(Map* map)函数中方便赋值,initSnake(Map* map)函数用来初始化蛇的位置,坐标,显示字符等,传入的参数是地图的地址。然后就是四个基本的移动函数:
void MoveUp(Map* map)
void MoveDown(Map* map)
void MoveRight(Map* map)
void MoveLeft(Map* map)
具体的移动方法请自行查看。基本框架搭建完毕,在主函数中实验一下,代码如下
int main()
{
Map map;
Snake snake;
for(int i = 0; i <= 19; i++)
{
for(int j = 0; j <= 19; j++)
{
map.map[i][j].setPoint(i,j);
map.map[i][j].symbol = ' ';
map.map[i][j].isSafe = true;
map.indexMap[i][j].setPoint(i,j);
map.indexMap[i][j].symbol = ' ';
map.indexMap[i][j].isSafe = true;
}
}
snake.initSnake(&map);
snake.MoveRight(&map);
snake.MoveDown(&map);
snake.MoveLeft(&map);
snake.MoveUp(&map);
map.show();
}
显示结果如下
另外增加一个产生果实的函数Seed(Map* map),代码如下
void Seed(Map* map)
{
srand((unsigned)time(NULL));
int x = rand()%19;
int y = rand()%19;
if(map->map[x][y].isSafe)
{
map->map[x][y].isSafe = false;
map->map[x][y].symbol = '&';
map->map[x][y].setPoint(x,y);
}
}
因为使用了随机数,所以需要包含#include 以及#include ,在主函数调用Seed(&map)就可以放置果实
整个贪吃蛇的基本框架已经搭建好,还剩下吃了果实会变长的功能以及智能寻路算法。会另外开文章。