From aae7990cfdde834084fa69344b5c7a6745f0f1fb Mon Sep 17 00:00:00 2001 From: LHY0125 <3364451258@qq.com> Date: Thu, 10 Jul 2025 10:07:34 +0800 Subject: [PATCH] Add files via upload --- config.c | 255 ++++++++++++++++++++++++++++++++++++++++++++++ config.h | 44 +++++++- game_mode.c | 13 +-- gobang.h | 2 +- gobang_config.ini | 12 +++ init_board.h | 18 ++-- main.c | 119 ++++++++++++++++++++++ record.c | 180 ++++++++++++++++++++++++-------- record.h | 44 +++++--- ui.c | 199 ++++++++++++++++++++++++++++++++++++ ui.h | 62 +++++++++++ 11 files changed, 871 insertions(+), 77 deletions(-) create mode 100644 config.c create mode 100644 gobang_config.ini create mode 100644 main.c create mode 100644 ui.c create mode 100644 ui.h diff --git a/config.c b/config.c new file mode 100644 index 0000000..48bd113 --- /dev/null +++ b/config.c @@ -0,0 +1,255 @@ +#include "config.h" +#include "ui.h" +#include +#include +#include + +// 配置文件路径 +#define CONFIG_FILE "gobang_config.ini" + +/** + * @brief 加载游戏配置 + */ +void load_game_config() +{ + FILE *file = fopen(CONFIG_FILE, "r"); + if (file == NULL) + { + // 配置文件不存在,使用默认配置 + printf("配置文件不存在,使用默认配置\n"); + return; + } + + char line[256]; + while (fgets(line, sizeof(line), file)) + { + // 去除换行符 + line[strcspn(line, "\n")] = 0; + + // 解析配置项 + if (strncmp(line, "BOARD_SIZE=", 11) == 0) + { + int size = atoi(line + 11); + if (size >= MIN_BOARD_SIZE && size <= MAX_BOARD_SIZE) + { + BOARD_SIZE = size; + } + } + else if (strncmp(line, "USE_FORBIDDEN_MOVES=", 20) == 0) + { + use_forbidden_moves = (atoi(line + 20) != 0); + } + else if (strncmp(line, "USE_TIMER=", 10) == 0) + { + use_timer = atoi(line + 10); + } + else if (strncmp(line, "TIME_LIMIT=", 11) == 0) + { + time_limit = atoi(line + 11); + } + else if (strncmp(line, "AI_DIFFICULTY=", 14) == 0) + { + int difficulty = atoi(line + 14); + if (difficulty >= 1 && difficulty <= 5) + { + // 根据难度设置AI搜索深度 + // 这里可以添加AI难度相关的配置 + } + } + } + + fclose(file); + printf("配置加载完成\n"); +} + +/** + * @brief 保存游戏配置 + */ +void save_game_config() +{ + FILE *file = fopen(CONFIG_FILE, "w"); + if (file == NULL) + { + printf("无法保存配置文件\n"); + return; + } + + fprintf(file, "# 五子棋游戏配置文件\n"); + fprintf(file, "# 棋盘大小 (范围: %d-%d)\n", MIN_BOARD_SIZE, MAX_BOARD_SIZE); + fprintf(file, "BOARD_SIZE=%d\n", BOARD_SIZE); + fprintf(file, "\n# 禁手规则 (0=关闭, 1=开启)\n"); + fprintf(file, "USE_FORBIDDEN_MOVES=%d\n", use_forbidden_moves ? 1 : 0); + fprintf(file, "\n# 计时器 (0=关闭, 1=开启)\n"); + fprintf(file, "USE_TIMER=%d\n", use_timer); + fprintf(file, "\n# 时间限制 (分钟)\n"); + fprintf(file, "TIME_LIMIT=%d\n", time_limit); + + fclose(file); + printf("配置保存完成\n"); +} + +/** + * @brief 重置为默认配置 + */ +void reset_to_default_config() +{ + BOARD_SIZE = DEFAULT_BOARD_SIZE; + use_forbidden_moves = DEFAULT_USE_FORBIDDEN_MOVES; + use_timer = DEFAULT_USE_TIMER; + time_limit = DEFAULT_TIME_LIMIT; + + printf("已重置为默认配置\n"); +} + +/** + * @brief 显示当前配置 + */ +void display_current_config() +{ + printf("\n===== 当前游戏配置 =====\n"); + printf("棋盘大小: %d x %d\n", BOARD_SIZE, BOARD_SIZE); + printf("禁手规则: %s\n", use_forbidden_moves ? "开启" : "关闭"); + printf("计时器: %s\n", use_timer ? "开启" : "关闭"); + if (use_timer) + { + printf("时间限制: %d 分钟\n", time_limit / 60); + } + printf("=====================\n"); +} + +/** + * @brief 配置棋盘大小 + */ +void config_board_size() +{ + printf("\n当前棋盘大小: %d x %d\n", BOARD_SIZE, BOARD_SIZE); + printf("请输入新的棋盘大小 (%d-%d): ", MIN_BOARD_SIZE, MAX_BOARD_SIZE); + + int new_size; + if (scanf("%d", &new_size) == 1) + { + if (new_size >= MIN_BOARD_SIZE && new_size <= MAX_BOARD_SIZE) + { + BOARD_SIZE = new_size; + printf("棋盘大小已设置为: %d x %d\n", BOARD_SIZE, BOARD_SIZE); + } + else + { + printf("无效的棋盘大小!\n"); + } + } + else + { + printf("输入格式错误!\n"); + // 清除输入缓冲区 + while (getchar() != '\n'); + } +} + +/** + * @brief 配置禁手规则 + */ +void config_forbidden_moves() +{ + printf("\n当前禁手规则: %s\n", use_forbidden_moves ? "开启" : "关闭"); + printf("是否启用禁手规则?(1=开启, 0=关闭): "); + + int choice; + if (scanf("%d", &choice) == 1) + { + use_forbidden_moves = (choice != 0); + printf("禁手规则已%s\n", use_forbidden_moves ? "开启" : "关闭"); + } + else + { + printf("输入格式错误!\n"); + while (getchar() != '\n'); + } +} + +/** + * @brief 配置计时器 + */ +void config_timer() +{ + printf("\n当前计时器: %s\n", use_timer ? "开启" : "关闭"); + printf("是否启用计时器?(1=开启, 0=关闭): "); + + int choice; + if (scanf("%d", &choice) == 1) + { + use_timer = choice; + if (use_timer) + { + printf("请输入时间限制(分钟): "); + int new_limit; + if (scanf("%d", &new_limit) == 1 && new_limit > 0) + { + time_limit = new_limit * 60; // 转换为秒数存储 + printf("计时器已开启,时间限制: %d 分钟\n", time_limit / 60); + } + else + { + printf("无效的时间限制!\n"); + while (getchar() != '\n'); + } + } + else + { + printf("计时器已关闭\n"); + } + } + else + { + printf("输入格式错误!\n"); + while (getchar() != '\n'); + } +} + +/** + * @brief 配置管理主菜单 + */ +void config_management_menu() +{ + int choice; + + while (1) + { + clear_screen(); + display_settings_menu(); + display_current_config(); + + printf("请选择操作: "); + if (scanf("%d", &choice) != 1) + { + printf("输入格式错误!\n"); + while (getchar() != '\n'); + pause_for_input("按任意键继续..."); + continue; + } + + switch (choice) + { + case 1: + config_board_size(); + break; + case 2: + config_forbidden_moves(); + break; + case 3: + config_timer(); + break; + case 4: + printf("AI难度设置功能开发中...\n"); + break; + case 5: + save_game_config(); + return; + default: + printf("无效的选择!\n"); + break; + } + + pause_for_input("按任意键继续..."); + } +} \ No newline at end of file diff --git a/config.h b/config.h index d45dc43..f1f3f9a 100644 --- a/config.h +++ b/config.h @@ -15,6 +15,7 @@ //---------- 棋盘相关参数 ----------// #define MAX_BOARD_SIZE 25 // 支持的最大棋盘尺寸 +#define MIN_BOARD_SIZE 5 // 支持的最小棋盘尺寸 #define DEFAULT_BOARD_SIZE 15 // 默认棋盘尺寸 #define MAX_STEPS (MAX_BOARD_SIZE * MAX_BOARD_SIZE) // 游戏最大步数 @@ -34,7 +35,7 @@ //---------- 游戏设置默认值 ----------// #define DEFAULT_USE_FORBIDDEN_MOVES false // 默认不启用禁手规则 #define DEFAULT_USE_TIMER 0 // 默认不启用计时器 -#define DEFAULT_TIME_LIMIT 30 // 默认时间限制为30秒 +#define DEFAULT_TIME_LIMIT 30 // 默认时间限制为30秒(内部存储) //---------- AI参数 ----------// #define DEFAULT_AI_DEPTH 3 // 默认AI搜索深度 @@ -91,4 +92,45 @@ #define RECORDS_DIR "records" // 记录文件目录 #define MAX_PATH_LENGTH 256 // 最大路径长度 +//---------- 配置管理函数声明 ----------// +/** + * @brief 加载游戏配置 + */ +void load_game_config(); + +/** + * @brief 保存游戏配置 + */ +void save_game_config(); + +/** + * @brief 重置为默认配置 + */ +void reset_to_default_config(); + +/** + * @brief 显示当前配置 + */ +void display_current_config(); + +/** + * @brief 配置棋盘大小 + */ +void config_board_size(); + +/** + * @brief 配置禁手规则 + */ +void config_forbidden_moves(); + +/** + * @brief 配置计时器 + */ +void config_timer(); + +/** + * @brief 配置管理主菜单 + */ +void config_management_menu(); + #endif // CONFIG_H \ No newline at end of file diff --git a/game_mode.c b/game_mode.c index 23d7ce3..b64fea3 100644 --- a/game_mode.c +++ b/game_mode.c @@ -222,11 +222,8 @@ void run_ai_game() { // 重置评分计算标志,确保每局游戏都会重新计算评分 scores_calculated = 0; - - setup_game_options(); // AI对战模式 - setup_board_size(); int AI_DEPTH = 3; AI_DEPTH = get_integer_input("请选择AI难度(1~5), 数字越大越强,注意数字越大AI思考时间越长!):", 1, 5); @@ -255,6 +252,13 @@ void run_ai_game() } if (step_count > old_step_count) { + // 检查玩家是否获胜 + Step last_step = steps[step_count - 1]; + if (check_win(last_step.x, last_step.y, PLAYER)) + { + printf("\n玩家获胜!\n"); + break; + } current_player = AI; } } @@ -307,11 +311,8 @@ void run_pvp_game() { // 重置评分计算标志,确保每局游戏都会重新计算评分 scores_calculated = 0; - - setup_game_options(); // 双人对战模式 - setup_board_size(); empty_board(); int current_player = determine_first_player(PLAYER1, PLAYER2); print_board(); diff --git a/gobang.h b/gobang.h index 3cdedb0..d07a513 100644 --- a/gobang.h +++ b/gobang.h @@ -23,7 +23,7 @@ extern int board[MAX_BOARD_SIZE][MAX_BOARD_SIZE]; // 存储棋盘状态的二 extern int step_count; // 当前游戏的总步数 extern bool use_forbidden_moves; // 是否启用禁手规则的标志 extern int use_timer; // 是否启用计时器的标志 -extern int time_limit; // 每回合的时间限制(秒) +extern int time_limit; // 每回合的时间限制(秒,内部存储) extern const int direction[4][2]; // 定义四个基本搜索方向:水平、垂直、左斜、右斜 // 数据结构 diff --git a/gobang_config.ini b/gobang_config.ini new file mode 100644 index 0000000..ad94da2 --- /dev/null +++ b/gobang_config.ini @@ -0,0 +1,12 @@ +# Ϸļ +# ̴С (Χ: 5-25) +BOARD_SIZE=15 + +# ֹ (0=ر, 1=) +USE_FORBIDDEN_MOVES=1 + +# ʱ (0=ر, 1=) +USE_TIMER=1 + +# ʱ () +TIME_LIMIT=60 diff --git a/init_board.h b/init_board.h index e57a185..253bf41 100644 --- a/init_board.h +++ b/init_board.h @@ -3,32 +3,32 @@ #include "gobang.h" -// --- Ϸʼ --- +// --- 游戏初始化 --- /** - * @brief ʼ̣λΪ(EMPTY) + * @brief 初始化棋盘,将所有位置设置为空(EMPTY) */ void empty_board(); /** - * @brief ǰ״̬ӡ̨ + * @brief 将当前棋盘状态打印到控制台 */ void print_board(); /** - * @brief õǰϷ̴С + * @brief 设置当前游戏的棋盘大小 */ void setup_board_size(); /** - * @brief ϷѡǷý֡ʱ + * @brief 设置游戏选项,如是否启用禁手、计时器等 */ void setup_game_options(); /** - * @brief - * @param player1 1ıʶ - * @param player2 2ıʶ - * @return ҵıʶ + * @brief 决定先手玩家 + * @param player1 玩家1的标识 + * @param player2 玩家2的标识 + * @return 返回先手玩家的标识 */ int determine_first_player(int player1, int player2); diff --git a/main.c b/main.c new file mode 100644 index 0000000..ee55393 --- /dev/null +++ b/main.c @@ -0,0 +1,119 @@ +/** + * @file 五子棋.c + * @brief 五子棋游戏核心逻辑头文件 + * @details 游戏核心逻辑实现 + * @author 刘航宇(3364451258@qq.com、15236416560@163.com、lhy3364451258@outlook.com) + * @date 2025-07-07 + * @version 4.0 + * @note + * 1. 新增功能: + * - 增加了对禁手规则的支持,防止玩家进行无意义的走法。 + * - 新增了游戏计时器功能,限制每回合的思考时间。 + * - 添加了复盘功能,支持保存和回顾对局记录。 + * - 实现了评分系统,可以对每一步棋进行评分和分析。 + * 2. 性能优化: + * - 优化了评估函数的性能,减少了不必要的计算。 + * - 引入了 Alpha-Beta 剪枝算法,提高了 AI 搜索的效率。 + * - 改进了内存管理,减少了资源占用。 + * 3. 用户界面改进: + * - 新增了命令行界面,提供更友好的交互体验。 + * - 可以自定义棋盘大小,增加游戏的灵活性。 + * - 优化了提示信息,使游戏操作更加直观。 + * 4. 代码结构优化: + * - 将游戏逻辑和用户界面分离,提高代码的可读性和可维护性。 + * - 优化了代码结构,提高了代码的可读性和可维护性。 + * - 模块化设计,便于功能扩展和维护。 + * 5. 异常处理: + * - 增加了输入错误的异常处理机制,确保游戏的稳定性。 + * - 修复了一些已知的 bug,提高游戏的稳定性。 + * - 增强了错误提示,帮助用户快速定位问题。 + * 6. 文档更新: + * - 完善了代码注释,提高了代码的可读性。 + * - 更新了文档,包括功能描述、使用方法、注意事项等。 + * 7. 版本控制: + * - 使用 Git 进行版本控制,方便团队协作和代码管理。 + * 8. 测试: + * - 进行了全面的测试,确保游戏的稳定性和功能的正确性。 + * 9. 开源协议: + * - 选择了 MIT 开源协议,允许用户自由使用、修改和分发代码。 + * 10. 贡献者: + * - 刘航宇 + * 11. 联系信息: + * - 项目主页:[https://github.com/LHY0125/Gobang-Game] + * - 联系邮箱:[3364451258@qq.com][15236416560@163.com][lhy3364451258@outlook.com] + */ + +#include "game_mode.h" +#include "ui.h" +#include "config.h" +#include +#ifdef _WIN32 +#include +#include +#endif + +/** + * @brief 将指令复制到powershell + * gcc -o gobang.exe main.c gobang.c game_mode.c ai.c record.c init_board.c ui.c config.c + * gcc 为编译器,五子棋.c gobang.c game_mode.c 为源文件,output/为输出目录 + * @brief 将指令复制到powershell + * .\gobang.exe + */ + +int main(int argc, char *argv[]) +{ + // 设置控制台编码为UTF-8 +#ifdef _WIN32 + system("chcp 65001 > nul"); // 设置控制台编码为UTF-8 + SetConsoleOutputCP(65001); // 设置控制台输出编码 + SetConsoleCP(65001); // 设置控制台输入编码 + _mkdir("records"); +#endif + + // 加载游戏配置 + load_game_config(); + + // 选择模式 + while(1) + { + clear_screen(); + display_main_menu(); + int mode = get_integer_input("请输入模式(1-7): ", 1, 7); + + switch (mode) + { + case 1: + run_ai_game(); + break; + case 2: + run_pvp_game(); + break; + case 3: + run_review_mode(); + break; + case 4: + config_management_menu(); + break; + case 5: + clear_screen(); + display_game_rules(); + pause_for_input("\n按任意键返回主菜单..."); + break; + case 6: + clear_screen(); + display_about(); + pause_for_input("\n按任意键返回主菜单..."); + break; + case 7: + save_game_config(); + printf("感谢使用五子棋游戏!\n"); + return 0; + default: + printf("无效的选择!\n"); + pause_for_input("按任意键继续..."); + break; + } + } + + return 0; +} \ No newline at end of file diff --git a/record.c b/record.c index 14dce19..e2bd876 100644 --- a/record.c +++ b/record.c @@ -34,6 +34,7 @@ int player1_final_score = 0; int player2_final_score = 0; int scores_calculated = 0; +char winner_info[50] = "平局或未完成"; // 存储胜负信息 void review_process(int game_mode) { @@ -42,45 +43,7 @@ void review_process(int game_mode) // 如果评分尚未计算,则计算评分 if (!scores_calculated) { - // 评估双方表现 - player1_final_score = 0; - player2_final_score = 0; - - // 遍历所有步数,累积每一步的得分,后期步骤权重更高 - for (int i = 0; i < step_count; i++) - { - // 计算时间权重因子:步数越靠后,权重越大 - double time_weight = 1.0 + (double)i / step_count * TIME_WEIGHT_FACTOR; // 最后的步骤权重是开始步骤的(1+TIME_WEIGHT_FACTOR)倍 - - if (steps[i].player == PLAYER || steps[i].player == PLAYER1) - { - player1_final_score += (int)(calculate_step_score(steps[i].x, steps[i].y, steps[i].player) * time_weight); - } - else - { - player2_final_score += (int)(calculate_step_score(steps[i].x, steps[i].y, steps[i].player) * time_weight); - } - } - - // 胜负加权:获胜方获得额外的评分奖励 - if (step_count > 0) - { - Step last_step = steps[step_count - 1]; - if (check_win(last_step.x, last_step.y, last_step.player)) - { - // 获胜方获得额外奖励分数 - if (last_step.player == PLAYER || last_step.player == PLAYER1) - { - player1_final_score += WIN_BONUS; // 获胜奖励 - } - else - { - player2_final_score += WIN_BONUS; // 获胜奖励 - } - } - } - - scores_calculated = 1; // 标记评分已计算 + calculate_game_scores(); } else { @@ -164,11 +127,92 @@ void review_process(int game_mode) ; // 等待回车 } } + + // 显示胜负结果(直接使用文件中的信息) + printf("\n===== 对局结果 ====="); + if (strcmp(winner_info, "玩家获胜") == 0) + { + printf("\n? 恭喜!玩家获胜!\n"); + } + else if (strcmp(winner_info, "AI获胜") == 0) + { + printf("\n? AI获胜!\n"); + } + else if (strcmp(winner_info, "玩家1获胜") == 0) + { + printf("\n? 恭喜!玩家1(黑棋)获胜!\n"); + } + else if (strcmp(winner_info, "玩家2获胜") == 0) + { + printf("\n? 恭喜!玩家2(白棋)获胜!\n"); + } + else + { + printf("\n?? 对局平局或未完成\n"); + } + printf("\n复盘结束!按Enter查看评分..."); getchar(); // 等待用户按键 } // 显示评分结果 + display_game_scores(game_mode); + + getchar(); +} + +/** + * @brief 计算游戏评分 + */ +void calculate_game_scores() +{ + // 评估双方表现 + player1_final_score = 0; + player2_final_score = 0; + + // 遍历所有步数,累积每一步的得分,后期步骤权重更高 + for (int i = 0; i < step_count; i++) + { + // 计算时间权重因子:步数越靠后,权重越大 + double time_weight = 1.0 + (double)i / step_count * TIME_WEIGHT_FACTOR; // 最后的步骤权重是开始步骤的(1+TIME_WEIGHT_FACTOR)倍 + + if (steps[i].player == PLAYER || steps[i].player == PLAYER1) + { + player1_final_score += (int)(calculate_step_score(steps[i].x, steps[i].y, steps[i].player) * time_weight); + } + else + { + player2_final_score += (int)(calculate_step_score(steps[i].x, steps[i].y, steps[i].player) * time_weight); + } + } + + // 胜负加权:获胜方获得额外的评分奖励 + if (step_count > 0) + { + Step last_step = steps[step_count - 1]; + if (check_win(last_step.x, last_step.y, last_step.player)) + { + // 获胜方获得额外奖励分数 + if (last_step.player == PLAYER || last_step.player == PLAYER1) + { + player1_final_score += WIN_BONUS; // 获胜奖励 + } + else + { + player2_final_score += WIN_BONUS; // 获胜奖励 + } + } + } + + scores_calculated = 1; // 标记评分已计算 +} + +/** + * @brief 显示游戏评分结果和MVP评选 + * @param game_mode 游戏模式(1-人机对战, 2-双人对战) + */ +void display_game_scores(int game_mode) +{ printf("\n===== 对局评分 =====\n"); double sum_score = (long double)player1_final_score + (long double)player2_final_score; @@ -217,8 +261,6 @@ void review_process(int game_mode) { printf("\n双方势均力敌!\n"); } - - getchar(); } /** @@ -309,8 +351,42 @@ int save_game_to_file(const char *filename, int game_mode) return 2; // 文件打开失败 } + // 判断胜负结果 + strcpy(winner_info, "平局或未完成"); + if (step_count > 0) + { + Step last_step = steps[step_count - 1]; + if (check_win(last_step.x, last_step.y, last_step.player)) + { + if (game_mode == 1) + { + // 人机对战 + if (last_step.player == PLAYER) + { + strcpy(winner_info, "玩家获胜"); + } + else + { + strcpy(winner_info, "AI获胜"); + } + } + else + { + // 双人对战 + if (last_step.player == PLAYER1) + { + strcpy(winner_info, "玩家1获胜"); + } + else + { + strcpy(winner_info, "玩家2获胜"); + } + } + } + } + // 写入CSV文件头部 - if (fprintf(file, "游戏模式,棋盘大小,玩家1得分,玩家2得分\n%d,%d,%d,%d\n\n", game_mode, BOARD_SIZE, player1_final_score, player2_final_score) < 0) + if (fprintf(file, "游戏模式,棋盘大小,玩家1得分,玩家2得分,对局结果\n%d,%d,%d,%d,%s\n\n", game_mode, BOARD_SIZE, player1_final_score, player2_final_score, winner_info) < 0) { fclose(file); return 3; // 文件写入失败 @@ -368,10 +444,26 @@ int load_game_from_file(const char *filename) // 读取游戏模式、棋盘大小和评分结果 int game_mode, size; - if (fscanf(file, "%d,%d,%d,%d", &game_mode, &size, &player1_final_score, &player2_final_score) != 4 || (game_mode != 1 && game_mode != 2)) + + // 尝试读取新格式(包含胜负信息) + int read_count = fscanf(file, "%d,%d,%d,%d,%49s", &game_mode, &size, &player1_final_score, &player2_final_score, winner_info); + + if (read_count == 4) + { + // 旧格式文件,没有胜负信息 + strcpy(winner_info, "未知"); + } + else if (read_count != 5) + { + // 文件格式错误 + fclose(file); + return 0; + } + + if (game_mode != 1 && game_mode != 2) { fclose(file); - return 0; // 无效的游戏模式或文件格式 + return 0; // 无效的游戏模式 } if (size < 5 || size > MAX_BOARD_SIZE) { diff --git a/record.h b/record.h index 0cf6d59..cb023a5 100644 --- a/record.h +++ b/record.h @@ -3,37 +3,49 @@ #include "gobang.h" -// ȫֱڴ洢Ծ֣ȷս͸ģʽʹͬ -extern int player1_final_score; -extern int player2_final_score; -extern int scores_calculated; +// 全局变量,用于存储对局评分,确保对战结束和复盘模式使用相同的评分 +extern int player1_final_score; // 玩家1最终得分 +extern int player2_final_score; // 玩家2最终得分 +extern int scores_calculated; // 评分计算标志 +extern char winner_info[50]; // 存储胜负信息 -// --- ¼ --- +// --- 复盘与记录功能 --- /** - * @brief 븴̣عϷ - * @param game_mode Ϸģʽ1Ϊ˻2Ϊ˫ˣ + * @brief 进入复盘流程,回顾整局游戏 + * @param game_mode 游戏模式(1为人机对战,2为双人对战) */ void review_process(int game_mode); /** - * @brief ǰԾּ¼浽ļ - * @param filename Ҫ浽ļ - * @param game_mode Ϸģʽ - * @return 0ʾɹ0ʾʧ + * @brief 将当前对局记录保存到文件 + * @param filename 要保存到的文件名 + * @param game_mode 游戏模式 + * @return 0表示成功,非0表示失败 */ int save_game_to_file(const char *filename, int game_mode); /** - * @brief Ϸ¼߼ - * @param game_mode Ϸģʽ + * @brief 处理保存游戏记录的逻辑 + * @param game_mode 游戏模式 */ void handle_save_record(int game_mode); /** - * @brief ļϷ¼ - * @param filename Ҫصļ - * @return 0ʾɹ0ʾʧ + * @brief 从文件加载游戏记录 + * @param filename 要加载的文件名 + * @return 游戏模式(1或2),0表示失败 */ int load_game_from_file(const char *filename); +/** + * @brief 计算游戏评分 + */ +void calculate_game_scores(); + +/** + * @brief 显示游戏评分结果和MVP评选 + * @param game_mode 游戏模式(1-人机对战,2-双人对战) + */ +void display_game_scores(int game_mode); + #endif // RECORD_H \ No newline at end of file diff --git a/ui.c b/ui.c new file mode 100644 index 0000000..301ab40 --- /dev/null +++ b/ui.c @@ -0,0 +1,199 @@ +#include "ui.h" +#include "config.h" +#include +#include +#ifdef _WIN32 +#include +#include +#else +#include +#endif + +/** + * @brief 显示游戏主菜单 + */ +void display_main_menu() +{ + printf("===== 五子棋游戏 =====\n"); + printf("1. AI模式\n"); + printf("2. 玩家比赛\n"); + printf("3. 复盘模式\n"); + printf("4. 游戏设置\n"); + printf("5. 游戏规则\n"); + printf("6. 关于游戏\n"); + printf("7. 退出游戏\n"); + printf("=====================\n"); +} + +/** + * @brief 显示棋盘 + */ +void display_board() +{ + printf("\n "); + // 打印列号 + for (int j = 0; j < BOARD_SIZE; j++) + { + printf("%2d", j); + } + printf("\n"); + + // 打印棋盘内容 + for (int i = 0; i < BOARD_SIZE; i++) + { + printf("%2d", i); // 打印行号 + for (int j = 0; j < BOARD_SIZE; j++) + { + if (board[i][j] == EMPTY) + { + printf(" ·"); // 空位用点表示 + } + else if (board[i][j] == PLAYER || board[i][j] == PLAYER1) + { + printf(" ●"); // 玩家1用实心圆表示 + } + else + { + printf(" ○"); // 玩家2/AI用空心圆表示 + } + } + printf("\n"); + } + printf("\n"); +} + +/** + * @brief 显示游戏状态信息 + * @param current_player 当前玩家 + * @param step_count 当前步数 + */ +void display_game_status(int current_player, int step_count) +{ + printf("当前步数: %d\n", step_count); + if (current_player == PLAYER || current_player == PLAYER1) + { + printf("当前玩家: ●\n"); + } + else + { + printf("当前玩家: ○\n"); + } +} + +/** + * @brief 显示获胜信息 + * @param winner 获胜者 + */ +void display_winner(int winner) +{ + printf("\n? 游戏结束!\n"); + if (winner == PLAYER) + { + printf("? 玩家获胜!\n"); + } + else if (winner == AI) + { + printf("? AI获胜!\n"); + } + else if (winner == PLAYER1) + { + printf("? 玩家1获胜!\n"); + } + else if (winner == PLAYER2) + { + printf("? 玩家2获胜!\n"); + } + else + { + printf("? 平局!\n"); + } +} + +/** + * @brief 显示游戏设置菜单 + */ +void display_settings_menu() +{ + printf("\n===== 游戏设置 =====\n"); + printf("1. 棋盘大小设置\n"); + printf("2. 禁手规则设置\n"); + printf("3. 计时器设置\n"); + printf("4. AI难度设置\n"); + printf("5. 返回主菜单\n"); + printf("==================\n"); +} + +/** + * @brief 清屏函数 + */ +void clear_screen() +{ +#ifdef _WIN32 + system("cls"); +#else + system("clear"); +#endif +} + +/** + * @brief 暂停等待用户输入 + * @param prompt 提示信息 + */ +void pause_for_input(const char* prompt) +{ + printf("%s", prompt); +#ifdef _WIN32 + _getch(); +#else + getchar(); +#endif +} + +/** + * @brief 显示游戏规则 + */ +void display_game_rules() +{ + printf("\n===== 五子棋游戏规则 =====\n"); + printf("1. 游戏目标:\n"); + printf(" 在棋盘上连成五个同色棋子(横、竖、斜均可)\n\n"); + printf("2. 游戏流程:\n"); + printf(" - 黑棋先行,双方轮流落子\n"); + printf(" - 输入坐标格式:行号 列号(如:7 7)\n"); + printf(" - 棋子落在棋盘交叉点上\n\n"); + printf("3. 胜负判定:\n"); + printf(" - 率先连成五子者获胜\n"); + printf(" - 棋盘下满无人获胜则为平局\n\n"); + printf("4. 禁手规则(可选):\n"); + printf(" - 三三禁手:同时形成两个活三\n"); + printf(" - 四四禁手:同时形成两个冲四\n"); + printf(" - 长连禁手:连成六子或以上\n\n"); + printf("5. 特殊功能:\n"); + printf(" - 悔棋:输入 'R' 或 'r' 可悔棋\n"); + printf(" - 保存:游戏结束后可保存对局记录\n"); + printf(" - 复盘:可加载历史对局进行复盘\n"); + printf("========================\n"); +} + +/** + * @brief 显示关于信息 + */ +void display_about() +{ + printf("\n===== 关于五子棋游戏 =====\n"); + printf("🎮 游戏名称:五子棋人机对战\n"); + printf("📦 版本:4.0\n"); + printf("👨‍💻 开发者:刘航宇\n"); + printf("📧 联系邮箱:3364451258@qq.com\n"); + printf("🌐 项目主页:https://github.com/LHY0125/Gobang-Game\n\n"); + printf("✨ 主要特性:\n"); + printf(" 🤖 智能AI对战(支持多种难度)\n"); + printf(" 👥 双人对战模式\n"); + printf(" 📝 对局记录与复盘\n"); + printf(" 🚫 禁手规则支持\n"); + printf(" ⏱️ 计时器功能\n"); + printf(" 📏 自定义棋盘大小\n"); + printf(" 📊 评分系统\n\n"); + printf("🙏 感谢使用!\n"); + printf("========================\n"); +} \ No newline at end of file diff --git a/ui.h b/ui.h new file mode 100644 index 0000000..0875d51 --- /dev/null +++ b/ui.h @@ -0,0 +1,62 @@ +#ifndef UI_H +#define UI_H + +#include "gobang.h" + +/** + * @brief UI模块 - 用户界面相关功能 + * @author 刘航宇 + * @date 2025-01-15 + * @version 1.0 + */ + +/** + * @brief 显示游戏主菜单 + */ +void display_main_menu(); + +/** + * @brief 显示棋盘 + */ +void display_board(); + +/** + * @brief 显示游戏状态信息 + * @param current_player 当前玩家 + * @param step_count 当前步数 + */ +void display_game_status(int current_player, int step_count); + +/** + * @brief 显示获胜信息 + * @param winner 获胜者 + */ +void display_winner(int winner); + +/** + * @brief 显示游戏设置菜单 + */ +void display_settings_menu(); + +/** + * @brief 清屏函数 + */ +void clear_screen(); + +/** + * @brief 暂停等待用户输入 + * @param prompt 提示信息 + */ +void pause_for_input(const char* prompt); + +/** + * @brief 显示游戏规则 + */ +void display_game_rules(); + +/** + * @brief 显示关于信息 + */ +void display_about(); + +#endif // UI_H \ No newline at end of file