C语言 【贪吃蛇】 完整代码实现 源码分享

本文详细介绍了如何使用C语言编写一款贪吃蛇游戏,包括游戏结构、关键函数、控制台操作以及游戏逻辑,展示了从创建蛇身、绘制地图到处理用户输入和游戏结束的全过程。
摘要由CSDN通过智能技术生成

成品展示

贪吃蛇

代码分享

test.c:

#define _CRT_SECURE_NO_WARNINGS 1
#include "snake.h"


void test()
{
	int ch;
	do
	{
		//创建贪吃蛇
		Snake snake = { 0 };

		GameStart(&snake);//游戏开始前的初始化
		GameRun(&snake);//玩游戏的过程
		GameEnd(&snake);//善后的工作
		SetPos(25,17);
		printf("再来一局吗?(1/0):");
		scanf("%d", &ch);
		getchar();//清理\n
	} while (ch == 1);
}


int main()
{
	//修改适配本地中文环境
	setlocale(LC_ALL, "");

	test();//贪吃蛇游戏测试
	SetPos(0, 36);
	return 0;
}

snake.h:


#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#include <stdlib.h>
#include <locale.h>
#include <windows.h>
#include <stdbool.h>

#define WALL L'¤'
#define BODY L'●'
#define POS_X 24
#define POS_Y 6
#define Food L'☆'
#define KEY_PAESS(VK) ((GetAsyncKeyState(VK) & 0X1) ? 1:0)

enum GAME_STATUS//游戏状态
{
	OK = 1,
	ESC,
	KILL_BY_WALL,
	KILL_BY_SELF
};

enum DIRECTION//方向
{
	UP=1,
	DOWN,
	LEFT,
	RIGHT
};

//蛇身节点定义
typedef struct SnakeNode
{
	int x;
	int y;
	struct SnakeNode* next;
}SnakeNode,* pSnakeNode;

//蛇身维护
typedef struct Snake
{
	pSnakeNode pSnake;//维护整条蛇的指针
	pSnakeNode pFood;//指向食物的指针
	int Score;//当前累计的分数
	int FoodWeight;//一个食物的分数
	int SleepTime;//蛇休眠的时间,休眠的时间越短,蛇的速度越快,越长,越慢
	enum GAME_STATUS status;//游戏当前状态
	enum DIRECTION dir;//蛇当前走的方向
}Snake,* pSnake;

//获取光标位置
void SetPos(int x, int y);

//游戏开始前的初始化
void GameStart(pSnake ps);

//游戏开始欢迎界面的打印
void WelcomeToGame();

//打印地图
void CreateMap();

//初始化蛇身
void InitSnake(pSnake ps);

//创建食物
void CreateFood(pSnake ps);

//玩游戏的过程
void GameRun(pSnake ps);

//打印帮助信息
void PrintHelpInfo();

//蛇的移动
void SnakeMove(pSnake ps);

//判断下一点是否是食物
int NextIsFood(pSnake ps,pSnakeNode pNext);

//是食物吃掉
void EatFood(pSnake ps, pSnakeNode pNext);

//不是食物不吃
void NotEatFood(pSnake ps, pSnakeNode pNext);

//检测是否撞墙
void KillByWall(pSnake ps);

//检测是否撞到自己
void KillBySelf(pSnake ps);

//游戏结束
void GameEnd(pSnake ps);

snake.c:

#define _CRT_SECURE_NO_WARNINGS 1
#include "snake.h"

//获取光标位置
void SetPos(int x,int y)
{
	//获得设置句柄
	HANDLE handle = GetStdHandle(STD_OUTPUT_HANDLE);
	//根据句柄设置光标的位置
	COORD pos = { x,y };
	SetConsoleCursorPosition(handle, pos);
}

void WelcomeToGame()
{
	//欢迎信息
	SetPos(49, 15);
	printf("Welcome to gluttonous Snake Mini Game");
	SetPos(57, 24);
	system("pause");
	system("cls");

	//功能介绍信息
	SetPos(45, 13);
	printf("用 ↑.↓.←.→ 来控制蛇的移动,F3是加速,F4是减速\n");
	SetPos(55, 17);
	printf("加速将能得到更高的分数");
	SetPos(57, 24);
	system("pause");
	system("cls");
}

