共享会话
更新和改進
分享于 2026年8月17日 13:21
QuanAI以下修改只調整「計時邊界」,不改動目前活法、共活、單接、雙接的統計/索引方式,也不改動候選生成、仲裁、MCTS、minimax 的既有判定流程。
修改目標
目前 startAI() 中:
startTime在建立根局面、建立增量索引、統計共活/單接/雙接、產生候選、戰術預搜尋之前就開始;overallDeadline因此會把上述前置統計耗時算進 AI 設定的限時;- 最終訊息框的
totalTime也是從這個時間開始計算。
要改為:
- 完整總耗時:從 AI 開始思考起算,供最後訊息框顯示。
- MCTS/minimax 的搜尋限時:等活法/共活/單接/雙接等前置統計、候選整理、戰術預搜尋完成後才開始計時,並獲得設定中的完整
timeLimit。 - 最終訊息框仍顯示從 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 的評估與選擇流程。