#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<Windows.h>
#include<ctime>
#include<conio.h>
enum { height = 30, width = 60 };
//蛇的偏移量
//全局函数--默认值为0
int offset_x;
int offset_y;
//记录蛇尾的位置---方便将移动后的蛇尾变成空格
int end_x;
int end_y;
//检测头尾是否构成循环,一旦构成循环,那么因为蛇头先出现在蛇尾的位置,覆盖掉原来的蛇尾,然后在更新后,把蛇尾的位置变成了空格,造成蛇头消失的bug
int isRool;
//无键盘按下,默认向右移动
char ch = 'd';
//记录前一次按下的键
char prveCh = 'd';
//得分
int score = 0;
//刷新时间
int sleepTime = 300;
//当前速度
int speed = 300;
struct Body
{
//蛇身的坐标
int x;
int y;
};
struct Snake
{
//结构体嵌套结构体
//蛇身最大长度
struct Body body[(width-2)*(height-2)];
//蛇的长度
int size;
}snake;//定义一个全局变量
struct Food
{
int x;
int y;
}food;
//画墙
void initWall();
//光标定位
void gotoxy(int x, int y);
//隐藏光标
VOID HideConsoleCursor();
//初始化蛇
void initSnake();
//初始化食物
void initFood();
//更新显示
void upgrate();
//实现蛇的移动
void moveSnake();
//蛇吃到食物
void snakeEatFood();
//死亡检测
void checkDead();
//游戏结束 1是撞墙 2是撞自身
void gameOver(int mode);
//设置控制台输出文本颜色
void setTextColor(int num);
int main()
{
HideConsoleCursor();
initWall();
initSnake();
initFood();
//当蛇身碰到墙,停止前进
while (snake.body[0].x>=1&& snake.body[0].x<=58&& snake.body[0].y>=1&& snake.body[0].y<=28)
{
checkDead();
moveSnake();
upgrate();
snakeEatFood();
Sleep(sleepTime);
}
gameOver(1);
return 0;
}
//画墙
void initWall()
{
//画墙---看做一个二维数组--行和列
for (int i = 0; i < height; i++)
{
for (int j = 0; j < width; j++)
{
if (i == 0 || j == 0 || i == height - 1 || j == width - 1)
{
printf("*");
}
else {
printf(" ");
}
}
if(i==5)
printf(" 贪吃蛇大作战");
if(i==8)
printf(" AWSD操作");
if (i == 11)
printf(" 游戏制作人:大忽悠");
printf("\n");
}
}
//光标定位
void gotoxy(int x, int y)
{
COORD coord = { x,y };
//1.获取窗口输出句柄
HANDLE handle=GetStdHandle ( STD_OUTPUT_HANDLE);
SetConsoleCursorPosition(handle, coord);
}
//隐藏光标
VOID HideConsoleCursor()
{
CONSOLE_CURSOR_INFO cursor_info = { 1, 0 };
SetConsoleCursorInfo(GetStdHandle(STD_OUTPUT_HANDLE),
&cursor_info);
}
//初始化蛇
void initSnake()
{
//设计初始化蛇身为3段
snake.size = 3;
//设置初始的蛇位置
snake.body[2].x = width/2 - 2;
snake.body[2].y = height/2;
snake.body[1].x = width/2 - 1;
snake.body[1].y = height/2 ;
snake.body[0].x = width/2 ;
snake.body[0].y = height/2;
}
//初始化食物
void initFood()
{
//播撒随机数种子
srand((size_t)time(NULL));
while (1)
{
//判断食物是否与身体重合
bool flag = true;
food.x = rand() % (width - 2) + 1; //x轴上食物出现的范围:1~58
food.y = rand() % (height - 2) + 1;//y轴上食物出现的随机范围:1~28
//判断每次食物的出现坐标是否与蛇的身体有重合,如果有重合,让食物重新再生成一次,直到不与身体重合
for (int i = 0; i < snake.size; i++)
{
//当食物与蛇身一节身体产生重合时就退出后续身体比对,让食物重新生成一次
if (snake.body[i].x == food.x && snake.body[i].y == food.y)
{
flag = false;
break;
}
}
//如果没有重合,那么此次食物生成没有问题,不用重新生成
if (flag == true)
{
break;
}
}
}
//更新显示
void upgrate()
{
for (int i = 0; i < snake.size; i++)
{
//将光标移动
gotoxy(snake.body[i].x, snake.body[i].y);
if (i == 0)
{
printf("@");
}
else
{
printf("#");
}
}
gotoxy(food.x, food.y);
printf("X");
if (end_x != 0 && end_y != 0)
{
gotoxy(end_x, end_y);
printf(" ");
}
//如果构成循环,就将被空格的蛇头,重新显示出来
if (isRool)
{
gotoxy(snake.body[0].x, snake.body[0].y);
printf("@");
}
//显示分数
gotoxy(82,14);
printf("分数:%d", score);
gotoxy(80, 17);
printf("当前速度:%d", speed);
}
//实现蛇的移动
void moveSnake()
{
//如果有键盘按下,默认值失效
if (_kbhit())//有键盘按下为真,没键盘按下没假
{
ch = _getch();
}
//解决按下a再按下d,蛇直接死亡的冲突的问题
if (prveCh == 'd' && ch == 'a' || prveCh == 'w' && ch == 's' || prveCh == 'a' && ch == 'd' || prveCh == 's' && ch == 'w')
{
ch = prveCh;
}
else
{
prveCh = ch;
}
//键盘按下,改变偏移量
switch (ch)
{
case 'a':
offset_x = -1;
offset_y = 0;
break;
case 'd':
offset_x = 1;
offset_y = 0;
break;
case 'w':
offset_x = 0;
offset_y = -1;
break;
case 's':
offset_x = 0;
offset_y = 1;
break;
default:
break;
}
//记录蛇尾的位置
end_x = snake.body[snake.size-1].x;
end_y = snake.body[snake.size - 1].y;
//要从尾节点开始往前移动,不然先移动走了蛇头,蛇头后的节点就找不到蛇头了
for (int i =snake.size-1;i>0;i--)
{
//从蛇尾开始一个个往前移动,到蛇头结束
snake.body[i].x = snake.body[i - 1]. x;
snake.body[i].y = snake.body[i - 1].y;
}
//移动蛇头
snake.body[0].x += offset_x;
snake.body[0].y += offset_y;
//判断是否构成循环
if (snake.body[0].x == end_x && snake.body[0].y == end_y)
{
isRool = 1;
}
else
{
isRool = 0;
}
}
//蛇吃到食物
void snakeEatFood()
{
//蛇头碰到食物
if (snake.body[0].x == food.x && snake.body[0].y == food.y)
{
//蛇身长度加1
snake.size++;
//食物位置重新初始化
initFood();
//每次吃到食物,分数加100
score += 100;
//每次吃到食物,刷新时间减少10
sleepTime-= 10;
//当前速度加10
speed += 10;
}
}
//死亡检测
void checkDead()
{
//两种情况
//1.撞墙 2.撞自身
//2.撞自身
//检测蛇的身体是否与蛇身相碰撞
for (int i = 1; i < snake.size ; i++)
{
if (snake.body[0].x == snake.body[i].x && snake.body[0].y == snake.body[i].y)
{
gameOver(2);
}
}
}
//游戏结束 1是撞墙 2是撞自身
void gameOver(int mode)
{
if (mode == 2)
{
gotoxy(snake.body[0].x, snake.body[0].y);
printf(" ");
system("cls");
gotoxy(45, 10);
printf("游戏结束,大侠请重新来过\n");
gotoxy(45, 12);
system("pause");
exit(0);
}
//撞墙
system("cls");
gotoxy(45, 10);
printf("游戏结束,大侠请重新来过\n");
gotoxy(45, 12);
system("pause");
exit(0);
}
//设置控制台输出文本颜色
void setTextColor(int num)
{
HANDLE handle = GetStdHandle(STD_OUTPUT_HANDLE);
SetConsoleTextAttribute(handle, num);
}