C语言实现贪吃蛇

适合新手的基础版贪吃蛇
<graphics.h>必须得安装easyx,挺简单的,还有官方文档

snake.h

/*
	绘制蛇身的头文件
*/

#pragma once

#include <graphics.h>
#include <stdlib.h>

/*
	贪吃蛇使用链表实现
	蛇身就是一个一个的点连起来
	每一个点的定义如下
	x坐标,y坐标以及到下一个点的指针
*/
typedef struct snake_node
{
	int x;
	int y;
	snake_node *next;
}Node;

/*
	定义蛇走的方向: L-left R-right T-top B-bottom D-未赋值
*/
enum direction {L, R, T, B, D};

/*
	贪吃蛇的每一个点用正方形实现
	定义正方形的边长SIDE
*/
#define SIDE 20

/*
	初始长度INITLENGTH
*/
#define INITLENGTH 5

/*
	获得蛇头
*/
Node* get_head();

/*
	获得蛇身长度
*/
int get_body();

/*
	设置蛇身长度
*/
int get_body();

/*
	设置蛇头方向
*/
void set_dir(direction d);

/*
	获得蛇头方向
*/
direction get_dir();

/*
	初始化蛇身
*/
void init_snake();

/*
	绘画蛇身
*/
void paint_snake();

/*
	重置贪吃蛇
*/
void reset_snake(int new_x, int new_y);

/*
	移动贪吃蛇
*/
void move_snake();

/*
	增加蛇的长度
*/
void add_snake();

snake.cpp

#include "snake.h"

/*
	定义蛇头,因为是链表,有蛇头就能够访问整个蛇
*/
Node *head;

/*
	当前蛇的长度
*/
int body;

/*
	蛇头的方向
*/
direction dir = R;

/*
	获得蛇头
*/
Node* get_head()
{
	return head;
}

/*
	获得蛇身长度
*/
int get_body()
{
	return body;
}

/*
	设置蛇身长度
*/
void set_body(int l)
{
	body = l;
}

/*
	获得蛇头方向
*/
direction get_dir()
{
	return dir;
}

/*
	设置蛇头方向
*/
void set_dir(direction d)
{
	dir = d;
}

/*
	初始化蛇身的某一个结点
*/
void init_node(Node *node, int x_cor, int y_cor)
{
	node->x = x_cor;
	node->y = y_cor;
	node->next = (Node *)malloc( sizeof(Node) );
}

/*
	初始化蛇身
*/
void init_snake()
{
	// 游标cur从蛇头开始,遍历整个蛇身
	// 界面是 600*600 的,一个单元格是20为边长的,整个界面也就是 30*30的
	// 蛇的初始位置在 (6,6),一条横向的蛇,蛇头向右
	// 把蛇的蛇身长度设置为初始长度
	// 蛇尾的下一个结点是空

	Node *cur;
	int init_x = 7 * SIDE;
	int init_y = 7 * SIDE;
	int count = 0;

	body = INITLENGTH;
	head = (Node *)malloc( sizeof(Node) );
	
	cur = head;
	while ( count < INITLENGTH )
	{
		init_node(cur, init_x, init_y);
		init_x -= SIDE;
		cur = cur->next;
		count++;
	}

	cur->next = NULL;
}

/*
	绘制贪吃蛇蛇身
*/
void paint_snake()
{
	// 遍历蛇身,把每个点用矩形画出来

	int left;
	int right;
	int top;
	int bottom;

	int count = 0;

	Node *cur;
	cur = head;

	while ( count < body )
	{
		left = cur->x;
		top = cur->y;
		right = cur->x + SIDE;
		bottom = cur->y + SIDE;

		setfillcolor(GREEN);
		fillrectangle(left, top, right, bottom);

		cur = cur->next;
		count++;
	}
}