void InitSnake(pSnake ps)
{
	//创建四个蛇身
	pSnakeNode cur = NULL;

	for (int i = 0; i < 4; i++)
	{
		cur = (pSnakeNode)malloc(sizeof(SnakeNode));
		if (cur == NULL)
		{
			perror("InitSnake malloc:");
			return 1;
		}

		cur->x = POS_X + i * 2;
		cur->y = POS_Y;
		cur->next = NULL;

		//头插法
		if (ps->pSnake == NULL)
		{
			ps->pSnake = cur;
		}
		else
		{
			cur->next = ps->pSnake;
			ps->pSnake = cur;
		}
	}

	//打印蛇身
	//cur = ps->pSnake;
	while (cur)
	{
		SetPos(cur->x, cur->y);
		wprintf(L"%lc", BODY);
		cur = cur->next;
	}

	//贪吃蛇其他信息初始化
	ps->dir = RIGHT;
	ps->FoodWeight = 10;
	ps->pFood = NULL;
	ps->Score = 0;
	ps->SleepTime = 200;
	ps->status = OK;
}


void CreateMap()
{
	//上
	SetPos(0, 0);
	for (int i = 0; i < 70; i += 2)
	{
		wprintf(L"%lc",WALL);
	}
	//下
	SetPos(0, 35);
	for (int i = 0; i < 70; i += 2)
	{
		wprintf(L"%lc", WALL);
	}
	//左
	SetPos(0, 1);
	for (int i = 0; i < 34; i++)
	{
		wprintf(L"%lc\n", WALL);
	}
	//右
	for (int i = 1; i < 35; i++)
	{
		SetPos(68, i);
		wprintf(L"%lc\n", WALL);
	}
}

void CreateFood(pSnake ps)
{
	pSnakeNode cur = ps->pSnake;
	//生成随机位置
	int x = 0;
	int y = 0;

	again:
	do
	{
		x = rand() % 65 + 2;
		y = rand() % 34 + 1;
	} while (x % 2 != 0);

	//判断是否与蛇身重复
	while (cur)
	{
		if (cur->x == x && cur->y == y)
		{
			goto again;
		}
		cur = cur->next;
	}

	//创建食物
	pSnakeNode pFood = (pSnakeNode)malloc(sizeof(SnakeNode));
	if (pFood == NULL)
	{
		perror("CreateFood()::malloc:");
			return 1;
	}

	pFood->x = x;
	pFood->y = y;
	ps->pFood = pFood;

	SetPos(x, y);
	wprintf(L"%lc", Food);
}

void GameStart(pSnake ps)
{
	//设置控制台信息,窗口大小,窗口名
	system("mode con cols=140 lines=40");
	system("title 贪吃蛇");

	//光标隐藏
	HANDLE handle = GetStdHandle(STD_OUTPUT_HANDLE);
	CONSOLE_CURSOR_INFO CursorInfo;
	GetConsoleCursorInfo(handle, &CursorInfo);//获取控制光标的信息
	CursorInfo.bVisible = false;
	SetConsoleCursorInfo(handle, &CursorInfo);

	//打印欢迎信息
	WelcomeToGame();
	//绘制地图
	CreateMap();
	//初始化蛇
	InitSnake(ps);
	//创建食物
	CreateFood(ps);
}

void PrintHelpInfo()
{
	SetPos(80, 16);
	printf("温馨提示:");
	SetPos(80, 18);
	printf("1、按空格开始游戏");
	SetPos(80, 19);
	printf("2、不能穿墙,不能咬到自己");
	SetPos(80, 20);
	printf("3、用 ↑.↓.←.→ 来控制蛇的移动");
	SetPos(80, 21);
	printf("4、F3是加速,F4是减速");
	SetPos(80, 22);
	printf("5、速度越快食物分值越高,速度越慢食物分值越低");
	SetPos(80, 23);
	printf("6、退出游戏按ESC,暂停按空格");
	/*SetPos(88, 27);
	printf("版权@宋东京先生");*/
}

