分享和讨论Javascript相关的话题
头像
ejsoon
一枝独秀
帖子: 6350
注册时间: 2022年 11月 18日 17:36
联系:

Re: 準備為三菱棋做遊戲和MCTS搜尋

帖子 ejsoon »

gemini 3.1 pro

代码: 全选

這兩個是非常專業且切中要害的優化方向。針對您的需求,我已經為您準備了對應的代碼修改。

關於第二點(`rolloutPolicy`),我仔細檢查了現有的代碼:目前的條件是 `36 - movesPlayed <= mctsHeuristicN`。因為在 Rollout 的 `while(true)` 迴圈中,每模擬一手棋,`workPieces` 就會減少,導致 `movesPlayed` 增加,因此 `36 - movesPlayed`(剩餘手數)會不斷遞減。**這意味著只要進入了倒數第 $n$ 手,後續的每一手都會自動符合這個條件,現有邏輯確實已經涵蓋了「從第 $n$ 手直到結束」的所有步驟。**

**但是,這段邏輯中有一個隱藏的效能與邏輯盲區:**
目前的代碼寫法是 `let checkCount = Math.min(moves.length, 6);` 然後直接跑 `for (let j = 0; j < checkCount; j++)`。因為 `getValidMoves` 產生的合法著法陣列是**固定順序(由左上到右下)**,這會導致 AI 在局末預判時,**永遠只檢查棋盤左上角的前 6 個格子**,完全忽略了盤面其他地方的得分或阻擋機會。

我們需要加入「局部洗牌(Partial Shuffle)」來確保隨機抽樣整個盤面,同時完成您要求的第一點「探索完畢提前中止」。

以下是具體需要修改與替換的代碼段落:

### 一、 新增 AI 探索完畢提前中止搜尋

**1. 修改 `MCTSNode` 的構造函數**
在 `class MCTSNode { constructor(...) { ... } }` 中,大約 1493 行,加入 `isFullyExplored` 屬性。

```javascript
// 替換這段
if (this.unexpanded.length === 0) {
  this.isTerminal = true;
  let scores = state.calculateScores();
  this.terminalScore = scores.p1Score - scores.p2Score;
} else {
  this.isTerminal = false;
}

// 改為:
if (this.unexpanded.length === 0) {
  this.isTerminal = true;
  this.isFullyExplored = true; // [新增] 終局節點視為已完全探索
  let scores = state.calculateScores();
  this.terminalScore = scores.p1Score - scores.p2Score;
} else {
  this.isTerminal = false;
  this.isFullyExplored = false; // [新增] 
}

```

**2. 修改 `runMCTSBatch` 中的 Backpropagation 階段**
在 `runMCTSBatch` 函數底部(大約 1630 行),加入向上回溯時判斷節點是否窮盡的邏輯。

```javascript
// 替換這段
// 4. Backpropagation
let scaledAdvantage = advantage / 20.0;
let curr = node;
while (curr !== null) {
  curr.visits++;
  if (curr.player === 1) {
    curr.wins += -scaledAdvantage;
  } else {
    curr.wins += scaledAdvantage;
  }
  curr = curr.parent;
}

// 改為:
// 4. Backpropagation
let scaledAdvantage = advantage / 20.0;
let curr = node;
while (curr !== null) {
  curr.visits++;
  if (curr.player === 1) {
    curr.wins += -scaledAdvantage;
  } else {
    curr.wins += scaledAdvantage;
  }
  
  // [新增] 檢查該節點是否已經被完全探索 (無未展開著法,且所有子節點皆已完全探索)
  if (curr.unexpanded.length === 0 && curr.children.length > 0) {
    let allExplored = true;
    for (let i = 0; i < curr.children.length; i++) {
      if (!curr.children[i].isFullyExplored) {
        allExplored = false;
        break;
      }
    }
    if (allExplored) curr.isFullyExplored = true;
  }
  
  curr = curr.parent;
}

```

**3. 在控制迴圈中攔截並提前跳出**
分別在 `triggerAITurn()`(大約 1664 行)和 `runAISearch()`(大約 1762 行)的 `while` 迴圈中修改跳出條件。

在 `triggerAITurn` 中:

```javascript
// 替換這段
let ran = runMCTSBatch(root, rootState, workPieces, 20);
iterations += ran;
if (ran === 0) break; // 若樹已走盡則跳出

// 改為:
let ran = runMCTSBatch(root, rootState, workPieces, 20);
iterations += ran;
if (ran === 0 || root.isFullyExplored) break; // [修改] 若根節點已被完全探索,立即中斷思考

```

在 `runAISearch` 中:

```javascript
// 替換這段
let ran = runMCTSBatch(root, rootState, workPieces, 30);
iterations += ran;
if (ran === 0) break;

// 改為:
let ran = runMCTSBatch(root, rootState, workPieces, 30);
iterations += ran;
if (ran === 0 || root.isFullyExplored) break; // [修改] 若根節點已被完全探索,立即中斷分析

```

---

### 二、 修復並完善預判策略 (Rollout Policy) 的抽樣盲區

在 `runMCTSBatch` 內(大約 1568 行),在進行「條件一」與「條件二」的檢查前,我們利用 Fisher-Yates 洗牌算法進行局部洗牌,確保 AI 隨機抽取盤面上任何地方的合法著法進行預判。

```javascript
// 替換這段
let myScoringMoves = [];
// 為了極致效能,每次最多只檢查 6 個隨機著法,避免嚴重拖慢搜尋
let checkCount = Math.min(moves.length, 6);

// 條件一:找出能使我方得分的步
for (let j = 0; j < checkCount; j++) {
  let move = moves[j];
// ... (略過中間) ...

} else {
  // 條件二:找尋能阻擋對方得分的步
  let oppMoves = simState.getValidMoves(oppPlayer, workPieces);
  let blockingMoves = [];
  let oppCheckCount = Math.min(oppMoves.length, 6);
  for (let j = 0; j < oppCheckCount; j++) {
    let move = oppMoves[j];


// 改為:
let myScoringMoves = [];
let checkCount = Math.min(moves.length, 6);

// [新增] 局部洗牌:隨機抽出前 checkCount 個著法,避免永遠只檢查左上角的死角
for (let i = 0; i < checkCount; i++) {
  let r = i + Math.floor(Math.random() * (moves.length - i));
  let temp = moves[i];
  moves[i] = moves[r];
  moves[r] = temp;
}

// 條件一:找出能使我方得分的步
for (let j = 0; j < checkCount; j++) {
  let move = moves[j];
// ... (保留原本的邏輯) ...

} else {
  // 條件二:找尋能阻擋對方得分的步
  let oppMoves = simState.getValidMoves(oppPlayer, workPieces);
  let blockingMoves = [];
  let oppCheckCount = Math.min(oppMoves.length, 6);
  
  // [新增] 對對手的著法也進行局部洗牌抽樣
  for (let i = 0; i < oppCheckCount; i++) {
    let r = i + Math.floor(Math.random() * (oppMoves.length - i));
    let temp = oppMoves[i];
    oppMoves[i] = oppMoves[r];
    oppMoves[r] = temp;
  }
  
  for (let j = 0; j < oppCheckCount; j++) {
    let move = oppMoves[j];

```
https://ejsoon.vip/
弈趣極光:享受思維樂趣
头像
ejsoon
一枝独秀
帖子: 6350
注册时间: 2022年 11月 18日 17:36
联系:

