用C语言写一个贪吃蛇游戏

一、游戏界面

 二、代码

consfun.h

/* WINDOWS命令窗口编程常用功能实现
   李乾宝,2010年12月
*/
void consinit(short x,short y);
void gotoxy(int,int);
void clrscr(void);
void textcolor(short newcolor);
void textbackground(short newcolor);
void textattr(short newattr);
void HideCursor();

snake.c

#include"consfun.h"
#include<conio.h>
#include<stdio.h>
#include<Windows.h>
#include<time.h>
#define UP 72
#define DOWN 80
#define LEFT 75
#define RIGHT 77

int i,j;
int mode=0;			//游戏模式
int n=4;			//n=1,2,3,4分别表示用户控制蛇的方向
int food_sum=0;		//累计吃的食物
int food=0;				//食物
int fx=5,fy=5;				//事物的坐标
int score=0;					//分数
int top_score[3];					//最高分
int speed=200;				//蛇的速度
int Mode=1;					//是否可以穿墙,1不可穿墙,2可以
#define high 28
#define width 45		//围墙的高和宽
int x=high/2,y=width/2; //蛇头的坐标
int snake[high][width+10]={0};//用来储存蛇,围墙,食物
void gx()						//蛇数组元素全部归0
{
	for(i=1;i<high-1;i++)
		for(j=1;j<width-1;j++)
		snake[i][j]=0;
}
void chu_shi_hua()				//每次重新开始,数据数据都要初始化
{
	
	n=4;			//n=1,2,3,4分别表示用户控制蛇的方向
	food_sum=0;		//累计吃的食物
	food=0;				//食物
	fx=5,fy=5;				//食物的坐标
	score=0;					//分数
	speed=300;				//蛇的速度(这个越大越慢)
	Mode=1;					//游戏模式
	x=high/2,y=width/2; //蛇头的坐标
	switch(mode)
	{
	case 1:for(j=5;j<width-5;j++)//基本围墙已经在page_1函数初始化,所以只需初始化新增的墙壁
			{
				snake[high/2-1][j]=-1;	
			};break;
	case 2:for(j=10;j<=width-10;j++)//基本围墙已经在page_1函数初始化,所以只需初始化新增的墙壁
			{
				if(j<20||j>25)
				{
				snake[high/4][j]=-1;
				snake[3*high/4][j]=-1;
				}
			}
			for(i=high/4;i<=3*high/4;i++)
			{
				if(i<high/4+5||i>3*high/4-5)
				{
				snake[i][10]=-1;
				snake[i][width-10]=-1;
				}
			};break;
	case 3:for(j=3;j<=width-3;j++)//基本围墙已经在page_1函数初始化,所以只需初始化新增的墙壁
			{
				if((j>=3&&j<=6)||(j>=width-7&&j<=width-4))//初始化四个角的四个横
				{
				snake[6][j]=-1;
				snake[high-7][j]=-1;
				}
				if((j>=width/4&&j<=width/4+6)||(j>=3*width/4-8&&j<=3*width/4))//初始化‘F’的第一横和‘T’的横
				{
				snake[8][j]=-1;
				}
				if((j>=width/4&&j<=width/4+6))//初始化‘F’的第二横
				{
				snake[high/2-1][j]=-1;
				}
			}
			for(i=3;i<=high-3;i++)
			{
				if((i>=3&&i<=6)||(i>=high-6&&i<high-3))//初始化四个角的四个竖
				{
				snake[i][6]=-1;
				snake[i][width-7]=-1;
				}
				if(i>=8&&i<=high-8)//初始化‘F’的竖
				{
				snake[i][width/4]=-1;
				}
				if(i>=8&&i<=high-8&&i!=high/2)//初始化‘T’的竖
				{
				snake[i][3*width/4-4]=-1;
				}
			};break;
	case 4:for(j=3;j<=width-3;j++)//基本围墙已经在page_1函数初始化,所以只需初始化新增的墙壁
			{
				if((j>=3&&j<=6)||(j>=width-7&&j<=width-4))//初始化四个角的四个横
				{
				snake[6][j]=-1;
				snake[high-7][j]=-1;
				}
		   }
			for(i=3;i<=high-3;i++)
			{
				if((i>=3&&i<=6)||(i>=high-6&&i<high-3))//初始化四个角的四个竖
				{
				snake[i][6]=-1;
				snake[i][width-7]=-1;
				}
			}
			for(i=0;i<high;i++)
				for(j=0;j<width;j++)
				{
				if(i==high/2+1&&(j==width/2-2||j==width/2))//鼻子
				{
				snake[i][j]=-1;
				}
				if(i==high/2-1&&j==width/2-1)//鼻子
				{
				snake[i][j]=-1;
				}
				if(i==high/2-4&&(j==width/2-3||j==width/2+1||j==width/2-6||j==width/2+4))//眉毛
				{
				snake[i][j]=-1;
				}
				if(i==high/2-5&&(j==width/2-4||j==width/2+2))
				{
				snake[i][j]=-1;
				}
				if(i==high/2-6&&(j==width/2-5||j==width/2+3))
				{
				snake[i][j]=-1;
				}
				if(i==high/2-7&&(j==width/2-6||j==width/2+4))
				{
				snake[i][j]=-1;
				}
				if(i==high/2-8&&(j==width/2-7||j==width/2+5||j==width/2-9||j==width/2+7))
				{
				snake[i][j]=-1;
				}
				if(i==high/2-9&&(j==width/2-8||j==width/2+6))
				{
				snake[i][j]=-1;
				}
				if(i==high/2+4&&j>=width/2-4&&j<=width/2+2)//嘴巴
				{
				snake[i][j]=-1;
				}
				if(i==high/2+5&&j>=width/2-2&&j<=width/2)//嘴巴
				{
				snake[i][j]=-1;
				}
				}
			;break;

	}
	snake[high/2][width/2]=1;	//以下将蛇头数值设为1

	for(i=1;i<5+food_sum;i++)		//以下将蛇身数值设为>1
	snake[high/2][width/2-i]=1+i;
	
}

