You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

992 lines
22 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <Windows.h>
#include <stdlib.h>
#include <time.h>
#include <conio.h>
#include <math.h>
#define ROW 22 // 游戏区行数
#define COL 42 // 游戏区列数
#define KONG 0 // 标记空
#define WALL 1 // 标记墙
#define FOOD 2 // 标记普通食物
#define HEAD 3 // 标记蛇头
#define BODY 4 // 标记蛇身
#define OBSTACLE 5 // 标记障碍物
#define SPECIAL_FOOD 6 // 标记特殊食物
#define UP 72 // 方向键:上
#define DOWN 80 // 方向键:下
#define LEFT 75 // 方向键:左
#define RIGHT 77 // 方向键:右
#define SPACE 32 // 暂停
#define ESC 27 // 退出
#define ENTER 13 // 回车键
// 蛇头
struct Snake
{
int len; // 记录蛇身长度
int x; // 蛇头横坐标
int y; // 蛇头纵坐标
} snake;
// 蛇身
struct Body
{
int x; // 蛇身横坐标
int y; // 蛇身纵坐标
} body[ROW * COL]; // 开辟足以存储蛇身的结构体数组
int face[ROW][COL]; // 标记游戏区各个位置的状态
// 隐藏光标
void HideCursor();
// 光标跳转
void CursorJump(int x, int y);
// 初始化界面
void InitInterface();
// 颜色设置
void color(int c);
// 从文件读取最高分
void ReadGrade();
// 更新最高分到文件
void WriteGrade();
// 初始化蛇
void InitSnake();
// 随机生成食物
void RandFood();
// 随机生成障碍物
void RandObstacles(int count);
// 判断得分与结束
int JudgeFunc(int x, int y); // 修改:返回游戏状态
// 打印蛇与覆盖蛇
void DrawSnake(int flag);
// 移动蛇
void MoveSnake(int x, int y);
// 执行按键
void run(int x, int y);
// 游戏主体逻辑函数
void Game();
// 显示开始菜单
void ShowMenu();
// 显示游戏结束界面
void ShowGameOver();
// 显示帮助信息
void ShowHelp();
// 改变游戏速度
void ChangeSpeed(int delta);
// 添加特殊食物
void AddSpecialFood();
// 移除特殊食物
void RemoveSpecialFood();
// 绘制游戏边框
void DrawBorder();
// 更新分数显示
void UpdateScoreDisplay();
// 移动障碍物
void MoveObstacles();
// 保存游戏
void SaveGame();
// 加载游戏
void LoadGame();
int max, grade; // 全局变量
int game_speed = 300; // 游戏速度(毫秒)- 修改:降低初始速度
int obstacles_mode = 0; // 障碍物模式0-无1-固定障碍物2-移动障碍物
int obstacles_count = 0; // 当前障碍物数量
int special_food_active = 0; // 特殊食物是否激活
int special_food_x, special_food_y; // 特殊食物位置
int game_paused = 0; // 游戏是否暂停
int game_started = 0; // 游戏是否开始
int pass_wall = 0; // 是否开启穿墙模式
time_t start_time; // 游戏开始时间
int last_move_time; // 上一次移动障碍物的时间
int special_food_timer = 0; // 特殊食物计时器
int main()
{
max = 0, grade = 0; // 初始化变量
system("title 贪吃蛇完美版"); // 设置cmd窗口的名字
system("mode con cols=84 lines=23"); // 设置cmd窗口的大小
HideCursor(); // 隐藏光标
ReadGrade(); // 从文件读取最高分到max变量
while (1) {
ShowMenu(); // 显示开始菜单
InitInterface(); // 初始化界面
InitSnake(); // 初始化蛇
srand((unsigned int)time(NULL)); // 设置随机数生成起点
RandFood(); // 随机生成食物
if (obstacles_mode == 1 || obstacles_mode == 2) {
RandObstacles(10); // 生成10个障碍物
}
DrawSnake(1); // 打印蛇
Game(); // 开始游戏
// 游戏结束后询问是否返回主菜单
char ch;
CursorJump(COL, ROW / 2 + 6);
printf("返回主菜单?(y/n):");
ch = _getch();
if (ch == 'n' || ch == 'N') {
break;
}
// 重置游戏状态
game_speed = 300; // 重置为初始速度
obstacles_mode = 0;
obstacles_count = 0;
special_food_active = 0;
game_paused = 0;
game_started = 0;
pass_wall = 0;
grade = 0;
special_food_timer = 0;
system("cls");
}
return 0;
}
// 隐藏光标
void HideCursor()
{
CONSOLE_CURSOR_INFO curInfo;
curInfo.dwSize = 1;
curInfo.bVisible = FALSE;
HANDLE handle = GetStdHandle(STD_OUTPUT_HANDLE);
SetConsoleCursorInfo(handle, &curInfo);
}
// 光标跳转
void CursorJump(int x, int y)
{
COORD pos;
pos.X = x;
pos.Y = y;
HANDLE handle = GetStdHandle(STD_OUTPUT_HANDLE);
SetConsoleCursorPosition(handle, pos);
}
// 绘制游戏边框
void DrawBorder()
{
color(6); // 土黄色边框
for (int i = 0; i < ROW; i++)
{
for (int j = 0; j < COL; j++)
{
if (j == 0 || j == COL - 1)
{
face[i][j] = WALL;
CursorJump(2 * j, i);
printf("");
}
else if (i == 0 || i == ROW - 1)
{
face[i][j] = WALL;
CursorJump(2 * j, i);
printf("");
}
else
{
face[i][j] = KONG;
}
}
}
}
// 初始化界面
void InitInterface()
{
// 清空游戏区域
for (int i = 0; i < ROW; i++)
{
for (int j = 0; j < COL; j++)
{
face[i][j] = KONG;
}
}
// 绘制边框
DrawBorder();
// 绘制分数和状态信息
color(7); // 白色
CursorJump(0, ROW);
printf("当前得分: %d", grade);
CursorJump(COL, ROW);
printf("最高得分: %d", max);
CursorJump(COL * 2 - 10, ROW);
printf("速度: %d", 4000 / game_speed);
// 显示控制提示
CursorJump(0, ROW + 1);
printf("控制: ←→↑↓ 移动 | SPACE 暂停 | ESC 退出 | S 保存 | L 加载");
}
// 更新分数显示
void UpdateScoreDisplay()
{
color(7);
CursorJump(10, ROW);
printf("%d", grade);
CursorJump(COL + 10, ROW);
printf("%d", max);
CursorJump(COL * 2 - 4, ROW);
printf("%d", 4000 / game_speed);
}
// 颜色设置
void color(int c)
{
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), c);
}
// 从文件读取最高分
void ReadGrade()
{
FILE* pf = fopen("snake_max_score.dat", "rb");
if (pf == NULL)
{
pf = fopen("snake_max_score.dat", "wb");
if (pf) {
fwrite(&max, sizeof(int), 1, pf);
fclose(pf);
pf = fopen("snake_max_score.dat", "rb");
}
}
if (pf) {
fread(&max, sizeof(int), 1, pf);
fclose(pf);
}
}
// 更新最高分到文件
void WriteGrade()
{
FILE* pf = fopen("snake_max_score.dat", "wb");
if (pf) {
fwrite(&max, sizeof(int), 1, pf);
fclose(pf);
}
}
// 初始化蛇
void InitSnake()
{
snake.len = 2;
snake.x = COL / 2;
snake.y = ROW / 2;
body[0].x = COL / 2 - 1;
body[0].y = ROW / 2;
body[1].x = COL / 2 - 2;
body[1].y = ROW / 2;
face[snake.y][snake.x] = HEAD;
face[body[0].y][body[0].x] = BODY;
face[body[1].y][body[1].x] = BODY;
}
// 随机生成食物
void RandFood()
{
int i, j, attempts = 0;
do {
i = rand() % (ROW - 2) + 1;
j = rand() % (COL - 2) + 1;
attempts++;
if (attempts > 100) return; // 防止死循环
} while (face[i][j] != KONG);
face[i][j] = FOOD;
color(12); // 红色
CursorJump(2 * j, i);
printf("");
}
// 随机生成障碍物
void RandObstacles(int count)
{
for (int c = 0; c < count; c++)
{
int i, j, attempts = 0;
do {
i = rand() % (ROW - 2) + 1;
j = rand() % (COL - 2) + 1;
attempts++;
if (attempts > 100) return;
} while (face[i][j] != KONG);
face[i][j] = OBSTACLE;
color(8); // 灰色
CursorJump(2 * j, i);
printf("");
obstacles_count++;
}
}
// 移动障碍物
void MoveObstacles()
{
static int move_count = 0;
move_count++;
// 每10次移动一次障碍物
if (obstacles_mode == 2 && move_count % 10 == 0)
{
// 创建临时障碍物数组
int temp_obs[ROW * COL][2];
int count = 0;
// 收集所有障碍物位置
for (int i = 1; i < ROW - 1; i++)
{
for (int j = 1; j < COL - 1; j++)
{
if (face[i][j] == OBSTACLE)
{
temp_obs[count][0] = i;
temp_obs[count][1] = j;
count++;
}
}
}
// 移动每个障碍物
for (int idx = 0; idx < count; idx++)
{
int i = temp_obs[idx][0];
int j = temp_obs[idx][1];
// 随机选择移动方向
int dir = rand() % 4;
int ni = i, nj = j;
switch (dir)
{
case 0: ni--; break; // 上
case 1: ni++; break; // 下
case 2: nj--; break; // 左
case 3: nj++; break; // 右
}
// 检查目标位置是否有效
if (ni > 0 && ni < ROW - 1 && nj > 0 && nj < COL - 1 &&
face[ni][nj] == KONG)
{
// 清除原位置
face[i][j] = KONG;
CursorJump(2 * j, i);
printf(" ");
// 设置新位置
face[ni][nj] = OBSTACLE;
color(8);
CursorJump(2 * nj, ni);
printf("");
}
}
}
}
// 添加特殊食物
void AddSpecialFood()
{
if (special_food_active) return;
int i, j, attempts = 0;
do {
i = rand() % (ROW - 2) + 1;
j = rand() % (COL - 2) + 1;
attempts++;
if (attempts > 100) return;
} while (face[i][j] != KONG);
face[i][j] = SPECIAL_FOOD;
special_food_x = j;
special_food_y = i;
special_food_active = 1;
special_food_timer = 0;
color(14); // 黄色
CursorJump(2 * j, i);
printf("");
}
// 移除特殊食物
void RemoveSpecialFood()
{
if (!special_food_active) return;
face[special_food_y][special_food_x] = KONG;
special_food_active = 0;
CursorJump(2 * special_food_x, special_food_y);
printf(" ");
}
// 判断得分与结束 (修改:返回游戏状态)
int JudgeFunc(int x, int y)
{
int new_x = snake.x + x;
int new_y = snake.y + y;
// 穿墙模式处理
if (pass_wall)
{
if (new_x == 0) new_x = COL - 2;
else if (new_x == COL - 1) new_x = 1;
if (new_y == 0) new_y = ROW - 2;
else if (new_y == ROW - 1) new_y = 1;
}
// 检查边界
if (new_x <= 0 || new_x >= COL - 1 || new_y <= 0 || new_y >= ROW - 1)
{
if (!pass_wall) // 非穿墙模式撞墙结束游戏
{
return 1; // 游戏结束
}
}
// 检查新位置
switch (face[new_y][new_x])
{
case FOOD:
snake.len++;
grade += 10;
UpdateScoreDisplay();
RandFood();
// 每100分增加一个特殊食物
if (grade % 100 == 0 && !special_food_active)
{
AddSpecialFood();
}
return 0; // 继续游戏
case SPECIAL_FOOD:
grade += 50;
UpdateScoreDisplay();
RemoveSpecialFood();
// 增加游戏速度
if (game_speed > 500)
{
ChangeSpeed(-200);
}
return 0; // 继续游戏
case OBSTACLE:
case BODY:
return 1; // 游戏结束
default:
return 0; // 继续游戏
}
}
// 打印蛇与覆盖蛇
void DrawSnake(int flag)
{
if (flag == 1) // 打印蛇
{
color(10); // 绿色蛇头
CursorJump(2 * snake.x, snake.y);
printf("");
for (int i = 0; i < snake.len; i++)
{
// 蛇身颜色渐变
color(2 + (i % 6));
CursorJump(2 * body[i].x, body[i].y);
printf("");
}
}
else // 覆盖蛇
{
if (body[snake.len - 1].x != 0)
{
CursorJump(2 * body[snake.len - 1].x, body[snake.len - 1].y);
printf(" ");
}
}
}
// 移动蛇
void MoveSnake(int x, int y)
{
DrawSnake(0);
face[body[snake.len - 1].y][body[snake.len - 1].x] = KONG;
face[snake.y][snake.x] = BODY;
// 更新蛇身位置
for (int i = snake.len - 1; i > 0; i--)
{
body[i].x = body[i - 1].x;
body[i].y = body[i - 1].y;
}
body[0].x = snake.x;
body[0].y = snake.y;
// 更新蛇头位置
snake.x += x;
snake.y += y;
// 穿墙处理
if (pass_wall)
{
if (snake.x == 0) snake.x = COL - 2;
else if (snake.x == COL - 1) snake.x = 1;
if (snake.y == 0) snake.y = ROW - 2;
else if (snake.y == ROW - 1) snake.y = 1;
}
// 标记新的蛇头位置
face[snake.y][snake.x] = HEAD;
DrawSnake(1);
}
// 改变游戏速度
void ChangeSpeed(int delta)
{
game_speed += delta;
if (game_speed < 100) game_speed = 100; // 设置最小速度
if (game_speed > 1000) game_speed = 1000; // 设置最大速度
UpdateScoreDisplay();
}
// 保存游戏
void SaveGame()
{
FILE* pf = fopen("snake_save.dat", "wb");
if (pf == NULL) return;
// 保存游戏状态
fwrite(&grade, sizeof(int), 1, pf);
fwrite(&snake, sizeof(struct Snake), 1, pf);
fwrite(body, sizeof(struct Body), snake.len, pf);
fwrite(face, sizeof(int), ROW * COL, pf);
fwrite(&obstacles_mode, sizeof(int), 1, pf);
fwrite(&pass_wall, sizeof(int), 1, pf);
fwrite(&game_speed, sizeof(int), 1, pf);
fwrite(&special_food_active, sizeof(int), 1, pf);
if (special_food_active)
{
fwrite(&special_food_x, sizeof(int), 1, pf);
fwrite(&special_food_y, sizeof(int), 1, pf);
}
fclose(pf);
// 提示保存成功
CursorJump(COL, ROW + 2);
color(10);
printf("游戏已保存!");
Sleep(1000);
CursorJump(COL, ROW + 2);
printf(" ");
}
// 加载游戏
void LoadGame()
{
FILE* pf = fopen("snake_save.dat", "rb");
if (pf == NULL) return;
// 清除当前游戏状态
system("cls");
InitInterface();
// 加载游戏状态
fread(&grade, sizeof(int), 1, pf);
fread(&snake, sizeof(struct Snake), 1, pf);
fread(body, sizeof(struct Body), snake.len, pf);
fread(face, sizeof(int), ROW * COL, pf);
fread(&obstacles_mode, sizeof(int), 1, pf);
fread(&pass_wall, sizeof(int), 1, pf);
fread(&game_speed, sizeof(int), 1, pf);
fread(&special_food_active, sizeof(int), 1, pf);
if (special_food_active)
{
fread(&special_food_x, sizeof(int), 1, pf);
fread(&special_food_y, sizeof(int), 1, pf);
}
fclose(pf);
// 重新绘制游戏界面
for (int i = 0; i < ROW; i++)
{
for (int j = 0; j < COL; j++)
{
switch (face[i][j])
{
case WALL:
color(6);
CursorJump(2 * j, i);
printf("");
break;
case FOOD:
color(12);
CursorJump(2 * j, i);
printf("");
break;
case OBSTACLE:
color(8);
CursorJump(2 * j, i);
printf("");
break;
case SPECIAL_FOOD:
color(14);
CursorJump(2 * j, i);
printf("");
break;
default:
break;
}
}
}
DrawSnake(1);
UpdateScoreDisplay();
// 提示加载成功
CursorJump(COL, ROW + 2);
color(10);
printf("游戏已加载!");
Sleep(1000);
CursorJump(COL, ROW + 2);
printf(" ");
}
// 显示帮助信息
void ShowHelp()
{
system("cls");
color(11); // 浅蓝色
CursorJump(COL / 2 - 10, 2);
printf("========== 贪吃蛇游戏帮助 ==========");
color(10); // 绿色
CursorJump(COL / 4, 4);
printf("控制方式:");
CursorJump(COL / 4, 5);
printf("方向键: 控制蛇的移动方向");
CursorJump(COL / 4, 6);
printf("空格键: 暂停/继续游戏");
CursorJump(COL / 4, 7);
printf("ESC键: 退出游戏");
CursorJump(COL / 4, 8);
printf("S键: 保存游戏");
CursorJump(COL / 4, 9);
printf("L键: 加载游戏");
CursorJump(COL / 4, 11);
printf("游戏规则:");
CursorJump(COL / 4, 12);
printf("1. 吃到红色食物 ● 得10分蛇身变长");
CursorJump(COL / 4, 13);
printf("2. 吃到黄色食物 ★ 得50分增加速度");
CursorJump(COL / 4, 14);
printf("3. 撞到墙壁、障碍物或自身游戏结束");
CursorJump(COL / 4, 16);
printf("特殊功能:");
CursorJump(COL / 4, 17);
printf("穿墙模式: 可以穿过墙壁");
CursorJump(COL / 4, 18);
printf("障碍物模式: 无、固定障碍物、移动障碍物");
color(14); // 黄色
CursorJump(COL / 2 - 10, ROW - 2);
printf("按任意键返回主菜单...");
_getch(); // 添加等待按键
}
// 显示游戏结束界面
void ShowGameOver()
{
system("cls");
color(12); // 红色
CursorJump(COL / 2 - 10, ROW / 2 - 3);
// 计算游戏时间
time_t end_time = time(NULL);
int game_time = (int)difftime(end_time, start_time);
int minutes = game_time / 60;
int seconds = game_time % 60;
// 显示得分信息
if (grade > max)
{
printf("恭喜打破记录!新纪录: %d", grade);
max = grade;
WriteGrade();
}
else if (grade == max)
{
printf("平最高记录!得分: %d", grade);
}
else
{
printf("当前得分: %d (最高: %d)", grade, max);
}
// 显示游戏信息
CursorJump(COL / 2 - 10, ROW / 2);
printf("游戏时间: %02d:%02d", minutes, seconds);
CursorJump(COL / 2 - 10, ROW / 2 + 2);
printf("G A M E O V E R");
// 显示选项
color(14); // 黄色
CursorJump(COL / 2 - 10, ROW / 2 + 4);
printf("按任意键返回主菜单...");
_getch(); // 添加等待按键
}
// 游戏主体逻辑函数
void Game()
{
// 游戏主循环
int key = 0;
int dx = 1, dy = 0; // 初始向右移动
int last_dx = dx, last_dy = dy; // 记录上一次移动方向
int game_over = 0; // 游戏结束标志
DWORD last_move_time = GetTickCount(); // 记录上次移动时间
while (!game_over)
{
// 处理输入
if (_kbhit())
{
key = _getch();
switch (key)
{
case UP:
if (last_dy == 0) { dx = 0; dy = -1; }
break;
case DOWN:
if (last_dy == 0) { dx = 0; dy = 1; }
break;
case LEFT:
if (last_dx == 0) { dx = -1; dy = 0; }
break;
case RIGHT:
if (last_dx == 0) { dx = 1; dy = 0; }
break;
case SPACE:
game_paused = !game_paused;
if (game_paused) {
CursorJump(COL, ROW + 2);
color(12);
printf("游戏暂停中...");
}
else {
CursorJump(COL, ROW + 2);
printf(" ");
}
break;
case ESC:
return; // 退出游戏
case 's':
case 'S':
SaveGame();
break;
case 'l':
case 'L':
LoadGame();
break;
}
}
if (game_paused)
{
Sleep(100);
continue;
}
// 更新特殊食物计时器
if (special_food_active)
{
special_food_timer++;
if (special_food_timer > 100)
{
RemoveSpecialFood();
}
}
// 移动障碍物
if (obstacles_mode == 2)
{
DWORD current_time = GetTickCount();
if (current_time - last_move_time > 500) { // 每500毫秒移动一次
MoveObstacles();
last_move_time = current_time;
}
}
// 定时移动蛇
DWORD current_time = GetTickCount();
if (current_time - last_move_time > game_speed)
{
last_move_time = current_time;
// 判断是否结束
if (JudgeFunc(dx, dy))
{
game_over = 1;
continue;
}
// 移动蛇
MoveSnake(dx, dy);
// 保存当前移动方向
last_dx = dx;
last_dy = dy;
}
// 小延迟减少CPU占用
Sleep(10);
}
// 游戏结束处理
ShowGameOver();
}
// 显示开始菜单
void ShowMenu()
{
system("cls");
color(11);
CursorJump(COL / 2 - 5, ROW / 4);
printf(" 小张版贪吃蛇");
color(10);
CursorJump(COL / 2 - 5, ROW / 4 + 2);
printf("1. 开始游戏");
CursorJump(COL / 2 - 5, ROW / 4 + 3);
printf("2. 游戏设置");
CursorJump(COL / 2 - 5, ROW / 4 + 4);
printf("3. 游戏帮助");
CursorJump(COL / 2 - 5, ROW / 4 + 5);
printf("4. 退出游戏");
color(14);
CursorJump(COL / 2 - 5, ROW / 4 + 7);
printf("请选择: ");
char choice = _getch();
switch (choice)
{
case '1': // 开始游戏
game_started = 1;
start_time = time(NULL);
break;
case '2': // 游戏设置
{
system("cls");
color(11);
CursorJump(COL / 2 - 5, ROW / 4);
printf("★ 游戏设置 ★");
int setting = 1;
while (setting)
{
color(10);
CursorJump(COL / 2 - 10, ROW / 4 + 2);
printf("a. 游戏速度: %d", 4000 / game_speed);
CursorJump(COL / 2 - 10, ROW / 4 + 3);
printf("b. 障碍物模式: %s",
obstacles_mode == 0 ? "" :
obstacles_mode == 1 ? "固定障碍物" : "移动障碍物");
CursorJump(COL / 2 - 10, ROW / 4 + 4);
printf("c. 穿墙模式: %s", pass_wall ? "开启" : "关闭");
CursorJump(COL / 2 - 10, ROW / 4 + 5);
printf("d. 返回主菜单");
color(14);
CursorJump(COL / 2 - 10, ROW / 4 + 7);
printf("请选择: ");
char option = _getch();
switch (option)
{
case 'a': // 调整速度
system("cls");
color(11);
CursorJump(COL / 2 - 5, ROW / 4);
printf("★ 设置游戏速度 ★");
color(10);
CursorJump(COL / 2 - 10, ROW / 4 + 2);
printf("当前速度: %d", 4000 / game_speed);
CursorJump(COL / 2 - 10, ROW / 4 + 3);
printf("按 + 加速,按 - 减速");
CursorJump(COL / 2 - 10, ROW / 4 + 4);
printf("按回车返回");
while (1)
{
char speedKey = _getch();
if (speedKey == '+') ChangeSpeed(-100);
else if (speedKey == '-') ChangeSpeed(100);
else if (speedKey == ENTER) break;
CursorJump(COL / 2, ROW / 4 + 2);
printf("%d ", 4000 / game_speed);
}
system("cls");
break;
case 'b': // 障碍物模式
obstacles_mode = (obstacles_mode + 1) % 3;
break;
case 'c': // 穿墙模式
pass_wall = !pass_wall;
break;
case 'd': // 返回
setting = 0;
break;
}
}
system("cls");
ShowMenu();
break;
}
case '3': // 游戏帮助
ShowHelp();
system("cls");
ShowMenu();
break;
case '4': // 退出游戏
exit(0);
default:
ShowMenu();
break;
}
}