Re: 準備為三菱棋做遊戲和MCTS搜尋

帖子 ejsoon »

這盤棋正式宣告:三菱棋AI碾壓人類!我謹代表所有地球人,向AI俯首稱臣!

代码: 全选

[(0,0),(1,0)] [(0,-2),(0,-1)] [(0,2),(0,1)] [(-1,3),(0,3)] [(0,-3),(-1,-3)] [(1,-2),(1,-1)] [(-1,1),(-1,0)] [(-2,0),(-2,1)] [(-1,-1),(-1,-2)] [(-2,2),(-1,2)] [(1,2),(1,1)] [(-2,-2),(-2,-1)] [(2,-3),(1,-3)] [(2,-2),(3,-2)] [(3,-1),(3,0)] [(1,3),(2,3)] [(2,0),(2,-1)] [(0,4),(1,4)] [(0,-4),(1,-4)] [(2,1),(2,2)] [(3,1),(3,2)] [(1,-6),(1,-5)] [(4,-3),(3,-3)] [(-1,-5),(-1,-4)] [(4,1),(4,0)] [(4,2),(5,2)] [(3,-4),(2,-4)] [(3,-6),(3,-5)] [(1,5),(1,6)] [(4,-5),(4,-4)] [(-1,5),(-1,4)] [(2,-6),(2,-5)] [(0,5),(0,6)] [(0,-6),(0,-5)] [(4,-2),(4,-1)] [(5,0),(5,1)]
附件
Screenshot from 2026-07-08 01-10-26.png
Screenshot from 2026-07-08 01-10-26.png (199.12 KiB) 查看 988 次
https://ejsoon.vip/
弈趣極光:享受思維樂趣
头像
ejsoon
一枝独秀
帖子: 6350
注册时间: 2022年 11月 18日 17:36
联系:

Re: 準備為三菱棋做遊戲和MCTS搜尋

帖子 ejsoon »

修復和改進:
在runMCTSBatch中,目前的代碼寫法是 `let checkCount = Math.min(moves.length, 6);` 然後直接跑 `for (let j = 0; j < checkCount; j++)`。因為 `getValidMoves` 產生的合法著法陣列是**固定順序(由左上到右下)**,這會導致 AI 在局末預判時,**永遠只檢查棋盤左上角的前 6 個格子**,完全忽略了盤面其他地方的得分或阻擋機會。

我們需要加入「局部洗牌(Partial Shuffle)」來確保隨機抽樣整個盤面。

後來你給我的更改建議是:

```
let myScoringMoves = [];
let checkCount = Math.min(moves.length, 6);

// [新增] 局部洗牌:隨機抽出前 checkCount 個著法,避免永遠只檢查左上角的死角
for (let i = 0; i < checkCount; i++) {
let r = i + Math.floor(Math.random() * (moves.length - i));
let temp = moves;
moves = moves[r];
moves[r] = temp;
}

// 條件一:找出能使我方得分的步
for (let j = 0; j < checkCount; j++) {
let move = moves[j];
// ... (保留原本的邏輯) ...

} else {
// 條件二:找尋能阻擋對方得分的步
let oppMoves = simState.getValidMoves(oppPlayer, workPieces);
let blockingMoves = [];
let oppCheckCount = Math.min(oppMoves.length, 6);

// [新增] 對對手的著法也進行局部洗牌抽樣
for (let i = 0; i < oppCheckCount; i++) {
let r = i + Math.floor(Math.random() * (oppMoves.length - i));
let temp = oppMoves;
oppMoves = oppMoves[r];
oppMoves[r] = temp;
}

for (let j = 0; j < oppCheckCount; j++) {
let move = oppMoves[j];

```

但是我感覺for循環不是很高效。你要把它改成更高效的打亂方式。

同時把這個「6」寫成變量,並把它放到AI設置窗口中,各等級的值為「簡單6,困難12,專家24,自定48」。

注意翻譯也要同步更新。

需要明確給出所要修改的地方和所要替換的代碼。

代码: 全选

修復和改進:
在runMCTSBatch中,目前的代碼寫法是 `let checkCount = Math.min(moves.length, 6);` 然後直接跑 `for (let j = 0; j < checkCount; j++)`。因為 `getValidMoves` 產生的合法著法陣列是**固定順序(由左上到右下)**,這會導致 AI 在局末預判時,**永遠只檢查棋盤左上角的前 6 個格子**,完全忽略了盤面其他地方的得分或阻擋機會。

我們需要加入「局部洗牌(Partial Shuffle)」來確保隨機抽樣整個盤面。

後來你給我的更改建議是:

```
let myScoringMoves = [];
let checkCount = Math.min(moves.length, 6);

// [新增] 局部洗牌:隨機抽出前 checkCount 個著法,避免永遠只檢查左上角的死角
for (let i = 0; i < checkCount; i++) {
  let r = i + Math.floor(Math.random() * (moves.length - i));
  let temp = moves[i];
  moves[i] = moves[r];
  moves[r] = temp;
}

// 條件一:找出能使我方得分的步
for (let j = 0; j < checkCount; j++) {
  let move = moves[j];
// ... (保留原本的邏輯) ...

} else {
  // 條件二:找尋能阻擋對方得分的步
  let oppMoves = simState.getValidMoves(oppPlayer, workPieces);
  let blockingMoves = [];
  let oppCheckCount = Math.min(oppMoves.length, 6);
  
  // [新增] 對對手的著法也進行局部洗牌抽樣
  for (let i = 0; i < oppCheckCount; i++) {
    let r = i + Math.floor(Math.random() * (oppMoves.length - i));
    let temp = oppMoves[i];
    oppMoves[i] = oppMoves[r];
    oppMoves[r] = temp;
  }
  
  for (let j = 0; j < oppCheckCount; j++) {
    let move = oppMoves[j];

```

