You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何为3、4、5阶井字棋正确实现Minimax AI?

正确实现n阶井字棋Minimax AI的方案

问题背景

需要为3、4、5阶井字棋实现Minimax AI,游戏规则如下:

  • n阶井字棋拥有n行n列共n²个单元格的棋盘,初始为空
  • 两名玩家交替落子,每次必须选择未被占据的单元格
  • 当某玩家占据完整一行、一列或一条对角线,或所有单元格被占时,游戏结束

已完成3、4阶合法状态枚举,当前尝试实现Minimax AI时遇到以下问题:

  • 重写的基础Minimax代码仅返回10/-10/0三种得分,未区分早获胜与晚获胜的权重
  • 自行编写的Minimax变体逻辑不清晰,不确定正确性
  • 需要针对大棋盘(4、5阶)优化性能,同时优先处理能直接获胜或阻止对手的关键落位点

棋盘采用位编码方案:用2n²位存储两名玩家的占位情况,o和x分别代表O和X的位掩码,full = o | x表示已占位单元格,通过o << n*n | x打包为整数,解压用o = full >> n*n; x = full & ((1 << n*n) - 1)。


核心实现方案

1. 明确Minimax核心逻辑

Minimax是零和博弈算法,核心逻辑为:

  • 最大化玩家(如X)追求当前状态的最高可能得分
  • 最小化玩家(如O)追求当前状态的最低可能得分
  • 终端状态得分规则:
    • 获胜玩家获得正分,失败玩家获得负分,平局得0分
    • 获胜回合越早,得分权重越高(比如用得分 * (剩余可走步数 + 1),让早赢的路径优先级更高)
  • 非终端状态得分:最大化玩家取所有子状态的最大得分,最小化玩家取所有子状态的最小得分

2. 修复基础Minimax的得分缺陷

之前的代码未考虑递归深度,导致所有获胜状态得分相同。调整终端状态得分计算:

# 示例:SCORES定义为(获胜得分, 失败得分, 平局得分)
SCORES = (10, -10, 0)
# depth为剩余可走步数(初始值为n² - 已走步数)
score = SCORES[winner] * (depth + 1)

例如3阶井字棋中,第3步获胜得10*7=70,第5步获胜得10*5=50,算法会优先选择更快获胜的路径。

3. 加入Alpha-Beta剪枝提升性能

针对4、5阶大棋盘,必须加入Alpha-Beta剪枝避免不必要的递归:

  • alpha:最大化玩家能保证的最低得分
  • beta:最小化玩家能保证的最高得分
  • 当子节点得分超过beta(最大化玩家)或低于alpha(最小化玩家)时,直接停止该分支的搜索

同时优先搜索关键落位点,大幅提升剪枝效率:

  1. 先搜索能直接获胜的落位点
  2. 再搜索需要阻止对手获胜的落位点
  3. 最后搜索剩余空位

4. 完善状态缓存(Transposition Table)

缓存需存储状态的精确得分、搜索深度和状态类型,避免重复计算:

# 缓存格式:{棋盘哈希值: (得分, 搜索深度, 状态类型)}
# 状态类型:EXACT(精确得分)、LOWER(下界)、UPPER(上界)
transposition_table = {}

5. 通用n阶Minimax实现代码

from typing import Dict, Tuple, List

def generate_lines(n: int) -> List[int]:
    """生成所有获胜线的位掩码"""
    lines = []
    # 生成行掩码
    for i in range(n):
        line = 0
        for j in range(n):
            pos = i * n + j
            line |= 1 << pos
        lines.append(line)
    # 生成列掩码
    for j in range(n):
        line = 0
        for i in range(n):
            pos = i * n + j
            line |= 1 << pos
        lines.append(line)
    # 生成主对角线掩码
    diag1 = 0
    for i in range(n):
        pos = i * n + i
        diag1 |= 1 << pos
    lines.append(diag1)
    # 生成副对角线掩码
    diag2 = 0
    for i in range(n):
        pos = i * n + (n - 1 - i)
        diag2 |= 1 << pos
    lines.append(diag2)
    return lines

def check_win(player_mask: int, lines: List[int]) -> bool:
    """检查玩家是否获胜"""
    for line in lines:
        if (player_mask & line) == line:
            return True
    return False

