C/C++——控制台贪吃蛇的实现以及自动操作

游戏附件

链接: https://pan.baidu.com/s/1PIQjvM_tvDAJBWVlWSAtUQ
提取码: hmi4

头文件、全局变量以及函数声明

#include <windows.h>
#include <stdlib.h>
#include <stdio.h>
#include <conio.h>
#include <time.h>
#define INC(x) ((x)%30+1)
#define DEC(x) (((x)+28)%30+1)
COORD snake[901],egg[22];
int slong=1,dir=3,eggnum=0,eggscore=90,speed=100;
int scoremax=0,score=0,voice=1,eggmax=1,hard=2,cheatopen=0,mode=0;
int speednum[5]={200,100,50,25,10}; 
char hd[5][10]={"Easy","Normal","Hard","Crazy","丧心病狂"};
void gotoxy(int x, int y);	//移动光标
void color(int b);	//设置颜色
void drawboard();	//绘制背景
void drawsnake();	//绘制蛇
void start();		//游戏流程控制
void options();		//游戏设置
void move(int d);	//蛇的移动
int eat();			//吃蛋
int die();			//死亡判定
void drawegg();		//绘制蛋
void getegg();		//生成新蛋
void Pautoplay();	//自动操作模式1:扫荡
void autoplay();	//自动操作模式2:搜索
void (*cheatF)();	//自动模式设置指针

主函数以及基本函数

int main()
{
	system("mode 64,34");
	system("color 0f");
	system("title 贪吃蛇 By Eyizoha");
	HANDLE hOut = GetStdHandle(STD_OUTPUT_HANDLE);
	CONSOLE_CURSOR_INFO cci;
	GetConsoleCursorInfo(hOut,&cci);
	cci.bVisible=false;
	SetConsoleCursorInfo(hOut,&cci);
	srand(time(NULL));cheatF=autoplay;
	char ch;
	while(ch!=0x1B)
	{
		system("cls");drawboard();color(15);
		gotoxy(15,4);printf("贪吃蛇");
		gotoxy(14,5);printf("By Eyizoha");
		gotoxy(14,6);printf("最高分:%d",scoremax);
		gotoxy(13,7);printf("按空格开始游戏");
		gotoxy(12,8);printf("按A查看说明和设置");
		gotoxy(13,9);printf("按ESC退出游戏");
		while (ch!=0x1B)
    	{
    		ch=getch();
			if(ch==' '){start();break;}
			if(ch=='a'||ch=='A'){options();break;}
		}
	}
}
void gotoxy(int x,int y)
{
	COORD pos;
	pos.X=2*x; 
	pos.Y=y;
	SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE),pos);
} 
void color(int b)
{
	HANDLE hConsole=GetStdHandle((STD_OUTPUT_HANDLE)); 
	SetConsoleTextAttribute(hConsole,b) ;
}
void options()
{
	char ch;
	system("cls");drawboard();color(15);
	gotoxy(12,4);printf("游戏说明:游戏中");
	gotoxy(13,5);printf("按空格暂停,");
	gotoxy(12,6);printf("按WASD控制蛇方向,");
	gotoxy(13,7);printf("按ESC结束游戏,");
	gotoxy(10,8);printf("蛇头触碰到蛇身则游戏失败。");
	gotoxy(10,11);printf("Easy(一个蛋价值50分)");
	gotoxy(10,12);printf("Normal(一个蛋价值90分)");
	gotoxy(10,13);printf("Hard(一个蛋价值130分)");
	gotoxy(10,14);printf("Crazy(一个蛋价值170分)");
	gotoxy(10,15);printf("难度越高蛇速度越快。");
	gotoxy(10,19);printf("每个多一个蛋使每个蛋价值减2分");
	gotoxy(11,26);printf(".按空格返回上层菜单.");
	while(ch!=' ')
	{	
		gotoxy(10,10);printf("\t\t\t\t\t");
		gotoxy(10,10);printf("设置游戏难度(W和S),当前为%s!",hd[hard-1]);
		gotoxy(10,16);printf("当前蛇速度%d步/秒",1000/speed);
		gotoxy(10,18);printf("设置蛋的数量(A减D加),当前为%d个",eggmax);
		gotoxy(10,20);printf("当前一个蛋价值%d分",eggscore);
		if(voice){gotoxy(14,22);printf(".音效:开.\a");}
		else {gotoxy(14,22);printf(".音效:关.");}
		if(cheatopen==0){
		gotoxy(14,23);printf(".作弊:关.");
		gotoxy(10,24);printf("\t\t\t\t");}
		else {
		gotoxy(14,23);printf(".作弊:开.");
		gotoxy(10,24);
		if(mode)printf(".扫荡模式:慢,稳定,不灵活.");
		else printf(".搜索模式:快,灵活,不稳定.");}
		ch=getch();
    	switch(ch)
    	{
	    	case 'w':case 'W':hard=hard%4+1;break;
	    	case 's':case 'S':hard=(hard+2)%4+1;break;
			case 'a':case 'A':if(eggmax>1)eggmax-=1;break;
			case 'd':case 'D':if(eggmax<21)eggmax+=1;break;
			case 'e':case 'E':hard=5;break;
			case 'q':case 'Q':voice=(voice+1)%2;break;
			case '!':cheatopen=(cheatopen+1)%2;break;
			case '	':mode=(mode+1)%2;
					  if(mode)cheatF=Pautoplay;
					  else cheatF=autoplay;
					 break;
	    }
	    speed=speednum[hard-1];
		if(hard==5) eggscore=240-2*eggmax+2;
		else eggscore=10+40*hard-2*eggmax+2;
    }
}