但是我感覺for循環不是很高效。你要把它改成更高效的打亂方式。

同時把這個「6」寫成變量,並把它放到AI設置窗口中,各等級的值為「簡單6,困難12,專家24,自定48」。

注意翻譯也要同步更新。

需要明確給出所要修改的地方和所要替換的代碼。
附件
trirhombus285.html.7z
(26.14 KiB) 已下载 27 次
https://ejsoon.vip/
弈趣極光:享受思維樂趣
头像
ejsoon
一枝独秀
帖子: 6350
注册时间: 2022年 11月 18日 17:36
联系:

Re: 準備為三菱棋做遊戲和MCTS搜尋

帖子 ejsoon »

貪食蛇AI大戰5.html

用claude改的。
附件
貪食蛇AI大戰5.html.7z
(11.45 KiB) 已下载 23 次
https://ejsoon.vip/
弈趣極光:享受思維樂趣
头像
ejsoon
一枝独秀
帖子: 6350
注册时间: 2022年 11月 18日 17:36
联系:

Re: 準備為三菱棋做遊戲和MCTS搜尋

帖子 ejsoon »

gemini 3.1 pro 跟我飆英文:

代码: 全选

I completely understand wanting to ditch a `for` loop for something that feels more elegant or modern. However, I have to be completely candid with you: **the partial `for` loop you provided (a partial Fisher-Yates shuffle) is actually the absolute most efficient way to do this in JavaScript.** Other methods like `moves.sort(() => 0.5 - Math.random()).slice(0, checkCount)` might look cleaner, but they force the engine to shuffle the *entire* array (which takes $O(N \log N)$ time) just to pick a few items. Your `for` loop only runs `checkCount` times ($O(k)$ time), which is exactly what a high-performance MCTS engine needs!

That said, we *can* optimize it slightly for the MCTS loop by using a bitwise operator (`| 0`) instead of `Math.floor` to shave off some floating-point math overhead.

Here are the exact modifications to make the check count configurable in the UI and apply the highly optimized partial shuffle.

### 1. 更新 AI 設定預設值與全域變數

在 `<script>` 區塊的開頭,找到 `aiConfigs` 與全域變數宣告的地方,加入 `heuristicChecks` 參數:

**找到這段:**

```javascript
      const aiConfigs = {
        simple: {
          time: 12000,
          c: 2.4,
          heuristicN: 4,
          icon: ' <rect x="5" y="7" width="14" height="11" rx="3" fill="none" stroke="currentColor" stroke-width="2" /> <line x1="12" y1="2" x2="12" y2="6" stroke="currentColor" stroke-width="2" /> <circle cx="9" cy="12" r="1.2" fill="currentColor" /> <circle cx="15" cy="12" r="1.2" fill="currentColor" /> '
        },
        hard: {
          time: 24000,
          c: 3.6,
          heuristicN: 8,
          icon: ' <rect x="2" y="9" width="2" height="6" rx="1" fill="currentColor" /> <rect x="20" y="9" width="2" height="6" rx="1" fill="currentColor" /> <rect x="5" y="6" width="14" height="12" rx="3" fill="none" stroke="currentColor" stroke-width="2" /> <circle cx="9" cy="11" r="1.2" fill="currentColor" /> <circle cx="15" cy="11" r="1.2" fill="currentColor" /> <rect x="9" y="14" width="6" height="2" fill="currentColor" /> '
        },
        expert: {
          time: 36000,
          c: 4.8,
          heuristicN: 12,
          icon: '<path d="M12 1L3 5v6c0 5.55 3.84 10.74 9 12 5.16-1.26 9-6.45 9-12V5l-9-4zm0 10.99h7c-.53 4.12-3.28 7.79-7 8.94V12H5V6.3l7-3.11v8.8z" fill="currentColor"/>'
        },
        custom: {
          time: 48000,
          c: 6.0,
          heuristicN: 16,
          icon: '<path d="M12 2l3.09 6.26L22 9.27l-5 4.87 1.18 6.88L12 17.77l-6.18 3.25L7 14.14 2 9.27l6.91-1.01L12 2z" fill="currentColor"/>'
        },
        human: {
          icon: '<path d="M12 12c2.21 0 4-1.79 4-4s-1.79-4-4-4-4 1.79-4 4 1.79 4 4 4zm0 2c-2.67 0-8 1.34-8 4v2h16v-2c0-2.66-5.33-4-8-4z" fill="currentColor"/>'
        }
      };
      let isAiThinking = false;
      let abortAiSearch = false;
      let mctsExplorationConstant = 3.6;
      let mctsHeuristicN = 8;

```

**替換為:**

```javascript
      const aiConfigs = {
        simple: {
          time: 12000,
          c: 2.4,
          heuristicN: 4,
          heuristicChecks: 6,
          icon: ' <rect x="5" y="7" width="14" height="11" rx="3" fill="none" stroke="currentColor" stroke-width="2" /> <line x1="12" y1="2" x2="12" y2="6" stroke="currentColor" stroke-width="2" /> <circle cx="9" cy="12" r="1.2" fill="currentColor" /> <circle cx="15" cy="12" r="1.2" fill="currentColor" /> '
        },
        hard: {
          time: 24000,
          c: 3.6,
          heuristicN: 8,
          heuristicChecks: 12,
          icon: ' <rect x="2" y="9" width="2" height="6" rx="1" fill="currentColor" /> <rect x="20" y="9" width="2" height="6" rx="1" fill="currentColor" /> <rect x="5" y="6" width="14" height="12" rx="3" fill="none" stroke="currentColor" stroke-width="2" /> <circle cx="9" cy="11" r="1.2" fill="currentColor" /> <circle cx="15" cy="11" r="1.2" fill="currentColor" /> <rect x="9" y="14" width="6" height="2" fill="currentColor" /> '
        },
        expert: {
          time: 36000,
          c: 4.8,
          heuristicN: 12,
          heuristicChecks: 24,
          icon: '<path d="M12 1L3 5v6c0 5.55 3.84 10.74 9 12 5.16-1.26 9-6.45 9-12V5l-9-4zm0 10.99h7c-.53 4.12-3.28 7.79-7 8.94V12H5V6.3l7-3.11v8.8z" fill="currentColor"/>'
        },
        custom: {
          time: 48000,
          c: 6.0,
          heuristicN: 16,
          heuristicChecks: 48,
          icon: '<path d="M12 2l3.09 6.26L22 9.27l-5 4.87 1.18 6.88L12 17.77l-6.18 3.25L7 14.14 2 9.27l6.91-1.01L12 2z" fill="currentColor"/>'
        },
        human: {
          icon: '<path d="M12 12c2.21 0 4-1.79 4-4s-1.79-4-4-4-4 1.79-4 4 1.79 4 4 4zm0 2c-2.67 0-8 1.34-8 4v2h16v-2c0-2.66-5.33-4-8-4z" fill="currentColor"/>'
        }
      };
      let isAiThinking = false;
      let abortAiSearch = false;
      let mctsExplorationConstant = 3.6;
      let mctsHeuristicN = 8;
      let mctsHeuristicChecks = 12;

```

