#include<stdio.h>
#include<string.h>
#include<windows.h>
#include<time.h>
#include<conio.h>
#define up 'w'
#define down 's'
#define left 'a'
#define right 'd'
#define bool int
#define true 1
#define false 0
void welcome(); // 开始界面
void finish(); // 结束界面
void createGraph(); // 围墙打印
void gotoxy(int x, int y); // 光标跳转,横为X 0,1,2..
void gotoPrint(int x, int y); // 跳转打印
void gotoDelete(int x, int y);// 跳转删除
void createFood(); // 食物产生
int clickControl(); // 获取键盘信号
int judge(); // 游戏结束判断
void movingBody(); // 蛇的移动
void eating(); // 蛇吃到东西后的操作(伸长)
void changeBody(int x, int y); // 蛇的坐标变换
/*全局变量 + 预处理:*/
typedef struct Snakes
{
int x;
int y;
struct Snakes* next;
}snake;
snake* head; // 声明蛇头指针
// 申明并定义食物
struct Food
{
int x;
int y;
}food;
char name[20]; // 保存用户名 有兴趣可以制作登录系统
int score = 0; // 分数
char click = 1; // 记录敲下的键盘按键
int speed = 10; // 速度 其实是延迟的毫秒数
void showCopy(){
system("cls");
printf("\n\n\t*************************欢迎进入游戏世界***************************");
printf("\n\n\t**************************源辰信息版权所有**************************");
}
int main()
{
system("color 0B"); // 设置控制台字体颜色
welcome(); // 欢迎界面
createGraph(); // 创建地图
createFood(); // 新建食物
// 捕获鼠标按键 ClickControl
if (clickControl() == 0) return 0;
return 0;
}
// 欢迎界面
void welcome()
{
showCopy();
gotoxy(26, 10);
printf("欢迎来到贪吃蛇游戏");
gotoxy(14, 14);
printf("[请在英文输入法中操作,反向移动等同于吃到自己,wasd控制p暂停]");
gotoxy(26, 18);
printf("请输入您的昵称:");
HANDLE hOutput = GetStdHandle(STD_OUTPUT_HANDLE);
CONSOLE_CURSOR_INFO cursor; // 获取光标
cursor.bVisible = TRUE; // 隐藏光标
cursor.dwSize = sizeof(cursor);
SetConsoleCursorInfo(hOutput, &cursor); // 显示光标
scanf("%s", name);
system("cls");
}
// 围墙打印
void createGraph() {
int i;
// 注意这里横坐标是每次+2 因为控制台字符宽高比为1:2
for (i = 0; i < 58; i += 2)
{
gotoPrint(i, 0); // 顶部围墙
gotoPrint(i, 26); // 底部围墙
}
for (i = 1; i < 26; i++)
{
gotoPrint(0, i); // 左边围墙
gotoPrint(56, i); // 右边围墙
}
gotoxy(63, 8);
printf("你好 %s, 欢迎进入游戏世界", name);
gotoxy(63, 13);
printf("当前分数:%d", score);
gotoxy(63, 18);
printf("源辰信息科技提供");
// 初始化蛇
head = (snake*)malloc(sizeof(snake)); // 给蛇头申请空间
snake* p = (snake*)malloc(sizeof(snake)); // 蛇身
snake* q = (snake*)malloc(sizeof(snake)); // 蛇尾
// 设置蛇头的坐标
head->x = 16; // 行数
head->y = 15; // 列数
// 第一节蛇身的坐标
p->x = 16;
p->y = 16;
// 第二节蛇身的坐标
q->x = 16;
q->y = 17;
head->next = p; // 蛇头的下一个节点是第一节蛇身
p->next = q; // 第一节蛇身的下一个节点是第二节蛇身
q->next = NULL; // 第二节蛇身的下一个节点为空
}
// 移动光标到指定位置
void gotoxy(int x, int y)
{
COORD pos; // 更新光标位置
// GetStdHandle()返回标准的输入、输出或错误的设备的句柄,也就是获得输入、输出/错误的屏幕缓冲区的句柄。
// STD_INPUT_HANDLE 标准输入的句柄
// STD_OUTPUT_HANDLE 标准输出的句柄
// STD_ERROR_HANDLE 标准错误的句柄
HANDLE hOutput = GetStdHandle(STD_OUTPUT_HANDLE);
pos.X = x;
pos.Y = y;
SetConsoleCursorPosition(hOutput, pos); // 设置控制台(cmd)光标位置
CONSOLE_CURSOR_INFO cursor; // 获取光标
cursor.bVisible = FALSE; // 隐藏光标
cursor.dwSize = sizeof(cursor);
SetConsoleCursorInfo(hOutput, &cursor); // 隐藏光标
}
// 输出墙砖
void gotoPrint(int x, int y)
{
gotoxy(x, y);
printf("■");
}
// 删除指定位置的内容
void gotoDelete(int x, int y)
{
gotoxy(x, y);
printf(" ");
}
// 创建食物
void createFood()
{
// 随机产生一个食物
bool flag = false; // 标记食物是否已经吃了
while (!flag) // 如果食物已经吃了
{
flag = true;
// srand函数是随机数发生器的初始化函数
// time(t) 是指返回自 Unix 纪元(January 1 1970 00:00:00 GMT)起的当前时间的秒数的函数,主要用来获取当前的系统时间,返回的结果是一个time_t类型。
// 如果t是空指针,直接返回当前时间。如果t不是空指针,返回当前时间的同时,将返回值赋予t指向的内存空间。
srand((int)time(NULL));
// 随机生成食物的坐标
food.y = rand() % 25 + 1;
food.x = rand() % (54 - 1) + 2;
if (food.x % 2 != 0)
{
food.x = food.x + 1;
}
snake* judge = head; // 定义指针judge指向蛇头
// 遍历蛇,保证生成的这个食物不在蛇身上
while (1) // 遍历排除蛇身重复
{
if (judge->next == NULL) break; // 说明遍历结束
if (food.x == judge->x && food.y == judge->y) // 说明生成的食物在蛇身上,则需要重新生成食物
{
flag = false;
break;
}
judge = judge->next; // 否则判断是否在下一个蛇身节点上
}
}
gotoxy(food.x, food.y); // 移动光标到食物位置
printf("⊙"); // 输出食物
}
// 捕获鼠标 游戏主循环
int clickControl()
{
while (1)
{
if (judge() == 0) return 0; // 如果游戏已经结束
if (kbhit()) // kbhit()是一个C和C++函数,用于非阻塞地响应键盘输入事件 ,检查当前是否有键盘输入,若有则返回一个非0值,否则返回0。
{
click = getch(); // 获取键盘输入
}
movingBody(); // 移动蛇
eating(); // 吃食物
}
return 1;
}
// 移动蛇
void movingBody() {
// 获取蛇头的位置
int x = head->x, y = head->y;
snake* p = head;
// 通过先清空后打印实现动画效果
// 移动p让其指向蛇尾
while (p->next != NULL) {
p = p->next;
}
gotoDelete(p->x, p->y); // 消除尾节点
switch (click)
{
case up:
y -= 1;
break;
case down:
y += 1;
break;
case left:
x -= 2;
break;
case right:
x += 2;
break;
default:
break;
}
if (x != head->x || y != head->y) {
// 改变坐标时更新蛇的位置
changeBody(x, y);
}
p = head; // 修改p指向新的舌头
gotoPrint(p->x, p->y); // 打印蛇头
// 蛇速度控制,分数越高,速度越快
int count = score / 10;
if (count <= 10) speed = 150;
else if (count > 10 && count <= 20) speed = 100;
else if (count > 20 && count <= 40) speed = 50;
else speed = 10;
Sleep(speed);
}
// 吃到食物处理 添加一个尾巴
void eating()
{
if (head->x == food.x && head->y == food.y) // 如果舌头跟食物的位置重合
{
createFood(); // 创建一个新的食物
snake* _new = (snake*)malloc(sizeof(snake)); // 申请一个蛇身空间
snake* p;
p = head;
while (1)
{
if (p->next == NULL) break;
p = p->next;
}
// 在蛇尾添加一节
p->next = _new;
_new->next = NULL;
score += 10; // 得分 +10
gotoxy(72, 13); // 修改得分
printf("%d", score);
}
}
// 更新蛇体坐标 只需要消除尾结点 然后把新坐标结点置为头结点即可
void changeBody(int x, int y)
{
snake* p = head;
while (p->next->next != NULL) { // 移动p都蛇尾的上一个
p = p->next;
}
free(p->next); // 释放蛇尾的空间
p->next = NULL; // 丢弃蛇尾
snake* new_head = (snake*)malloc(sizeof(snake)); // 申请一个新的蛇头空间
// 设置蛇头的坐标为刚才移动的位置坐标
new_head->x = x;
new_head->y = y;
// 改变蛇头
new_head->next = head;
head = new_head;
}
// 判断是否游戏结束
int judge()
{
// 如果撞墙了
if (head->x == 0 || head->x == 56 || head->y == 0 || head->y == 26)
{
finish();
return 0;
}
snake* p = head->next;
while (1)
{
if (p == NULL) break;
if (head->x == p->x && head->y == p->y) // 如果咬到自己了
{
finish();
return 0;
}
p = p->next;
}
return 1;
}
void finish()
{
system("cls");
showCopy();
gotoxy(20, 10);
printf("欢迎来到贪吃蛇游戏");
gotoxy(20, 14);
printf("游戏结束...");
gotoxy(20, 18);
printf("最终得分为: %d", score);
gotoxy(20, 22);
// 释放空间
snake* p = head, * q;
while (p != NULL) {
q = p->next;
free(p);
p = q;
}
system("pause");
}