游戏的核心函数strat(),控制游戏流程与交互

void start()
{
	//蛇的初始位置设置
	snake[0].X=16;snake[0].Y=16;
	snake[1].X=15;snake[1].Y=16;
	snake[2].X=14;snake[2].Y=16;
	snake[3].X=13;snake[3].Y=16;
	slong=3;score=0;dir=3;eggnum=0;
	
	char ch;int cheat=1;
	drawboard();getegg();
	color(15);
	gotoxy(0,32);printf("SCORE:%d",score);
	gotoxy(8,32);printf(" LENGTH:%d",slong);
	while (ch!=0x1B)
	{
		drawsnake();drawegg();
        Sleep(speed);//此处调整游戏速度
        if (kbhit())//键盘响应
        {	
			ch=getch();
			switch(ch)
			{
				case 'O':if(cheatopen)
				{
					cheat=(cheat+1)%3;
					color(15);gotoxy(0,33);printf("AUTO MODE!");
					if(dir!=2&&snake[0].Y!=1&&snake[0].Y!=30)dir=0;
				}break;
				case 'w':case 'W':
				if(cheat==2||dir==0||dir==2)break;dir=0;break;
				case 'a':case 'A':
				if(cheat==2||dir==1||dir==3)break;dir=1;break;
				case 's':case 'S':
				if(cheat==2||dir==0||dir==2)break;dir=2;break;
				case 'd':case 'D':
				if(cheat==2||dir==1||dir==3)break;dir=3;break;
				case ' ':{
							ch='a';
							gotoxy(0,33);color(15);printf("\t\t\r暂停!");
							while(ch!=0x1B&&ch!=' ')if(kbhit())ch=getch();
							gotoxy(0,33);printf("\t\t");	
							if(cheat==2){gotoxy(0,33);printf("AUTO MODE!");}
						 }
			}
		}

		if(cheat==2)cheatF();
		else if(cheat==0){gotoxy(0,33);printf("          ");cheat=1;}
        move(dir);
        if(die())break;
        if(eat())score+=eggscore;
       	getegg();
		color(15);
		gotoxy(3,32);printf("%d",score);
		gotoxy(12,32);printf("%d",slong);
	}
	gotoxy(0,33);
	color(15);
	printf("Game Over!");
	if(score>scoremax)
	{
		printf("New High Score!");
		scoremax=score;
	}
	printf("Press ESC to return!");
	while (getch()!=0x1B);
}

蛇的绘制与移动

void drawsnake()
{
	for(int i=slong;i>=0;i--) 
	{	color(14);gotoxy(snake[i].X,snake[i].Y);
		if(i==0){color(12);printf("◆");}
		else if(i==slong)printf("  ");
		else 
		{
			if(snake[i-1].Y==snake[i].Y&&snake[i].Y==snake[i+1].Y)printf("━");
		else if(snake[i-1].X==snake[i].X&&snake[i].X==snake[i+1].X)printf("┃");
		else if(snake[i-1].X==DEC(snake[i].X)&&INC(snake[i].Y)==snake[i+1].Y)printf("┓");
		else if(snake[i+1].X==DEC(snake[i].X)&&INC(snake[i].Y)==snake[i-1].Y)printf("┓");
		else if(snake[i-1].X==INC(snake[i].X)&&INC(snake[i].Y)==snake[i+1].Y)printf("┏");
		else if(snake[i+1].X==INC(snake[i].X)&&INC(snake[i].Y)==snake[i-1].Y)printf("┏");
		else if(snake[i-1].X==DEC(snake[i].X)&&DEC(snake[i].Y)==snake[i+1].Y)printf("┛");
		else if(snake[i+1].X==DEC(snake[i].X)&&DEC(snake[i].Y)==snake[i-1].Y)printf("┛");
		else if(snake[i-1].X==INC(snake[i].X)&&DEC(snake[i].Y)==snake[i+1].Y)printf("┗");
		else if(snake[i+1].X==INC(snake[i].X)&&DEC(snake[i].Y)==snake[i-1].Y)printf("┗");
		}
		if(i==slong)i=2;
	}
}
void move(int d)
{
	if(d==-1)return;
	for(int i=slong+1;i>0;i--)
	{
		snake[i].X=snake[i-1].X;
		snake[i].Y=snake[i-1].Y;
	}
	switch(d)
	{
		case 0:snake[0].Y=DEC(snake[0].Y);break;
		case 1:snake[0].X=DEC(snake[0].X);break;
		case 2:snake[0].Y=INC(snake[0].Y);break;
		case 3:snake[0].X=INC(snake[0].X);break;
	}
}

