C语言实现贪吃蛇

本程序思想参考《C语言程序设计与游戏开发实践教程》,但并未照搬代码,难免疏漏,请多批评。
游戏截图:
在这里插入图片描述

#include<stdio.h>
#include<conio.h>
#include<windows.h>
#include<stdlib.h>
#include<time.h>

#pragma warning(disable:4996)//在vs环境下屏蔽C4996错误:因为使用getch()
#define High 21//地图的高度
#define Width 21//地图的宽度
#define Speed 200//控制每次刷新的频率(蛇的速度)

int num = 1;//给出大号食物与小号食物的比例
int direction=3, d =3 ;//初始化方向(1上2左3右4下)、d记录前进方向
int x_h, y_h;//蛇头位置
int scare=0,s=0;//判断应得大号食物的20分还是小号食物的10分
int a[High][Width] = { 0 };//将地图清零

void Point();//去掉光标
void Setxy(short x, short y);
void Food();//生成食物


void Start();//初始化
void Show();//打印
int UpdateWithoutInput(int *);//与输入无关操作
void UpdateWithInput();//与输入有关操作

int main()
{
	char c1;
	int live = 1;//判断游戏是否结束、
	Start();
	while (live)//一直运行,直到死亡
	{
		Sleep(Speed);	
		Show();
		UpdateWithInput();
		if (UpdateWithoutInput(&live)==1)
			continue;
	}
	system("cls");//清屏函数:只执行一次,且以下输出不能覆盖已输出的最后一次地图,故使用
	printf("\n\n\n\n\n\n\n\n\n\n\t\t\t\t\t\tYou lose."
	"\n\t\t\t\t\t    Your scare is %d."
	"\n\t\t\t\t\t      按任意键退出\n\n\n\n", scare);
	c1 = getch();//在失败界面上停留直到按下任意键
	return 0;
}
void Start()
{
	system("CLS");//清屏
	Point();//移去光标
	printf("\n\n\n\n\n\n\n\n\n\n\t\t\t\t\t请将输入法切换成英文,再按任意键开始\n"
	"\t\t\t\t\t\t使用wasd移动");
	srand((unsigned)time(NULL));//初始化随机数种子,以便food()中利用rand()生成伪随机数

	char b = getch();
	system("CLS");
	for (int i = 0; i < Width; i++)
	{
		a[0][i] = a[High - 1][i] = -1;
		if (i < High)
		{
			a[i][0] = a[i][Width - 1] = -1;
		}
	}//用一个for循环来定边界
	a[y_h = High / 2][x_h = Width / 2] = 1;//初始化蛇头为地图中间
	for (int i = 1; i <= 4; i++)
	{
		a[High / 2][Width / 2 - i] = i + 1;
	}//蛇身
	Food();//生成食物
}
void Point() {
	HANDLE fd = GetStdHandle(STD_OUTPUT_HANDLE);
	CONSOLE_CURSOR_INFO cinfo;
	cinfo.dwSize = 1;
	cinfo.bVisible = 0;
	SetConsoleCursorInfo(fd, &cinfo);
}
void Setxy(short x, short y)  //x,y为光标座标
{
	COORD coord = { x, y };
	SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), coord);
}
void Food() {	
	int a1, b1;//
	if (num % 4 == 0)//每三个小号食物生成一个大号食物
	{
		while (1) 
		{
			srand((unsigned)time(NULL));
			a1 = (int)(rand() % High);
			b1 = (int)(rand() % Width);
			if (a[a1][b1] == 0) {//找到一个空白处
				a[a1][b1] = -2;
				break;
			}
		}
	}
	else 
	{
		while (1) {
			a1 = (int)(rand() % (High - 3) + 1);
			b1 = (int)(rand() % (Width - 3) + 1);

			if (a[a1][b1] == 0 && a[a1 + 1][b1] == 0 && a[a1][b1 + 1] == 0 && a[a1 + 1][b1 + 1] == 0)
			{
				a[a1][b1] = a[a1 + 1][b1] = a[a1][b1 + 1] = a[a1 + 1][b1 + 1] = -2;		
				break;
			}

		}
	}
	num++;
}

