/*
@ BY COOL_BREEZE
@ 2020-08-27
*/
#include<stdio.h>
#include<stdlib.h>
#include<time.h>
#include<conio.h>
#include<windows.h>
#define HIGHT 30
#define WIDTH 100
// dos窗口显示模式
#define MODE(X, Y) "MODE CON COLS="#X" LINES="#Y
#define HAD (GetStdHandle(STD_OUTPUT_HANDLE))
// 蛇的结构体开辟内存空间
#define CALLSNAKE ((SNAKEP)malloc(sizeof(SNAKEV)))
void hidecursor(void); // 改变光标的显示方式
void gotoxy_print(int x, int y, char icon); // 移动光标到指定位置打印图标
void __init__(void); // 初始化边框
void create_snake(void); // 生成蛇
void move_snake(void); // 移动蛇
void create_food(void); // 生成食物
void get_keyborad_input(void); // 获取键盘值
int hit_the_wall(void); // 蛇撞墙
void game_method(void);
typedef struct SNAKE
{
int x;
int y;
struct SNAKE* next;
} SNAKEV, *SNAKEP;
SNAKEP SNAKE_HEAD; //蛇头
SNAKEP SNAKE_FOOD; //食物
//蛇前进的方向
#define U 0X48 // 上
#define D 0X50 // 下
#define L 0X4B // 左
#define R 0X4D // 右
#define S 0X7F // 空格 暂停
char DIRECTION = L; // 默认蛇往左边移动
char HEAD_ICON = 0XF;
char BODY_ICON = 0XB;
char FOOD_ICON = 0X24;
char NULL_ICON = 0X20;
int main(void)
{
system(MODE(130, 30));
system("title 贪吃蛇游戏");
game_method();
hidecursor();
__init__();
SNAKE_FOOD = CALLSNAKE;
create_snake();
create_food();
int ser; // 延迟
while(1)
{
ser = 0;
move_snake();
if (kbhit()) // 检查键盘是否被按下,非阻塞的 (相当于多线程)
{
get_keyborad_input();
Sleep(20);
ser = 1;
}
if (hit_the_wall())
{
gotoxy_print((WIDTH-8) / 2, HIGHT / 2, NULL_ICON);
printf("游戏结束");
system("pause >nul");
break;
}
if (ser)
Sleep(10);
else Sleep(200);
}
return 0;
}
void hidecursor(void)
{
CONSOLE_CURSOR_INFO show = {1, 0};
SetConsoleCursorInfo(HAD, &show);
}
void gotoxy_print(int x, int y, char icon)
{
COORD poin = {x, y}; // 结构体
SetConsoleCursorPosition(HAD, poin); // 跳转到窗口指定位置
printf("%c", icon);
}
void game_method(void)
{
gotoxy_print(WIDTH + 5, HIGHT/2-7, NULL_ICON);
printf("DATE 2020-08-27");
gotoxy_print(WIDTH + 5, HIGHT/2-6, NULL_ICON);
printf("BY COOL_BREEZE");
gotoxy_print(WIDTH + 5, HIGHT/2-4, NULL_ICON);
printf("空格 暂停");
gotoxy_print(WIDTH + 5, HIGHT/2-3, NULL_ICON);
printf("向上 ↑");
gotoxy_print(WIDTH + 5, HIGHT/2-2, NULL_ICON);
printf("向下 ↓");
gotoxy_print(WIDTH + 5, HIGHT/2-1, NULL_ICON);
printf("向左 ←");
gotoxy_print(WIDTH + 5, HIGHT/2, NULL_ICON);
printf("向右 →");
}
void __init__(void)
{
int i = 0;
gotoxy_print(0, 0, 0X1); // 角落 1
gotoxy_print(WIDTH-1, 0, 0X2); // 角落 2
for (i=1; i<WIDTH-1; i++) // 上下边框
{
gotoxy_print(i, 0, 0X6);
gotoxy_print(i, HIGHT-1, 0X6);
}
for (i=1;i<HIGHT-1; i++) // 左右边框
{
gotoxy_print(0, i, 0X5);
gotoxy_print(WIDTH-1, i, 0X5);
}
gotoxy_print(0, HIGHT-1, 0X3); // 角落 3
gotoxy_print(WIDTH-1, HIGHT-1, 0X4); // 角落 4
}
void create_snake(void)
{
int i = 0,x = (WIDTH-6) / 2, y = HIGHT / 2; //在中心创建蛇
SNAKEP phead, pend;
for (; i<6; i++) // 蛇默认长度为6
{
pend = CALLSNAKE;
if (!i)
{
SNAKE_HEAD = phead = pend;
pend->x = x;
pend->y = y;
gotoxy_print(SNAKE_HEAD->x, SNAKE_HEAD->y, HEAD_ICON);
}
else
{
phead->next = pend;
pend->x = x + i; // 蛇身体在头右方
pend->y = y;
gotoxy_print(pend->x, pend->y, BODY_ICON);
phead = pend;
}
}
pend->next = NULL;
}
void move_snake(void)
{
SNAKEP head = CALLSNAKE; // 蛇新的头部
switch (DIRECTION)
{
case U:
head->x = SNAKE_HEAD->x;
head->y = SNAKE_HEAD->y - 1;
break;
case D:
head->x = SNAKE_HEAD->x;
head->y = SNAKE_HEAD->y + 1;
break;
case L:
head->x = SNAKE_HEAD->x - 1;
head->y = SNAKE_HEAD->y;
break;
case R:
head->x = SNAKE_HEAD->x + 1;
head->y = SNAKE_HEAD->y;
break;
case S:
system("pause >nul");
break;
}
head->next = SNAKE_HEAD; // 旧头部连接新头部 使旧头部成为身体
SNAKE_HEAD = head; // 获取头部
if (SNAKE_HEAD->x == SNAKE_FOOD->x && SNAKE_HEAD->y == SNAKE_FOOD->y) // 检查蛇是否吃到食物
{
while (head) // 吃到了食物蛇蛇尾不需要释放
{
if (head == SNAKE_HEAD)
gotoxy_print(SNAKE_HEAD->x, SNAKE_HEAD->y, HEAD_ICON);
else
gotoxy_print(head->x, head->y, BODY_ICON);
head = head->next;
}
create_food();
}
else
{
while (head->next->next) // 没吃到食物释放掉蛇尾
{
if (head == SNAKE_HEAD)
gotoxy_print(SNAKE_HEAD->x, SNAKE_HEAD->y, HEAD_ICON);
else
gotoxy_print(head->x, head->y, BODY_ICON);
head = head->next;
}
gotoxy_print(head->next->x, head->next->y, NULL_ICON);
free(head->next);
head->next = NULL;
}
}
void create_food(void)
{
SNAKEP temp;
while (1)
{
srand(time(NULL)); // 获取新种子 生成新的随机数
SNAKE_FOOD->x = rand() % (WIDTH - 2) + 1; // 确保坐标只会出现在表格内(不包括表格)
SNAKE_FOOD->y = rand() % (HIGHT - 2) + 1; // 确保坐标只会出现在表格内(不包括表格)
temp = SNAKE_HEAD;
while(temp) // 食物落在蛇身上重新获取
{
if (temp->x == SNAKE_FOOD->x && temp->y == SNAKE_FOOD->y)
continue;
else
{
gotoxy_print(SNAKE_FOOD->x, SNAKE_FOOD->y, FOOD_ICON);
break;
}
}
break;
}
}
void get_keyborad_input(void)
{
int key = 0;
while (1)
{
key = getch();
if (key == 224) // 键盘方向键需要获取两次
{
key = getch();
if (key == U ||\
key == D ||\
key == L ||\
key == R)
{
DIRECTION = key;
break;
}
else continue;
}
}
}
int hit_the_wall(void)
{
if (SNAKE_HEAD->x == 0 || SNAKE_HEAD->x == WIDTH-1 ||\
SNAKE_HEAD->y == 0 || SNAKE_HEAD->y == HIGHT-1 )
return 1;
return 0;
}