void page_1()首页,功能选择页
{
	char  input,*p[5]={"  ","简单","困难","复杂","开挂"};
	gx();///将数组全部元素归0,因为在选择重新开始的时候要将游戏区内容清除
	for(j=0;j<width+10;j++)	//以下将围墙数值设为-1
	{
		snake[0][j]=-1;
		snake[high-1][j]=-1;		
	}
	for(i=0;i<high;i++)
	{
		snake[i][0]=-1;
		snake[i][width-1]=-1;
		snake[i][width+9]=-1;
	}
	while(1)
	{
	for(i=0;i<high;i++)
		for(j=0;j<width+10;j++)
		{
			if(snake[i][j]==-1)		//画出围墙
			{	
				textbackground(3);
				gotoxy(i,2*j);
				printf("  ");
			}
				if(i==high/2-6&&j==width/2-7)
				{textattr(06);
				gotoxy(i,2*j);
				printf("@@");
				textattr(32);
				printf("                               ");}
				if(i==high/2-3&&j==width/2-5)
				{textattr(03);
				gotoxy(i,2*j);
				printf("欢迎来到贪吃蛇游戏!");}
				if(i==high/2-1&&j==width/2-2)
				{textattr(03);
				gotoxy(i,2*j);
				printf("1.简单");}
				if(i==high/2&&j==width/2-2)
				{textattr(03);
				gotoxy(i,2*j);
				printf("2.困难");}
				if(i==high/2+1&&j==width/2-2)
				{textattr(03);
				gotoxy(i,2*j);
				printf("3.复杂");}
				if(i==high/2+2&&j==width/2-2)
				{textattr(03);
				gotoxy(i,2*j);
				printf("4.开挂");}
				if(i==high/2+4&&j==width/2-5)
				{textattr(06);
				gotoxy(i,2*j);
				printf("你选择的模式是:%s",p[mode]);}
				if(i==high/2+6&&j==width/2-5)
				{textattr(03);
				gotoxy(i,2*j);
				printf("请按回车开始游戏");}
			
		}
		
		if(kbhit())//当按键时执行
		{
			input=getch();
			if(input=='1')
			{mode=1;}
			if(input=='2')
			{mode=2;}
			if(input=='3')
			{mode=3;}
			if(input=='4')
			{mode=4;}
			if(input==13)13是回车的ASCII码,按回车开始游戏
			{break;}
		}
	}
}

