头文件 snake.h
#include<stdio.h>
#include<stdlib.h>
#include<windows.h>
#include<locale.h>
#include<stdbool.h>
#include<time.h>
#define POS_X 24
#define POS_Y 5
#define BODY L'●'
#define FOOD L'★'
#define KEY_PRESS(VK) ((GetAsyncKeyState(VK)&0x1) ? 1 : 0)
enum DIRECTION
{
UP = 1,
DOWN,
LEFT,
RIGHT
};
enum GAME_STATES
{
OK,//正常状态
KILL_BY_WALL,//撞墙
KILL_BY_SELF,//撞到自己
END_NORMAL//正常结束
};
typedef struct SnakeNode
{
//坐标
int x;
int y;
//指向下一个节点的指针
struct SnakeNode* next;
}SnakeNode,*pSnakeNode;
typedef struct Snake
{
pSnakeNode _pSnake;//指向蛇头的指针
pSnakeNode _pFood;//指向食物的指针
enum DIRECTION _dir;//蛇的方向
enum GAME_STATES _state; //游戏的状态
int _food_weight;//食物的分数
int _score;//总成绩
int _sleep_time;//休息时间,时间越短速度越快
}Snake, * pSnake;
void GameStart(pSnake ps);
void WelcomGame();
void CreatMap();
void InitSnake(pSnake ps);
void CreatFood(pSnake ps);
void GameRun(pSnake ps);
void SnakeMove(pSnake ps);
int NextIsFood(pSnakeNode pn, pSnake ps);
void EatFood(pSnakeNode psn, pSnake ps);
void NoFood(pSnakeNode psn, pSnake ps);
int KillByWall(pSnake ps);
int KillBySelf(pSnake ps);
void GameOver(pSnake ps);
snake.c
#include"snake.h"
//设置光标的坐标
void SetPos(short x, short y)
{
COORD pos = { x, y };
HANDLE hOutput = NULL;
//获取标准输出的句柄(⽤来标识不同设备的数值)
hOutput = GetStdHandle(STD_OUTPUT_HANDLE);
//设置标准输出上光标的位置为pos
SetConsoleCursorPosition(hOutput, pos);
}
void WelcomGame()
{
SetPos(40,14);
wprintf(L"欢迎来到贪吃蛇小游戏\n");
SetPos(42, 20);
system("pause");
system("cls");
SetPos(25, 14);
wprintf(L"用↑.↓.←.→ 来控制蛇的移动,F3加速F4减速\n");
SetPos(25, 15);
wprintf(L"加速获得的分数更多\n");
SetPos(42, 20);
system("pause");
system("cls");
}
void CreatMap()
{
int i = 0;
//上
for(i = 0; i < 29; i++)
{
wprintf(L"%lc", L'□');
}
//下
SetPos(0, 26);
for (i = 0; i < 29; i++)
{
wprintf(L"%lc", L'□');
}
//左
for (i = 1; i <= 25; i++)
{
SetPos(0, i);
wprintf(L"%lc", L'□');
}
//右
for (i = 1; i <= 25; i++)
{
SetPos(56, i);
wprintf(L"%lc", L'□');
}
}
void InitSnake(pSnake ps)
{
pSnakeNode cur = NULL;
int i = 0;
for (i = 0;i<5; i++)
{
cur = (pSnakeNode)malloc(sizeof(SnakeNode));
if (cur == NULL)
{
perror("malloc");
exit(1);
}
cur->next = NULL;
cur->x = POS_X + 2 * i;
cur->y = POS_Y;
//头插法
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->_state = OK;//游戏状态
ps->_dir = RIGHT;//蛇的初始方向朝右
ps->_food_weight = 10;//一个食物10分
ps->_score = 0;//初始为0分
ps->_sleep_time = 200;//单位ms
}
void CreatFood(pSnake ps)
{
int x = 0;
int y = 0;
again:
do
{
x = rand() % 53 + 2;
y = rand() % 25 + 1;
} while (x % 2 != 0);
//x y 的坐标不能和蛇的身体冲突
pSnakeNode cur = ps->_pSnake;
while (cur)
{
if (x == cur->x && y == cur->y)
{
goto again;
}
cur = cur->next;
}
//创建食物的节点
pSnakeNode pFood = (pSnakeNode)malloc(sizeof(SnakeNode));
if (pFood == NULL)
{
perror("malloc");
exit(1);
}
pFood->x = x;
pFood->y = y;
pFood->next = NULL;
SetPos(x, y);
wprintf(L"%lc", FOOD);
ps->_pFood = pFood;
}
void GameStart(pSnake ps)
{
//先设置窗口大小,光标隐藏
system("mode con cols=100 lines=30");
system("title 贪吃蛇");
HANDLE houtput = GetStdHandle(STD_OUTPUT_HANDLE);
//隐藏光标操作
CONSOLE_CURSOR_INFO CursorInfo;
GetConsoleCursorInfo(houtput, &CursorInfo);//获取控制台光标信息
CursorInfo.bVisible = false; //隐藏控制台光标
SetConsoleCursorInfo(houtput, &CursorInfo);//设置控制台光标状态
//getchar();
//初始化游戏
//打印环境界面
WelcomGame();
//地图绘制
CreatMap();
//创建蛇
InitSnake(ps);
//创建食物
CreatFood(ps);
}
void PrintHelpInfo()
{
//打印提⽰信息
SetPos(64, 14);
wprintf(L"先按esc启动贪吃蛇\n");
SetPos(64, 15);
wprintf(L"不能穿墙,不能咬到自己\n");
SetPos(64, 16);
wprintf(L"用↑.↓.←.→ 来控制蛇的移动\n");
SetPos(64, 17);
wprintf(L"F3 为加速,F4为减速\n");
SetPos(64, 18);
wprintf(L"ESC :退出游戏.space:暂停游戏");
SetPos(64, 20);
wprintf(L"景风制作");
}
//pSnakeNode psn 是下⼀个节点的地址
//pSnake ps 维护蛇的指针
int NextIsFood(pSnakeNode psn, pSnake ps)
{
return (psn->x == ps->_pFood->x) && (psn->y == ps->_pFood->y);
}
//pSnakeNode psn 是下⼀个节点的地址
//pSnake ps 维护蛇的指针
void EatFood(pSnakeNode psn, pSnake ps)
{
//头插法
psn->next = ps->_pSnake;
ps->_pSnake = psn;
//打印蛇
pSnakeNode cur = ps->_pSnake;
while (cur)
{
SetPos(cur->x, cur->y);
wprintf(L"%c", BODY);
cur = cur->next;
}
ps->_score += ps->_food_weight;
//释放⻝物节点
free(ps->_pFood);
//创建新的⻝物
CreatFood(ps);
}
void NoFood(pSnakeNode psn, pSnake ps)
{
//头插法
psn->next = ps->_pSnake;
ps->_pSnake = psn;
//打印蛇
pSnakeNode cur = ps->_pSnake;
while (cur->next->next)
{
SetPos(cur->x, cur->y);
wprintf(L"%c", BODY);
cur = cur->next;
}
//最后⼀个位置打印空格,然后释放节点
SetPos(cur->next->x, cur->next->y);
printf(" ");
free(cur->next);
cur->next = NULL;
}
void SnakeMove(pSnake ps)
{
//创建下⼀个节点
pSnakeNode pNextNode = (pSnakeNode)malloc(sizeof(SnakeNode));
if (pNextNode == NULL)
{
perror("SnakeMove()::malloc()");
return;
}
//确定下⼀个节点的坐标,下⼀个节点的坐标根据,蛇头的坐标和⽅向确定
switch(ps->_dir)
{
case UP:
{
pNextNode->x = ps->_pSnake->x;
pNextNode->y = ps->_pSnake->y - 1;
}
break;
case DOWN:
{
pNextNode->x = ps->_pSnake->x;
pNextNode->y = ps->_pSnake->y + 1;
}
break;
case LEFT:
{
pNextNode->x = ps->_pSnake->x-2;
pNextNode->y = ps->_pSnake->y;
}
break;
case RIGHT:
{
pNextNode->x = ps->_pSnake->x+2;
pNextNode->y = ps->_pSnake->y;
}
break;
}
//如果下⼀个位置就是食物
if (NextIsFood(pNextNode, ps))
{
EatFood(pNextNode, ps);
}
else//如果没有食物
{
NoFood(pNextNode, ps);
}
KillByWall(ps);
KillBySelf(ps);
}
void pause()
{
while (1)
{
Sleep(300);
if (KEY_PRESS(VK_ESCAPE))
{
break;
}
}
}
int KillByWall(pSnake ps)
{
if ((ps->_pSnake->x == 0)
|| (ps->_pSnake->x == 56)
|| (ps->_pSnake->y == 0)
|| (ps->_pSnake->y == 26))
{
ps->_state = KILL_BY_WALL;
return 1;
}
return 0;
}
int KillBySelf(pSnake ps)
{
pSnakeNode cur = ps->_pSnake->next;
while (cur)
{
if ((ps->_pSnake->x == cur->x)
&& (ps->_pSnake->y == cur->y))
{
ps->_state = KILL_BY_SELF;
return 1;
}
cur = cur->next;
}
return 0;
}
void GameRun(pSnake ps)
{
//打印右侧帮助信息
PrintHelpInfo();
do
{
SetPos(64, 10);
printf("得分:% d ", ps->_score);
printf("每个食物得分:%2d分", ps->_food_weight);
if (KEY_PRESS(VK_UP) && ps->_dir != DOWN)
{
ps->_dir = UP;
}
else if (KEY_PRESS(VK_DOWN) && ps->_dir != UP)
{
ps->_dir = DOWN;
}
else if (KEY_PRESS(VK_LEFT) && ps->_dir != RIGHT)
{
ps->_dir = LEFT;
}
else if (KEY_PRESS(VK_RIGHT) && ps->_dir != LEFT)
{
ps->_dir = RIGHT;
}
else if (KEY_PRESS(VK_SPACE))
{
pause();
}
else if (KEY_PRESS(VK_ESCAPE))
{
ps->_state = END_NORMAL;
break;
}
else if (KEY_PRESS(VK_F3))
{
if (ps->_sleep_time >= 50)
{
ps->_sleep_time -= 30;
ps->_food_weight += 2;//⼀个⻝物分数最⾼是20分
}
}
else if (KEY_PRESS(VK_F4))
{
if (ps->_sleep_time < 350)
{
ps->_sleep_time += 30;
ps->_food_weight -= 2;//⼀个⻝物分数最⾼是20分
}
}
//蛇每次⼀定之间要休眠的时间,时间短,蛇移动速度就快
Sleep(ps->_sleep_time);
SnakeMove(ps);
} while (ps->_state == OK);
}
void GameOver(pSnake ps)
{
pSnakeNode cur = ps->_pSnake;
SetPos(24, 12);
switch (ps->_state)
{
case END_NORMAL:
printf("您主动退出游戏\n");
break;
case KILL_BY_SELF:
printf("您撞上自己了,游戏结束!\n");
SetPos(0, 27);
break;
case KILL_BY_WALL:
printf("您撞墙了,游戏结束!\n");
SetPos(0, 27);
break;
}
//释放蛇⾝的节点
while (cur)
{
pSnakeNode del = cur;
cur = cur->next;
free(del);
}
}
test.c
#include"snake.h"
void test()
{
int ch = 0;
do
{
system("cls");
//创建贪吃蛇
Snake snake = { 0 };
//初始化游戏
//打印环境界面
//功能介绍
//地图绘制
//创建蛇
//创建食物
//设置游戏相关数据
GameStart(&snake);
//运行游戏
GameRun(&snake);
//结束游戏,善后工作
GameOver(&snake);
SetPos(20, 15);
printf("再来一局吗(y/n)\n");
ch = getchar();
getchar();
SetPos(0, 27);
} while(ch=='y'||ch=='Y');
}
int main()
{
setlocale(LC_ALL, "");
srand((unsigned int)time(NULL));
test();
return 0;
}