/*
	重置贪吃蛇
*/
void reset_snake(int new_x, int new_y)
{
	// 蛇头的新位置就是传进来的参数
	// 蛇身是链表,旧蛇头的每一个点的坐标都设置为它们前一项的点的坐标

	Node *cur;

	int setx = new_x;
	int sety = new_y;
	int savex;
	int savey;

	int count = 0;

	cur = head;
	while (count < body)
	{
		// 重置的过程
		// 每次都是先保存,再赋值,然后把这次保存的置为下次赋值的
		// 保存的坐标用savex,savey,赋值的坐标用setx,sety
		savex = cur->x;
		savey = cur->y;
		cur->x = setx;
		cur->y = sety;
		setx = savex;
		sety = savey;

		cur = cur->next;
		count++;
	}
}

/*
	移动贪吃蛇
*/
void move_snake()
{
	// 根据当前方向重置蛇头坐标

	int head_x = head->x;
	int head_y = head->y;

	switch (dir)
	{
	case L:
		head_x -= SIDE;
		break;
	case R:
		head_x += SIDE;
		break;
	case T:
		head_y -= SIDE;
		break;
	case B:
		head_y += SIDE;
		break;
	default:
		break;
	}

	reset_snake(head_x, head_y);
}

/*
	增加蛇的长度
*/
void add_snake()
{
	// 找到蛇的最后两个结点
	// 根据最后两个结点的坐标关系决定新增结点的坐标

	Node *cur;
	int tail_x;
	int tail_y;
	int front_x;
	int front_y;
	int new_x;
	int new_y;
	int count = 0;

	// 找到最后两个点
	cur = head;
	while ( count < body )
	{
		if ( count == (body-2) )
		{
			front_x = cur->x;
			front_y = cur->y;
		}

		if (count == (body - 1))
		{
			tail_x = cur->x;
			tail_y = cur->y;
			cur->next = (Node*)malloc(sizeof(Node));
		}

		cur = cur->next;
		count++;
	}

	// 决定新增节点的坐标
	if ( tail_x == front_x )
	{
		new_x = tail_x;
		if ( tail_y > front_y )
		{
			new_y = tail_y + SIDE;
		}
		else if ( tail_y < front_y )
		{
			new_y = tail_y - SIDE;
		}
	}
	else if (tail_y == front_y)
	{
		new_y = tail_y;
		if ( tail_x < front_x )
		{
			new_x = tail_x - SIDE;
		}
		else if (tail_x > front_x)
		{
			new_x = tail_x + SIDE;
		}
	}

	// 新增节点
	cur->x = new_x;
	cur->y = new_y;
	cur->next = NULL;
	body++;
}

apple.h

/*
	绘制苹果的头文件
*/

#pragma once

#include <graphics.h>
#include <stdlib.h>
#include <stdio.h>

#include "snake.h"
#include "background.h"

/*
	生成苹果
*/
void create_apple();

/*
	绘制苹果
*/
void paint_apple();

/*
	返回苹果变量的坐标
*/
void get_apple(int *apple_x, int *apple_y);

apple.cpp

/*
	绘制苹果
*/

#include "apple.h"

/*
	定义一个苹果,苹果只包含横纵坐标
*/
struct Apple
{
	int x;
	int y;
}apple;

/*
	生成一个苹果,用随机数
*/
void create_apple()
{
	// 生成两个 0~29 的随机数,作为苹果左上角点的坐标
	// 如果这个坐标是蛇身中的某一个点,那么重新生成

	int unit_length = (LENGTH - 2*SIDE) / SIDE;
	int apple_x;
	int apple_y;
	int count = 0;
	Node *cur;

	while (1)
	{
		apple_x = rand() % unit_length + 1;
		apple_y = rand() % unit_length + 1;

		cur = get_head();
		while ( count < get_body() )
		{
			if ( ( apple_x * SIDE == cur->x ) 
			  && ( apple_y * SIDE == cur->y ) )
			{
				break;
			}
			cur = cur->next;
			count++;
		}

		// 直到蛇身最后一个位置都不与生成的坐标重合,那么count才会加到body
		if (count == get_body())
		{
			break;
		}
	}

	apple.x = apple_x * SIDE;
	apple.y = apple_y * SIDE;
}