---

### 2. 更新多語言字典 (I18N) 與 UI 切換邏輯

在 `const I18N = { ... }` 內,分別為中文和英文加上翻譯:

**在 `zh` 內加入:**

```javascript
          heuristicChecks: '預判檢查數 (局部洗牌)',

```

**在 `en` 內加入:**

```javascript
          heuristicChecks: 'Heuristic Checks (Shuffle)',

```

接著在 `toggleLanguage()` 函數中,加入更新 UI 標籤的語法:

**找到:**

```javascript
        if (document.getElementById('label-heuristic-n'))
          document.getElementById('label-heuristic-n').innerText = getText('heuristicN');

```

**在下方加入:**

```javascript
        if (document.getElementById('label-heuristic-checks'))
          document.getElementById('label-heuristic-checks').innerText = getText('heuristicChecks');

```

---

### 3. 將控制項加入 HTML 介面

在 HTML 的 `#ai-modal` 中,找到 `heuristic-n` 的 `setting-row` 區塊:

**在以下這段的 *下方* 加入新代碼:**

```html
        <div class="setting-row">
          <label id="label-heuristic-n">智能預判步數 (局末介入)</label>
          <div class="num-ctrl">
            ...
          </div>
        </div>

```

**加入:**

```html
        <div class="setting-row">
          <label id="label-heuristic-checks">預判檢查數 (局部洗牌)</label>
          <div class="num-ctrl">
            <button class="num-btn" onclick="adjustGlobalParam('heuristic-checks', -1, 1, 100, 0)">-</button>
            <input
              type="number"
              id="heuristic-checks"
              class="num-input"
              value="12"
              step="1"
              onchange="updateGlobalParams()"
            />
            <button class="num-btn" onclick="adjustGlobalParam('heuristic-checks', 1, 1, 100, 0)">+</button>
          </div>
        </div>

```

---

### 4. 同步 UI 事件與全域變數

更新與 AI Tab 連動的三個函數 `selectAiTab`、`markCustom` 和 `updateGlobalParams`,讓它們能夠控制這個新變數:

**找到這三個函數並完全替換為:**

```javascript
      // === UI 控制:選擇 AI 強度標籤 ===
      function selectAiTab(preset) {
        document.querySelectorAll('.ai-tab').forEach(tab => {
          if (tab.dataset.preset === preset) tab.classList.add('active');
          else tab.classList.remove('active');
        });
        if (aiConfigs[preset]) {
          document.getElementById('ai-time').value = aiConfigs[preset].time / 1000;
          document.getElementById('mcts-c').value = aiConfigs[preset].c.toFixed(1);
          document.getElementById('heuristic-n').value = aiConfigs[preset].heuristicN;
          document.getElementById('heuristic-checks').value = aiConfigs[preset].heuristicChecks;
          updateGlobalParams();
        }
      }

      // === UI 控制:數值變更時自動切換至「自訂」標籤 ===
      function markCustom() {
        let t = parseFloat(document.getElementById('ai-time').value) * 1000;
        let c = parseFloat(document.getElementById('mcts-c').value);
        let n = parseInt(document.getElementById('heuristic-n').value, 10);
        let checks = parseInt(document.getElementById('heuristic-checks').value, 10);

        let matchedPreset = 'custom';
        for (let k of ['simple', 'hard', 'expert']) {
          if (aiConfigs[k].time === t && Math.abs(aiConfigs[k].c - c) < 0.01 && aiConfigs[k].heuristicN === n && aiConfigs[k].heuristicChecks === checks) {
            matchedPreset = k;
            break;
          }
        }
        document.querySelectorAll('.ai-tab').forEach(tab => {
          if (tab.dataset.preset === matchedPreset) tab.classList.add('active');
          else tab.classList.remove('active');
        });
        aiConfigs.custom.time = t;
        aiConfigs.custom.c = c;
        aiConfigs.custom.heuristicN = n;
        aiConfigs.custom.heuristicChecks = checks;
      }

      function updateGlobalParams() {
        mctsExplorationConstant = parseFloat(document.getElementById('mcts-c').value);
        mctsHeuristicN = parseInt(document.getElementById('heuristic-n').value, 10);
        mctsHeuristicChecks = parseInt(document.getElementById('heuristic-checks').value, 10);
        markCustom();
      }

```

---

### 5. 更新核心邏輯 (`runMCTSBatch`)

最後,我們將高效的位元運算局部洗牌(Bitwise Partial Fisher-Yates)放入模擬迴圈中,並接上剛才創建的 UI 變數 `mctsHeuristicChecks`。

**找到 `runMCTSBatch` 中關於啟發式探索的這段代碼:**

```javascript
              // 啟發式探索:在局末 n 手內介入
              if (36 - movesPlayed <= mctsHeuristicN) {
                let baseScores = simState.calculateScores();
                let myBase = simPlayer === 1 ? baseScores.p1Score : baseScores.p2Score;
                let oppPlayer = simPlayer === 1 ? 2 : 1;
                let oppBase = oppPlayer === 1 ? baseScores.p1Score : baseScores.p2Score;

                let myScoringMoves = [];
                // 為了極致效能,每次最多只檢查 6 個隨機著法,避免嚴重拖慢搜尋
                let checkCount = Math.min(moves.length, 6);

                // 條件一:找出能使我方得分的步
                for (let j = 0; j < checkCount; j++) {
                  let move = moves[j];
                  // ... 後續尋找阻擋步的邏輯 ...

```

**將整個 `if (36 - movesPlayed <= mctsHeuristicN) { ... }` 區塊完全替換為:**

