#include "game_mode.h" #include "init_board.h" #include "gobang.h" #include "ai.h" #include "record.h" #include "config.h" #include #include #include // 全局变量定义 int BOARD_SIZE = DEFAULT_BOARD_SIZE; // 实际使用的棋盘尺寸 int board[MAX_BOARD_SIZE][MAX_BOARD_SIZE] = {0}; // 棋盘状态存储数组(默认棋盘全空为0) Step steps[MAX_STEPS]; // 存储所有落子步骤的数组 const int direction[4][2] = {{1, 0}, {0, 1}, {1, 1}, {1, -1}}; // 四个方向:向下、向右、右下、左下 int step_count = 0; // 当前步数计数器 bool use_forbidden_moves = DEFAULT_USE_FORBIDDEN_MOVES; // 是否启用禁手规则 int use_timer = DEFAULT_USE_TIMER; // 是否启用计时器 int time_limit = DEFAULT_TIME_LIMIT; // 每回合的时间限制(秒) /** * @brief 检查棋盘(x, y)位置是否为空 * @param x 行坐标(0-base) * @param y 列坐标(0-base) * @return true-空, false-非空 */ bool have_space(int x, int y) { return x >= 0 && x < BOARD_SIZE && y >= 0 && y < BOARD_SIZE && board[x][y] == EMPTY; } // 函数定义 /** * @brief 检查是否为禁手 * * @param x * @param y * @param player * @return true * @return false */ bool is_forbidden_move(int x, int y, int player) { if (!use_forbidden_moves) { return false; } if (player != PLAYER && player != PLAYER1) { return false; } board[x][y] = player; int three_count = 0; int four_count = 0; for (int i = 0; i < 4; i++) { DirInfo info = count_specific_direction(x, y, direction[i][0], direction[i][1], player); if (info.continuous_chess > 5) { board[x][y] = EMPTY; return true; // 长连禁手 } if (info.continuous_chess == 3 && info.check_start && info.check_end) { three_count++; } if (info.continuous_chess == 4 && (info.check_start || info.check_end)) { four_count++; } } board[x][y] = EMPTY; if (three_count >= 2 || four_count >= 2) { return true; // 三三或四四禁手 } return false; } /** * @brief 执行玩家落子操作 * @param x 行坐标(0-base) * @param y 列坐标(0-base) * @return true 落子成功 * @return false 落子失败(位置无效) */ bool player_move(int x, int y, int player) { // 位置无效则返回false if (!have_space(x, y)) return false; if (is_forbidden_move(x, y, player)) { printf("禁手!请选择其他位置。\n"); return false; } // 更新棋盘状态 board[x][y] = player; // 记录落子步骤:玩家标识和坐标 steps[step_count++] = (Step){player, x, y}; return true; } /** * @brief 计算特定方向上连续同色棋子数量 * @param x 起始行坐标 * @param y 起始列坐标 * @param dx 行方向增量(-1,0,1) * @param dy 列方向增量(-1,0,1) * @param player 玩家标识(PLAYER/AI) * @return DirInfo 包含连续棋子数和方向开放状态的结构体 * @note 检查正反两个方向,统计连续棋子数并判断端点是否开放 */ DirInfo count_specific_direction(int x, int y, int dx, int dy, int player) { DirInfo info; info.continuous_chess = 1; // 起始位置已经有一个棋子 info.check_start = false; // 起点方向是否开放 info.check_end = false; // 终点方向是否开放 // 检查正方向(dx, dy) int nx = x + dx, ny = y + dy; while (nx >= 0 && nx < BOARD_SIZE && ny >= 0 && ny < BOARD_SIZE && board[nx][ny] == player) { info.continuous_chess++; // 连续棋子计数增加 nx += dx; // 沿当前方向前进 ny += dy; } // 判断正方向端点是否开放(遇到空位) if (nx >= 0 && nx < BOARD_SIZE && ny >= 0 && ny < BOARD_SIZE) { if (board[nx][ny] == EMPTY) { info.check_end = true; } } // 检查反方向(-dx, -dy) nx = x - dx, ny = y - dy; while (nx >= 0 && nx < BOARD_SIZE && ny >= 0 && ny < BOARD_SIZE && board[nx][ny] == player) { info.continuous_chess++; // 连续棋子计数增加 nx -= dx; // 沿相反方向前进 ny -= dy; } // 判断反方向端点是否开放(遇到空位) if (nx >= 0 && nx < BOARD_SIZE && ny >= 0 && ny < BOARD_SIZE) { if (board[nx][ny] == EMPTY) { info.check_start = true; } } return info; } bool check_win(int x, int y, int player) { // 检查四个方向是否存在五连珠 for (int i = 0; i < 4; i++) { DirInfo info = count_specific_direction(x, y, direction[i][0], direction[i][1], player); if (info.continuous_chess >= 5) // 连续棋子>=5即获胜 { return true; } } return false; // 四个方向都没有五连珠 } /** * @brief 悔棋功能实现 * * @param steps_to_undo 要悔棋的步数 * @return true 悔棋成功 * @return false 悔棋失败(步数不足) */ bool return_move(int steps_to_undo) { if (step_count < steps_to_undo) { return false; } for (int i = 0; i < steps_to_undo; i++) { if (step_count > 0) { step_count--; board[steps[step_count].x][steps[step_count].y] = EMPTY; } } return true; } /** * @brief 评估玩家在整盘棋局中的表现 * @param player 要评估的玩家(PLAYER/AI) * @return int 总分(已考虑方向重复计算) * @note 改进后的评分标准: * - 五连:5000 (提高权重,更强调获胜) * - 活四:2000 冲四:1000 死四:300 (提高权重,强调进攻性) * - 活三:500 眠三:200 死三:80 (提高权重,强调战略价值) * - 活二:100 眠二:40 死二:15 (适当提高权重) * - 开放单子:15 半开放单子:8 封闭单子:2 (适当提高权重) * @note 实现细节: * 1. 遍历棋盘所有位置 * 2. 对每个棋子检查四个方向 * 3. 统计所有连子情况并评分 * 4. 最终分数除以4(消除方向重复计算影响) */ int calculate_step_score(int x, int y, int player) { int step_score = 0; // 检查四个方向 for (int k = 0; k < 4; k++) { DirInfo info = count_specific_direction(x, y, direction[k][0], direction[k][1], player); // 根据连子数评分 switch (info.continuous_chess) { case 5: step_score += SCORE_FIVE; break; // 五连 case 4: if (info.check_start && info.check_end) step_score += SCORE_LIVE_FOUR; // 活四 else if (info.check_start || info.check_end) step_score += SCORE_RUSH_FOUR; // 冲四 else step_score += SCORE_DEAD_FOUR; // 死四 break; case 3: if (info.check_start && info.check_end) step_score += SCORE_LIVE_THREE; // 活三 else if (info.check_start || info.check_end) step_score += SCORE_SLEEP_THREE; // 眠三 else step_score += SCORE_DEAD_THREE; // 死三 break; case 2: if (info.check_start && info.check_end) step_score += SCORE_LIVE_TWO; // 活二 else if (info.check_start || info.check_end) step_score += SCORE_SLEEP_TWO; // 眠二 else step_score += SCORE_DEAD_TWO; // 死二 break; case 1: if (info.check_start && info.check_end) step_score += SCORE_LIVE_ONE; // 开放单子 else if (info.check_start || info.check_end) step_score += SCORE_HALF_ONE; // 半开放单子 else step_score += SCORE_DEAD_ONE; // 封闭单子 break; } } // 位置奖励:越靠近中心分数越高 int center_x = BOARD_SIZE / 2; int center_y = BOARD_SIZE / 2; int distance = abs(x - center_x) + abs(y - center_y); // 曼哈顿距离 int position_bonus = POSITION_BONUS_FACTOR * (BOARD_SIZE - distance); // 距离中心越近奖励越高 return step_score + position_bonus; }