这两天刚考完试,有两天的休息时间。本打算出去逛逛,可是又不知道去哪好......所以就待在学校写代码吧!:laughing:(其实是因为没有女票qwq)
贪吃蛇大家都玩过吧,实现起来也并不难,主要思路就是用数组来模拟画布(即蛇活动的那个地方)。关于蛇的移动与吃食物后变长,用的是移动前后数组中各个元素间的关系的方法。详见代码。
另外,为了熟悉C++中类的使用,这次用的是OOP的思想(而不再是“用C++的语法写C”),写了两个类,即Snake类和Canvas类,分别表示蛇和画布。
代码如下
#include <windows.h>
#include <conio.h>
#include <cstdlib>
#include <cstdio>
#include <ctime>
#include <iostream>
#include <cstdlib>
int new_tail_i,new_tail_j;
void gotoxy(int x,int y) //将光标的坐标设为(x,y)
{
HANDLE handle=GetStdHandle(STD_OUTPUT_HANDLE); //取得一个句柄
COORD pos;
pos.X=x;
pos.Y=y;
SetConsoleCursorPosition(handle,pos); //设置光标的坐标
}
class Canvas{ //定义一个Canvas类来表示画布
public:
Canvas(); //构造函数
void show(); //显示画布
void generate_food(); //生成画布上的食物
int get_hei(); //得到画布高度
int get_wid(); //得到画布宽度
void change_canvas(int i,int j,int value_to_write); //改变画布(即修改私有成员canvas数组中的某个元素值)
int show_ele(int i,int j); //得到画布上某个位置的状态(即canvas中某个元素的元素值)
private:
int canvas[100][100]; //用一个数组来表示画布各处的状态
int hei; //画布高度
int wid; //画布宽度
};
void Canvas::generate_food()
{
int success=1;
int flag=1;
for(int i=1;i<hei-1;i++)
{
for(int j=1;j<wid-1;j++)
{
if(canvas[i][j]==-2)
{
flag=0;
goto out_loop;
}
}
}
//上面一段代码用于判断画布中是否有食物。若有,则不需要生成新食物;否则生成新食物
out_loop:;
if(flag)
{
while(success) //加个while循环是为了在生成错误(指食物生成到了蛇的身上或墙壁上)的情况下重新再生成
{
int x=rand()%(hei-2)+1;
int y=rand()%(wid-2)+1;
if(canvas[x][y]==0)
{
canvas[x][y]=-2;
success=0;
}
}
return ;
}
else
return ;
}
Canvas::Canvas()
{
hei=20;wid=40;
system("cls"); //清屏
memset(canvas,0,sizeof(canvas));
for(int i=0;i<hei;i++)
{
canvas[i][0]=-1;
canvas[i][wid-1]=-1;
}
for(int j=1;j<wid-1;j++)
{
canvas[0][j]=-1;
canvas[hei-1][j]=-1;
}
}
void Canvas::show()
{
// system("cls");
gotoxy(0,0);
for(int i=0;i<hei;i++)
{
for(int j=0;j<wid;j++)
{
if(canvas[i][j]==-1)
printf("#");
if(canvas[i][j]==0)
printf(" ");
if(canvas[i][j]==1)
printf("@");
if(canvas[i][j]>1)
printf("*");
if(canvas[i][j]==-2)
printf("F");
}
printf("\n");
}
}
int Canvas::show_ele(int i,int j)
{
return canvas[i][j];
}
void Canvas::change_canvas(int i,int j,int value_to_write)
{
canvas[i][j]=value_to_write;
}
int Canvas::get_hei()
{
return hei;
}
int Canvas::get_wid()
{
return wid;
}
class Snake{
public:
void move(int dir,Canvas& can);
void grow(int x,int y,Canvas& can);
void put_snake(Canvas& sele_can,int x,int y,int len);
bool check(Canvas& can,int x,int y);
int get_score();
int get_head_i();
int get_head_j();
void quit();
Snake();
private:
int head_i,head_j;
int old_tail_i,old_tail_j;
int score;
int live;
};
Snake::Snake()
{
live=3;
score=0;
}
int Snake::get_score()
{
return score;
}
int Snake::get_head_i()
{
return head_i;
}
int Snake::get_head_j()
{
return head_j;
}
bool Snake::check(Canvas& can,int x,int y) //用于判断蛇是否撞墙或咬到自己
{
if(can.show_ele(x,y)>0||can.show_ele(x,y)==-1)
return false;
return true;
}
void Snake::put_snake(Canvas& sele_can,int x,int y,int len) //把蛇放到一个画布上。这样做的目的是为了蛇和画布能更彻底地分离
{
int hei=sele_can.get_hei();
int wid=sele_can.get_wid();
int body=1;
for(int j=y;j>=y-len;j--,body++)
sele_can.change_canvas(x,j,body);
}
void Snake::move(int dir,Canvas& can) //蛇的移动
{
int hei=can.get_hei();
int wid=can.get_wid();
for(int i=1;i<hei-1;i++)
{
for(int j=1;j<wid-1;j++)
{
if(can.show_ele(i,j)>=1)
{
int tmp=can.show_ele(i,j);
can.change_canvas(i,j,tmp+1);
}
}
}
//将蛇的身体"+1"
int maxi=-10;
for(int i=1;i<hei-1;i++)
{
for(int j=1;j<wid-1;j++)
{
if(can.show_ele(i,j)==2)
{
head_i=i;
head_j=j;
}
if(can.show_ele(i,j)>maxi)
{
maxi=can.show_ele(i,j);
old_tail_i=i;
old_tail_j=j;
}
}
}
//找到头部坐标
//下面则是根据移动方向放置蛇头
if(dir==1) //左移
{
if(can.show_ele(head_i,head_j-1)==-2)
grow(head_i,head_j-1,can);
else
{
if(check(can,head_i,head_j-1))
{
can.change_canvas(head_i,head_j-1,1);
can.change_canvas(old_tail_i,old_tail_j,0);
can.show();
}
else
{
quit(); //这个还没写
return;
}
}
}
else if(dir==2) //上移
{
if(can.show_ele(head_i-1,head_j)==-2)
grow(head_i-1,head_j,can);
else
{
if(check(can,head_i-1,head_j))
{
can.change_canvas(head_i-1,head_j,1);
can.change_canvas(old_tail_i,old_tail_j,0);
can.show();
}
else
{
quit();
return ;
}
}
}
else if(dir==3) //右移
{
if(can.show_ele(head_i,head_j+1)==-2)
grow(head_i,head_j+1,can);
else
{
if(check(can,head_i,head_j+1))
{
can.change_canvas(head_i,head_j+1,1);
can.change_canvas(old_tail_i,old_tail_j,0);
can.show();
}
else
{
quit();
return ;
}
}
}
else if(dir==4) //下移
{
if(can.show_ele(head_i+1,head_j)==-2)
grow(head_i+1,head_j,can);
else
{
if(check(can,head_i+1,head_j))
{
can.change_canvas(head_i+1,head_j,1);
can.change_canvas(old_tail_i,old_tail_j,0);
can.show();
}
else
{
quit();
return ;
}
}
}
}
//蛇的变长函数,其原理与蛇的移动函数相似,只是不需要擦除蛇尾
void Snake::grow(int x,int y,Canvas& can)
{
for(int i=1;i<can.get_hei()-1;i++)
{
for(int j=1;j<can.get_wid()-1;j++)
{
if(can.show_ele(i,j)>=1)
{
int tmp=can.show_ele(i,j);
can.change_canvas(i,j,tmp+1);
}
}
}
can.change_canvas(x,y,1);
score++;
can.generate_food();
can.show();
printf("\t Your score is %d.\n",score);
}
void Snake::quit()
{
printf("\nI(the snake) lose!\n");
printf("Press any key to quit.\n");
getchar();
exit(0);
}
//应实现,同一条蛇可以在两个或多个不同的画布上移动、吃食物
//主体应该是蛇本身,而非画布
int main()
{
srand(time(NULL)); //用时间作为随机数种子,这样每一局的食物分布都会与上一局不同
Snake sna;
Canvas canv;
sna.put_snake(canv,canv.get_hei()-3,(canv.get_wid())/2+2,0);
canv.generate_food();
canv.show();
printf("\t Your score is %d.\n",sna.get_score());
while(1)
{
char op;
op=getch();
switch(op)
{
case 'a':sna.move(1,canv);break;
case 'w':sna.move(2,canv);break;
case 'd':sna.move(3,canv);break;
case 's':sna.move(4,canv);break;
}
}
return 0;
}
简单说一下蛇移动的思路:
首先,蛇的表示方法是这样的:若数组中某个元素为1,则它表示蛇头所在处;若数组中某个元素大于1,则它表示蛇身。从蛇头到蛇尾,是一个从1开始的连续自然数序列(即1,2,3,4,5,···)
通过观察下面的图片,我们可以得出这样的方法:若要移动蛇,只需要先将蛇头到蛇身的每一个数都++,然后将移动方向所对应的元素的值改为1,并将移动前的蛇尾的元素值改为0(0表示空白处,即蛇可以到达的地方)。
移动前
将蛇头和蛇身++
完成移动
大致就是如此,接下来应该还会有所补充,现在就先这样了吧(其实是懒)