```javascript
              // 啟發式探索:在局末 n 手內介入
              if (36 - movesPlayed <= mctsHeuristicN) {
                let baseScores = simState.calculateScores();
                let myBase = simPlayer === 1 ? baseScores.p1Score : baseScores.p2Score;
                let oppPlayer = simPlayer === 1 ? 2 : 1;
                let oppBase = oppPlayer === 1 ? baseScores.p1Score : baseScores.p2Score;

                let myScoringMoves = [];
                // 使用 UI 設定的預判檢查數
                let checkCount = moves.length < mctsHeuristicChecks ? moves.length : mctsHeuristicChecks;

                // [優化] 高效局部洗牌 (Partial Fisher-Yates),使用位元運算 (|0) 加速
                for (let i = 0; i < checkCount; i++) {
                  let r = i + (Math.random() * (moves.length - i) | 0);
                  let temp = moves[i];
                  moves[i] = moves[r];
                  moves[r] = temp;
                }

                // 條件一:找出能使我方得分的步
                for (let j = 0; j < checkCount; j++) {
                  let move = moves[j];
                  let simPid = (move >> 24) & 0xff;
                  let p1Idx = (move >> 12) & 0xfff;
                  let p2Idx = move & 0xfff;
                  let res = simState.tryPlacePiece(
                    simPid + 1,
                    simState._idxToCoord(p1Idx),
                    simState._idxToCoord(p2Idx),
                    false
                  );
                  if (res.success) {
                    let newScore = simPlayer === 1 ? res.scores.p1Score : res.scores.p2Score;
                    simState.undo();
                    if (newScore > myBase) myScoringMoves.push(move);
                  }
                }

                if (myScoringMoves.length > 0) {
                  m = myScoringMoves[Math.random() * myScoringMoves.length | 0];
                } else {
                  // 條件二:找尋能阻擋對方得分的步
                  let oppMoves = simState.getValidMoves(oppPlayer, workPieces);
                  let blockingMoves = [];
                  let oppCheckCount = oppMoves.length < mctsHeuristicChecks ? oppMoves.length : mctsHeuristicChecks;

                  // [優化] 對手步也進行高效局部洗牌
                  for (let i = 0; i < oppCheckCount; i++) {
                    let r = i + (Math.random() * (oppMoves.length - i) | 0);
                    let temp = oppMoves[i];
                    oppMoves[i] = oppMoves[r];
                    oppMoves[r] = temp;
                  }

                  for (let j = 0; j < oppCheckCount; j++) {
                    let move = oppMoves[j];
                    let simPid = (move >> 24) & 0xff;
                    let p1Idx = (move >> 12) & 0xfff;
                    let p2Idx = move & 0xfff;
                    let res = simState.tryPlacePiece(
                      simPid + 1,
                      simState._idxToCoord(p1Idx),
                      simState._idxToCoord(p2Idx),
                      false
                    );
                    if (res.success) {
                      let newScore = oppPlayer === 1 ? res.scores.p1Score : res.scores.p2Score;
                      simState.undo();
                      if (newScore > oppBase) {
                        for (let k = 0; k < checkCount; k++) { // 只需對照已洗牌過的 moves
                          let myP1 = (moves[k] >> 12) & 0xfff;
                          let myP2 = moves[k] & 0xfff;
                          if (myP1 === p1Idx || myP1 === p2Idx || myP2 === p1Idx || myP2 === p2Idx) {
                            blockingMoves.push(moves[k]);
                          }
                        }
                      }
                    }
                  }
                  if (blockingMoves.length > 0) {
                    m = blockingMoves[Math.random() * blockingMoves.length | 0];
                  } else {
                    // 退回從洗好的安全池子裡隨便選一個
                    m = moves[Math.random() * checkCount | 0];
                  }
                }
              }

```
https://ejsoon.vip/
弈趣極光:享受思維樂趣
头像
ejsoon
一枝独秀
帖子: 6350
注册时间: 2022年 11月 18日 17:36
联系:

Re: 準備為三菱棋做遊戲和MCTS搜尋

帖子 ejsoon »

我現在應該是沒法贏AI了,甚至逼和都很難。

又輸一局:
Screenshot from 2026-07-08 16-48-40.png
Screenshot from 2026-07-08 16-48-40.png (200.74 KiB) 查看 961 次

代码: 全选

[(0,0),(1,0)] [(0,-2),(0,-1)] [(0,1),(0,2)] [(1,-2),(1,-1)] [(0,-3),(-1,-3)] [(-1,3),(0,3)] [(-1,1),(-1,0)] [(-2,0),(-2,1)] [(1,2),(1,1)] [(-2,2),(-1,2)] [(2,-3),(1,-3)] [(2,-1),(2,0)] [(3,-1),(3,0)] [(1,3),(2,3)] [(0,-4),(1,-4)] [(0,4),(1,4)] [(3,-2),(2,-2)] [(3,1),(4,1)] [(1,5),(1,6)] [(0,6),(0,7)] [(-1,7),(-1,6)] [(-3,3),(-2,3)] [(2,5),(2,4)] [(0,-6),(0,-5)] [(0,5),(-1,5)] [(2,-5),(2,-4)] [(-1,-1),(-1,-2)] [(-2,-2),(-2,-1)] [(4,2),(4,3)] [(-1,-5),(-1,-4)] [(3,-4),(3,-5)] [(1,-6),(1,-5)] [(2,1),(2,2)] [(4,-1),(4,0)] [(3,3),(3,2)] [(2,-7),(2,-6)]
附件
trirhombus286.html.7z
(26.5 KiB) 已下载 28 次
https://ejsoon.vip/
弈趣極光:享受思維樂趣
头像
ejsoon
一枝独秀
帖子: 6350
注册时间: 2022年 11月 18日 17:36
联系:

Re: 準備為三菱棋做遊戲和MCTS搜尋

帖子 ejsoon »

14比9!我不玩啦!

代码: 全选

