目录
一、 snake.h
#pragma once
#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<stdlib.h>
#include<windows.h>
#include<stdbool.h>
#include<locale.h>
#define ROW 27 //游戏区行数
#define COL 57 //游戏区列数
#define WALL L'□'
#define BODY L'●'
#define FOOD L'★'
#define Origin_x 24 // 初始蛇头的x坐标
#define Origin_y 5 // 初始蛇头的y坐标
//检测按键是否被按过,间返回的虚拟值按位与上1,相同为1,相异为0,进行检测
//封装为一个宏
#define KEY_PRESS(vk) (GetAsyncKeyState(vk)&0x1?1:0)
//贪吃蛇游戏状态
enum GAMSTATUS {
OK=1, //正常运行
EXIT, //按ESC键退出
KILL_BY_WALL, //撞墙了!
KILL_BY_SELF //咬到自己了!
};
//贪吃蛇行走方向
enum SNAKEWAY {
UP = 1, //前进
DOWN, //向下
LEFT, //左拐
RIGHT //右拐
};
//贪吃蛇,蛇身节点的定义
typedef struct SnakeNode {
//节点的位置
int x;
int y;
//下一个蛇身节点
struct SnakeNode* next;
}SnakeNode;
//整局游戏贪吃蛇的维护
typedef struct Snake {
//贪吃蛇,蛇头指针,方便找到贪吃蛇的位置/维护整条蛇的指针
SnakeNode* SnakePhead;
//指向食物的指针
SnakeNode* FoodNode;
//玩家当前得分
int Scour;
//每个食物的得分
int FoofScour;
//蛇休眠的时间,休眠的时间越短,蛇的速度越快,休眠的时间月长,蛇的速度越慢
int SleepTime;
//贪吃蛇游戏状态
enum GAMSTATUS Status;
//贪吃蛇行进的方向
enum SNAKEWAY way;
}Snake;
//游戏开始前的准备
void GameStart(Snake*ps);
//游戏开始欢迎界面
void welcome();
//绘制地图
void GameMap();
//初始化蛇
void InitSnake(Snake*ps);
//定位光标位置
void SearchLocal(int x, int y);
//打印蛇身
void SnakePrint(Snake* ps);
//创建食物
void CreatFood(Snake* ps);
//贪吃蛇游戏运行
void GameRun(Snake*ps);
//贪吃蛇开始移动
void SnakeMove(Snake*ps);
//下一个位置是否位食物;
int NextFood(Snake* ps, SnakeNode* pnext);
//吃食物
void EatFood(Snake* ps, SnakeNode* pnext);
//不吃食物
void NotEatFood(Snake* ps, SnakeNode* pnext);
//检测是否穿墙
void Kill_Wall(Snake* ps);
//检测是否咬到自己
void Kill_Self(Snake* ps);
//贪吃蛇游戏结束的善后
void GameEnd(Snake* ps);
二、snake.c
#include"snake.h"
//定位光标位置
void SearchLocal(int x,int y) {
//获取控制台的句柄,控制权限,HANDLE 结构体指针
HANDLE handle = GetStdHandle(STD_OUTPUT_HANDLE);
COORD pos = { x,y };
// 获取设备句柄,传入坐标COORD
SetConsoleCursorPosition(handle, pos);
}
//初始化贪吃蛇界面
void welcome() {
//将光标定位到界面的中间位置
SearchLocal(35, 10);
printf("欢迎来到贪吃蛇小游戏\n");
SearchLocal(38, 20);
system("pause");
system("cls");
//功能介绍
SearchLocal(15, 10);
printf("用↑ . ↓ . ← . → 来控制蛇的移动,F3为加速按键,F4为减速\n");
SearchLocal(35, 12);
printf("加速能够获得更高的分数!!!!");
SearchLocal(38, 20);
system("pause");
system("cls");
}
//绘制地图
void GameMap() {
//上
SearchLocal(0, 0);
for (int i = 0; i < COL; i += 2) {
wprintf(L"%c", WALL);
}
//左
for (int i = 1; i < ROW; i++) {
SearchLocal(0, i);
wprintf(L"%lc", WALL);
}
//右
for (int i = 1; i < ROW; i++) {
SearchLocal(COL-1, i);
wprintf(L"%lc", WALL);
}
//下
SearchLocal(0, ROW-1);
for (int i = 0; i < COL; i += 2) {
wprintf(L"%lc", WALL);
}
}
//打印蛇身
void SnakePrint(Snake* ps) {
SnakeNode* pcur = ps->SnakePhead;
while (pcur) {
SearchLocal(pcur->x, pcur->y);
wprintf(L"%lc", BODY);
pcur = pcur->next;
}
}
//初始化蛇
void InitSnake(Snake*ps) {
//创建5个蛇身节点
SnakeNode* pcur = NULL;
for (int i = 0; i < 5; i++) {
pcur = (SnakeNode*)malloc(sizeof(SnakeNode));
if (pcur == NULL) {
perror("malloc");
return;
//exit(1)
}
//头插
pcur->x = Origin_x+2*i;
pcur->y = Origin_y;
pcur->next = NULL;
if (ps->SnakePhead == NULL) {
ps->SnakePhead = pcur;
}
else {
pcur->next = ps->SnakePhead;
ps->SnakePhead = pcur;
}
}
//打印蛇身
SnakePrint(ps);
//初始化
ps->Scour = 0;
ps->FoodNode = NULL;
ps->FoofScour = 10;
ps->SleepTime = 200;
ps->Status = OK;
ps->way = RIGHT;
}
//创建食物
void CreatFood(Snake* ps) {
//随机生成食物的坐标(x,y)
//1.不能超过墙,且不能重叠 x:2~54 -- 52+2 y:1~24 -- 23+1
//2.食物不能与蛇身重叠,遍历蛇身
//创建食物节点
SnakeNode* FoodNode = (SnakeNode*)malloc(sizeof(SnakeNode));
if (FoodNode == NULL) {
perror("creat FoodNode fail");
return;
}
int x = 0;
int y = 0;
again:
do {
x = rand() % (COL-4) + 2;
y = rand() % (ROW-2) + 1;
} while (x % 2 != 0); //1.不能超过墙,且不能重叠
//2.食物不能与蛇身重叠,遍历蛇身
SnakeNode* pcur = ps->SnakePhead;
while (pcur) {
if (x == pcur->x && y == pcur->y) {
goto again;
}
pcur = pcur->next;
}
//真正创建了属于食物的坐标
FoodNode->x = x;
FoodNode->y = y;
FoodNode->next = NULL;
ps->FoodNode = FoodNode;
//打印食物
SearchLocal(x,y);
wprintf(L"%lc", FOOD);
}
void GameStart(Snake* ps) {
//设定控制台屏幕大小
system("mode con cols=100 lines=30");
//设置控制台标题
system("title 贪吃蛇");
//设定光标,进行隐藏
//获取控制台句柄
HANDLE handle = GetStdHandle(STD_OUTPUT_HANDLE);
//创建结构体来接收控制台光标的数据
//有两个参数:dwSize 光标占空比; bVisible 光标可见度 默认true
CONSOLE_CURSOR_INFO cursor_info = { 0 };
GetConsoleCursorInfo(handle, &cursor_info);
//隐藏光标
cursor_info.bVisible = false;
//将修改的光标属性值传给控制台进行控制,参数类型跟GetConsoleCursorInfo一样
SetConsoleCursorInfo(handle, &cursor_info);
//游戏开始欢迎界面
welcome();
//绘制地图
GameMap();
//贪吃蛇初始化
InitSnake(ps);
//创建食物
CreatFood(ps);
}
//打印帮助信息
void PrintOption() {
SearchLocal(63, 17);
printf("用↑ . ↓ . ← . → 来控制蛇的移动");
SearchLocal(63, 18);
printf("space(空格)为暂停/回到游戏按键");
SearchLocal(68, 19);
printf("F3为加速按键,F4为减速");
SearchLocal(72, 20);
printf("ESC为退出按键");
SearchLocal(63, 21);
printf("注意:不能撞墙,不能咬到自己!!!");
}
//休眠函数
void pause() {
while (1) {
Sleep(200);
if (KEY_PRESS(VK_ESCAPE)) {
break;
}
}
}
//下一个位置是否位食物;
int NextFood(Snake* ps, SnakeNode* pnext) {
if (pnext->x == ps->FoodNode->x && pnext->y == ps->FoodNode->y) {
return 1;
}
else{
return 0;
}
}
//吃食物
void EatFood(Snake* ps, SnakeNode* pnext) {
pnext->next = ps->SnakePhead;
ps->SnakePhead = pnext;
//得分增加
ps->Scour += ps->FoofScour;
//释放旧的食物
free(ps->FoodNode);
ps->FoodNode = NULL;
//重新创建食物
CreatFood(ps);
}
//不吃食物
void NotEatFood(Snake* ps, SnakeNode* pnext) {
pnext->next = ps->SnakePhead;
ps->SnakePhead = pnext;
SnakeNode* pcur = ps->SnakePhead;
while (pcur->next->next) {
pcur = pcur->next;
}
//到达蛇尾,记录蛇尾位置用两个空格替换,然后进行删除
SearchLocal(pcur->next->x, pcur->next->y);
printf(" ");
free(pcur->next);
pcur->next = NULL;
}
//检测是否穿墙
void Kill_Wall(Snake* ps) {
if (ps->SnakePhead->x == 0 ||
ps->SnakePhead->x == (COL-1) ||
ps->SnakePhead->y == 0 ||
ps->SnakePhead->y == (ROW-1))
{
ps->Status = KILL_BY_WALL;
}
}
//检测是否咬到自己
void Kill_Self(Snake* ps) {
SnakeNode* pcur = ps->SnakePhead->next;
while (pcur) {
if (ps->SnakePhead->x == pcur->x && ps->SnakePhead->y == pcur->y) {
ps->Status = KILL_BY_SELF;
return;
}
pcur = pcur->next;
}
}
//贪吃蛇开始移动
void SnakeMove(Snake*ps){
//创建指针记录蛇移动的下一个位置
SnakeNode* pnext = (SnakeNode*)malloc(sizeof(SnakeNode));
if (pnext == NULL) {
perror("SnakeMove::malloc");
return;
}
pnext->next = NULL;
//记录蛇移动的下一个位置
switch (ps->way){
case UP:
pnext->x = ps->SnakePhead->x;
pnext->y = ps->SnakePhead->y - 1;
break;
case DOWN:
pnext->x = ps->SnakePhead->x;
pnext->y = ps->SnakePhead->y + 1;
break;
case LEFT:
pnext->x = ps->SnakePhead->x-2;
pnext->y = ps->SnakePhead->y;
break;
case RIGHT:
pnext->x = ps->SnakePhead->x + 2;
pnext->y = ps->SnakePhead->y;
break;
}
//判断下一个位置是否为食物
//是食物,吃掉 --- 头插
if (NextFood(ps,pnext)) {
EatFood(ps, pnext);
}
//不是食物,头插删除尾节点
else {
NotEatFood(ps, pnext);
}
//打印蛇身
SnakePrint(ps);
//检测是否穿墙
Kill_Wall(ps);
//检测是否咬到自己
Kill_Self(ps);
}
//贪吃蛇游戏运行
void GameRun(Snake* ps) {
//打印操作介绍
PrintOption();
do{
//打印当前得分情况
SearchLocal(65, 11);
printf("当前得分:%d",ps->Scour);
SearchLocal(78, 11);
printf("当前食物分值:%02d", ps->FoofScour);
//检测按键是否按过
if (KEY_PRESS(VK_UP)&& ps->way !=DOWN) {
ps->way = UP;
}
else if(KEY_PRESS(VK_DOWN) && ps->way != UP) {
ps->way = DOWN;
}
else if (KEY_PRESS(VK_LEFT) && ps->way != RIGHT) {
ps->way = LEFT;
}
else if (KEY_PRESS(VK_RIGHT) && ps->way != LEFT) {
ps->way = RIGHT;
}
else if (KEY_PRESS(VK_ESCAPE)) {
ps->way = EXIT;
break;
}
//空格进入暂停,死循环睡眠,在次按下空格进行游戏
else if (KEY_PRESS(VK_SPACE)) {
pause();
}
//F3加速,睡眠时间减少,食物分值增加,限制条件睡眠时间不为负数
else if (KEY_PRESS(VK_F3)) {
//时间不能为负,每次睡眠时间-30,至多减到20;
if (ps->SleepTime > 20) {
ps->SleepTime -= 30;
ps->FoofScour += 2;
}
}
//F4减速,睡眠时间增加,食物分值减少,限制条件食物分值不为0
else if (KEY_PRESS(VK_F4)) {
if (ps->FoofScour > 2) {
ps->SleepTime += 30;
ps->FoofScour -= 2;
}
}
//贪吃蛇开始移动
SnakeMove(ps);
//睡眠一下
Sleep(ps->SleepTime);
} while (ps->Status == OK);
}
//贪吃蛇游戏结束的善后
void GameEnd(Snake* ps) {
SearchLocal(15, 10);
//打印结束状态
switch (ps->Status) {
case EXIT:
printf("正常退出,游戏结束了");
break;
case KILL_BY_WALL:
printf("很遗憾,游戏结束,你撞到墙了!!!");
break;
case KILL_BY_SELF:
printf("不小心咬到自己了,游戏结束了!!!");
break;
}
//释放空间
SnakeNode* pcur = ps->SnakePhead;
SnakeNode* del = NULL;
while (pcur) {
del = pcur->next;
free(pcur);
pcur = del;
}
free(ps->FoodNode);
ps = NULL;
}
三、Test.c
#include"snake.h"
void snakegame() {
int ch = 0;
do {
//创建贪吃蛇
Snake snake = { 0 };
//贪吃蛇游戏界面初始化
GameStart(&snake);
//贪吃蛇游戏运行
GameRun(&snake);
//贪吃蛇游戏结束的善后
GameEnd(&snake);
SearchLocal(15, 8);
printf("要再来一局吗?? Y/N:");
ch = getchar();
getchar();// 清理\n
SearchLocal(0, 27);
} while (ch == 'Y' || ch == 'y');
}
int main() {
//修改适配本地中文环境
setlocale(LC_ALL, "");
贪吃蛇游戏
snakegame();
return 0;
}