def get_empty_positions(full_mask: int, n: int) -> List[int]:
    """获取所有空位的位置"""
    empty = []
    total_cells = n * n
    for pos in range(total_cells):
        if not (full_mask & (1 << pos)):
            empty.append(pos)
    return empty

def find_critical_moves(o_mask: int, x_mask: int, lines: List[int], n: int) -> List[int]:
    """找到关键落位点:直接获胜或阻止对手获胜的位置"""
    full_mask = o_mask | x_mask
    critical = set()
    # 检查当前玩家(X)是否能一步获胜
    for line in lines:
        if (x_mask & line).bit_count() == n - 1 and (full_mask & line).bit_count() == n - 1:
            empty_pos = line ^ (x_mask & line)
            pos = (empty_pos).bit_length() - 1
            critical.add(pos)
    # 检查对手(O)是否能一步获胜,需要阻止
    for line in lines:
        if (o_mask & line).bit_count() == n - 1 and (full_mask & line).bit_count() == n - 1:
            empty_pos = line ^ (o_mask & line)
            pos = (empty_pos).bit_length() - 1
            critical.add(pos)
    return list(critical)

def minimax(
    o_mask: int,
    x_mask: int,
    lines: List[int],
    n: int,
    is_maximizing: bool,
    depth: int,
    alpha: float,
    beta: float,
    trans_table: Dict[int, Tuple[int, int, str]]
) -> int:
    """带Alpha-Beta剪枝和状态缓存的通用Minimax函数"""
    full_mask = o_mask | x_mask
    board_hash = (o_mask << (n*n)) | x_mask

    # 读取缓存
    if board_hash in trans_table:
        cached_score, cached_depth, flag = trans_table[board_hash]
        if cached_depth >= depth:
            if flag == 'EXACT':
                return cached_score
            elif flag == 'LOWER':
                alpha = max(alpha, cached_score)
            elif flag == 'UPPER':
                beta = min(beta, cached_score)
            if alpha >= beta:
                return cached_score

    # 检查终端状态
    if check_win(x_mask, lines):
        score = 10 * (depth + 1)
        trans_table[board_hash] = (score, depth, 'EXACT')
        return score
    if check_win(o_mask, lines):
        score = -10 * (depth + 1)
        trans_table[board_hash] = (score, depth, 'EXACT')
        return score
    if full_mask.bit_count() == n*n:
        trans_table[board_hash] = (0, depth, 'EXACT')
        return 0

    # 确定搜索顺序:优先关键落位点
    empty_positions = get_empty_positions(full_mask, n)
    critical_moves = find_critical_moves(o_mask, x_mask, lines, n)
    if critical_moves:
        search_order = critical_moves + [pos for pos in empty_positions if pos not in critical_moves]
    else:
        search_order = empty_positions

    if is_maximizing:
        max_score = -float('inf')
        for pos in search_order:
            new_x_mask = x_mask | (1 << pos)
            current_score = minimax(o_mask, new_x_mask, lines, n, False, depth - 1, alpha, beta, trans_table)
            max_score = max(max_score, current_score)
            alpha = max(alpha, current_score)
            if beta <= alpha:
                break
        trans_table[board_hash] = (max_score, depth, 'EXACT' if max_score > alpha else 'UPPER')
        return max_score
    else:
        min_score = float('inf')
        for pos in search_order:
            new_o_mask = o_mask | (1 << pos)
            current_score = minimax(new_o_mask, x_mask, lines, n, True, depth - 1, alpha, beta, trans_table)
            min_score = min(min_score, current_score)
            beta = min(beta, current_score)
            if beta <= alpha:
                break
        trans_table[board_hash] = (min_score, depth, 'EXACT' if min_score < beta else 'LOWER')
        return min_score

# 3阶井字棋使用示例
n = 3
lines = generate_lines(n)
trans_table = {}
initial_score = minimax(0, 0, lines, n, True, n*n, -float('inf'), float('inf'), trans_table)

6. 大棋盘(4、5阶)额外优化

  • 深度限制:设置最大搜索深度(如8步),超过深度时使用启发式评估函数(比如计算玩家拥有的潜在获胜线数量,按已占子数加权打分)
  • 并行搜索:用多线程/多进程并行搜索不同落位点,提升搜索速度
  • 内存优化:利用Python原生int的任意长度特性存储位掩码,避免额外内存开销

内容的提问来源于stack exchange,提问作者Ξένη Γήινος

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.06 15:48:11