从五子棋AI算法设计感慨现在大模型是真牛逼

举报
yd_218587850 发表于 2026/09/03 09:22:22 2026/09/03
【摘要】 以下是一个基于 Minimax + Alpha-Beta 剪枝 的增强版五子棋 AI,针对 5×15 棋盘优化,支持高/中/低三档难度。相比原贪心方案,它具备更强的棋力,同时保留了性能优势和难度可调性。// ==================== 五子棋AI增强版(5×15) ====================// 棋盘:5行15列,0空 1黑(玩家) 2白(AI)// 核心:Min...

以下是一个基于 Minimax + Alpha-Beta 剪枝 的增强版五子棋 AI,针对 5×15 棋盘优化,支持高/中/低三档难度。相比原贪心方案,它具备更强的棋力,同时保留了性能优势和难度可调性。

// ==================== 五子棋AI增强版(5×15) ====================
// 棋盘:5行15列,0空 1黑(玩家) 2白(AI)
// 核心:Minimax + Alpha-Beta剪枝 + 全局局面评估 + 纵向加权 + 组合威胁加分
// 难度:high(深度4) / mid(深度2) / low(深度1+杀点防守)

const H = 5;               // 行数
const W = 15;              // 列数
const WIN_SCORE = 10000000; // 胜利分数(极大值)

// ---------- 基础函数 ----------

/** 检查 player 是否获胜 */
function checkWin(board, player) {
    const dirs = [[0,1],[1,0],[1,1],[1,-1]];
    for (let y = 0; y < H; y++) {
        for (let x = 0; x < W; x++) {
            if (board[y][x] !== player) continue;
            for (let [dy, dx] of dirs) {
                let cnt = 1;
                let ny = y + dy, nx = x + dx;
                while (ny >= 0 && ny < H && nx >= 0 && nx < W && board[ny][nx] === player) {
                    cnt++;
                    ny += dy;
                    nx += dx;
                }
                if (cnt >= 5) return true;
            }
        }
    }
    return false;
}

/** 棋盘是否已满(平局) */
function isBoardFull(board) {
    for (let y = 0; y < H; y++) {
        for (let x = 0; x < W; x++) {
            if (board[y][x] === 0) return false;
        }
    }
    return true;
}

/** 获取所有空位坐标 */
function getEmptyCells(board) {
    const cells = [];
    for (let y = 0; y < H; y++) {
        for (let x = 0; x < W; x++) {
            if (board[y][x] === 0) cells.push({y, x});
        }
    }
    return cells;
}

// ---------- 棋型评分 ----------

/**
 * 根据连续棋子数和两端开口数返回棋型分值
 * @param {number} count    连续棋子数
 * @param {number} openEnds 两端空位数(0,1,2)
 */
function getShapeScore(count, openEnds) {
    if (count >= 5) return 1000000;          // 五连
    if (count === 4) {
        if (openEnds === 2) return 100000;   // 活四
        if (openEnds === 1) return 50000;    // 冲四
    } else if (count === 3) {
        if (openEnds === 2) return 5000;     // 活三
        if (openEnds === 1) return 800;      // 眠三
    } else if (count === 2) {
        if (openEnds === 2) return 300;      // 活二
        if (openEnds === 1) return 100;      // 眠二
    } else if (count === 1) {
        return 10;                           // 单子
    }
    return 0;
}

/**
 * 评估单个玩家的棋型总分(包含组合威胁和纵向加权)
 * @param {number[][]} board
 * @param {number}     player 1或2
 * @returns {number}   该玩家的总得分
 */
function evaluatePlayer(board, player) {
    let total = 0;
    let liveThreeCount = 0;  // 用于组合威胁统计
    const dirs = [[0,1],[1,0],[1,1],[1,-1]];

    for (let y = 0; y < H; y++) {
        for (let x = 0; x < W; x++) {
            if (board[y][x] !== player) continue;

            for (let [dy, dx] of dirs) {
                // 只从连续片段的起点开始统计(避免重复)
                const prevY = y - dy, prevX = x - dx;
                if (prevY >= 0 && prevY < H && prevX >= 0 && prevX < W && board[prevY][prevX] === player) {
                    continue; // 不是起点
                }

                // 统计连续长度
                let count = 0;
                let ny = y, nx = x;
                while (ny >= 0 && ny < H && nx >= 0 && nx < W && board[ny][nx] === player) {
                    count++;
                    ny += dy;
                    nx += dx;
                }

                // 检查两端状态
                const leftY = y - dy, leftX = x - dx;
                const rightY = ny, rightX = nx; // ny,nx 指向第一个非player位置
                const leftStatus = (leftY < 0 || leftY >= H || leftX < 0 || leftX >= W) ? 1 : board[leftY][leftX];
                const rightStatus = (rightY < 0 || rightY >= H || rightX < 0 || rightX >= W) ? 1 : board[rightY][rightX];
                let openEnds = 0;
                if (leftStatus === 0) openEnds++;
                if (rightStatus === 0) openEnds++;

                let score = getShapeScore(count, openEnds);

                // 纵向加权:5行棋盘纵向更容易成五连
                if (dy === 1 && dx === 0) {
                    score *= 1.5;
                }

                // 记录活三数量(用于双活三加分)
                if (count === 3 && openEnds === 2) {
                    liveThreeCount++;
                }

                total += score;
            }
        }
    }

    // 组合威胁:双活三几乎必胜,额外加分
    if (liveThreeCount >= 2) {
        total += 50000;
    }
    return total;
}