[(0,0),(1,0)] [(0,-2),(0,-1)] [(0,2),(0,1)] [(1,-2),(1,-1)] [(0,-3),(-1,-3)] [(-1,3),(0,3)] [(-1,-2),(-1,-1)] [(-2,0),(-1,0)] [(1,2),(1,1)] [(2,1),(2,2)] [(1,3),(1,4)] [(1,5),(2,5)] [(-1,4),(-1,5)] [(-2,-2),(-2,-1)] [(0,4),(0,5)] [(-3,1),(-2,1)] [(0,6),(0,7)] [(2,3),(2,4)] [(-1,6),(-1,7)] [(0,8),(1,8)] [(3,0),(2,0)] [(-3,-1),(-3,0)] [(2,-3),(1,-3)] [(2,6),(3,6)] [(1,-4),(0,-4)] [(0,-6),(0,-5)] [(2,-2),(2,-1)] [(-1,1),(-1,2)] [(3,1),(3,2)] [(-1,-5),(-1,-4)] [(1,-5),(2,-5)] [(1,6),(1,7)] [(-1,8),(-1,9)] [(3,-2),(3,-1)] [(-3,2),(-3,3)] [(3,4),(3,5)]
Screenshot_20260708_181959_Firefox.jpg
Screenshot_20260708_181959_Firefox.jpg (74.78 KiB) 查看 956 次
https://ejsoon.vip/
弈趣極光:享受思維樂趣
头像
ejsoon
一枝独秀
帖子: 6350
注册时间: 2022年 11月 18日 17:36
联系:

Re: 準備為三菱棋做遊戲和MCTS搜尋

帖子 ejsoon »

第二局打成平手
Screenshot_20260708_190053_Firefox.jpg
Screenshot_20260708_190053_Firefox.jpg (55.39 KiB) 查看 950 次
https://ejsoon.vip/
弈趣極光:享受思維樂趣
头像
ejsoon
一枝独秀
帖子: 6350
注册时间: 2022年 11月 18日 17:36
联系:

Re: 準備為三菱棋做遊戲和MCTS搜尋

帖子 ejsoon »

現在想贏AI基本不可能。

代码: 全选

[(0,0),(1,0)] [(0,-2),(0,-1)] [(0,2),(0,1)] [(1,2),(1,3)] [(2,3),(2,2)] [(1,-2),(1,-1)] [(2,1),(1,1)] [(0,4),(1,4)] [(0,6),(0,5)] [(-1,-1),(-1,0)] [(2,4),(2,5)] [(-1,1),(-1,2)] [(2,-3),(1,-3)] [(-1,7),(0,7)] [(1,6),(1,5)] [(2,-1),(2,0)] [(3,2),(3,1)] [(4,1),(4,2)] [(-2,0),(-2,-1)] [(3,4),(3,5)] [(-1,-2),(-2,-2)] [(-2,2),(-2,3)] [(3,-2),(2,-2)] [(1,-5),(1,-4)] [(3,0),(3,-1)] [(1,7),(2,7)] [(0,-3),(-1,-3)] [(2,6),(3,6)] [(4,-2),(4,-1)] [(-1,5),(-1,6)] [(1,8),(1,9)] [(-1,3),(0,3)] [(-1,9),(-1,8)] [(3,3),(4,3)] [(-3,3),(-3,2)] [(2,-5),(2,-4)]
Screenshot_20260709_050233_Firefox.jpg
Screenshot_20260709_050233_Firefox.jpg (62.25 KiB) 查看 937 次
https://ejsoon.vip/
弈趣極光:享受思維樂趣
头像
ejsoon
一枝独秀
帖子: 6350
注册时间: 2022年 11月 18日 17:36
联系:

Re: 準備為三菱棋做遊戲和MCTS搜尋

帖子 ejsoon »

輸第二盤

代码: 全选

[(0,0),(1,0)] [(0,-2),(0,-1)] [(0,2),(0,1)] [(1,2),(1,3)] [(2,3),(2,2)] [(1,-2),(1,-1)] [(2,1),(1,1)] [(0,4),(1,4)] [(0,6),(0,5)] [(-1,-1),(-1,0)] [(2,4),(2,5)] [(-1,1),(-1,2)] [(2,-3),(1,-3)] [(-1,7),(0,7)] [(1,6),(1,5)] [(2,-1),(2,0)] [(3,2),(3,1)] [(4,1),(4,2)] [(-2,0),(-2,-1)] [(3,4),(3,5)] [(-1,-2),(-2,-2)] [(-2,2),(-2,3)] [(3,-2),(2,-2)] [(1,-5),(1,-4)] [(3,0),(3,-1)] [(1,7),(2,7)] [(0,-3),(-1,-3)] [(2,6),(3,6)] [(4,-2),(4,-1)] [(-1,5),(-1,6)] [(1,8),(1,9)] [(-1,3),(0,3)] [(-1,9),(-1,8)] [(3,3),(4,3)] [(-3,3),(-3,2)] [(2,-5),(2,-4)] [(0,0),(1,0)] [(1,2),(1,1)] [(2,1),(2,2)] [(1,3),(1,4)] [(1,5),(2,5)] [(0,4),(0,3)] [(2,3),(2,4)] [(0,1),(0,2)] [(3,4),(3,5)] [(2,6),(2,7)] [(-1,1),(-1,2)] [(0,5),(0,6)] [(-1,5),(-1,6)] [(1,7),(1,6)] [(0,8),(1,8)] [(3,0),(2,0)] [(-1,7),(0,7)] [(2,-1),(1,-1)] [(3,6),(3,7)] [(4,5),(4,4)] [(1,-3),(1,-2)] [(3,3),(3,2)] [(0,-2),(0,-1)] [(4,1),(3,1)] [(2,8),(3,8)] [(-1,0),(-2,0)] [(1,9),(2,9)] [(4,7),(4,6)] [(1,10),(1,11)] [(4,2),(4,3)] [(-1,3),(-1,4)] [(-2,2),(-2,1)] [(2,-3),(2,-2)] [(3,-4),(2,-4)] [(0,10),(0,11)] [(1,-5),(2,-5)]
Screenshot_20260709_052930_Firefox.png
Screenshot_20260709_052930_Firefox.png (35.62 KiB) 查看 936 次
https://ejsoon.vip/
弈趣極光:享受思維樂趣
头像
ejsoon
一枝独秀
帖子: 6350
注册时间: 2022年 11月 18日 17:36
联系:

Re: 準備為三菱棋做遊戲和MCTS搜尋

帖子 ejsoon »

修復和改進:
一,改進縮放平移

當點擊棋盤,會出現九個縮放平移棋盤的按鈕,點擊這些按鈕,會出現消息框提示。取消顯示這個消息框。

當縮放平移動畫時,要用線性linear的動畫,不要用漸入漸出的動畫。

二,導出和導入棋譜的問題

當導入棋譜時,AI都要切換為人類。

藍色棋子的兩個三角形坐標,應從小到大排序,例如不能出現[(2,-3),(1,-3)],只能是[(1,-3),(2,-3)]。

包括紅色和橙色棋子也一樣,都應是前面的數字小,後面的數字大。如[(-2,0),(-2,-1)]應改為[(-2,-1),(-2,0)],因為0比-1大。