void draw()		//画围墙、蛇、食物
{
	while(food==0&&snake[fx][fy]!=0)//当食物生成的位置不是空位置,重新生成
	{
		fx=rand()%(high-1)+1;
		fy=rand()%(width-2)+2;
	}
		snake[fx][fy]=-2;
		food=1;
	
	for(i=0;i<high;i++)
		for(j=0;j<width+10;j++)
		{
			
			if(snake[i][j]==-1)		//画出围墙
			{	
				textbackground(3);
				gotoxy(i,2*j);
				printf("  ");
			}
			else if(snake[i][j]==0&&j<width)	//画出空白的空间
			{	
				textattr(03);
				gotoxy(i,2*j);
				printf("  ");
			}
			else if(snake[i][j]==1)	//画出蛇头
			{
				textattr(06);
				gotoxy(i,2*j);
				printf("@@");
			}
			else if(snake[i][j]>1)	//画出蛇身
			{
				textattr(06);
				textbackground(2);
				gotoxy(i,2*j);
				printf("  ");
			}
			else if(snake[i][j]==-2)
			{
				textcolor(06);
				gotoxy(fx,2*fy);
				printf("●");
			}
			if(i==2&&j==width+2)
				{textattr(03);
				gotoxy(i,2*j);
				printf("w或↑键向上");}
			if(i==4&&j==width+2)
				{textattr(03);
				gotoxy(i,2*j);
				printf("s或↓键向下");}
			if(i==6&&j==width+2)
				{textattr(03);
				gotoxy(i,2*j);
				printf("a或←键向左");}
			if(i==8&&j==width+2)
				{textattr(03);
				gotoxy(i,2*j);
				printf("d或→键向右");}
			if(i==10&&j==width+2)
				{textattr(03);
				gotoxy(i,2*j);
				printf("空格键暂停");}
			if(i==12&&j==width+2)
				{textattr(03);
				gotoxy(i,2*j);
				printf("p键:慢");}
			if(i==14&&j==width+2)
				{textattr(03);
				gotoxy(i,2*j);
				printf("l键:中");}
			if(i==16&&j==width+2)
				{textattr(03);
				gotoxy(i,2*j);
				printf("m键:快");}
			if(i==18&&j==width+2)
				{textattr(03);
				gotoxy(i,2*j);
				printf("t键:模式1");
			   }
			if(i==20&&j==width+1)
				{textattr(03);
				gotoxy(i,2*j);
				printf("y键:模式2(穿墙)");
				}
			if(i==22&&j==width+2)
				{textattr(07);
				gotoxy(i,2*j);
				printf("当前模式:%d",Mode);}
			if(i==24&&j==width+2)
				{textattr(03);
				gotoxy(i,2*j);
				printf("当前分数:");
				textattr(06);
				gotoxy(i,2*j+9);
				printf("%d",score);
				}

		}
}
	
