C语言链表-贪吃蛇
设计思路
贪吃蛇的整体移动,可以只简化为蛇头与蛇尾的移动,当贪吃蛇的蛇头碰到蛇身或障碍物或超出指定范围时,游戏结束,因此我们作出以下设计:
1. 蛇身体结构设计 :蛇身整体的移动,可看作一个先进先出的队列,因此我们使用一个无附加表头的循环链表作队,来存储蛇的每一个节点(二维坐标值);
2. 地图状态设计:我们使用一个二维坐标来标志地图的状态,用-1标志食物的位置,用1标志蛇身,用0标志无障碍。
效果展示(gif)
程序源码
#include<graphics.h>
#include<conio.h>
#include<stdlib.h>
#include <stdio.h>
#include<time.h>
#define R 16 //每个格子尺寸:R*R
#define W 27
#define L 42 //活动范围:(L-2)*(W-2)个格子
//template <class T>
struct dot{//坐标值结构体
int x;
int y;
};
//template <class T>
struct Node{
dot data;
Node *next;
};
//template <class T>
class Cqueue{ // 无附加表头的循环链表作队
private:
Node *rear;//尾指针
public:
Cqueue()//构造空队
{rear=NULL;}
bool Empty()//判断空否
{return !rear;}
struct dot DeQueue()
{//出队操作
struct dot d;
if(!rear ) throw "空";
Node *q=rear->next;
//d.x=q->data.x;
//d.y=q->data.y;
d=q->data;
rear->next=q->next;
if(rear==q) rear=NULL;//最后一个元素的特殊处理
delete q;
return d;
}
void EnQueue(int a,int b){//入队操作
Node *q=new Node;
q->data.x=a;q->data.y=b;
if(!rear){rear=q;rear->next=rear;}//空时的处理
else
{q->next=rear->next;rear->next=q;rear=q;}
}
};
void show(int* p)//输出状态数组值
{
int i,j;
char num[4];
//setbkmode(TRANSPARENT);
setcolor(RED);
for(i=0;i<L-2;i++)
for(j=0;j<W-2;j++){
sprintf(num, "%d", *(p++));
outtextxy(i*R+R,j*R+R,num);}
}
void setfood(int* p)//设置食物
{
int x,y;
do{
x=R+rand()%(R*L-2*R);
y=R+rand()%(R*W-2*R);
x=x/R;
y=y/R;
}while(*(p+(x-1)*(W-2)+(y-1))==1);
setfillcolor(RED);
fillcircle(x*R+R/2,y*R+R/2,R/2);
*(p+(x-1)*(W-2)+(y-1))=-1;
}
void finish(int n)//游戏结束时调用
{
setfont(2*R,0,"方正舒体", 0,0,2, 0,0,0);
setbkmode(TRANSPARENT);
setcolor(WHITE);
outtextxy((6+L)*R/2,(4+W)*R/2,"come over");
//system("pause");
Sleep(1500);
return;
}
void move(int x,int y,Cqueue Q,int* p)//蛇的移动
{
char c=getch();
char k=c;//k记录c的上一次状态
int num=0;
struct dot t;
while(c != 27)
{
//show(p);
Sleep(300);
// 获取按键
if (kbhit())
{
c = getch();
if(k=='a'||k=='d'){
if(c=='a'||c=='d')
c=k;}
else{
if(k=='w'||k=='s')
if(c=='w'||c=='s')
c=k;}
k=c;
}
// 根据输入,计算新的坐标
switch(c)
{
case 'a': x-=R; break;
case 'd': x+=R; break;
case 'w': y-=R; break;
case 's': y+=R; break;
case 27: break;
}
if(x<R||x>=R*(L-1)||y<R||y>=R*(W-1))
{
finish(num);
return;
}
if(*(p+(x/R-1)*(W-2)+y/R-1)==0)
{
Q.EnQueue(x,y);
*(p+(x/R-1)*(W-2)+y/R-1)=1;
//setcolor(RED);
setfillcolor(YELLOW);
solidrectangle(x,y,x+R,y+R);
t=Q.DeQueue();
*(p+(t.x/R-1)*(W-2)+t.y/R-1)=0;
solidrectangle(t.x,t.y,t.x+R,t.y+R);
}
else
if(*(p+(x/R-1)*(W-2)+y/R-1)==-1)
{
num++;
Q.EnQueue(x,y);
*(p+(x/R-1)*(W-2)+y/R-1)=1;
setfillcolor(RED);
fillcircle(x+R/2,y+R/2,R/2);
setfillcolor(YELLOW);
solidrectangle(x,y,x+R,y+R);
setfood(p);//重置食物
}
else{
finish(num);
p=NULL;
return;}
}
}
void main()
{
initgraph(R*L,R*W);
srand(time(NULL));
Cqueue Q;//循环链接队
//struct dot p;//蛇尾坐标
int wall[L-2][W-2];0:障碍物/空区域;-1:食物;1:蛇身
setcolor(GREEN);
//rectangle(R-1,R-1,641-R,641-R);
int i,j;
for(i=R;i<R*L;i+=R)
line(i,R,i,R*W-R);
for(j=R;j<R*W;j+=R)
line(R,j,R*L-R,j);
for(i=0;i<L-2;i++)//初始化数组wall
for(j=0;j<W-2;j++)
wall[i][j]=0;
setwritemode(R2_XORPEN);
int x,y;
do{//创建第一个结点为蛇首
x=rand()%(R*L);
y=rand()%(R*W);
}while(x<R||x>=R*(L-1)||y<R||y>=R*(W-1));
x=x/R*R;
y=y/R*R;
wall[x/R-1][y/R-1]=1;
Q.EnQueue(x,y);
//setcolor(RED);
setfillcolor(YELLOW);
solidrectangle(x,y,x+R,y+R);
setfood(wall[0]);//设置第一个食物
move(x,y,Q,wall[0]);
getch();
closegraph();
}