void pause()
{
	while (1)
	{
		Sleep(200);
		if (KEY_PAESS(VK_SPACE))
		{
			break;
		}
	}
}

int NextIsFood(pSnake ps, pSnakeNode pNext)
{
	if (ps->pFood->x == pNext->x && ps->pFood->y == pNext->y)
	{
		return 1;
	}
	else
	{
		return 0;
	}
}

void EatFood(pSnake ps, pSnakeNode pNext)
{
	//吃掉头插
	pNext->next = ps->pSnake;
	ps->pSnake = pNext;
	//打印蛇身
	pSnakeNode pcur = ps->pSnake;
	while (pcur)
	{
		SetPos(pcur->x, pcur->y);
		wprintf(L"%lc", BODY);
		pcur = pcur->next;
	}

	//总分变化
	ps->Score += ps->FoodWeight;

	//释放旧的食物,产生新的食物
	free(ps->pFood);
	CreateFood(ps);
}

void NotEatFood(pSnake ps, pSnakeNode pNext)
{
	//头插
	pNext->next = ps->pSnake;
	ps->pSnake = pNext;

	//释放尾节点
	pSnakeNode cur = ps->pSnake;
	while (cur->next->next)
	{
		SetPos(cur->x, cur->y);
		wprintf(L"%lc", BODY);
		cur = cur->next;
	}
	//将尾节点的位置打印成空白字符
	SetPos(cur->next->x, cur->next->y);
	printf("  ");

	free(cur->next);
	cur->next = NULL;
}

void KillByWall(pSnake ps)
{
	if (ps->pSnake->x == 0 ||
		ps->pSnake->x == 68 ||
		ps->pSnake->y == 0 ||
		ps->pSnake->y == 35)
	{
		ps->status = KILL_BY_WALL;
	}
}

void KillBySelf(pSnake ps)
{
	pSnakeNode cur = ps->pSnake->next;
	while (cur)
	{
		if (cur->x == ps->pSnake->x && cur->y == ps->pSnake->y)
		{
			ps->status = KILL_BY_SELF;
		}
		cur = cur->next;
	}
}

void SnakeMove(pSnake ps)
{
	pSnakeNode pNext = (pSnakeNode)malloc(sizeof(SnakeNode));
	if (pNext == NULL)
	{
		perror("SnakeMove()::malloc()");
		return;
	}
	pNext->next = NULL;
	//判断移动方向
	switch (ps->dir)
	{
	case UP:
		pNext->x = ps->pSnake->x;
		pNext->y = ps->pSnake->y - 1;
		break;
	case DOWN:
		pNext->x = ps->pSnake->x;
		pNext->y = ps->pSnake->y + 1;
		break;
	case LEFT:
		pNext->x = ps->pSnake->x - 2;
		pNext->y = ps->pSnake->y;
		break;
	case RIGHT:
		pNext->x = ps->pSnake->x + 2;
		pNext->y = ps->pSnake->y;
		break;
	}

	//判断下一个位置是否为食物
	if (NextIsFood(ps, pNext))
	{
		//是食物吃掉
		EatFood(ps,pNext);
	}
	else
	{
		//不是食物就正常走一步
		NotEatFood(ps,pNext);
	}

	//检测是否撞墙
	KillByWall(ps);

	//检测撞自己
	KillBySelf(ps);
}

