#include<stdio.h>
#include<conio.h>
#include<stdlib.h>
#include<windows.h>
#include<time.h>
#define MAX 3200
#define LEN sizeof(Snake)
typedef unsigned char uchar;
//------------------------
typedef struct snake{
char x;
char y;
struct snake *next;
struct snake *last;
}Snake;//双向链表
Snake *h,*tp1,*tp2;
Snake *t,*hp1,*hp2;//表尾tail
//----------------------(数组图片)结构体化---------------------------------
typedef struct Pic{
char x[MAX];
char y[MAX];
int pnum;
}PIC;
//-----------------------------------------------------------------------
PIC p0;
//------------------------
char f;//食物标志
int m=0,n=0;//食物位置
int a=20;
//------------------------
unsigned short color=5;//设置蛇头颜色
int score=0;
int _score=0;//历史分数变量
step=0;//记录吃食物所用步数
foodlose=0;//记录未吃到的食物数
//-------颜色设置-----------------
void colorf(unsigned short t)
{
HANDLE color;//创建句柄 color是变量名可以自己定义
color=GetStdHandle(STD_OUTPUT_HANDLE);//句柄实例化
SetConsoleTextAttribute(color,t);//设置字体颜色
}
//---------------------------
void point(int x,int y)//起始点(0,0)
{
int i,dx=0,dy=0;
static int x0=0,y0=0,j=1;//x0,y0表示当前行和当前列
if(x==-1&&y==-1) {x0=y0=0;j=1;}//用point(-1,-1)刷函数-相对于(0,0)点开始打点,未刷屏则相对于
//当前点,一般用于重新绘图时
if(x==0&&y==0&&j==1) { j=0;
if(x==h->x&&y==h->y) printf("○");
else if(x==24||x==0||y==0||y==20) {colorf(0x0f); printf("□"); colorf(9);}
else printf("●");return;}
if(x==0&&y==0&&j==0) return;//打两次(0,0)时略去第二个
dx=x-x0;
if(!dx) dy=y-y0; //dx=0,同一行
else dy=y;
if(dy<0||dx<0) return;
if(x==0&&y!=0&&dy==y&&j) printf(" ");
if(dy==0&&y!=0) return;
for(i=0;i<dx;i++) putchar('\n');
if(dx&&y!=0) printf(" ");
for(i=1;i<dy;i++) printf(" ");
//putchar(4);
//--------判断蛇头---------
if(x==h->x&&y==h->y) {colorf(color);printf("■");colorf(9);}
else if(x==24||x==0||y==0||y==20) {colorf(0x0f); printf("□"); colorf(9);}
else printf("●");
//---------------
x0=x;
y0=y;
}//注:每次打的点必须在前一个点后面,且第一次调用时必须先清屏
// ⊙★○▲△▽○◇☆♀♂㊣╲■▕╱╲▏▏卐卍♀◢◥◤
//--------整理pic-------
void sort(char*tx,char*ty)
{
int i,j,t,k;
int m,n,l;
//----第一步排tx(并保持tx与ty之间的对应关系)---
for(i=1;i<p0.pnum;i++)
{
t=tx[i];
k=ty[i];
for(j=i-1;j>=0;j--)
if(t<tx[j]) { tx[j+1]=tx[j]; ty[j+1]=ty[j]; }
else break;
tx[j+1]=t;
ty[j+1]=k;
}
//--------第二步逐行排ty----------
for(i=tx[0],j=0;i<=tx[p0.pnum-1];i++)//从第tx[0]到tx[p0.pnum-1]行
{
l=0; //j的最大值不能超过p0.pnum
while(tx[j]==i)
//此处只依靠tx[*]的值去做判断,容易出现超出数组寻址范围的情况,因为如果对于未定义的内存
//空间(此处是明确的,tx后的地址就是ty的地址),其值虽然相对
//不变,但总体(理论上)应该是不确定的(这可能取决于各自的机器),因此尽管超出规定的
//范围,仍然有可能
//满足这个判别式,从而造成寻址越界的情况
{
if(j==p0.pnum) break;//后来加的限制条件,没有它会出现一系列的奇葩现象
j++;l++;
}//得到tx[*]行的点数l
//当tx[*]全赋0且ty[0]=0,此处寻址越界,由于tx[*]寻址越界后,寻到了ty[*]处,他们地址是连续的,
//而此时的ty[0]正好满足ty[0]=tx[0]=0,这就造成了寻址越界,因此加上一条限制语句(如上),
//也证明上面的猜想是正确的
if(l==1||l==0) continue;
//*************************************
for(m=j-l;m<j-1;m++)
{ k=m;
for(n=m+1;n<j;n++)
if(ty[n]<ty[k] ) k=n;
if(k!=m)
{
t=ty[k];
ty[k]=ty[m];
ty[m]=t;} }
}
}
//-------光标定位-------------------------
void gotoxy(int x,int y)
{
COORD c;
c.X=x;c.Y=y;
SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE),c);
}
//--------------------------------------
void picture()
{
int i;
sort(p0.x,p0.y);
//------刷新--------
point(-1,-1);
system("cls");
//--------------------------------
for(i=0;i<p0.pnum;i++)
//{gotoxy(p0.y[i]*2,p0.x[i]);printf("□"); } //测试gotoxy()
point(p0.x[i],p0.y[i]);
putchar(10);
}
//------------------------------
void s_play(Snake *h);
void s_add_t(char a,char b)//加尾
{
//---开辟新节点并链接---
tp2=(Snake*)malloc(LEN);
tp2->x=a;
tp2->y=b;
tp1->next=tp2;
tp2->last=tp1;
tp2->next=NULL;
//-------------------
t=tp1=tp2;
}
void s_add_h(char a,char b)//加头
{
hp2=(Snake*)malloc(LEN);
hp2->x=a;
hp2->y=b;
hp2->last=NULL;
hp2->next=hp1;
hp1->last=hp2;
h=hp1=hp2;
}
void s_sub(char a)
{
Snake* p;
if(a>0){
p=h;
hp1=h=h->next;//此处要当心hp1应始终和h保持同步,在创建一个新h时默认hp1指向当前h,此处若不更新
//hp1将为NULL,导致链接错误
h->last=NULL;} //删去蛇头
if(a<0){
p=t;
tp1=t=t->last;
t->next=NULL;
}//删去蛇尾
free(p);
}
void s_play(Snake *p)
{
int i=89;//前面点做图框
//--------------(墙壁)------------------------
int j=0;
for(j=0;j<20;j++)
{ p0.x[j]=24; p0.y[j]=j; }
for(j=0;j<20;j++)
{ p0.x[j+20]=0;p0.y[j+20]=j; }
for(j=0;j<25;j++)
{ p0.x[j+40]=j;p0.y[j+40]=0; }
for(j=0;j<25;j++)
{ p0.x[j+65]=j;p0.y[j+65]=20; }
//---------------------------------------------
if(f) { p0.x[i]=m=rand()%23+1; p0.y[i]=n=rand()%19+1; f=0;}//放置食物
else { p0.x[i]=m; p0.y[i]=n; }
//----------- -----------------
i++;
p0.x[i]=p->x;
p0.y[i]=p->y;
// printf("%d\t%d\n",p->x,p->y);
i++;
do
{
if(p->next==NULL) break;
p=p->next;
p0.x[i]=p->x;
p0.y[i]=p->y;
// printf("%d\t%d\n",p->x,p->y);
i++;
} while(p->next!=NULL);
p0.pnum=i;
//getch();
colorf(9);
picture();
colorf(10);
printf("\n当前得分:%d\n当前蛇头(%d,%d)\t当前食物(%d,%d)\n当前步数:%d(step<=50)\t未吃到食物数:%d\n",score,h->x,h->y,m,n,step,foodlose);
printf("按z可自动行走步数:%d\t按q退出\t按e暂停\n",a);
gotoxy(48,8);
printf("%s\t%s",__DATE__,__TIME__);
gotoxy(2,23); //可以在不破坏原图的情况下定位光标
//system("cls");point(-1,-1);point(23,2);
printf("版权制作 吴侯所有 213.6.23");
}
void init()
{
system("color 07");
//------食物设置---------
srand(time(NULL));
f=1;
m=n=0;
//---------使用说明--------
printf("\n\n\n\n\n\n\n\t\t按键盘w、s、a、d键控制蛇的上下左右移动\n");
printf("\t\t按z键可自动行走\n");
printf("\t\t按键盘q键选择退出 按e键暂停\n");
printf("\t\t注意:当蛇头触碰到墙壁时会:game over!\n\t\t");
printf("输入你希望的蛇头颜色(0-15):\n\t\t");
scanf("%d",&color);
system("pause");
//getch();
system("color 09");
//-----------------------
t=h=hp1=tp1=(Snake*)malloc(LEN);
tp1->last=NULL;
tp1->next=NULL;//空链表
tp1->x=8;
tp1->y=4; //蛇头
//-----初始化----------
s_add_t(8,5);
s_add_t(8,6);
s_add_t(8,7);
s_play(h);
//----------------
}
void main()
{
char c;
char c_='d';
char c__='a';
//--------函数声明-------
void save();
void load();
void creat();
void gameover();
void move(char c);
char autogo();
//------------------------
creat();//外部文件生成判断
init();
while(1)
{
if(kbhit()) { c_=c=getch(); move(c); }
else {
if(c_=='e'||c_=='z'||c_=='E'||c=='Z') c_=c__;
move(c_);
c__=c_;
_sleep(200);}
}
}
//-----方向控制-------------------------
void move(char c)
{
if(h->x<=0||h->x>=24||h->y<=0||h->y>=20) gameover(); //撞墙判断
switch(c)
{
case 'W':
case 'w':{
{s_sub(-1);s_add_h(h->x-1,h->y);step++; }
break; }
case 'S':
case 's':{
{s_sub(-1);s_add_h(h->x+1,h->y);step++;}
break;}
case 'A':
case 'a':{
{s_sub(-1);s_add_h(h->x,h->y-1);step++;}
break; }
case'D':
case 'd':{
{ s_sub(-1);s_add_h(h->x,h->y+1);step++; }
break; }
case'Q':
case 'q' : { system("cls");
system("color 0a"); printf("\n\n\n\n\n\n\n\n\n\n\n\n\n\t\t\t退出?('y' or 'n')\n\t\t\t\t");
if(getchar()=='y'||getchar()=='Y') {printf("\t\t\t"); exit(0); }
else system("color 09"); }
}
//putchar(c_);
//getch();
if(h->x<=0||h->x>=24||h->y<=0||h->y>=20) gameover(); //撞墙判断
if(c=='z'||c=='Z') while(a) { move(autogo());a--; }
if(c=='e'||c=='E') system("pause"); //暂停
if((h->x==m&&h->y==n)||(t->x==m&&t->y==n))
{ s_add_t(t->x,t->y+1); step=0;score++;f=1;printf("\a");
if(a<20) a=20;else a++; } //吃到食物,增加节点,并重新放置食物
if(step==50) { step=0;f=1;foodlose++;printf("\a"); }//走完u步未吃到食物则更新食物
s_play(h);
}
//------------自动寻找食物演示----------------
char autogo()
{
char c=0;
if(h->x>m) c='w';
if(h->x<m) c='s';
if(h->x==m){
if(h->y>n) c='a';
if(h->y<n) c='d'; }
return c;
}
//------------------------------------------
void gameover()
{
load();
system("cls");
system("color 0a");
printf("\n\n\n\n\n\n\n\n\n\t\t\tgame over!\n\t\t");
printf("\t您的得分是:%d\n\t\t\t",score);
printf("历史最高分数:%d\n\t\t\t",_score);
save();
getch();
Sleep(1000);exit(0);
}
//-----------------数据保存-----------------------
void save()
{
FILE *fp;
fp=fopen("mtcs.mine","w");
if(fp==NULL) printf("\n\t\t\t数据保存失败!\n");
if(score<_score) {fclose(fp); return;}
fprintf(fp,"%d",score);
fclose(fp);
}
//------------------数据提取------------------
void load()
{
FILE *fp;
fp=fopen("mtcs.mine","r");
if(fp==NULL) printf("\n\t\t\t加载数据失败!\n");
fscanf(fp,"%d",&_score);
fclose(fp);
}
//------------------------------------------
void creat() //判断文件是否存在,若无则新建
{
void save();
FILE *fp;
fp=fopen("mtcs.mine","r");
if(fp==NULL) save();
else fclose(fp);
}
//-------------------------------------------------