void move()
{
	FILE *fp;						//文件指针,用于读取最高分和保存最高分
	int max=0;
	int oldTail_i,oldTail_j;//旧尾巴的位置
	int oldHead_i,oldHead_j;//旧蛇头的位置
	int newHead_i,newHead_j;//新蛇头的位置
	char input;
	for(i=1;i<high-1;i++)		//让蛇的每一节数字加一
		for(j=1;j<width-1;j++)
		{
			if(snake[i][j]>0)
			{
				snake[i][j]++;
				if(max<snake[i][j])//让数字最大的一节变0
					{
						max=snake[i][j];
						//snake[i][j]=0;/
						oldTail_i=i;
						oldTail_j=j;
					}
				if(snake[i][j]==2)
				{
					oldHead_i=i;
					oldHead_j=j;
				}
			}
		}
		
		if(kbhit())//当按键时执行
		{
			input=getch();
			if((input=='w'||input==72)&&n!=2)    //正在向上走,就不能执行向下
			{n=1;}
			if((input=='s'||input==80)&&n!=1)
			{n=2;}
			if((input=='a'||input==75)&&n!=4)
			{n=3;}
			if((input=='d'||input==77)&&n!=3)
			{n=4;}
			if(input=='p')
			{speed=200;}
			if(input=='l')
			{speed=100;}
			if(input=='m')
			{speed=0;}
			if(input=='t')
			{Mode=1;}
			if(input=='y')
			{Mode=2;}
			if(input==' ')
			{while(1)
			{
				if(kbhit()!=0)
					break;
			}
			}
			if(input==27)//27为Esc键的ASCII码
			{textattr(00);
			system("pause");
			}
		}
		if(n==1)
		{
			newHead_i=oldHead_i-1;newHead_j=oldHead_j;
		}
		if(n==2)
		{
			newHead_i=oldHead_i+1;newHead_j=oldHead_j;
		}
		if(n==3)
		{
			newHead_i=oldHead_i;newHead_j=oldHead_j-1;
		}
		if(n==4)
		{
			newHead_i=oldHead_i;newHead_j=oldHead_j+1;
		}
		if(snake[newHead_i][newHead_j]==-2)	//如果没有吃到食物,蛇的最后一节变为0,否则不变(即蛇的长度+1)
			{
			food=0;
			food_sum++;
			score++;
			}
		else switch(Mode)
		{
		case 1:if(snake[newHead_i][newHead_j]>1||snake[newHead_i][newHead_j]==-1)		//模式1,不能穿墙
					{
						fp=fopen("top_score.dat","wb+");
						fseek(fp,4*(mode-1),1);
						fread(&top_score[mode-1],sizeof(int),1,fp);
						if(score>top_score[mode-1])
							top_score[mode-1]=score;
						fwrite(&top_score[mode-1],sizeof(int),1,fp);
							while(1)
							 {
								textattr(03);
								gotoxy(high/2,width-7);
								printf("GAME OVER!");
								gotoxy(high/2+1,width-8);
								printf("按r键重新开始");
								gotoxy(high/2+2,width-8);
								printf("按b键退出游戏");
								textattr(06);
								gotoxy(high/2+4,width-10);
								printf("历史最高分:%d 你的分数:%d",top_score[mode-1],score);
								if(kbhit())//当按键时执行
								{
									input=getch();
									if(input=='r')
										main();
									if(input=='b')
									{
										gotoxy(high/2+4,width-8);
										exit(0);
									}
								}
						
							}
					}
				else snake[oldTail_i][oldTail_j]=0;/如果没有吃到食物,蛇的最后一节变为0,否则不变(即蛇的长度+1)
					break;
		case 2:if(snake[newHead_i][newHead_j]==-1&&n==1)							//模式2,可以穿墙
			   {newHead_i=high-2;}
			   else if(snake[newHead_i][newHead_j]==-1&&n==2)
			   {newHead_i=1;}
			   else if(snake[newHead_i][newHead_j]==-1&&n==3)
			   {newHead_j=width-2;}
			   else if(snake[newHead_i][newHead_j]==-1&&n==4)
			   {newHead_j=1;}
			   else if(snake[newHead_i][newHead_j]>1)
			   {
						fp=fopen("top_score.dat","wb+");
						fseek(fp,4*(mode-1),1);
						fread(&top_score[mode-1],sizeof(int),1,fp);
						if(score>top_score[mode-1])
							top_score[mode-1]=score;
						fwrite(&top_score[mode-1],sizeof(int),1,fp);
				   while(1)
				   {
						textattr(03);
						gotoxy(high/2,width-7);
						printf("GAME OVER!");
						gotoxy(high/2+1,width-8);
						printf("按r键重新开始");
						gotoxy(high/2+2,width-8);
						printf("按b键退出游戏");
						textattr(06);
						gotoxy(high/2+4,width-10);
						printf("最高分:%d 你的分数:%d",top_score[mode-1],score);
						if(kbhit())//当按键时执行
						{
							input=getch();
							if(input=='r')
								main();
							if(input=='b')
							{
								gotoxy(high/2+4,width-8);
								exit(0);
							}
						}
				   }
				}
				else snake[oldTail_i][oldTail_j]=0;/如果没有吃到食物,蛇的最后一节变为0,否则不变(即蛇的长度+1)
					break;
		}
		snake[newHead_i][newHead_j]=1;
}
int main()
{
	system("cls");
	HideCursor();
	srand((unsigned int)time(0));
	system("title 贪吃蛇");
	system("mode con cols=110 lines=28");//设定窗口大小
	SetWindowLong(GetConsoleWindow(),GWL_STYLE,GetWindowLong(GetConsoleWindow(),GWL_STYLE) & ~WS_SIZEBOX & ~WS_MAXIMIZEBOX & ~WS_MINIMIZEBOX);
	page_1();					//将page_1函数放在chu_shi_hua()函数前,待用户选择游戏模式再对数组进行初始化
	chu_shi_hua();				//数据初始化
	while(1)///游戏循环
	{
		
		Sleep(speed);
		draw();
		move();
		textattr(03);
		
	}

	
	return 0;
}

consfun.c