/*
	把苹果变量作为矩形绘制出来
*/
void paint_apple()
{
	int left = apple.x;
	int top = apple.y;
	int right = apple.x + SIDE;
	int bottom = apple.y + SIDE;

	setfillcolor(RED);
	fillrectangle(left, top, right, bottom);
}

/*
	返回苹果坐标
*/
void get_apple(int *apple_x, int *apple_y)
{
	*apple_x = apple.x;
	*apple_y = apple.y;
}

background.h

/*
	绘制背景的头文件
*/

#pragma once

#include <graphics.h>
#include <stdio.h>

#include "snake.h"
#include "apple.h"

// 定义游戏界面的长和宽
#define LENGTH 32*SIDE
#define WIDTH 32*SIDE

/*
	获得游戏分数
*/
int get_score();

/*
	设置游戏分数
*/
void set_score(int s);

/*
	重绘背景
*/
void redraw();

/*
	初始化背景
*/
void init();

/*
	欢迎界面
*/
void welcome();

/*
	创建画布
*/
void create_picture();

/*
	游戏结束
*/
void end_game();

background.cpp

/*
	绘制贪吃蛇游戏的背景
*/

#include "background.h"

/*
	游戏记分器
*/
int score = 0;

/*
	获得游戏分数
*/
int get_score()
{
	return score;
}

/*
	设置游戏分数
*/
void set_score(int s)
{
	score = s;
}

/*
	绘制边界线
*/
void paint_frontier()
{
	setlinecolor(GREEN);
	line(SIDE, SIDE, SIDE, 31 * SIDE);
	line(SIDE, SIDE, 31 * SIDE, SIDE);
	line(SIDE, 31 * SIDE, 31 * SIDE, 31 * SIDE);
	line(31 * SIDE, SIDE, 31 * SIDE, 31 * SIDE);
	setlinecolor(WHITE);
}

/*
	绘制游戏分数
*/
void paint_score()
{
	TCHAR s[5];
	_stprintf_s(s, _T("%d"), score);
	setcolor(WHITE);
	settextstyle(24, 0, _T("Consolas"));
	outtextxy(31 * SIDE, 0, s);
}

/*
	重新绘制背景
*/
void redraw()
{
	cleardevice();
	paint_frontier();
	paint_apple();
	paint_snake();
	paint_score();
}

/*
	初始化背景
*/
void init()
{
	cleardevice();

	// 画边界
	paint_frontier();

	// 初始化蛇
	init_snake();
	paint_snake();

	// 初始化苹果
	create_apple();
	paint_apple();

	// 初始化分数
	paint_score();
}

/*
	欢迎界面
*/
void welcome()
{
	TCHAR sentence1[] = "welcome to snake";
	TCHAR sentence2[] = "conduction:use 'wsad' to control directory";
	TCHAR sentence3[] = "press any key to continue……";

	setcolor(GREEN);
	settextstyle(16, 0, _T("Consolas"));
	outtextxy(10 * SIDE, 10 * SIDE, sentence1);
	outtextxy(10 * SIDE, 11 * SIDE, sentence2);
	outtextxy(10 * SIDE, 12 * SIDE, sentence3);
}

/*
	创建画布
*/
void create_picture()
{
	// 画背景
	initgraph(LENGTH, WIDTH);
	setbkcolor(BLACK);
	cleardevice();
}

/*
	游戏结束
*/
void end_game()
{
	TCHAR sentence1[] = "Game over";

	setcolor(RED);
	settextstyle(72, 0, _T("Consolas"));
	outtextxy(10 * SIDE, 10 * SIDE, sentence1);
}

brain.h

/*
	游戏逻辑的头文件
*/

#pragma once

#include <graphics.h>
#include <conio.h>
#include <Windows.h>

#include "snake.h"
#include "apple.h"
#include "background.h"

