//2020-6-18
#include <stdio.h>
#include <stdlib.h>
#include <conio.h>
#include <string.h>
#include <time.h>
#include <windows.h>

const int height = 20; //地图的高
const int length = 20; //地图的长
char Map[height][length]; //游戏地图

int key; //按键保存
int snakeLen = 4; //蛇的长度,刚开始时为4
int gameOver = 0; //自吃或碰墙时gameOver=1,游戏结束

//左、右、上、下的方向
int dx[4] = {0, 0, -1, 1};
int dy[4] = {-1, 1, 0, 0};

enum
{
LeftKey = 75,
RightKey = 77,
UpKey = 72,
DownKey = 80
};

struct Snake //蛇的每个节点的数据类型
{
int r; //行位置
int c; //列位置
int dir; //当前节点的方向, 0,1,2,3分别为左右上下
}Snake[height * length];

const char head = '@'; //蛇头
const char body = '#'; //蛇身
const char food = '*'; //食物

//函数声明
void Initial(); //地图的初始化
void CreateFood(); //在地图上随机产生食物
void Show(); //刷新显示地图
void GetPressKey(); //取出按键,并判断方向
void Move(); //蛇的移动
void CheckBorder(); //检查蛇头是否越界
void CheckHead(int r, int c); //检查蛇头移动后的位置情况

int main()
{
Initial();

Show();

return 0;
}

void Initial() //地图的初始化
{
int i, j;
int hx, hy;
system("title 贪吃蛇"); //控制台的标题
memset(Map, '.', sizeof(Map)); //初始化地图全部为空'.'
//system("cls");
srand(time(0)); //随机种子
hx = rand() % height; //产生蛇头
hy = rand() % length;
Map[hx][hy] = head;
Snake[0].r = hx; Snake[0].c = hy;
Snake[0].dir = -1;

CreateFood(); //随机产生食物

for(i = 0; i < height; i++) //地图显示
{
for(j = 0; j < length; j++)
{
printf("%c", Map[i][j]);
}
printf("\n");
}

printf("\nC语言贪吃蛇\n");
printf("按任意方向键开始游戏\n");

getch(); //先接受一个按键,使蛇开始往该方向走

GetPressKey(); //取出按键,并判断方向
}

void CreateFood() //在地图上随机产生食物
{
int fx, fy;
while(1)
{
fx = rand() % height;
fy = rand() % length;
if(Map[fx][fy] == '.') //不能出现在蛇所占有的位置
{
Map[fx][fy] = food;
break;
}
}
}

void Show() //刷新显示地图
{
int i, j;
while(1)
{
Sleep(500); //延迟半秒(1000为1s),即每半秒刷新一次地图

GetPressKey(); //先判断按键在移动

Move();

if(gameOver) //自吃或碰墙即游戏结束
{
printf("\n**游戏结束**\n");
printf(" >_<\n");
getchar();
break;
}

system("cls"); //清空地图再显示刷新吼的地图

for(i = 0; i < height; i++)
{
for(j = 0; j < length; j++)
{
printf("%c", Map[i][j]);
}
printf("\n");
}

printf("\nC语言贪吃蛇\n");
printf("按任意方向键开始游戏\n");
}
}

void GetPressKey() //取出按键,并判断方向
{
if(kbhit() != 0) //检查当前是否有键盘输入,若有则返回一个非0值,否则返回0
{
while(kbhit() != 0) //可能存在多个按键,要全部取完,以最后一个为主
{
key = getch(); //将按键从控制台中取出并保存到key中
}

switch(key)
{ //左
case LeftKey:
{
Snake[0].dir = 0;
break;
}
//右
case RightKey:
{
Snake[0].dir = 1;
break;
}
//上
case UpKey:
{
Snake[0].dir = 2;
break;
}
//下
case DownKey:
{
Snake[0].dir = 3;
break;
}
}
}
}

void Move() //蛇的移动
{
int i, r, c;
int len = snakeLen; //保存当前蛇的长度
//记录当前蛇头的位置,并设置为空,蛇头先移动

r = Snake[0].r;
c = Snake[0].c;
Map[r][c] = '.';

Snake[0].r = Snake[0].r + dx[ Snake[0].dir ];
Snake[0].c = Snake[0].c + dy[ Snake[0].dir ];

CheckBorder(); //蛇头是否越界

CheckHead(r, c); //蛇头移动后的位置情况,参数为: 蛇头的开始位置

if(snakeLen == len) //未吃到食物即蛇身移动哦
{
for(i = 1; i < snakeLen; i++) //要从蛇尾节点向前移动哦,前一个节点作为参照
{
if(i == 1) //尾节点设置为空再移动
{
Map[ Snake[i].r ][ Snake[i].c ] = '.';
}

if(i == snakeLen-1) //为蛇头后面的蛇身节点,特殊处理
{
Snake[i].r = r;
Snake[i].c = c;
Snake[i].dir = Snake[0].dir;
}
else //其他蛇身即走到前一个蛇身位置
{
Snake[i].r = Snake[i+1].r;
Snake[i].c = Snake[i+1].c;
Snake[i].dir = Snake[i+1].dir;
}
Map[ Snake[i].r ][ Snake[i].c ] = '#'; //移动后要置为'#'蛇身
}
}
}

void CheckBorder() //检查蛇头是否越界
{
if(Snake[0].r < 0 || Snake[0].r >= height || Snake[0].c < 0 || Snake[0].c >= length)
{
gameOver = 1;
}
}

void CheckHead(int r, int c) //检查蛇头移动后的位置情况
{
if(Map[ Snake[0].r ][ Snake[0].c ] == '.') //为空
{
Map[ Snake[0].r ][ Snake[0].c ] = '@';
}
else
{
if(Map[ Snake[0].r ][ Snake[0].c ] == '*') //为食物
{
Map[ Snake[0].r ][ Snake[0].c ] = '@';
Snake[snakeLen].r = r; //新增加的蛇身为蛇头后面的那个
Snake[snakeLen].c = c;
Snake[snakeLen].dir = Snake[0].dir;
Map[ Snake[snakeLen].r ][ Snake[snakeLen].c ] = '#';
snakeLen++;
CreateFood(); //食物吃完了马上再产生一个食物
}
else
{
gameOver = 1;
}
}
}