自动操作模式1:扫荡

思路:不断地上下扫荡,右侧一列无蛋可跳过,有蛋或者有蛇身则上下触边时转弯。
效果:100%玩到满分,速度较慢

void Pautoplay()
{
	if((snake[0].Y==1&&dir==0)||(snake[0].Y==30&&dir==2)){dir=3;return;}
	if(dir==3)
	{
		int px=snake[0].X;
		px=INC(INC(px));
		for(int i=1;i<slong;i++)
		if(px==snake[i].X){dir=(snake[0].Y==1?2:0);return;}
		px=snake[0].X;
		for(int i=1;i<=eggnum;i++)
		if(px==egg[i].X){dir=(snake[0].Y==1?2:0);return;}
	}
}
扫荡模式效果图:

扫荡模式

自动操作模式2:搜索

思路:扫描前左右三个方向,按一下规则选择方向:有蛋方向优先无蛋方向,蛋距离越近的方向越优先,无蛋时距离蛇身越远的方向越优先。优化点:识别出死胡同,即使有蛋也不进入死胡同方向。
效果:速度很快,但无法取得太高的分数

void autoplay()
{
	int num,retype,block,sc[4]={0};COORD p;
	for(int d=0;d<4;d++)
	{
		retype=0;num=0;p=snake[0];
		if(d!=(dir+2)%4)do{
			switch(d){
				case 0:p.Y=DEC(p.Y);break;
				case 1:p.X=DEC(p.X);break;
				case 2:p.Y=INC(p.Y);break;
				case 3:p.X=INC(p.X);break;}
			num++;
			for(int i=1;i<slong;i++)
			{if(p.X==snake[i].X&&p.Y==snake[i].Y){retype=1;break;}}
			if(retype==1||num==29){sc[d]=num;break;}
			for(int i=1;i<=eggnum;i++)
			{if(p.X==egg[i].X&&p.Y==egg[i].Y){retype=2;break;}}
			if(retype==2){sc[d]=60-num;break;}
		}while(1);
		
		p=snake[0];
		if(d!=(dir+2)%4)do
		{
			block=0;retype=0;
			switch(d)
				{
					case 0: p.Y=DEC(p.Y);
							for(int i=1;i<slong;i++)
							{if(p.X==snake[i].X&&p.Y==snake[i].Y){retype=1;break;}}
							for(int i=1;i<slong;i++)
							if(DEC(p.X)==snake[i].X&&p.Y==snake[i].Y){block++;break;}
							for(int i=1;i<slong;i++)
							if(INC(p.X)==snake[i].X&&p.Y==snake[i].Y){block++;break;}
							break;
					case 1: p.X=DEC(p.X);
							for(int i=1;i<slong;i++)
							{if(p.X==snake[i].X&&p.Y==snake[i].Y){retype=1;break;}}
							for(int i=1;i<slong;i++)
							if(p.X==snake[i].X&&DEC(p.Y)==snake[i].Y){block++;break;}
							for(int i=1;i<slong;i++)
							if(p.X==snake[i].X&&INC(p.Y)==snake[i].Y){block++;break;}
							break;
					case 2: p.Y=INC(p.Y);
							for(int i=1;i<slong;i++)
							{if(p.X==snake[i].X&&p.Y==snake[i].Y){retype=1;break;}}
							for(int i=1;i<slong;i++)
							if(DEC(p.X)==snake[i].X&&p.Y==snake[i].Y){block++;break;}
							for(int i=1;i<slong;i++)
							if(INC(p.X)==snake[i].X&&p.Y==snake[i].Y){block++;break;}
							break;
					case 3: p.X=INC(p.X);
							for(int i=1;i<slong;i++)
							{if(p.X==snake[i].X&&p.Y==snake[i].Y){retype=1;break;}}
							for(int i=1;i<slong;i++)
							if(p.X==snake[i].X&&DEC(p.Y)==snake[i].Y){block++;break;}
							for(int i=1;i<slong;i++)
							if(p.X==snake[i].X&&INC(p.Y)==snake[i].Y){block++;break;}
							break;
				}
			if((block!=2)&&(retype==0))break;
			if(retype==1)sc[d]=1;
		}while(1);
		
	}
	sc[(dir+2)%4]=-1;
	int maxsc=sc[0];int maxdir=0;
	for(int i=1;i<4;i++)if(maxsc<sc[i])maxsc=sc[i],maxdir=i;
	dir=maxdir;
}
搜索模式效果图:

搜索模式

猜你喜欢

转载自blog.csdn.net/Eyizoha/article/details/89388115