檢查AI在搜尋時,是否已把兩種棋子的表示方式都看成了同一種。

三,當輪到AI下棋時,它的棋子顯示不能點擊的效果

當一方下完,輪到另一方時,如果這一方是AI,則他的棋子應加上disable,並加上opacity:0.5(就跟沒輪到時一樣)。

四,彈窗按鈕的改動

當在第一局時,彈窗按鈕內將出現數字1,當到第二局時將出現數字2。數字以svg-path圖案的形式出現,正好能放進這個按鈕圖案留白的地方。

需要明確給出所要修改的地方和所要替換的代碼。

代码: 全选

修復和改進:
一,改進縮放平移

當點擊棋盤,會出現九個縮放平移棋盤的按鈕,點擊這些按鈕,會出現消息框提示。取消顯示這個消息框。

當縮放平移動畫時,要用線性linear的動畫,不要用漸入漸出的動畫。

二,導出和導入棋譜的問題

當導入棋譜時,AI都要切換為人類。

藍色棋子的兩個三角形坐標,應從小到大排序,例如不能出現[(2,-3),(1,-3)],只能是[(1,-3),(2,-3)]。

包括紅色和橙色棋子也一樣,都應是前面的數字小,後面的數字大。如[(-2,0),(-2,-1)]應改為[(-2,-1),(-2,0)],因為0比-1大。

檢查AI在搜尋時,是否已把兩種棋子的表示方式都看成了同一種。

三,當輪到AI下棋時,它的棋子顯示不能點擊的效果

當一方下完,輪到另一方時,如果這一方是AI,則他的棋子應加上disable,並加上opacity:0.5(就跟沒輪到時一樣)。

四,彈窗按鈕的改動

當在第一局時,彈窗按鈕內將出現數字1,當到第二局時將出現數字2。數字以svg-path圖案的形式出現,正好能放進這個按鈕圖案留白的地方。

需要明確給出所要修改的地方和所要替換的代碼。

gemini 3.1 pro

代码: 全选

這裡為您整理了四個部分的詳細修改方案,請直接將舊代碼替換為下方提供的新代碼。

### 一、 改進縮放平移與提示框

在 `animateTransform` 函數中,目前的數學算法 `(time - startTime) / 200` 其實**已經是純線性的 (linear)** 運算了。我們只需要移除結束時觸發的 `showNotification` 即可。

請找到 `animateTransform` 函數 (約在第 1243 行),將 `step` 函數的結尾修改如下:

```javascript
// 【修改前】
          if (progress < 1) {
            animReqId = requestAnimationFrame(step);
          } else {
            showNotification(
              `縮放矩陣: translate(${panX.toFixed(3)}, ${panY.toFixed(3)}) scale(${currentScale.toFixed(3)})`
            );
          }

// 【修改後】
          if (progress < 1) {
            animReqId = requestAnimationFrame(step);
          }
          // (time - startTime) / 200 本身已是嚴格的線性運算,已移除此處的 showNotification

```

---

### 二、 導出與導入棋譜的坐標排序與 AI 切換

**1. 導入時強制切換為人類**
請找到 `submitImport()` 函數 (約第 1022 行),在取得文字後立即調用 `forceHumanPlayer()`:

```javascript
// 【修改前】
      function submitImport() {
        let txt = document.getElementById('import-text').value.trim();
        if (!txt) return;
        let tokens = txt.split(/\s+/);

// 【修改後】
      function submitImport() {
        let txt = document.getElementById('import-text').value.trim();
        if (!txt) return;
        
        forceHumanPlayer(); // 導入棋譜時,強制切換雙方為人類,並中斷 AI
        
        let tokens = txt.split(/\s+/);

```

**2. 落子時對坐標進行排序**
我們需要在生成 `notation` 之前確保兩個坐標是「從小到大」(先比較 x,再比較 y)。請修改兩處:

第一處:在 `commitMove` 函數中 (約第 905 行):

```javascript
// 【修改前】
        if (result.success) {
          p1CurrentScore = result.scores.p1Score;
          p2CurrentScore = result.scores.p2Score;
        }

        let notation = `[(${t1.idx},${t1.N}),(${t2.idx},${t2.N})]`;

// 【修改後】
        if (result.success) {
          p1CurrentScore = result.scores.p1Score;
          p2CurrentScore = result.scores.p2Score;
        }

        // 確保坐標從小到大排序
        let sortedC1, sortedC2;
        if (t1.idx < t2.idx || (t1.idx === t2.idx && t1.N < t2.N)) {
          sortedC1 = t1; sortedC2 = t2;
        } else {
          sortedC1 = t2; sortedC2 = t1;
        }
        let notation = `[(${sortedC1.idx},${sortedC1.N}),(${sortedC2.idx},${sortedC2.N})]`;

```

第二處:在 `autoPlayFirstBlue` 函數中 (第一手自動落子,約第 1215 行):

```javascript
// 【修改前】
            let result = gameLogic.tryPlacePiece(1, { x: 0, y: 0 }, { x: 1, y: 0 }, true);
            if (result.success) {
              p1CurrentScore = result.scores.p1Score;
              p2CurrentScore = result.scores.p2Score;
            }
            let notation = `[(${t1.idx},${t1.N}),(${t2.idx},${t2.N})]`;

// 【修改後】
            let result = gameLogic.tryPlacePiece(1, { x: 0, y: 0 }, { x: 1, y: 0 }, true);
            if (result.success) {
              p1CurrentScore = result.scores.p1Score;
              p2CurrentScore = result.scores.p2Score;
            }
            let sortedC1, sortedC2;
            if (t1.idx < t2.idx || (t1.idx === t2.idx && t1.N < t2.N)) {
              sortedC1 = t1; sortedC2 = t2;
            } else {
              sortedC1 = t2; sortedC2 = t1;
            }
            let notation = `[(${sortedC1.idx},${sortedC1.N}),(${sortedC2.idx},${sortedC2.N})]`;

```

> **關於 AI 搜索的查驗:**
> 在 `HexGridGame.getValidMoves` 邏輯中,AI 是使用一維 `index` 來遍歷的,並在第 641 行使用了 `if (p1Idx > p2Idx) continue;` 進行去重。這意味著 AI 從最底層運算開始,就**已經將 `[A, B]` 和 `[B, A]` 嚴格視為同一種走法**並主動剪枝,不會發生將其看作兩種情況而浪費算力的問題,您可以放心。

---