void GameRun(pSnake ps)
{
	//提示信息
	PrintHelpInfo();

	do
	{
		//当前分数情况
		SetPos(80, 10);
		printf("当前得分:%5d", ps->Score);
		SetPos(80, 12);
		printf("当前每个食物de分值:%02d", ps->FoodWeight);
		//检测按键
		//↑↓←→ ESC 空格 F3 F4
		if (KEY_PAESS(VK_UP) && ps->dir != DOWN)
		{
			ps->dir = UP;
		}
		else if (KEY_PAESS(VK_DOWN) && ps->dir != UP)
		{
			ps->dir = DOWN;
		}
		else if (KEY_PAESS(VK_LEFT) && ps->dir != RIGHT)
		{
			ps->dir = LEFT;
		}
		else if (KEY_PAESS(VK_RIGHT) && ps->dir != LEFT)
		{
			ps->dir = RIGHT;
		}
		else if (KEY_PAESS(VK_ESCAPE))
		{
			ps->status = ESC;
			break;
		}
		else if (KEY_PAESS(VK_SPACE))
		{
			pause();
		}
		else if (KEY_PAESS(VK_F3))
		{
			if (ps->SleepTime > 20)
			{
				ps->SleepTime -= 30;
				ps->FoodWeight += 2;
			}
		}
		else if (KEY_PAESS(VK_F4))
		{
			if (ps->FoodWeight > 2)
			{
				ps->SleepTime += 30;
				ps->FoodWeight -= 2;
			}
		}

	//走一步
		SnakeMove(ps);
		//睡眠时间
		Sleep(ps->SleepTime);
	} while (ps->status == OK);
}

void GameEnd(pSnake ps)
{
	SetPos(70, 20);
	switch (ps->status)
	{
	case ESC:
		SetPos(30, 15);
		printf("已退出游戏");
		break;
	case KILL_BY_WALL:
		SetPos(25, 15);
		printf("很遗憾,撞墙游戏结束");
		break;
	case KILL_BY_SELF:
		SetPos(23, 15);
		printf("很遗憾,撞到自己游戏结束");
		break;
	}

	//释放空间
	pSnakeNode cur = ps->pSnake;
	pSnakeNode del = NULL;

	while (cur)
	{
		del = cur;
		cur = cur->next;
		free(del);
	}

	free(ps->pFood);
	ps = NULL;
}

如果有用的话可以点赞支持一下,拜托了🙏在这里插入图片描述

  • 44
    点赞
  • 37
    收藏
    觉得还不错? 一键收藏
  • 16
    评论
