//第二天:把墙打出来了。横向一个字符跨度为2,因为C语言一个汉字char 占2字节。完善剩下的函数
/***********************
*** 2020贪吃蛇**********
***作者:Tjhon9*********
***时间:2020.02.24*****
************************/
#include <stdio.h>
#include <time.h>
#include <windows.h>//设置光标,控制台必不可少。
#include <stdlib.h>
#include <conio.h>//控制台
#define W 100//游戏界面宽度是100
#define H 40 //游戏界面高度是40
#define UPLEVEL 5//游戏等级提升所需食物个数是5
#define OUTLINE 102 //界外菜单信息。
#define LEN sizeof(struct score)//结构体的大小
const char *FILE_BIN_PATH="Data.bin";//二进制文件的路径
struct snake
{
int X[105]; //蛇身的横坐标
int Y[105]; //蛇身的纵坐标
int length; //蛇的长度
int count; //蛇吃到食物个数
int speed; //蛇移动速度
};
struct food
{
int X; //食物横坐标
int Y; //食物纵坐标
};
struct score
{
int score;
struct score *next;
//char name[20];
};
//全局变量
int i,j,k,max; //i,j,k为循环变量,max作为记录历史最高分。
int key,directionkey; //按键,与记录上一个方向按键。
struct snake s,*ps=&s; //定义蛇的结构体指针并指向蛇的结构体
struct food f,*pf=&f; //定义食物结构体指针并指向食物结构体
struct score *head=NULL,*pr,*p;
//有关文件、链表的3个函数
/*从文件读取数据到链表*/
void loadFromFile();
/*将链表数据储存到文件中*/
void saveToBfile();
/*获取历史最高分*/
int GetMaxScore(struct score *head);
/*设置控制台标题与其大小*/
void setTitle();
/*设置控制台颜色*/
void setColor(int colorchoose);
/*设置光标位置*/
void setPos(int x,int y);
/*设置游戏的界面,四周的围墙*/
void Table();
/*显示提示和菜单*/
void showMenu();
/*得到用户输入*/
void getKey();
/*初始化蛇身*/
void initSnake();
/*移动蛇身,刷新蛇身信息*/
void moveSnake();
/*显示蛇身*/
void printSnake();
/*产生食物*/
void produceFood();
/*吃掉食物,变长*/
void eatFood();
/*穿墙*/
void crossWall();
/*死亡*/
void die();
/*游戏结束*/
void gameOver();
/*游戏开始*/
void gameStar();
/*主函数*/
int main()
{
gameStar();
return 0;
}
/*设置控制台标题与其大小*/
void setTitle()
{
//设置控制台窗口大小
system("mode con cols=150 lines=45");
//设置控制台标题
SetConsoleTitle("2020贪吃蛇");
}
/*设置光标位置*/
void setPos(int x,int y)
{
HANDLE a;//句柄HANDLE
COORD c;//COORD是个结构体 ,有两个成员int X与int Y
c.X=x;
c.Y=y;
a=GetStdHandle(STD_OUTPUT_HANDLE);
SetConsoleCursorPosition(a,c);
}
/*设置控制台颜色*/
void setColor(int colorchoose)
{/*10,12,14绿红黄*/
HANDLE a;
a=GetStdHandle(STD_OUTPUT_HANDLE);
SetConsoleTextAttribute(a,colorchoose);//具体用法:CSDN搜索:SetConsoleTextAttribute函数用法 作者 花狗Fdog。
}
/*设置游戏的界面,四周的围墙*/
void Table()
{
for(i=0;i<H;i++)
{
if(i==0||i==H-1)//上墙与下墙的纵坐标分别是0与H-1
{ for(k=0;k<W;k+=2)//k是横坐标 //左右墙的横坐标分别为0~1 与98~99
{
setPos(k,i);//k,i,原来是i,j。
printf("卍"); //因为一个字符卍占据两个位置,方块■也是,所以横坐标一次性+2.
}
}
else
{ for(j=0;j<W;j+=2)//j是横坐标。
{
if(j==0||j==W-2)//j==98
{
setPos(j,i);//j,i,原来是i,j
printf("卍");
}
}
}
}
}
/*显示提示和菜单*/
void showMenu()
{
setPos(OUTLINE,4);
setColor(12);
printf("欢迎来到贪吃蛇");
setPos(OUTLINE,8);
printf("↑↓←→ 控制蛇头方向");
setPos(OUTLINE,12);
printf("按+怎加蛇移动速度,按-减少蛇移动速度");
setPos(OUTLINE,16);
printf("Esc 退出游戏");
setPos(OUTLINE,20);
printf("按 空格 暂停游戏");
setPos(OUTLINE,24);
printf("您的当前得分是: %d",ps->count*100);
setPos(OUTLINE,28);
printf("贪吃蛇移动速度: %d MS移动一下",ps->speed);
setPos(OUTLINE,32);
printf("您当前游戏等级是 %d",ps->count/UPLEVEL+1);
}
/*初始化蛇身*/
void initSnake()
{
ps->length=3;//蛇的长度为3
ps->count=0;//吃的食物为0;
ps->speed=300;//初始速度300ms
ps->X[0]=W/2;//蛇尾起步位置
ps->Y[0]=H/2; //蛇尾起步位置。
for(i=1;i<ps->length;i++)
{
ps->X[i]=ps->X[i-1]+2;//加2是因为一节蛇身,蛇头或蛇尾都占两个字节 ,如果加1的话,就用#@x做蛇身体各个部位 ,而不用▲做蛇部位(只不过看起来与上下移动不协调)。
ps->Y[i]=ps->Y[i-1];//蛇开始是横着的,因为上面+1了,所以蛇开始是横着的,所以纵坐标相同。
}
}
/*显示蛇身*/
void printSnake()
{
for(i=0;i<ps->length;i++)
{
setPos(ps->X[i],ps->Y[i]);
if(i==0)//打印蛇尾
{
printf("蛇");
}
else if(i==ps->length-1)
{
printf("赖");//打印蛇头
}
else
{
printf("皮");//打印蛇身。
}
}
}
/*移动蛇身,刷新蛇身信息*/
void moveSnake()
{
setPos(ps->X[0],ps->Y[0]);
printf(" ");
for(i=1;i<ps->length;i++)
{
ps->X[i-1]=ps->X[i];
ps->Y[i-1]=ps->Y[i];
}
}
/*得到用户输入*/
//内含蛇移动的 关键!
void getKey()
{
//key=getch();
switch(key)
{
//Esc
case 27:gameOver();
break;
//空格
case 32:setPos(OUTLINE,36);
setColor(5);
Sleep(0);
printf("游戏暂停,按任意方向键继续\n");
setPos(0,H);
break;
//+
case 43:if(ps->speed<=20)
{
break;
}
else
{
ps->speed-=20;
break;
}
//-
case 45:
ps->speed+=20;
break;
//↑
case 72:if(directionkey==80)//防止开回马车。
{
key=80;
break;
}
moveSnake();
ps->Y[ps->length-1]-=1;
break;
// ↓
case 80:if(directionkey==72)
{
key=72;
break;
}
moveSnake();
ps->Y[ps->length-1]+=1;
break;
// ←
case 75:if(directionkey==77)
{
key=77;
break;
}
moveSnake();
ps->X[ps->length-1]-=2;
break;
// →
case 77:if(directionkey==75)
{
key=75;
break;
}
moveSnake();//后面部位先移动位置把头覆盖住,然后头再移位,然后再打印出来。
ps->X[ps->length-1]+=2;
break;
}
}
/*产生食物*/
void produceFood()
{
srand(time(NULL));
while(1)
{ //食物横坐标范围:2~98所有偶数(因为食物是汉字打出来的,踩得是偶数 占据的偶数与奇数的宽度);因为左右墙是0~1与98~99,所以)。 即为(1~48)*2
pf->X=(rand() % 48+1)*2;
//食物横坐标范围:1~38所有的数。(因为上下墙是0与39)
pf->Y=(rand() % 39+1);
for(i=0;i<ps->length;i++)
{
if(ps->X[i]==pf->X&&ps->Y[i]==pf->Y)
break;
}
if(i==ps->length)//确保食物不在蛇身上,然后把食物表示出来之后就不循环了。
{
setPos(pf->X,pf->Y);
//setColor(8);
printf("★");
break;
}
}
}
/*吃掉食物,变长*/
void eatFood()
{
if(ps->X[ps->length-1]==pf->X && ps->Y[ps->length-1]==pf->Y)
{
ps->length++;
//长度增加后,新坐标就等于原来贪吃蛇的头坐标,然后依次向后缩,缩到尾巴被覆盖。下一步移动时,蛇身体就拉伸了。
for(i=ps->length-1;i>=1;i--)
{
ps->X[i]=ps->X[i-1];
ps->Y[i]=ps->Y[i-1];
}
produceFood();
ps->count++;
if(ps->count%UPLEVEL==0)
{
if(ps->speed>=20)
ps->speed-=20;
else
{
ps->speed=5;
printf("速度不会变快了");
}
}
}//end of if
}
/*穿墙
进墙蛇头刚好等于墙,出墙蛇头不要等于墙,刚好出墙为好。
上下墙纵坐标分别为 0 与 H-1,左右墙横坐标分别为 0~1 与 98~99,横向蛇头踩偶数*/
void crossWall()
{
//穿右墙 ,用key保证蛇不碰到墙
if( key==77 && ps->X[ps->length-1]==98)
{
ps->X[ps->length-1]=2;//原来错在这里==双等号,没有赋值操作
}
//穿左墙
if(ps->X[ps->length-1]==0 && key==75)
{
ps->X[ps->length-1]=96;//
}
//穿上墙
if(ps->Y[ps->length-1]==0 && key==72)
{
ps->Y[ps->length-1]=H-2;
}
//穿下墙
if(ps->Y[ps->length-1]==H-1 && key==80)
{
ps->Y[ps->length-1]=1;
}
}
/*从文件读取数据到链表*/
void loadFromFile()
{
FILE *fp;
int flag=2;
fp=fopen(FILE_BIN_PATH,"rb");
if(fp==NULL)
{
printf("没有找到文件!\n");
//exit(-1);
return;
}
while(1)
{
p=(struct score*)malloc(LEN);
flag=fread(p,LEN,1,fp);
if(flag==0)
break;
if(head==NULL)
{
head=pr=p;
pr->next=NULL;
}
else
{
pr->next=p;
pr=p;
pr->next=NULL;
}
}
fclose(fp);
}
/*将链表数据储存到文件中*/
void saveToBfile()
{
FILE *fp;
int cnt=0;
fp=fopen(FILE_BIN_PATH,"wb");
if(fp==NULL)
{
printf("打开文件失败\n");
exit(-1);
}
if(head==NULL)
{
printf("没有需要写入文件的数据\n");
return ;
}
else
{
for(p=head;p!=NULL;p=p->next)
{
fwrite(p,LEN,1,fp);
cnt++;
}
}
fclose(fp);
}
/*获取历史最高分*/
int GetMaxScore(struct score *head)
{
if(head==NULL) return NULL;
else
{
max=head->score;
for(p=head->next;p!=NULL;p=p->next)
{
if(max<p->score)
{
max=p->score;
}
}
}
return max;
}
/*游戏结束*/
void gameOver()
{
system("cls");
setPos(W/2,H/2);
if(head==NULL)
{
p=(struct score*)malloc(sizeof(struct score));
pr=head=p;
pr->score=ps->count*100;
pr->next=NULL;
}
else
{
p=(struct score*)malloc(sizeof(struct score));
p->score=ps->count*100;
pr->next=p;
pr=p;
pr->next=NULL;
}
max=GetMaxScore(head);
setPos(W/2,H/2);
printf("游戏结束,你的得分是%d,历史最高分是%d\n",ps->count*100,max);
saveToBfile();
showMenu();
setPos(0,H);
exit(0);
}
/* 判断蛇的死亡 */
void die()
{
for(i=0;i<ps->length-1;i++) //i遍历的就是蛇身,从0开始则包括了蛇尾
{ /* 当蛇头碰到蛇身,蛇死亡。 */
if(ps->X[ps->length-1]==ps->X[i]&&ps->Y[ps->length-1]==ps->Y[i])//
{
gameOver();
}
}
}
/*游戏开始*/
void gameStar()
{
setTitle();
setColor(14);
Table();
initSnake();
printSnake();
key=77;
produceFood();
while(2)
{
if(key==72||key==75||key==77||key==80)
{
directionkey=key;
}
j=0;
if(kbhit())//没有它蛇会停在原地等待接收key.
{
key=getch();
if(kbhit())//库函数,如果长按键盘上的值
{
Sleep(20);
j=1;
}
}
getKey(); //让蛇自己移动需要 一个key值,一个getKey函数,然后放在while(1)中
if(key==43||key==45)
{
key=directionkey;
}
eatFood();
crossWall();
printSnake();
showMenu();
die();
if(j==1) Sleep(10);//Sleep函数控制蛇的移动速度。
else Sleep(ps->speed);
}
}
C语言贪吃蛇(光标版)(me)
最新推荐文章于 2023-02-22 19:45:49 发布