/**
 * 全局局面评估:AI得分 - 玩家得分
 * @returns {number} 正数表示AI优势,负数表示玩家优势
 */
function evaluateBoard(board, aiPlayer, humanPlayer) {
    const aiScore = evaluatePlayer(board, aiPlayer);
    const humanScore = evaluatePlayer(board, humanPlayer);
    return aiScore - humanScore;
}

// ---------- Minimax + Alpha-Beta ----------

/**
 * Alpha-Beta搜索
 * @param {number} depth        剩余深度
 * @param {number} alpha
 * @param {number} beta
 * @param {boolean} isMaximizing true=AI(白)回合,false=玩家(黑)回合
 * @returns {number} 局面评分(AI视角)
 */
function alphaBeta(board, depth, alpha, beta, isMaximizing, aiPlayer, humanPlayer) {
    // 终止条件:胜利或深度耗尽或平局
    if (checkWin(board, aiPlayer)) return WIN_SCORE;
    if (checkWin(board, humanPlayer)) return -WIN_SCORE;
    if (depth === 0 || isBoardFull(board)) {
        return evaluateBoard(board, aiPlayer, humanPlayer);
    }

    const empty = getEmptyCells(board);

    if (isMaximizing) {
        let maxEval = -Infinity;
        for (const cell of empty) {
            board[cell.y][cell.x] = aiPlayer;
            const evalScore = alphaBeta(board, depth - 1, alpha, beta, false, aiPlayer, humanPlayer);
            board[cell.y][cell.x] = 0;
            maxEval = Math.max(maxEval, evalScore);
            alpha = Math.max(alpha, evalScore);
            if (beta <= alpha) break; // 剪枝
        }
        return maxEval;
    } else {
        let minEval = Infinity;
        for (const cell of empty) {
            board[cell.y][cell.x] = humanPlayer;
            const evalScore = alphaBeta(board, depth - 1, alpha, beta, true, aiPlayer, humanPlayer);
            board[cell.y][cell.x] = 0;
            minEval = Math.min(minEval, evalScore);
            beta = Math.min(beta, evalScore);
            if (beta <= alpha) break; // 剪枝
        }
        return minEval;
    }
}

/**
 * 计算所有空位的评分(模拟AI落子后调用Alpha-Beta)
 * @param {number} depth 搜索深度(AI落子后剩余的深度)
 * @returns {Array<{y:number,x:number,score:number}>}
 */
function getScoredMoves(board, aiPlayer, humanPlayer, depth) {
    const empty = getEmptyCells(board);
    const moves = [];

    for (const cell of empty) {
        board[cell.y][cell.x] = aiPlayer;
        let score;
        if (checkWin(board, aiPlayer)) {
            score = WIN_SCORE;
        } else {
            // 注意:AI已落子,接下来轮到玩家(Min层)
            score = alphaBeta(board, depth - 1, -Infinity, Infinity, false, aiPlayer, humanPlayer);
        }
        board[cell.y][cell.x] = 0;
        moves.push({ y: cell.y, x: cell.x, score });
    }
    return moves;
}

// ---------- AI主入口 ----------

/**
 * AI选择落子
 * @param {number[][]} board       当前棋盘
 * @param {number}     aiPlayer    AI棋子编号(2)
 * @param {number}     humanPlayer 玩家棋子编号(1)
 * @param {string}     level       'high' | 'mid' | 'low'
 * @returns {{y:number,x:number}|null} 落子坐标,无空位返回null
 */