CJds_snakeView类 Struct list { int x; int y; }; 点结构,x表示横坐标,y表示纵坐标。 成员变量: int start;start=1开始游戏,start=0停止游戏。 int difficulty;表示游戏难度,等于10表示容易,等于6表示普通,等于3表示困难,等于2表示特困。 int last_aspect;表示蛇之前前进的方向,用于判断防止蛇逆行的。 int can_new;表示是否添加新食物。等于1表示产生新的食物,等于0表示产生之后不不要再产生食物。 int count;表示蛇的节数; CRect rct;表示矩形方格; list a[100];保存蛇的长度; list b;用来保存蛇头的坐标位置; int aspect;表示蛇前进的方向 等于0表示蛇向右前进; 等于1表示蛇向下前进; 等于2表示蛇向左前进; 等于3表示蛇向上前进; int xx;表示食物的x坐标; int yy;表示食物的y坐标; 成员函数: void Init();初始化蛇的节数,并确定了节数的坐标位置。首先初始化蛇的前进方向aspect=0是向右前进的,last_aspect=0用来保存蛇当前前进的方向。并且初始化蛇的节数为4节,并指明了它们的坐标位置。can_new=1表示产生一个新的食物。 void aliveordie();判断蛇是死还是活着。有两种情况,一种是蛇自己撞到自己,表示死了,start=0从新开始游戏,另一种是超出了界面(20,20,420,420)撞到了墙start=0也从新开始游戏。 double random(double start,double end);在start和end坐标之间产生随即数; 消息处理事件: (1)void onTimer(UINT nIDEvent);在OnCreate()创建计时器SetTimer(),来触发OnTimer事件,所以要首先销毁时间计时器,然后调用aliveordie()函数判断蛇的生死情况,CString str_count; str_count.Format("分数:%d",count-4)显示蛇的节数,TextOut()指出坐标输出文本,创建设备上下文,和创建位图。然后把位图选择到设备上下文上,填充颜色为白色。 在位图上绘制两个矩形线框: Rectangle(CRect(10,10,440,440)); Rectangle(CRect(20,20,430,430)); 显示位图(BitBlt(0,0,1000,1000,&MenDC,0,0,SRCCOPY)); 判断如果游戏开始(start==1),b.x=a[0].x用b.x来保存蛇头的横坐标;b.y=a[0].y用b.y来保存蛇头的纵坐标。如果aspect==0表示蛇向右前进,aspect=1表示蛇向下前进,aspect==2表示蛇向左前进,aspect==3表示蛇向上前进,a[1].x=b.x;a[1].y=b.y;把蛇头的坐标付给了a[1],说明蛇头改变了前进的方向。绘制蛇头矩形方格,并且设置蛇头的颜色为红色,蛇身是三个黄色的矩形方格。if (a[0].x==xx && a[0].y==yy)判断蛇头的位置与食物的关系,现在的情况说明蛇吃了食物,蛇的节数加一,各个节数的坐标向前进一位。调用random()函数产生随即的食物,然后判断食物随即产生的位置与蛇的位置,如果产生的食物在蛇身体上,要从新调用random()函数随即再产生食物。产生的食物颜色为绿色。int m_timer=SetTimer(1,difficulty*30,NULL);计时间隔,计时间隔的快慢是根据难易程度的不而确定的。销毁位图、销毁设备上下文。 (2)OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags);判断蛇当前前进的方向,当aspect返回0,表示蛇前进的方向是向右,当aspect返回1,表示蛇前进的方向是下,当aspect返回2,表示蛇前进的方向是向左,当aspect返回3,表示蛇前进的方向是向上。 (3)点击菜单按钮调用以下函数: OnDifficultyEasy();OnDifficultyHard();OnDifficultyNormal();OnDifficultyVeryhard();OnButtonStart();
好的,下面是完整的 C 语言贪吃蛇游戏代码: ```c #include <stdio.h> #include <stdlib.h> #include <conio.h> #include <time.h> #include <windows.h> #define BOARD_WIDTH 20 #define BOARD_HEIGHT 20 typedef struct { int x; int y; } SnakePart; typedef struct { SnakePart parts[100]; int length; } Snake; int board[BOARD_WIDTH][BOARD_HEIGHT]; void init_board() { for (int i = 0; i < BOARD_WIDTH; i++) { for (int j = 0; j < BOARD_HEIGHT; j++) { board[i][j] = 0; } } } void draw_snake(Snake* snake) { for (int i = 0; i < snake->length; i++) { board[snake->parts[i].x][snake->parts[i].y] = 1; } } void move_snake(Snake* snake, int direction) { SnakePart new_part = { snake->parts[0].x, snake->parts[0].y }; switch (direction) { case 0: new_part.y--; break; // 上 case 1: new_part.x++; break; // 右 case 2: new_part.y++; break; // 下 case 3: new_part.x--; break; // 左 } snake->length++; for (int i = snake->length - 1; i > 0; i--) { snake->parts[i] = snake->parts[i - 1]; } snake->parts[0] = new_part; board[snake->parts[snake->length - 1].x][snake->parts[snake->length - 1].y] = 0; snake->length--; } int check_food(Snake* snake, SnakePart food) { if (snake->parts[0].x == food.x && snake->parts[0].y == food.y) { snake->length++; snake->parts[snake->length - 1] = food; return 1; } return 0; } int main() { init_board(); Snake snake = { { {10, 10}, {10, 9}, {10, 8} }, 3 }; draw_snake(&snake); SnakePart food = { 5, 5 }; board[food.x][food.y] = 2; int direction = 0; while (1) { if (kbhit()) { switch (getch()) { case 'w': direction = 0; break; case 'd': direction = 1; break; case 's': direction = 2; break; case 'a': direction = 3; break; } } move_snake(&snake, direction); if (check_food(&snake, food)) { srand(time(NULL)); food.x = rand() % BOARD_WIDTH; food.y = rand() % BOARD_HEIGHT; board[food.x][food.y] = 2; } system("cls"); for (int i = 0; i < BOARD_WIDTH; i++) { for (int j = 0; j < BOARD_HEIGHT; j++) { if (board[i][j] == 0) { printf(" "); } else if (board[i][j] == 1) { printf("O"); } else if (board[i][j] == 2) { printf("*"); } } printf("\n"); } Sleep(100); } return 0; } ``` 在这个代码中,我们使用了 Windows.h 库中的 Sleep 函数来控制游戏循环的速度,以保证游戏运行的流畅性。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值