### 三、 輪到 AI 下棋時的棋子禁用狀態

請找到 `updateUI` 函數 (約在第 1163 行),將內部渲染棋子區域判斷的 `if` 條件加入「是否為非人類玩家」的判斷:

```javascript
// 【修改前】
        for (let p = 1; p <= 2; p++) {
          for (let c = 0; c < 3; c++) {
            let countEl = document.getElementById(`count-${p}-${c}`);
            if (countEl) countEl.innerText = piecesLeft[p][c];
            let box = document.getElementById(`p${p}-${c}`);
            if (piecesLeft[p][c] === 0 || currentPlayer !== p) {
              box.style.opacity = '0.5';
              box.style.cursor = 'not-allowed';
            } else {
              box.style.opacity = '1';
              box.style.cursor = 'pointer';
            }
          }
        }

// 【修改後】
        for (let p = 1; p <= 2; p++) {
          for (let c = 0; c < 3; c++) {
            let countEl = document.getElementById(`count-${p}-${c}`);
            if (countEl) countEl.innerText = piecesLeft[p][c];
            let box = document.getElementById(`p${p}-${c}`);
            
            // 加入條件:若該玩家是 AI,棋子固定顯示為無法點擊且半透明
            if (piecesLeft[p][c] === 0 || currentPlayer !== p || playerTypes[p] !== 'human') {
              box.style.opacity = '0.5';
              box.style.cursor = 'not-allowed';
            } else {
              box.style.opacity = '1';
              box.style.cursor = 'pointer';
            }
          }
        }

```

---

### 四、 彈窗按鈕動態加入局數數字

為了將動態的 `1` 與 `2` SVG 路徑完美置入 `#restore-go-btn` 按鈕留白處,我們可以在 `updateUI` 的最尾端動態更新它的 `innerHTML`。

請在 `updateUI` 函數的結尾 (大約第 1178 行,`if (typeof updateLayoutForTurn === 'function') ...` 的下方) 加入以下代碼:

```javascript
// 【新增代碼,放在 updateUI() 函數的最後面】

        // 動態更新狀態按鈕的局數圖示
        let restoreBtn = document.getElementById('restore-go-btn');
        if (restoreBtn) {
          if (currentRound === 1) {
            restoreBtn.innerHTML = `
              <path d="M4 4h16v16H4V4zm2 4v10h12V8H6z" fill="#333" />
              <path d="M12 9.5 v5 M10.5 10.5 l1.5 -1" stroke="#333" stroke-width="1.5" fill="none" stroke-linecap="round" />
            `;
          } else {
            restoreBtn.innerHTML = `
              <path d="M4 4h16v16H4V4zm2 4v10h12V8H6z" fill="#333" />
              <path d="M9.5 10.5 c0 -1.5 4 -1.5 4 0 c0 1.5 -4 2 -4 4 h4" stroke="#333" stroke-width="1.5" fill="none" stroke-linecap="round" stroke-linejoin="round"/>
            `;
          }
        }

```
https://ejsoon.vip/
弈趣極光:享受思維樂趣
头像
ejsoon
一枝独秀
帖子: 6350
注册时间: 2022年 11月 18日 17:36
联系:

Re: 準備為三菱棋做遊戲和MCTS搜尋

帖子 ejsoon »

點擊下載功能的改進

代码: 全选

點擊下載功能的改進
當點擊「下載」時,程式會直接下載當前的svg棋盤。現在改為彈出一個全屏窗口,其具體要求描述如下:

一,窗口右上角有叉形關閉按鈕,其內部有svg顯示區,下方選項區,以及下載按鈕。所有的元素都要展示出來。

二,svg顯示區將等比例顯示所要下載的svg圖像文件,其內容為svg棋盤的clone。

三,選項區有「是否顯示網格,是否顯示數字,是否下載動畫」,做成現代美觀的切換按鈕,其左方文字為「網格、數字、動畫」。把三者排在一行,平分間隔。

四,網格默認是勾選的,當取消時,將把clone中的網格去掉。

五,數字默認不勾選,當勾選時,將在ejdrop內的最後加上text標籤:

<text x="棋子横坐標" y="棋子縱坐標" stroke="none" fill="black" font-size="24" font-family="ZT Nature">棋子次序</text>

按clone中每個use棋子的次序添加對應的數字,在代碼中寫明數字的横坐標和縱坐標相對於棋子中心坐標的偏移量,並分為一位數和兩位數,使我在測試中可以修改。

之後在clone的.etboard中添加:

```
<style>
@font-face {
    font-family: 'ZT Nature';
    src: url(...) format('woff2');
    font-weight: normal;
    font-style: normal;
    font-display: swap;
}
</style>
```

我會自行補充url的內容。

六,當動畫勾選時,數字將不能勾選,二者相斥。

動畫也會在clone的.etboard中添加同樣的style。

動畫的步驟為:
https://ejsoon.vip/
弈趣極光:享受思維樂趣
头像
ejsoon
一枝独秀
帖子: 6350
注册时间: 2022年 11月 18日 17:36
联系:

Re: 準備為三菱棋做遊戲和MCTS搜尋

帖子 ejsoon »

中英文切換要高亮
https://ejsoon.vip/
弈趣極光:享受思維樂趣
头像
ejsoon
一枝独秀
帖子: 6350
注册时间: 2022年 11月 18日 17:36
联系:

Re: 準備為三菱棋做遊戲和MCTS搜尋

帖子 ejsoon »

為什麼增加了「搜完就停」的功能,到最後一步還是要搜完36秒?
https://ejsoon.vip/
弈趣極光:享受思維樂趣
头像
ejsoon
一枝独秀
帖子: 6350
注册时间: 2022年 11月 18日 17:36
联系:

Re: 準備為三菱棋做遊戲和MCTS搜尋

帖子 ejsoon »

批次處理大小 (Batch Size)

作用: 目前你的程式碼中寫死了 runMCTSBatch 一次跑 20 或 30 次。

參數化設定: 允許使用者或系統根據裝置效能調整此數值。

調節效果: 雖然不直接影響棋力上限,但能大幅改善 UI 的流暢度與搜尋效率。較高的批次值能減少讓出主執行緒 (UI 更新) 的次數,在相同時間內跑出更多迭代。

打開狀態窗口,顯示第幾回合。
https://ejsoon.vip/
弈趣極光:享受思維樂趣
回复
  • 相似主题
    回复总数
    阅读次数
    最新帖子

在线用户

正浏览此版面之用户: 没有注册用户 和 24 访客