/*	WINDOWS命令窗口编程常用功能
	程序设计:李乾宝
	修改时间:2010年12月--2017年1月
*/

#include <windows.h>
#include <wincon.h>

/* 设置光标位置:使后续的输出从屏幕的x行y列输出
   参数:x-行(从0开始)
         y-列(从0开始)
*/
void gotoxy(int x, int y)
{
    COORD Position;
	Position.X=y; Position.Y=x;
	SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE),Position);
}

/* 功能:初始化命令窗口,设置窗口的行列数
   参数:y-行数,x-列数
*/
void consinit(short x, short y)
{  
    COORD scr={x,y};
    CONSOLE_SCREEN_BUFFER_INFO info;
	SMALL_RECT rect={0,0,x-1,y-1};
	HANDLE stdOut = GetStdHandle(STD_OUTPUT_HANDLE);
	GetConsoleScreenBufferInfo(stdOut,&info);	//获取命令窗口信息
	//如果原屏缓冲区小必须先设置新的缓冲区大小,再调整窗口大小
	if(info.dwSize.X*info.dwSize.Y<x*y)			
	{	SetConsoleScreenBufferSize(stdOut,scr);
		SetConsoleWindowInfo(stdOut,TRUE,&rect);//缓冲区比需要的小时会出错
	}
	else
	{	SetConsoleWindowInfo(stdOut,TRUE,&rect);
		SetConsoleScreenBufferSize(stdOut,scr);	//设置值比窗口区域需要的小时会出错
	}
 	gotoxy(0,0);

}

/* 清屏:用当前的背景颜色清除屏幕,并将光标定位在左上角(0,0)
   参数:无
*/
void clrscr(void)
{
    COORD scr={0,0};
	DWORD num;
	HANDLE stdOut = GetStdHandle(STD_OUTPUT_HANDLE);
	CONSOLE_SCREEN_BUFFER_INFO info;
    GetConsoleScreenBufferInfo(stdOut,&info);
	num=info.dwMaximumWindowSize.X*info.dwMaximumWindowSize.Y;
    FillConsoleOutputAttribute(stdOut,(WORD)info.wAttributes,num,scr,&num);
	FillConsoleOutputCharacter(stdOut,' ',num,scr,&num);
	gotoxy(0,0);
}

/* 设置字体颜色:使以后的输出字体前景颜色改为给定值
   参数:newcolor-新的颜色值
   颜色取值范围为:0x00 ~ 0x0f
*/
void textcolor(short newcolor)
{
    HANDLE stdOut = GetStdHandle(STD_OUTPUT_HANDLE);
	CONSOLE_SCREEN_BUFFER_INFO info;
    GetConsoleScreenBufferInfo(stdOut,&info);
	SetConsoleTextAttribute(stdOut, (WORD)(newcolor|info.wAttributes & 0xF0));
}

/* 设置背景颜色:使以后的输出的背景颜色改为给定值
   参数:newcolor-新的颜色值
   颜色取值范围为:0x00 ~ 0x0f
*/
void textbackground( short newcolor)
{
    HANDLE stdOut = GetStdHandle(STD_OUTPUT_HANDLE);
    CONSOLE_SCREEN_BUFFER_INFO info;
	GetConsoleScreenBufferInfo(stdOut,&info);
	SetConsoleTextAttribute(stdOut,(WORD)((newcolor<<4)|(info.wAttributes&0x0F)));
}

/* 设置文本属性:同时设定前景和背景颜色
   参数:newattr-新的属性值
   颜色取值范围为:0x00 ~ 0xff,低4位为前景颜色,高4位为背景颜色
*/
void textattr(short newattr)
{
    HANDLE stdOut = GetStdHandle(STD_OUTPUT_HANDLE);
	SetConsoleTextAttribute(stdOut,newattr);
}


void HideCursor()			//隐藏光标的函数,网上搜的,需加头文件<windows.h>
{CONSOLE_CURSOR_INFO cursor_info={1,0};
SetConsoleCursorInfo(GetStdHandle(STD_OUTPUT_HANDLE),&cursor_info);
}

        上面的头函数和窗口编程代码系汕大李乾宝老师所编写,我写的是snake.c 。snake.c 是大一期末熬一个通宵码的代码,比较冗长,不过勉强可以玩一下,我懒得改啦~,哈哈哈感兴趣的小伙伴可以对代码进行模块化以及相应的删改优化。

  • 3
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值