void Show()
{
	Setxy(0, 0);//代替cls,界面不闪动,将坐标改到(0,0),覆盖上一次输出(在每次输出范围相同时使用)
	for (size_t i = 0; i < High; i++)
	{
		for (size_t j = 0; j < Width; j++)
		{
			
			if (a[i][j] == -1)//边界
				printf("■");
			else if (a[i][j] == 1)//蛇头
				printf("■");
			else if (a[i][j] > 1)//蛇身
				printf("□");
			else if (a[i][j] == -2)//食物
				printf("* ");
			else
				printf("  ");//空白
			
		}
		if (i == 10)
			printf("Scrae:%d", scare);
		printf("\n");
	}
}
int UpdateWithoutInput(int* p_live)
{
	int i, j;
	int max = 0, x_max = 0, y_max = 0;
	for (i = 0; i < High; i++)
	{
		for (j = 0; j < Width; j++)
		{
			if (a[i][j] > max)
			{
				y_max = i;
				x_max = j;
				max = a[i][j];//应交换值
			}
		}
	}//挑出最大值
	a[y_max][x_max] = 0;//将蛇尾置0,找出蛇尾:生成时即为最大值
	
	for (i = 0; i < High; i++)
	{
		for (j = 0; j < Width; j++)
		{
			if (a[i][j] > 0)
				a[i][j]++;
		}
	}

	if ((d + direction) % 5 == 0)//识别输入的方向与正在行进的方向相反,违规操作
	{
		d = direction;
		return 1;
	}
	d = direction;

	switch (direction)
	{
	case 1:--y_h; break;
	case 2:--x_h; break;
	case 3:++x_h; break;
	case 4:++y_h; break;
	}//修改蛇头位置

	if (a[y_h][x_h] == -2)//蛇头吃到了食物
	{
		int i1, j1, i2, j2;
		int max = 0;
		for (i = 0; i < High; i++)
		{
			for (j = 0; j < Width; j++)
			{
				if (a[i][j] == -2)
				{
					a[i][j] = 0;
					s++;
				}
				if (a[i][j] > max)
				{
					i1 = i; j1 = j;
					max = a[i][j];
				}
			}
		}
		if (s == 1)
			scare += 10;
		else
			scare += 20;
		s = 0;
		if (a[i1 - 1][j1] == max - 1)
		{
			a[i1 + 1][j1] = max + 1;
		}
		else if (a[i1 + 1][j1] == max - 1)
		{
			a[i1 - 1][j1] = max + 1;
		}
		else if (a[i1][j1 + 1] == max - 1)
		{
			a[i1][j1 - 1] = max + 1;
		}
		else if (a[i1][j1 - 1] == max - 1)
		{
			a[i1][j1 + 1] = max + 1;
		}
		Food();
	}
	else if (a[y_h][x_h] != 1 && a[y_h][x_h] != 0 )//蛇头咬住了自己或撞上了墙
	{
		*p_live = 0;
	}
	a[y_h][x_h] = 1;//设置新蛇头值为1
	return 0;
}
void UpdateWithInput()
{
	if (kbhit())//接受到键盘输入,函数值置1,否则置0。与main函数中while(live)结合,解决自动行走问题
	{
		char c = getch();
		switch (c)
		{
		case 'w':direction = 1; break;
		case 'a':direction = 2; break;
		case 'd':direction = 3; break;
		case 's':direction = 4; break;
		default:break;
		}
	}
}

教训:

  1. 几乎所有在两个函数都使用的变量都设置为全局变量,耦合性太强,不易修改调试

核心方法:

  1. 在移动时将蛇尾消去:在初始化时,将蛇尾设为地图数组的最大值,移动时将它设为与空白相同的值。
  2. 蛇头为1(以下皆为数组中的值),与蛇头更近(逻辑上)的值越小,蛇尾值最大。
  • 5
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值