QuanAI只读快照

共享会话

更新和改進

分享于 2026年8月17日 13:21
提问者

更新和改進 當AI在思考時,MCTS或minimax搜尋要用完設置中的時間,這個時間不要包含對當前局面的「活法、共活、單連、雙接」等統計。但當MCTS或minimax搜尋完畢後,消息框需要給出所用的全部時間。

回答要求:

不要影響當前正確的流程,明確給出所要修改的地方和所要替換的代碼。

trigolden_game2642.htmltext/plain · 242.6 KB预览下载
QuanAI

以下修改只調整「計時邊界」,不改動目前活法、共活、單接、雙接的統計/索引方式,也不改動候選生成、仲裁、MCTS、minimax 的既有判定流程

修改目標

目前 startAI() 中:

  • startTime 在建立根局面、建立增量索引、統計共活/單接/雙接、產生候選、戰術預搜尋之前就開始;
  • overallDeadline 因此會把上述前置統計耗時算進 AI 設定的限時;
  • 最終訊息框的 totalTime 也是從這個時間開始計算。

要改為:

  1. 完整總耗時:從 AI 開始思考起算,供最後訊息框顯示。
  2. MCTS/minimax 的搜尋限時:等活法/共活/單接/雙接等前置統計、候選整理、戰術預搜尋完成後才開始計時,並獲得設定中的完整 timeLimit
  3. 最終訊息框仍顯示從 AI 開始到搜尋完成為止的全部耗時,所以數字可能大於設定秒數;這是符合「統計時間不佔搜尋限時,但消息框顯示全部時間」的需求。

1. 替換 startAI() 內的初始計時區塊

async function startAI() 中,找到:

js
        const timeLimit = Math.max(0.2, aiConfig.settings[aiPlayer].time || 3) * 1000;

        const configuredDepth = Math.max(2, aiConfig.settings[aiPlayer].n || 3);

        const startTime = performance.now();
        const overallDeadline = startTime + timeLimit;

        let searchCount = 0;

替換為:

js
        const timeLimit = Math.max(0.2, aiConfig.settings[aiPlayer].time || 3) * 1000;

        const configuredDepth = Math.max(2, aiConfig.settings[aiPlayer].n || 3);

        /*
         * 全部耗時:從 AI 開始思考起算。
         * 包含:
         * - 活法/共活/單接/雙接索引與統計;
         * - 候選生成與排序;
         * - 戰術預搜尋;
         * - 最終 MCTS 或 minimax 搜尋。
         *
         * 此時間只用於最後消息框顯示,絕不作為 MCTS/minimax 的截止時間。
         */
        const totalStartTime = performance.now();

        let searchCount = 0;

2. 替換戰術預搜尋的截止時間

在同一個 startAI() 中,找到:

js
        /*
         * 戰術搜索最多使用約 40% 總時間。
         * 若完整深度來不及完成,runTacticalPresearch
         * 會丟棄不完整深度,只保留完整的直接戰術評估。
         */
        const tacticalDeadline = Math.min(overallDeadline, startTime + Math.max(250, timeLimit * 0.4));

替換為:

js
        /*
         * 戰術預搜尋屬於 MCTS/minimax 前的候選分析階段。
         *
         * 它不可扣除最終 MCTS/minimax 的設定限時,因此使用獨立時間窗。
         * 後續真正進入 MCTS/minimax 時,會重新取得完整 timeLimit。
         */
        const tacticalStartTime = performance.now();
        const tacticalDeadline = tacticalStartTime + Math.max(250, timeLimit * 0.4);

這樣保留原本「戰術預搜尋最多約 40%」的既有流程,但它不再壓縮正式 MCTS/minimax 的時間。


3. 在進入 MCTS/minimax 前,建立正式搜尋計時與截止時間

在這段程式碼後面:

js
        rootMoves = orderMovesForSearch(rootState, rootMoves, tacticalScores);

        if (cancelAi) {
          aiThinking = false;
          return;
        }

        const totalPiecesRemaining = Object.values(rootState.counts).reduce(

請在 if (cancelAi) { ... } 區塊之後、const totalPiecesRemaining 之前,插入

js
        /*
         * 到此為止,活法、共活、單接、雙接、候選與戰術預搜尋皆已完成。
         *
         * 從這一刻開始,MCTS/minimax 才開始使用 AI 設定中的完整限時。
         * 因此前置統計耗時不會侵蝕正式搜尋時間。
         */
        const searchStartTime = performance.now();
        const searchDeadline = searchStartTime + timeLimit;

插入後應是:

js
        rootMoves = orderMovesForSearch(rootState, rootMoves, tacticalScores);

        if (cancelAi) {
          aiThinking = false;
          return;
        }

        /*
         * 到此為止,活法、共活、單接、雙接、候選與戰術預搜尋皆已完成。
         *
         * 從這一刻開始,MCTS/minimax 才開始使用 AI 設定中的完整限時。
         * 因此前置統計耗時不會侵蝕正式搜尋時間。
         */
        const searchStartTime = performance.now();
        const searchDeadline = searchStartTime + timeLimit;

        const totalPiecesRemaining = Object.values(rootState.counts).reduce(

4. 替換正式搜尋呼叫的 overallDeadline

接著找到:

js
        if (performance.now() >= overallDeadline) {
          result = {
            bestMove: rootMoves[0],
            value: 0,
            searchCount: 0
          };
        } else if (useMinimax) {
          result = await runStateMinimax(rootState, rootMoves, turnsLeft, overallDeadline, tacticalScores);
        } else {
          result = await runStateMCTS(rootState, rootMoves, configuredDepth, overallDeadline, tacticalScores, MCTS_C);
        }

整段替換為:

js
        /*
         * searchDeadline 在完成前置統計後才建立,
         * 因此 MCTS/minimax 一律擁有設定中的完整搜尋時間。
         */
        if (useMinimax) {
          result = await runStateMinimax(rootState, rootMoves, turnsLeft, searchDeadline, tacticalScores);
        } else {
          result = await runStateMCTS(rootState, rootMoves, configuredDepth, searchDeadline, tacticalScores, MCTS_C);
        }

為何可刪除原本的 if (performance.now() >= overallDeadline)

因為 searchDeadline 剛剛才設定成:

js
const searchDeadline = performance.now() + timeLimit;

所以此刻不會已逾時。正式搜尋一定可取得完整的設定時間。


5. 修改完成訊息框的總時間來源

最後,找到:

js
        const totalTime = ((performance.now() - startTime) / 1000).toFixed(1);

替換為:

js
        /*
         * 最終訊息要顯示 AI 從開始思考到 MCTS/minimax 完成的全部耗時,
         * 所以前置統計與候選分析也必須包含在內。
         */
        const totalTime = ((performance.now() - totalStartTime) / 1000).toFixed(1);

原本後面的訊息框程式可保持不變:

js
        showMessage(
          t('ai-done', {
            time: totalTime,
            count: searchCount,
            val: valueText
          }),
          3000,
          true
        );

修改後的時間流程

流程图
正在绘制流程图…

結果

  • 「活法、共活、單接、雙接」及候選資料的統計時間,不會扣除 MCTS/minimax 的設定秒數。
  • MCTS 或 minimax 開始時,會有完整的 aiConfig.settings[aiPlayer].time 時間可用。
  • 搜尋完成後,消息框中的時間是 AI 從開始思考起至搜尋完成的全部實際時間
  • 不更動既有:
    • 增量索引;
    • 共活/單接/雙接判定;
    • 仲裁判定;
    • 合法回合生成;
    • MCTS 與 minimax 的評估與選擇流程。