/*
	运行游戏
*/
void play();

brain.cpp

/*
	控制游戏逻辑
*/

#include "brain.h"

/*
	游戏结束的标志,结束为1,否则为0
*/
int end = 0;

/*
	检测输入方向
*/
int check_direction(direction dir)
{
	// 如果输入的是当前的反方向,返回0

	int result = 1;

	switch ( get_dir() )
	{
	case L:
		if ( R == dir )
		{
			result = 0;
		}
		break;
	case R:
		if ( L == dir )
		{
			result = 0;
		}
		break;
	case T:
		if ( B == dir )
		{
			result = 0;
		}
		break;
	case B:
		if ( T == dir )
		{
			result = 0;
		}
		break;
	default:
		break;
	}

	return result;
}

/*
	检测游戏结束条件
*/
void check_end()
{
	// 蛇头的坐标与蛇身链表里的某一点相同,结束
	// 蛇身的横纵坐标超过 29*SIDE ,结束

	Node *cur;
	int count = 0;
	int head_x;
	int head_y;

	cur = get_head();
	head_x = cur->x;
	head_y = cur->y;
	while ( count < get_body() )
	{
		// 前四行是越界,最后一行是撞到身体
		if ( ( cur->x > 30 * SIDE )
		  || ( cur->y > 30 * SIDE ) 
		  || ( cur->x < 1 )
		  || ( cur->y < 1 )
		  || ( ( cur->x == head_x) && ( cur->y == head_y) && ( count > 0 ) )
		)
		{
			end = 1;
			break;
		}

		cur = cur->next;
		count++;
	}
}

/*
	检测是否得分
*/
void check_score()
{
	// 苹果的坐标与蛇头相同
	// 重新显示得分,并且重新生成苹果

	int apple_x;
	int apple_y;
	int score = get_score();
	Node *head = get_head();

	get_apple(&apple_x, &apple_y);
	if ( ( apple_x == head->x ) && ( apple_y == head->y ) )
	{
		score++;
		set_score(score);
		add_snake();
		create_apple();
		redraw();
	}
}

/*
	改变蛇头方向
*/
void change_direction()
{
	direction dir = D;
	char inputch;

	// 敲击键盘才执行以下语句
	if ( _kbhit() )
	{
		inputch = _getch();

		// 根据键盘输入 w,s,a,d 设置dir变量的值
		switch (inputch)
		{
		case 'a':
			dir = L;
			break;
		case 'A':
			dir = L;
			break;
		case 'd':
			dir = R;
			break;
		case 'D':
			dir = R;
			break;
		case 'w':
			dir = T;
			break;
		case 'W':
			dir = T;
			break;
		case 's':
			dir = B;
			break;
		case 'S':
			dir = B;
			break;
		default:
			break;
		}

		// 输入相反方向不做操作
		if (!check_direction(dir))
		{
			return;
		}


		// 蛇移动:设置方向,移动,看是否得分,看游戏是否结束
		if (!(D == dir))
		{
			set_dir(dir);
			move_snake();
			check_score();
			check_end();

			if (end)
			{
				return;
			}

			redraw();
		}
	}
}

/*
	重置游戏
*/
void reset_game()
{
	end = 0;
	set_score(0);
}

/*
	运行游戏
*/
void play()
{
	while (1)
	{
		// 创建画布
		create_picture();

		// 欢迎界面
		welcome();
		_getch();

		// 初始化页面
		init();

		// 执行游戏
		while (1)
		{
			// 改变蛇头方向
			change_direction();

			// 没结束蛇自动向前移动
			Sleep(250);
			move_snake();
			check_score();
			check_end();
			
			// 判断是否结束游戏
			if (end)
			{
				break;
			}

			redraw();
		}

		// 游戏结束
		end_game();
		_getch();

		reset_game();

		closegraph();
	}
}

main.cpp

/*
	游戏入口文件
*/

#include "brain.h"

int main()
{
	play();
	return 0;
}
  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值