c语言版贪吃蛇(Pro Max版)附源代码
1 背景
贪吃蛇是一款经典的电子游戏,最早出现在20世纪70年代的街机游戏中。游戏的核心玩法是玩家控制一条蛇在有限的空间内移动,通过吃食物来增长身体长度,同时避免撞到墙壁、障碍物或自身。随着蛇的长度增加,游戏难度逐渐提升。
本实验通过C语言实现一个增强版的贪吃蛇游戏,加入以下功能:
-
多种食物:普通食物和特殊食物,特殊食物具有额外效果。
-
障碍物:随机生成的障碍物增加游戏难度。
-
加速功能:玩家可以消耗能量加速蛇的移动。
-
传送门:蛇可以通过传送门在地图中快速移动。
-
统计系统:记录玩家的游戏数据,包括得分、游戏时间和最长蛇长度等
2 实验技术
2.1开发环境
编程语言:C语言
开发工具:Visual Studio 2022
操作系统:Windows 11
依赖库:Windows API(用于控制台操作和音效)
2.2 关键技术
控制台操作:
使用 SetConsoleCursorPosition 控制光标位置。
使用 SetConsoleTextAttribute 设置文本颜色。
使用 Beep 函数播放音效。
双缓冲技术:通过清空控制台并重新绘制地图,避免画面闪烁。
随机数生成:使用 rand 函数生成随机位置,确保食物和障碍物的随机性。
数据结构:
使用结构体管理游戏对象(蛇、食物、传送门等)。
使用二维数组表示地图。
文件操作:使用 fopen、fwrite 和 fread 实现游戏状态的保存与加载。
3 实现细节
下面来详细介绍贪吃蛇游戏的实现细节,涵盖了游戏的核心逻辑、数据结构、功能模块以及关键算法。
3.1 游戏核心数据结构
游戏的核心数据结构包括蛇、食物、传送门、游戏状态和统计信息等。
1.1 蛇 (Snake)
结构体定义:
typedef struct {Point body[WIDTH * HEIGHT]; // 蛇的身体坐标int length; // 蛇的长度int direction; // 当前移动方向int speed; // 移动速度int boost_available; // 加速能量int is_boosting; // 是否正在加速
} Snake;
说明:
蛇的身体由一系列坐标点组成,body[0] 是蛇头,body[length-1] 是蛇尾。
蛇的移动通过更新 body 数组实现。
加速功能通过 is_boosting 和 boost_available 控制。
1.2 食物 (Food)
结构体定义:
typedef struct {Point pos; // 食物位置int value; // 食物分值char type; // 食物类型(普通/特殊)int lifetime; // 特殊食物的存在时间int is_special; // 是否为特殊食物
} Food;
说明:
普通食物 (*) 分值为 10,特殊食物 (#) 分值为 20。
特殊食物有倒计时 (lifetime),超时后消失。
1.3 传送门 (Portal)
结构体定义:
typedef struct {Point pos1; // 传送门入口Point pos2; // 传送门出口int active; // 是否激活
} Portal;
说明:蛇头进入传送门后,从另一个传送门出口出现。
1.4 游戏状态 (GameState)
结构体定义:
typedef struct {int score; // 当前得分int high_score; // 最高得分int level; // 当前等级int is_running; // 游戏是否运行int is_paused; // 游戏是否暂停time_t start_time; // 游戏开始时间Difficulty difficulty; // 游戏难度int obstacles_count; // 障碍物数量int power_ups; // 特殊道具数量int portal_active; // 传送门是否激活
} GameState;
说明:管理游戏的运行状态、得分、等级、难度等信息。
1.5 统计信息 (Statistics)
结构体定义:
typedef struct {int games_played; // 总游戏次数int total_score; // 总得分int longest_snake; // 最长蛇长度time_t total_play_time; // 总游戏时间float average_score; // 平均得分int highest_level; // 最高等级int total_food_eaten; // 总食物数量
} Statistics;
说明:记录玩家的游戏数据,用于统计和展示。
3.2游戏整体框架

3.3模块划分
(1)游戏核心逻辑模块
组件 | 功能描述 | 重要性 |
Snake | 蛇的属性与行为管理 | 核心 |
Food | 食物生成与状态管理 | 核心 |
GameState | 游戏状态控制 | 重要 |
Score | 得分系统管理 | 次要 |
主要功能:
维护游戏核心数据结构
处理游戏逻辑
管理游戏状态转换
控制得分计算
(2)显示控制模块
组件 | 功能描述 | 重要性 |
width/height | 显示区域尺寸控制 | 基础 |
buffer | 显示缓冲区管理 | 核心 |
colors | 颜色方案控制 | 优化 |
主要功能:
管理显示区域大小
控制显示缓冲
处理颜色渲染
优化显示效果
(3)输入控制模块
组件 | 功能描述 | 重要性 |
device | 输入设备管理 | 核心 |
state | 输入状态维护 | 重要 |
callback | 输入响应处理 | 核心 |
主要功能:
处理用户输入
维护输入状态
执行回调函数
管理输入设备
3.4核心算法
(1) 蛇身移动算法
算法具体步骤如下:
1.保存尾部:
获取蛇身体最后一个元素(即尾部)的坐标,并将其存储在变量tail中。
2.身体移动:
从蛇的尾部开始,即数组body的最后一个元素,向前遍历至头部(即数组的第一个元素)。
在遍历过程中,每个元素的坐标被更新为前一个元素的坐标。这样,蛇的身体就向前移动了一个单位。
3.头部移动:
根据蛇当前的方向(dir),更新蛇头的坐标:
如果方向是UP,则蛇头的y坐标减1(向上移动)。
如果方向是DOWN,则蛇头的y坐标加1(向下移动)。
如果方向是LEFT,则蛇头的x坐标减1(向左移动)。
如果方向是RIGHT,则蛇头的x坐标加1(向右移动)。
(2)碰撞检测算法
算法具体步骤如下:
1.获取蛇头位置:从snake结构体中取出数组的第一个元素,这个元素表示蛇头的位置,并将其存储在变量head中。
2.检查边界碰撞:
判断蛇头的x坐标是否小于0或者大于等于游戏区域的宽度WIDTH。
判断蛇头的y坐标是否小于0或者大于等于游戏区域的高度HEIGHT。
如果以上任一条件成立,说明蛇头已经碰撞到边界,函数返回true表示发生碰撞。
3.检查自身碰撞:
从数组的第二个元素开始遍历(索引为1,因为第一个元素是蛇头,不需要与自己比较),直到蛇的长度snake->length。
在循环中,判断当前蛇身的x坐标和y坐标是否与蛇头的坐标相同。
如果找到任何一个蛇身部分的坐标与蛇头坐标相同,说明蛇头碰撞到了自己的身体,函数返回true表示发生碰撞。
4.如果以上两种碰撞情况都没有发生,则函数返回false,表示没有发生碰撞。
(3)双缓冲池技术
-
使用双缓冲技术避免画面闪烁。
-
通过
SetConsoleCursorPosition
和SetConsoleTextAttribute
控制控制台光标和颜色。
优化方法 | 实现要点 | 预期效果 |
增量更新 | 仅更新变化部分 | 提升刷新率 |
双缓冲 | 防止画面闪烁 | 平滑显示 |
局部刷新 | 减少刷新区域 | 降低开销 |
3.5游戏总体实现步骤
4 游戏展示
(1) 游戏主页面
(2)开始新游戏
(3)保存游戏状态
(4)加载之前保存的游戏状态
(5)选择游戏难度
(6)游戏说明
5 源代码
#include <stdio.h>
#include <stdlib.h>
#include <windows.h>
#include <conio.h>
#include <time.h>
#include <string.h>
#include <ctype.h> // 为tolower函数
//@author:小梁不秃捏
// link:https://blog.csdn.net/m0_73804764?spm=1000.2115.3001.5343
// 替代 usleep 函数
void my_sleep(unsigned long microseconds) {Sleep(microseconds / 1000);
}// 游戏配置
#define WIDTH 50
#define HEIGHT 25
#define STUDENT_ID "2022212396"
#define MAX_SPEED 50
#define MIN_SPEED 200// 游戏元素
#define SNAKE_HEAD 'O'
#define SNAKE_BODY 'o'
#define NORMAL_FOOD '*'
#define SPECIAL_FOOD '#'
#define WALL '#'
#define OBSTACLE 'X'
#define EMPTY ' '
#define PORTAL '@'// 方向枚举
enum Direction {UP,DOWN,LEFT,RIGHT
};// 控制键
#define KEY_UP 'w'
#define KEY_DOWN 's'
#define KEY_LEFT 'a'
#define KEY_RIGHT 'd'
#define KEY_QUIT 'q'
#define KEY_PAUSE 'p'
#define KEY_SAVE 'v'
#define KEY_BOOST 'b'// 颜色定义
#define COLOR_RED FOREGROUND_RED | FOREGROUND_INTENSITY
#define COLOR_GREEN FOREGROUND_GREEN | FOREGROUND_INTENSITY
#define COLOR_BLUE FOREGROUND_BLUE | FOREGROUND_INTENSITY
#define COLOR_YELLOW FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_INTENSITY
#define COLOR_PURPLE FOREGROUND_RED | FOREGROUND_BLUE | FOREGROUND_INTENSITY
#define COLOR_CYAN FOREGROUND_GREEN | FOREGROUND_BLUE | FOREGROUND_INTENSITY
#define COLOR_WHITE FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE | FOREGROUND_INTENSITY// 游戏难度
typedef enum {EASY = 1,MEDIUM = 2,HARD = 3
} Difficulty;// 基础结构体
typedef struct {int x, y;
} Point;typedef struct {Point body[WIDTH * HEIGHT];int length;int direction;int speed;int boost_available;int is_boosting;
} Snake;typedef struct {Point pos;int value;char type;int lifetime;int is_special;
} Food;typedef struct {Point pos1;Point pos2;int active;
} Portal;typedef struct {int score;int high_score;int level;int is_running;int is_paused;time_t start_time;Difficulty difficulty;int obstacles_count;int power_ups;int portal_active;
} GameState;typedef struct {int games_played;int total_score;int longest_snake;time_t total_play_time;float average_score;int highest_level;int total_food_eaten;
} Statistics;// 全局变量
Snake snake;
Food food;
Portal portal;
GameState game;
Statistics stats;
char map[HEIGHT][WIDTH];
HANDLE hConsole;
char obstacle_map[HEIGHT][WIDTH] = { 0 }; // 用于保存障碍物位置// 初始化双缓冲
void init_double_buffer() {// 获取标准输出句柄hConsole = GetStdHandle(STD_OUTPUT_HANDLE);// 设置控制台窗口和缓冲区大小COORD coord = { WIDTH * 2 + 30, HEIGHT + 15 };SMALL_RECT rect = { 0, 0, WIDTH * 2 + 29, HEIGHT + 14 };SetConsoleScreenBufferSize(hConsole, coord);SetConsoleWindowInfo(hConsole, TRUE, &rect);// 隐藏光标CONSOLE_CURSOR_INFO info = { 100, FALSE };SetConsoleCursorInfo(hConsole, &info);
}// 颜色控制函数
void set_color(int color) {SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), color);
}
// 终端控制函数
void init_terminal(void) {// 设置控制台窗口大小char cmd[100];sprintf_s(cmd, "mode con cols=%d lines=%d", WIDTH * 2 + 30, HEIGHT + 15);system(cmd);// 隐藏光标HANDLE consoleHandle = GetStdHandle(STD_OUTPUT_HANDLE);CONSOLE_CURSOR_INFO info;info.dwSize = 100;info.bVisible = FALSE;SetConsoleCursorInfo(consoleHandle, &info);
}void reset_terminal(void) {system("cls");
}void clear_screen(void) {system("cls");
}// 声音效果
void play_effect(int type) {switch (type) {case 1: // 吃到普通食物Beep(800, 50);break;case 2: // 吃到特殊食物Beep(1000, 50);Beep(1200, 50);Beep(1500, 50);Beep(2000, 50);break;case 3: // 游戏结束Beep(200, 200);Beep(150, 200);break;}
}// 动画效果
void show_animation(const char* text) {clear_screen();printf("\n\n\n");set_color(COLOR_YELLOW);for (int i = 0; text[i] != '\0'; i++) {printf("%c", text[i]);Sleep(50);fflush(stdout);}set_color(COLOR_WHITE);Sleep(1000);
}// 改进的随机位置生成
Point get_random_position() {Point pos;int attempts = 0;const int MAX_ATTEMPTS = 100;do {pos.x = rand() % (WIDTH - 2) + 1;pos.y = rand() % (HEIGHT - 2) + 1;attempts++;if (attempts >= MAX_ATTEMPTS) {for (int i = 1; i < HEIGHT - 1; i++) {for (int j = 1; j < WIDTH - 1; j++) {if (map[i][j] == EMPTY) {pos.x = j;pos.y = i;return pos;}}}}} while (map[pos.y][pos.x] != EMPTY);return pos;
}// 生成传送门
void generate_portal() {if (!game.portal_active) {portal.pos1 = get_random_position();portal.pos2 = get_random_position();map[portal.pos1.y][portal.pos1.x] = PORTAL;map[portal.pos2.y][portal.pos2.x] = PORTAL;portal.active = 1;game.portal_active = 1;}
}// 保存游戏状态
void save_game_state() {FILE* file;if (fopen_s(&file, "snake_save.dat", "wb") == 0) {fwrite(&snake, sizeof(Snake), 1, file);fwrite(&food, sizeof(Food), 1, file);fwrite(&portal, sizeof(Portal), 1, file);fwrite(&game, sizeof(GameState), 1, file);fwrite(map, sizeof(map), 1, file);fclose(file);show_animation("游戏已保存!");}
}// 加载游戏状态
void load_game_state() {FILE* file;if (fopen_s(&file, "snake_save.dat", "rb") == 0) {fread(&snake, sizeof(Snake), 1, file);fread(&food, sizeof(Food), 1, file);fread(&portal, sizeof(Portal), 1, file);fread(&game, sizeof(GameState), 1, file);fread(map, sizeof(map), 1, file);fclose(file);show_animation("游戏已加载!");}else {show_animation("没有找到存档!");}
}
// 设置游戏难度
void set_difficulty(int level) {switch (level) {case EASY:snake.speed = 200; // 较慢game.obstacles_count = 5;break;case MEDIUM:snake.speed = 150; // 中等game.obstacles_count = 10;break;case HARD:snake.speed = 100; // 较快game.obstacles_count = 15;break;default:snake.speed = 200; // 默认较慢game.obstacles_count = 5;}game.difficulty = (Difficulty)level;
}// 修改generate_food函数
void generate_food() {food.pos = get_random_position();// 确保食物不会生成在蛇身上或障碍物上int valid_position = 0;while (!valid_position) {valid_position = 1;// 检查是否在蛇身上for (int i = 0; i < snake.length; i++) {if (food.pos.x == snake.body[i].x && food.pos.y == snake.body[i].y) {food.pos = get_random_position();valid_position = 0;break;}}// 检查是否在障碍物上if (map[food.pos.y][food.pos.x] == OBSTACLE) {food.pos = get_random_position();valid_position = 0;continue;}}// 设置食物类型和属性food.is_special = (rand() % 10 == 0); // 10%的概率生成特殊食物if (food.is_special) {food.type = SPECIAL_FOOD;food.value = 20;food.lifetime = 50;}else {food.type = NORMAL_FOOD;food.value = 10;food.lifetime = -1;}// 在地图上放置食物map[food.pos.y][food.pos.x] = food.type;
}// 生成障碍物
void generate_obstacles() {for (int i = 0; i < game.obstacles_count; i++) {Point pos = get_random_position();map[pos.y][pos.x] = OBSTACLE;}
}// 初始化游戏
void init_game() {// 清空地图和障碍物地图memset(map, EMPTY, sizeof(map));memset(obstacle_map, 0, sizeof(obstacle_map));// 设置默认速度(如果没有设置难度)if (snake.speed == 0) {snake.speed = MIN_SPEED;}// 初始化边界for (int i = 0; i < WIDTH; i++) {map[0][i] = WALL;map[HEIGHT - 1][i] = WALL;}for (int i = 0; i < HEIGHT; i++) {map[i][0] = WALL;map[i][WIDTH - 1] = WALL;}// 初始化蛇snake.length = 3;snake.body[0].x = WIDTH / 2;snake.body[0].y = HEIGHT / 2;snake.body[1].x = snake.body[0].x - 1;snake.body[1].y = snake.body[0].y;snake.body[2].x = snake.body[1].x - 1;snake.body[2].y = snake.body[1].y;snake.direction = RIGHT;snake.boost_available = 100;snake.is_boosting = 0;// 在地图上放置蛇map[snake.body[0].y][snake.body[0].x] = SNAKE_HEAD;for (int i = 1; i < snake.length; i++) {map[snake.body[i].y][snake.body[i].x] = SNAKE_BODY;}// 初始化游戏状态game.score = 0;game.level = 1;game.is_running = 1;game.is_paused = 0;game.power_ups = 3;game.portal_active = 0;game.start_time = time(NULL);// 生成初始食物和障碍物// 生成障碍物时同时更新 obstacle_mapfor (int i = 0; i < game.obstacles_count; i++) {Point pos = get_random_position();map[pos.y][pos.x] = OBSTACLE;obstacle_map[pos.y][pos.x] = 1;}generate_food(); // 只生成一个食物// 初始化传送门portal.active = 0;game.portal_active = 0; // 确保初始状态无传送门
}// 更新分数
void update_score() {game.score += food.value;if (game.score > game.high_score) {game.high_score = game.score;}// 每100分升一级int new_level = game.score / 100 + 1;if (new_level != game.level) {game.level = new_level;snake.speed = snake.speed > MAX_SPEED ? snake.speed - 10 : snake.speed;show_animation("升级!");}
}// 更新统计信息
void update_statistics() {stats.games_played++;stats.total_score += game.score;stats.average_score = (float)stats.total_score / stats.games_played;if (snake.length > stats.longest_snake) {stats.longest_snake = snake.length;}if (game.level > stats.highest_level) {stats.highest_level = game.level;}stats.total_play_time += time(NULL) - game.start_time;
}// 保存统计信息
void save_statistics() {FILE* file;if (fopen_s(&file, "snake_stats.dat", "wb") == 0) {fwrite(&stats, sizeof(Statistics), 1, file);fclose(file);}
}
// 检查碰撞
int check_collision(int x, int y) {// 检查是否撞墙if (map[y][x] == WALL) return 1;// 检查是否撞到障碍物if (map[y][x] == OBSTACLE) return 1;// 检查是否撞到自己(除了尾巴,因为尾巴会移动)for (int i = 0; i < snake.length - 1; i++) {if (snake.body[i].x == x && snake.body[i].y == y) {return 1;}}return 0;
}// 在move_snake函数
void move_snake() {int new_x = snake.body[0].x;int new_y = snake.body[0].y;switch (snake.direction) {case UP: new_y--; break;case DOWN: new_y++; break;case LEFT: new_x--; break;case RIGHT: new_x++; break;}// 处理加速能量衰减if (snake.is_boosting && snake.boost_available > 0) {snake.boost_available--;if (snake.boost_available <= 0) {snake.is_boosting = 0;}}// 检查碰撞if (check_collision(new_x, new_y)) {game.is_running = 0;play_effect(3);return;}// 检查是否吃到食物if (new_x == food.pos.x && new_y == food.pos.y) {// 增加蛇的长度snake.length++;// 更新分数update_score();// 播放音效play_effect(food.is_special ? 2 : 1);// 如果是特殊食物,增加加速能量if (food.is_special) {snake.boost_available += 50;}// 先移动蛇身for (int i = snake.length - 1; i > 0; i--) {snake.body[i] = snake.body[i - 1];}// 更新蛇头位置snake.body[0].x = new_x;snake.body[0].y = new_y;// 生成新的食物generate_food();}else {// 如果没有吃到食物,正常移动// 移动蛇身for (int i = snake.length - 1; i > 0; i--) {snake.body[i] = snake.body[i - 1];}// 更新蛇头位置snake.body[0].x = new_x;snake.body[0].y = new_y;}// 更新地图memset(map, EMPTY, sizeof(map));// 重绘边界for (int i = 0; i < WIDTH; i++) {map[0][i] = WALL;map[HEIGHT - 1][i] = WALL;}for (int i = 0; i < HEIGHT; i++) {map[i][0] = WALL;map[i][WIDTH - 1] = WALL;}// 重绘障碍物for (int i = 0; i < HEIGHT; i++) {for (int j = 0; j < WIDTH; j++) {if (obstacle_map[i][j]) {map[i][j] = OBSTACLE;}}}// 重绘蛇map[snake.body[0].y][snake.body[0].x] = SNAKE_HEAD;for (int i = 1; i < snake.length; i++) {map[snake.body[i].y][snake.body[i].x] = SNAKE_BODY;}// 重绘食物map[food.pos.y][food.pos.x] = food.type;
}// 修改draw_game函数
void draw_game() {// 移动光标到起始位置COORD pos = { 0, 0 };SetConsoleCursorPosition(hConsole, pos);// 绘制游戏信息SetConsoleTextAttribute(hConsole, COLOR_YELLOW);printf("\n 得分: %d 最高分: %d 等级: %d 加速能量: %d%%\n",game.score, game.high_score, game.level, snake.boost_available);printf(" 难度: ");switch (game.difficulty) {case EASY: printf("简单"); break;case MEDIUM: printf("中等"); break;case HARD: printf("困难"); break;}printf(" 游戏时间: %d秒\n\n", (int)(time(NULL) - game.start_time));// 绘制地图for (int i = 0; i < HEIGHT; i++) {printf(" ");for (int j = 0; j < WIDTH; j++) {switch (map[i][j]) {case SNAKE_HEAD:SetConsoleTextAttribute(hConsole, COLOR_GREEN);printf("O ");break;case SNAKE_BODY:SetConsoleTextAttribute(hConsole, COLOR_GREEN);printf("o ");break;case NORMAL_FOOD:SetConsoleTextAttribute(hConsole, COLOR_RED);printf("* ");break;case SPECIAL_FOOD:SetConsoleTextAttribute(hConsole, COLOR_PURPLE);printf("# ");break;case OBSTACLE:SetConsoleTextAttribute(hConsole, COLOR_RED);printf("X ");break;case PORTAL:SetConsoleTextAttribute(hConsole, COLOR_CYAN);printf("@ ");break;default:SetConsoleTextAttribute(hConsole, COLOR_WHITE);printf(" ");}}printf("\n");}// 绘制控制说明SetConsoleTextAttribute(hConsole, COLOR_WHITE);printf("\n 控制: WASD移动 P暂停 Q退出 V保存 B加速\n");
}// 显示菜单
int show_menu() {clear_screen();set_color(COLOR_YELLOW);printf("\n");printf(" SSSSS N N AAA K K EEEEE\n");printf(" S NN N A A K K E \n");printf(" SSSSS N N N AAAAA KK EEEE \n");printf(" S N NN A A K K E \n");printf(" SSSSS N N A A K K EEEEE\n");printf("\n\n");printf(" ----------\n\n");printf(" 小梁贪吃蛇(pro max版)\n");printf(" 1. 新游戏\n");printf(" 2. 加载游戏\n");printf(" 3. 查看统计\n");printf(" 4. 选择难度\n");printf(" 5. 游戏说明\n");printf(" 6. 退出\n\n");printf(" 请选择: ");set_color(COLOR_WHITE);int choice;scanf_s("%d", &choice);return choice;
}// 显示统计信息
void show_statistics() {clear_screen();set_color(COLOR_CYAN);printf("\n\n");printf(" 游戏统计\n");printf(" --------\n\n");printf(" 总游戏次数: %d\n", stats.games_played);printf(" 总得分: %d\n", stats.total_score);printf(" 平均得分: %.2f\n", stats.average_score);printf(" 最长蛇长度: %d\n", stats.longest_snake);printf(" 最高等级: %d\n", stats.highest_level);printf(" 总游戏时间: %d分钟\n\n", (int)(stats.total_play_time / 60));printf(" 按任意键返回...");set_color(COLOR_WHITE);_getch();
}
// 显示游戏说明
void show_instructions() {clear_screen();set_color(COLOR_CYAN);printf("\n\n");printf(" 游戏说明\n");printf(" --------\n\n");printf(" 控制方式:\n");printf(" W - 向上移动\n");printf(" S - 向下移动\n");printf(" A - 向左移动\n");printf(" D - 向右移动\n");printf(" P - 暂停游戏\n");printf(" Q - 退出游戏\n");printf(" V - 保存游戏\n");printf(" B - 开启/关闭加速\n\n");printf(" 游戏规则:\n");printf(" * - 普通食物,得分+10\n");printf(" # - 特殊食物,得分+20,有特殊效果\n");printf(" @ - 传送门,可传送到另一端\n");printf(" X - 障碍物,碰到即死亡\n\n");printf(" 特殊食物效果:\n");printf(" 1. 增加加速能量\n");printf(" 2. 生成传送门\n");printf(" 3. 清除障碍物\n\n");printf(" 按任意键返回...");set_color(COLOR_WHITE);_getch();
}// 主函数
int main() {srand((unsigned)time(NULL));init_terminal();init_double_buffer(); // 添加这行while (1) {int choice = show_menu();switch (choice) {case 1: // 新游戏init_game();while (game.is_running) {if (!game.is_paused) {move_snake();draw_game();// 特殊食物倒计时if (food.is_special && food.lifetime > 0) {food.lifetime--;if (food.lifetime <= 0) {generate_food();}}// 控制游戏速度int delay = snake.is_boosting ? snake.speed / 2 : snake.speed;Sleep(delay);}// 处理输入if (_kbhit()) {char input = _getch();switch (tolower(input)) { // 转换为小写以支持大小写case 'w':if (snake.direction != DOWN) snake.direction = UP;break;case 's':if (snake.direction != UP) snake.direction = DOWN;break;case 'a':if (snake.direction != RIGHT) snake.direction = LEFT;break;case 'd':if (snake.direction != LEFT) snake.direction = RIGHT;break;case 'p':game.is_paused = !game.is_paused;break;case 'q':game.is_running = 0;break;case 'v':save_game_state();break;case 'b':if (snake.boost_available > 0) {snake.is_boosting = !snake.is_boosting;}break;}}}update_statistics();save_statistics();break;case 2: // 加载游戏load_game_state();break;case 3: // 查看统计show_statistics();break;case 4: // 选择难度clear_screen();printf("\n\n 选择难度(1-简单 2-中等 3-困难): ");int level;scanf_s("%d", &level);set_difficulty(level);break;case 5: // 游戏说明show_instructions();break;case 6: // 退出CloseHandle(hConsole);reset_terminal();return 0;}}return 0;
}
相关文章:
c语言版贪吃蛇(Pro Max版)附源代码
1 背景 贪吃蛇是一款经典的电子游戏,最早出现在20世纪70年代的街机游戏中。游戏的核心玩法是玩家控制一条蛇在有限的空间内移动,通过吃食物来增长身体长度,同时避免撞到墙壁、障碍物或自身。随着蛇的长度增加,游戏难度逐渐提升。 …...
蓝桥村打花结的花纸选择问题
在这篇文章中,我们将探讨一个有趣的算法问题,这个问题涉及到中国传统手工艺——打花结。我们需要判断给定的矩形花纸是否可以通过折叠操作使其面积变为特定的值 X,从而适合用来打花结。 问题描述 解题思路 这个问题可以通过循环方法来解决。…...
SSM开发(三) spring与mybatis整合(含完整运行demo源码)
目录 本文主要内容 一、Spring整合MyBatis的三个关键点 二、整合步骤 1、创建一个Maven项目 2、在pom.xml文件中添加jar包的依赖 3、配置MyBatis 注解实现方式 XML配置文件实现 4、配置Spring 5、测试运行 本文主要内容 1. Spring + Mybatis整合; 2. MyBatis两种SQL…...
【Matlab高端绘图SCI绘图模板】第006期 对比绘柱状图 (只需替换数据)
1. 简介 柱状图作为科研论文中常用的实验结果对比图,本文采用了3组实验对比的效果展示图,代码已调试好,只需替换数据即可生成相关柱状图,为科研加分。通过获得Nature配色的柱状图,让你的论文看起来档次更高࿰…...
Elasticsearch中的度量聚合:深度解析与实战应用
在大数据和实时分析日益重要的今天,Elasticsearch以其强大的搜索和聚合能力,成为了众多企业和开发者进行数据分析和处理的首选工具。本文将深入探讨Elasticsearch中的度量聚合(Metric Aggregations),展示其如何在数据分…...
重回C语言之老兵重装上阵(十六)C语言可变参数
C语言可变参数 在C语言中,标准库提供了一些函数允许接收可变数量的参数。最典型的例子就是 printf 和 scanf,它们能够处理不确定数量的参数。为了实现这一功能,C语言提供了可变参数函数的概念。 1. 可变参数函数的概念 可变参数函数是指函数…...
第4章 神经网络【1】——损失函数
4.1.从数据中学习 实际的神经网络中,参数的数量成千上万,因此,需要由数据自动决定权重参数的值。 4.1.1.数据驱动 数据是机器学习的核心。 我们的目标是要提取出特征量,特征量指的是从输入数据/图像中提取出的本质的数 …...
动态规划——斜率优化DP
题目清单 acwing300.任务安排1 状态表示f[i]: 集合:完成前i个任务且第i个任务为最后一个批次最后一个任务的方案。 属性:min 状态计算: f [ i ] m i n { f [ j ] s u m t [ i ] ∑ j 1 i w [ u ] s ∑ j 1 n w [ i ] } f[i]min\{f[j…...
函数栈帧的创建和销毁
1、总述: 大家在前期学习函数的时候,肯定会有诸多疑惑: 1、局部变量怎么创建的? 2、为什么有时候局部变量是随机值? 3、函数是怎么传参的?传参的顺序如何? 4、形参和实参是什么样的关系&am…...
【MQ】探索 Kafka
高性能 消息的顺序性、顺序写磁盘 零拷贝 RocketMQ内部主要是使用基于mmap实现的零拷贝,用来读写文件 减少cpu的拷贝次数和上下文切换次数,实现文件的高效读写操作 Kafka 零拷贝 Kafka 使用到了 mmap 和 sendfile 的方式来实现零拷贝。分别对应 Jav…...
c++ set/multiset 容器
1. set 基本概念 简介: 所有元素都会在插入时自动排序本质: set/multiset属于关联式容器,底层结构是用二叉树实现。set 和 multiset 区别: set容器不允许有重复的元素。 multiset允许有重复的元素。2. set 构造和赋值 构造&a…...
react-bn-面试
1.主要内容 工作台待办 实现思路: 1,待办list由后端返回,固定需要的字段有id(查详细)、type(本条待办的类型),还可能需要时间,状态等 2,一个集中处理待办中转路由页,所有待办都跳转到这个页面…...
【C++数论】880. 索引处的解码字符串|2010
本文涉及知识点 数论:质数、最大公约数、菲蜀定理 LeetCode880. 索引处的解码字符串 给定一个编码字符串 s 。请你找出 解码字符串 并将其写入磁带。解码时,从编码字符串中 每次读取一个字符 ,并采取以下步骤: 如果所读的字符是…...
shiro学习五:使用springboot整合shiro。在前面学习四的基础上,增加shiro的缓存机制,源码讲解:认证缓存、授权缓存。
文章目录 前言1. 直接上代码最后在讲解1.1 新增的pom依赖1.2 RedisCache.java1.3 RedisCacheManager.java1.4 jwt的三个类1.5 ShiroConfig.java新增Bean 2. 源码讲解。2.1 shiro 缓存的代码流程。2.2 缓存流程2.2.1 认证和授权简述2.2.2 AuthenticatingRealm.getAuthentication…...
Python案例--养兔子
兔子繁殖问题是一个经典的数学问题,最早由意大利数学家斐波那契在13世纪提出。这个问题不仅在数学领域具有重要意义,还广泛应用于计算机科学、生物学和经济学等领域。本文将通过一个具体的Python程序,深入探讨兔子繁殖问题的建模和实现&#…...
【搜索回溯算法】:BFS的魔力--如何使用广度优先搜索找到最短路径
✨感谢您阅读本篇文章,文章内容是个人学习笔记的整理,如果哪里有误的话还请您指正噢✨ ✨ 个人主页:余辉zmh–CSDN博客 ✨ 文章所属专栏:搜索回溯算法篇–CSDN博客 文章目录 一.广度优先搜索(BFS)解决最短路…...
JavaSE第十一天——集合框架Collection
一、List接口 List接口是一个有序的集合,允许元素有重复,它继承了Collection接口,提供了许多额外的功能,比如基于索引的插入、删除和访问元素等。 常见的List接口的实现类有ArrayList、LinkedList和Vector。 List接口的实现类 …...
Three城市引擎地图插件Geo-3d
一、简介 基于Three开发,为Three 3D场景提供GIS能力和城市底座渲染能力。支持Web墨卡托、WGS84、GCJ02等坐标系,支持坐标转换,支持影像、地形、geojson建筑、道路,植被等渲染。支持自定义主题。 二、效果 三、代码 //插件初始化…...
深度学习|表示学习|卷积神经网络|详细推导每一层的维度变化|14
如是我闻: 一个经典的卷积神经网络(CNN)架构,呈现的是输入图像通过多个卷积层、池化层以及全连接层,最终输出分类结果的过程。整个过程的核心是理解输入特征图的尺寸如何在每一层发生变化,我们可以通过卷积…...
多级缓存(亿级并发解决方案)
多级缓存(亿级流量(并发)的缓存方案) 传统缓存的问题 传统缓存是请求到达tomcat后,先查询redis,如果未命中则查询数据库,问题如下: (1)请求要经过tomcat处…...
BOM对象location与数组操作结合——查询串提取案例
BOM对象location与数组操作结合——查询串提取案例 前置知识 1. Location 对象 Location 对象是 JavaScript 提供的内置对象之一,它表示当前窗口或框架的 URL,并允许你通过它操作或获取 URL 的信息。可以通过 window.location 访问。 主要属性&#…...
读书笔记--分布式服务架构对比及优势
本篇是在上一篇的基础上,主要对共享服务平台建设所依赖的分布式服务架构进行学习,主要记录和思考如下,供大家学习参考。随着企业各业务数字化转型工作的推进,之前在传统的单一系统(或单体应用)模式中&#…...
GOGOGO 枚举
含义:一种类似于类的一种结构 作用:是Java提供的一个数据类型,可以设置值是固定的 【当某一个数据类型受自身限制的时候,使用枚举】 语法格式: public enum 枚举名{…… }有哪些成员? A、对象 public …...
【Linux】Linux基础开发工具
1 Linux 软件包管理器 yum 1.1软件包 在Linux下安装软件, 一个通常的办法是下载到程序的源代码, 并进行编译, 得到可执行程序. 但是这样太麻烦了, 于是有些人把一些常用的软件提前编译好, 做成软件包(可以理解成windows上的安装程序)放在一个服务器上,通过包管理器可以很方便的…...
嵌入式C语言:结构体的多态性之结构体中的void*万能指针
目录 一、void*指针在结构体中的应用 二、实现方式 2.1. 定义通用结构体 2.2. 定义具体结构体 2.3. 初始化和使用 三、应用场景 3.1. 内存管理函数 3.2. 泛型数据结构(链表) 3.3. 回调函数和函数指针 3.4. 跨语言调用或API接口(模拟…...
重构进行时:一秒告别 !=null 判空
重构进行时:一秒告别 !null 判空 空指针异常(NullPointerException)是Java开发中常见的错误之一。 许多开发者在遇到空指针问题时,往往会习惯性地使用! null来进行判断。 然而,当代码中频繁出现这种判断时ÿ…...
React 中hooks之useSyncExternalStore使用总结
1. 基本概念 useSyncExternalStore 是 React 18 引入的一个 Hook,用于订阅外部数据源,确保在并发渲染下数据的一致性。它主要用于: 订阅浏览器 API(如 window.width)订阅第三方状态管理库订阅任何外部数据源 1.1 基…...
Semaphore 与 线程池 Executor 有什么区别?
前言:笔者在看Semaphone时 突然脑子宕机,啥啥分不清 Semaphore 和 Executor 的作用,故再次记录。 一、什么是Semaphore? Semaphore 是 Java 并发编程(JUC)中一个重要的同步工具,它的作用是 控…...
Rust:高性能与安全并行的编程语言
引言 在现代编程世界里,开发者面临的最大挑战之一就是如何平衡性能与安全性。在许多情况下,C/C这样的系统级编程语言虽然性能强大,但其内存管理的复杂性导致了各种安全漏洞。为了解决这些问题,Rust 作为一种新的系统级编程语言进入…...
论文笔记(六十三)Understanding Diffusion Models: A Unified Perspective(六)(完结)
Understanding Diffusion Models: A Unified Perspective(六)(完结) 文章概括指导(Guidance)分类器指导无分类器引导(Classifier-Free Guidance) 总结 文章概括 引用: …...
Redis --- 分布式锁的使用
我们在上篇博客高并发处理 --- 超卖问题一人一单解决方案讲述了两种锁解决业务的使用方法,但是这样不能让锁跨JVM也就是跨进程去使用,只能适用在单体项目中如下图: 为了解决这种场景,我们就需要用一个锁监视器对全部集群进行监视…...
电脑怎么格式化?格式化详细步骤
格式化是我们在日常使用电脑时可能会用到的一种操作,无论是清理磁盘空间、安装新系统,还是解决磁盘读写错误,都可能需要格式化。不过,对于一些不熟悉电脑操作的用户来说,格式化听起来可能有些复杂。其实,只…...
TikTok广告投放优化策略:提升ROI的核心技巧
在短许多品牌和商家纷纷投入广告营销,争夺这片潜力巨大的市场。然而,在激烈的竞争环境中,如何精准有效地投放广告,优化广告效果,实现更高的投资回报率(ROI)成为了广告主关注的核心。 一. 精准受…...
视觉语言模型 (VLMs):跨模态智能的探索
文章目录 一. VLMs 的重要性与挑战:连接视觉与语言的桥梁 🌉二. VLMs 的核心训练范式:四种主流策略 🗺️1. 对比训练 (Contrastive Training):拉近正例,推远负例 ⚖️2. 掩码方法 (Masking):重构…...
第05章 08 绘制脑部体绘制图的阈值等值面
绘制脑部体绘制图的阈值等值面,例如肌肉和头骨骼,需要对医学图像数据进行阈值处理,并使用体绘制技术来可视化这些结构。以下是一个基于VTK/C的示例代码,展示如何读取DICOM图像数据,应用阈值过滤器来提取特定组织&#…...
类和对象(4)——多态:方法重写与动态绑定、向上转型和向下转型、多态的实现条件
目录 1. 向上转型和向下转型 1.1 向上转型 1.2 向下转型 1.3 instanceof关键字 2. 重写(overidde) 2.1 方法重写的规则 2.1.1 基础规则 2.1.2 深层规则 2.2 三种不能重写的方法 final修饰 private修饰 static修饰 3. 动态绑定 3.1 动态绑…...
动态规划<九>两个数组的dp
目录 引例 LeetCode经典OJ题 1.第一题 2.第二题 3.第三题 4.第四题 5.第五题 6.第六题 7.第七题 引例 OJ传送门LeetCode<1143>最长公共子序列 画图分析: 使用动态规划解决 1.状态表示 ------经验题目要求 经验为选取第一个字符串的[0,i]区间以及第二个字…...
Go:基于Go实现一个压测工具
文章目录 写在前面整体架构通用数据处理模块Http请求响应数据处理Curl参数解析处理 客户端模块Http客户端处理Grpc客户端处理Websocket客户端处理 连接处理模块GrpcHttp 统计数据模块统计原理实现过程 写在前面 本篇主要是基于Go来实现一个压测的工具,关于压测的内…...
2025年数学建模美赛 A题分析(2)楼梯使用频率数学模型
2025年数学建模美赛 A题分析(1)Testing Time: The Constant Wear On Stairs 2025年数学建模美赛 A题分析(2)楼梯磨损分析模型 2025年数学建模美赛 A题分析(3)楼梯使用方向偏好模型 2025年数学建模美赛 A题分…...
在Ubuntu上用Llama Factory命令行微调Qwen2.5的简单过程
半年多之前写过一个教程:在Windows上用Llama Factory微调Llama 3的基本操作_llama-factory windows-CSDN博客 如果用命令行做的话,前面的步骤可以参考上面这个博客。安装好环境后, 用自我认知数据集微调Lora模块:data/identity.j…...
虹科分享 | 汽车NVH小课堂之听音辨故障
随着车主开始关注汽车抖动异响问题,如何根据故障现象快速诊断异响来源,成了汽修人的必修课。 一个比较常用的方法就是靠“听”——“听音辨故障”。那今天,虹科Pico也整理了几个不同类型的异响声音,一起来听听看你能答对几个吧 汽…...
RoboVLM——通用机器人策略的VLA设计哲学:如何选择骨干网络、如何构建VLA架构、何时添加跨本体数据
前言 本博客内解读不少VLA模型了,包括π0等,且如此文的开头所说 前两天又重点看了下openvla,和cogact,发现 目前cogACT把openvla的动作预测换成了dit,在模型架构层面上,逼近了π0那为了进一步逼近&#…...
【SpringBoot教程】Spring Boot + MySQL + HikariCP 连接池整合教程
🙋大家好!我是毛毛张! 🌈个人首页: 神马都会亿点点的毛毛张 在前面一篇文章中毛毛张介绍了SpringBoot中数据源与数据库连接池相关概念,今天毛毛张要分享的是关于SpringBoot整合HicariCP连接池相关知识点以及底层源码…...
0.91英寸OLED显示屏一种具有小尺寸、高分辨率、低功耗特性的显示器件
0.91英寸OLED显示屏是一种具有小尺寸、高分辨率、低功耗特性的显示器件。以下是对0.91英寸OLED显示屏的详细介绍: 一、基本参数 尺寸:0.91英寸分辨率:通常为128x32像素,意味着显示屏上有128列和32行的像素点,总共409…...
【insert函数】
在 C 中,std::string::insert 是一个功能强大的成员函数,用于在字符串的指定位置插入内容。它有多个重载版本,支持插入 字符、字符串、子字符串 等。以下是 insert 所有相关函数的详细介绍: 1. 插入字符串 函数签名: …...
Python 如何进行文本匹配:difflib| python 小知识
Python 如何进行文本匹配:difflib| python 小知识 difflib是Python标准库中的一个工具,用于比较和处理文本差异。它提供了一组用于比较和处理文本差异的功能,可以用于比较字符串、文件等。本文将详细介绍difflib模块的用法和实现细节&#x…...
MySQL误删数据怎么办?
文章目录 1. 从备份恢复数据2. 通过二进制日志恢复数据3. 使用数据恢复工具4. 利用事务回滚恢复数据5. 预防误删数据的策略总结 在使用MySQL进行数据管理时,误删数据是一个常见且具有高风险的操作。无论是因为操作失误、系统故障,还是不小心执行了删除命…...
可以称之为“yyds”的物联网开源框架有哪几个?
有了物联网的发展,我们的生活似乎也变得更加“鲜活”、有趣、便捷,包具有科技感的。在物联网(IoT)领域中,也有许多优秀的开源框架支持设备连接、数据处理、云服务等,成为被用户们广泛认可的存在。以下给大家…...
为AI聊天工具添加一个知识系统 之74 详细设计之15 正则表达式 之2
本文要点 要点 本项目(为AI聊天工具添加一个知识系统)中的正则表达式。 正则表达式的三“比”。正则表达式被 一、排比为三种符号(元符号-圈号,特殊符号-引号,普通符号-括号) 引号<<a线性回归bo…...
Java 注解与元数据
Java学习资料 Java学习资料 Java学习资料 一、引言 在 Java 编程中,注解(Annotation)和元数据(Metadata)是两个重要的概念。注解为程序提供了一种在代码中嵌入额外信息的方式,这些额外信息就是元数据。元…...