mirror of
https://github.com/LHY0125/Gobang-Game.git
synced 2026-05-10 02:19:46 +08:00
Add files via upload
This commit is contained in:
@@ -0,0 +1,255 @@
|
||||
#include "config.h"
|
||||
#include "ui.h"
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
// 配置文件路径
|
||||
#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("按任意键继续...");
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
+7
-6
@@ -223,10 +223,7 @@ 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;
|
||||
}
|
||||
}
|
||||
@@ -308,10 +312,7 @@ 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();
|
||||
|
||||
@@ -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]; // 定义四个基本搜索方向:水平、垂直、左斜、右斜
|
||||
|
||||
// 数据结构
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
# 五子棋游戏配置文件
|
||||
# 棋盘大小 (范围: 5-25)
|
||||
BOARD_SIZE=15
|
||||
|
||||
# 禁手规则 (0=关闭, 1=开启)
|
||||
USE_FORBIDDEN_MOVES=1
|
||||
|
||||
# 计时器 (0=关闭, 1=开启)
|
||||
USE_TIMER=1
|
||||
|
||||
# 时间限制 (分钟)
|
||||
TIME_LIMIT=60
|
||||
+9
-9
@@ -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);
|
||||
|
||||
|
||||
@@ -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 <stdio.h>
|
||||
#ifdef _WIN32
|
||||
#include <windows.h>
|
||||
#include <direct.h>
|
||||
#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;
|
||||
}
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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
|
||||
@@ -0,0 +1,199 @@
|
||||
#include "ui.h"
|
||||
#include "config.h"
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#ifdef _WIN32
|
||||
#include <windows.h>
|
||||
#include <conio.h>
|
||||
#else
|
||||
#include <unistd.h>
|
||||
#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");
|
||||
}
|
||||
@@ -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
|
||||
Reference in New Issue
Block a user