function aiMove(board, aiPlayer, humanPlayer, level) {
    const empty = getEmptyCells(board);
    if (empty.length === 0) return null;

    // 1. 立即获胜:AI直接五连
    for (const cell of empty) {
        board[cell.y][cell.x] = aiPlayer;
        if (checkWin(board, aiPlayer)) {
            board[cell.y][cell.x] = 0;
            return cell;
        }
        board[cell.y][cell.x] = 0;
    }

    // 2. 防守唯一杀点:玩家下一步五连,必须堵
    const blockMoves = [];
    for (const cell of empty) {
        board[cell.y][cell.x] = humanPlayer;
        if (checkWin(board, humanPlayer)) {
            blockMoves.push(cell);
        }
        board[cell.y][cell.x] = 0;
    }
    if (blockMoves.length === 1) {
        return blockMoves[0];
    }

    // 3. 根据难度设置搜索深度
    let depth;
    if (level === 'high') depth = 4;
    else if (level === 'mid') depth = 2;
    else depth = 1; // low

    // 4. 获取所有候选落子的评分
    const moves = getScoredMoves(board, aiPlayer, humanPlayer, depth);
    moves.sort((a, b) => b.score - a.score);

    // 5. 按难度选择最终落子
    let chosen;
    if (level === 'high') {
        // 最高分,若有多个随机选
        const topScore = moves[0].score;
        const topMoves = moves.filter(m => m.score === topScore);
        chosen = topMoves[Math.floor(Math.random() * topMoves.length)];
    } else if (level === 'mid') {
        // 前3名随机
        const topN = moves.slice(0, Math.min(3, moves.length));
        chosen = topN[Math.floor(Math.random() * topN.length)];
    } else {
        // low:前8名随机(保留一定随机性,但已强制防守杀点)
        const topN = moves.slice(0, Math.min(8, moves.length));
        chosen = topN[Math.floor(Math.random() * topN.length)];
    }

    return { y: chosen.y, x: chosen.x };
}

// ==================== 使用示例 ====================
// 假设 board 是 5×15 二维数组,0空,1黑(玩家),2白(AI)
// const board = Array.from({length:5}, () => Array(15).fill(0));
// board[2][7] = 1; // 玩家先手
// const aiMoveResult = aiMove(board, 2, 1, 'high');
// console.log(aiMoveResult); // {y: ..., x: ...}

改进说明

模块 原方案 增强版
决策方式 单步贪心,仅评估当前落点 Minimax + Alpha-Beta 剪枝,前瞻 2~4 步
评估对象 单个空位的进攻/防守分 全局局面评估(AI得分 − 玩家得分)
棋型识别 仅连续长度和开口数 同左,但增加纵向加权、双活三组合威胁加分
难度控制 从 TOP-N 中随机 搜索深度差异 + 候选集随机(低深度下仍保证防守杀点)
性能 极快 依然流畅(5×15 搜索空间小,剪枝后节点数极少)

关键优化点

  1. 纵向加权:因棋盘仅 5 行,纵向五连极易形成,对纵向棋型赋予 1.5 倍权重,AI 会更重视纵线攻防。
  2. 双活三加分:当玩家或 AI 拥有两个活三时,对手无法同时防守,额外加 50000 分,使 AI 能主动制造或防守此类杀招。
  3. 强制防守杀点:无论难度高低,若对手下一步能直接五连,AI 必定阻挡(除非 AI 自己能立即获胜),避免低难度下出现“愚蠢送死”行为。
  4. Alpha-Beta 剪枝:深度 4 时搜索节点数大幅减少,浏览器无卡顿。

难度效果实测(预期)

  • 高难度(depth=4):能够计算到 4 步后的局势,主动构建双活三、冲四等组合进攻,同时精确防守玩家威胁,适合进阶练习。
  • 中难度(depth=2):会思考对手的下一步回应,但不会深算,偶尔因随机选择而失误,棋力适中。
  • 低难度(depth=1):只考虑当前一步,虽然有杀点防守,但整体进攻弱、防守差,适合新手。

你可以将以上代码直接嵌入 HTML 五子棋页面,替换原来的 aiMove 函数即可。

以上内容是deepseek输出的,原本就是随意想搞第一个例子,结果这个算法输出之后,真的有被震撼到
尤其是对深度的搜索和计算
关键这只是开胃菜

结合多种算法的混合方案
实际最强 AI 往往融合多种技术:
开局阶段:使用开局库(预先计算好的前几步最佳走法)。
中盘阶段:使用威胁空间搜索寻找强制获胜,若无法获胜则使用深度 Minimax。
残局阶段:使用精确搜索(深度不限,直到终局)。
例如,可以设计一个 AI:
先检查是否有直接获胜或必须防守的点。
尝试用 TSS 寻找 5 步以内的强制获胜序列。
若未找到,则调用深度 Minimax(带置换表和启发式)选择最佳走法。
评估函数采用手工设计 + 纵向加权 + 组合威胁加分。
这样的混合 AI 在 5×15 棋盘上将极其强大,几乎不可能被人类击败。

这。。。
现在的AI确实太强了
各种算法直接拿来就用
而我们人类如果不是专业的
真就感受到了碾压
。。。

【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。