代码: 全选
以下是針對三項需求的明確修改說明與替換代碼。
---
### 一、增加 SVG 圖標
在 `const SVG_ROBOT = ...` 與 `const SVG_CHECK = ...` **之後**加入以下完整定義:
```js
const SVG_ICONS = {
human: '<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"/>',
easy: '<path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm-1 14h-2v-2h2v2zm0-4h-2V7h2v5z" fill="currentColor"/>', // 簡化問號風格,代表簡單
hard: '<path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm1 15h-2v-2h2v2zm0-4h-2V7h2v6z" fill="currentColor"/>', // 感嘆號風格
expert: '<path d="M12 17.27L18.18 21l-1.64-7.03L22 9.24l-7.19-.61L12 2 9.19 8.63 2 9.24l5.46 4.73L5.82 21z" fill="currentColor"/>', // 星星
custom: '<path d="M19.14 12.94c.04-.3.06-.61.06-.94 0-.32-.02-.64-.06-.94l2.03-1.58a.49.49 0 0 0 .12-.61l-1.92-3.32a.49.49 0 0 0-.59-.22l-2.39.96c-.5-.38-1.03-.7-1.62-.94L14.4 2.81a.49.49 0 0 0-.48-.41h-3.84a.49.49 0 0 0-.47.41l-.36 2.54c-.59.24-1.13.57-1.62.94l-2.39-.96a.49.49 0 0 0-.59.22L2.74 8.87a.49.49 0 0 0 .12.61l2.03 1.58c-.04.3-.06.62-.06.94s.02.64.06.94l-2.03 1.58a.49.49 0 0 0-.12.61l1.92 3.32c.12.22.37.29.59.22l2.39-.96c.5.38 1.03.7 1.62.94l.36 2.54c.05.24.24.41.48.41h3.84c.24 0 .44-.17.47-.41l.36-2.54c.59-.24 1.13-.56 1.62-.94l2.39.96c.22.08.47 0 .59-.22l1.92-3.32a.49.49 0 0 0-.12-.61l-2.03-1.58zM12 15.6A3.6 3.6 0 1 1 12 8.4a3.6 3.6 0 0 1 0 7.2z" fill="currentColor"/>'
};
// 方便取得圖標的輔助函式
function getAIIconSvg(type) {
const path = SVG_ICONS[type] || SVG_ICONS.easy;
return `<svg viewBox="0 0 24 24" width="22" height="22">${path}</svg>`;
}
```
(可依喜好微調 path,以上已足夠區分)
---
### 二、更改 AI 設定窗口
#### 2.1 新增 CSS(放在 `<style>` 最後、`</style>` 前)
```css
/* AI 設定選項列表 */
.ai-option-list {
display: flex;
flex-direction: column;
gap: 8px;
margin-top: 6px;
}
.ai-option {
display: flex;
align-items: center;
gap: 10px;
padding: 8px 12px;
border: 2px solid transparent;
border-radius: 6px;
cursor: pointer;
background: rgba(255,255,255,0.05);
transition: 0.15s;
color: #ddd;
}
.ai-option:hover {
background: rgba(255,255,255,0.1);
}
.ai-option.selected {
border-color: #7289da;
background: rgba(114, 137, 218, 0.2);
color: white;
}
.ai-option svg {
flex-shrink: 0;
fill: currentColor;
}
```
#### 2.2 替換 HTML(`#ai-settings-dialog` 內的 `.ai-settings-grid` 整段)
把原本兩個 `<label> + <select>` 的區塊整段替換成:
```html
<div class="ai-settings-grid">
<div>
<div id="ai-label-p2-text" style="margin-bottom:6px;color:#ccc;font-size:0.9em;">後手方 (P2)</div>
<div class="ai-option-list" id="ai-p2-options">
<!-- 由 JS 動態生成 -->
</div>
</div>
<div>
<div id="ai-label-p1-text" style="margin-bottom:6px;color:#ccc;font-size:0.9em;">先手方 (P1)</div>
<div class="ai-option-list" id="ai-p1-options">
<!-- 由 JS 動態生成 -->
</div>
</div>
<!-- 原本的限時與 Minimax 參數區塊保持不變 -->
<div class="ai-param-row">
<div class="ai-param-title" id="ai-param-title-time">限時 (秒)</div>
<div class="ai-param-values">
<input type="number" id="ai-time-2" value="3" step="1" oninput="handleAIParamInput(2)" />
<input type="number" id="ai-time-1" value="18" step="1" oninput="handleAIParamInput(1)" />
</div>
</div>
<div class="ai-param-row">
<div class="ai-param-title" id="ai-param-title-n">Minimax 倒數回合</div>
<div class="ai-param-values">
<input type="number" id="ai-n-2" value="3" step="1" oninput="handleAIParamInput(2)" />
<input type="number" id="ai-n-1" value="6" step="1" oninput="handleAIParamInput(1)" />
</div>
</div>
</div>
```
#### 2.3 新增/替換相關 JS 函式
把原本的 `updateAIDialogLang`、`handleAITypeSelect` 等相關函式替換/新增如下:
```js
const AI_TYPE_ORDER = ['human', 'easy', 'hard', 'expert', 'custom'];
function buildAIOptionList(player) {
const container = document.getElementById(`ai-p${player}-options`);
if (!container) return;
container.innerHTML = '';
AI_TYPE_ORDER.forEach(type => {
const div = document.createElement('div');
div.className = 'ai-option';
div.dataset.type = type;
div.innerHTML = `${getAIIconSvg(type)}<span>${AI_TYPE_LABELS[type][currentLang]}</span>`;
div.onclick = () => selectAIOption(player, type);
container.appendChild(div);
});
// 標記目前選中
const currentType = document.getElementById(`ai-p${player}-type`)
? document.getElementById(`ai-p${player}-type`).value
: (aiConfig.type[player] || 'human');
// 因為我們已移除 select,改用隱藏值或直接從 aiConfig 讀
highlightAIOption(player, aiConfig.type[player] || 'human');
}
function highlightAIOption(player, type) {
const container = document.getElementById(`ai-p${player}-options`);
if (!container) return;
container.querySelectorAll('.ai-option').forEach(el => {
el.classList.toggle('selected', el.dataset.type === type);
});
}
function selectAIOption(player, type) {
// 模擬原本 select 的行為
aiConfig.type[player] = type;
highlightAIOption(player, type);
handleAITypeSelect(player); // 重用原本邏輯帶入預設參數
}
// 覆寫原本的 updateAIDialogLang
function updateAIDialogLang() {
// 重建選項列表(文字會隨語言更新)
buildAIOptionList(1);
buildAIOptionList(2);
}
// 修改 handleAITypeSelect,改為接受 type 參數(或保持從全域讀)
function handleAITypeSelect(p) {
const type = aiConfig.type[p] || 'human';
if (type === 'easy' || type === 'hard' || type === 'expert') {
const preset = aiConfig.params[type];
document.getElementById('ai-time-' + p).value = preset.time;
document.getElementById('ai-n-' + p).value = preset.n;
aiConfig.params.custom = { ...CUSTOM_AI_DEFAULT };
} else if (type === 'custom') {
document.getElementById('ai-time-' + p).value = aiConfig.params.custom.time;
document.getElementById('ai-n-' + p).value = aiConfig.params.custom.n;
} else {
// human
aiConfig.params.custom = { ...CUSTOM_AI_DEFAULT };
}
}
// 修改 syncAIDialogFromConfirmed
function syncAIDialogFromConfirmed() {
[1, 2].forEach(p => {
const type = aiConfig.type[p] || 'human';
highlightAIOption(p, type);
document.getElementById('ai-time-' + p).value = aiConfig.settings[p].time;
document.getElementById('ai-n-' + p).value = aiConfig.settings[p].n;
});
}
// 修改 openAISettingsDialog
function openAISettingsDialog() {
buildAIOptionList(1);
buildAIOptionList(2);
syncAIDialogFromConfirmed();
document.getElementById('ai-settings-dialog').style.display = 'flex';
}
// 修改 updateAIConfig(確認時)
function updateAIConfig() {
[1, 2].forEach(p => {
// 從目前 highlight 的選項取得 type
const selected = document.querySelector(`#ai-p${p}-options .ai-option.selected`);
const type = selected ? selected.dataset.type : 'human';
aiConfig.type[p] = type;
aiConfig[p] = type !== 'human';
if (type === 'easy' || type === 'hard' || type === 'expert') {
const preset = aiConfig.params[type];
document.getElementById('ai-time-' + p).value = preset.time;
document.getElementById('ai-n-' + p).value = preset.n;
}
aiConfig.settings[p] = {
time: parseFloat(document.getElementById('ai-time-' + p).value) || CUSTOM_AI_DEFAULT.time,
n: parseInt(document.getElementById('ai-n-' + p).value) || CUSTOM_AI_DEFAULT.n
};
});
updateUI();
checkAndTriggerAI();
}
```
(注意:原本依賴 `<select id="ai-p1-type">` 的地方都要改成從 `aiConfig.type[p]` 或 highlight 狀態讀取。)
---
### 三、更改遊戲界面(updateUI 中按鈕圖標邏輯)
找到被覆寫的 `updateUI` 函式中這一段:
```js
// AI 頭像切換(僅換圖示,不在此處理點擊行為)
if (aiConfig[p]) {
checkBtn.innerHTML = `<svg viewBox="0 0 24 24" class="robot-icon">${SVG_ROBOT}</svg>`;
checkBtn.classList.add('ai-active');
} else {
checkBtn.innerHTML = `<svg viewBox="0 0 24 24">${SVG_CHECK}</svg>`;
checkBtn.classList.remove('ai-active');
}
```
**整段替換成:**
```js
// 決定要顯示的圖標類型
let displayType = 'human';
if (aiConfig[p]) {
// 已是 AI → 顯示對應等級圖標
displayType = aiConfig.type[p] || 'easy';
checkBtn.innerHTML = getAIIconSvg(displayType);
checkBtn.classList.add('ai-active'); // 藍色背景
} else {
// 人類
if (currentPlayer !== p) {
// 非自己的回合 → 顯示「預設/上次使用的 AI 等級」圖標,仍保持綠色可點擊
displayType = (aiConfig.type[p] && aiConfig.type[p] !== 'human') ? aiConfig.type[p] : 'easy';
checkBtn.innerHTML = getAIIconSvg(displayType);
checkBtn.classList.remove('ai-active'); // 保持原本綠色(#43b581)
} else {
// 自己的回合且是人類 → 顯示打勾
checkBtn.innerHTML = `<svg viewBox="0 0 24 24">${SVG_CHECK}</svg>`;
checkBtn.classList.remove('ai-active');
}
}
```
同時,非自己回合的點擊邏輯(原本切換成 AI 的部分)保持幾乎不變,但確保使用正確的 type:
```js
checkBtn.onclick = e => {
if (!aiConfig[p]) {
// 切換成上次使用的 AI 等級(或 easy)
let type = (aiConfig.type[p] && aiConfig.type[p] !== 'human') ? aiConfig.type[p] : 'easy';
aiConfig.type[p] = type;
aiConfig[p] = true;
// 同步參數
if (type === 'easy' || type === 'hard' || type === 'expert') {
const preset = aiConfig.params[type];
aiConfig.settings[p] = { time: preset.time, n: preset.n };
}
updateUI();
checkAndTriggerAI();
}
};
```
(`crossBtn` 切回人類的邏輯維持原樣即可。)
---
### 補充注意事項
1. 移除所有殘留的 `document.getElementById('ai-p1-type')` / `ai-p2-type` 相關程式碼(因為 select 已不存在)。
2. `AI_TYPE_LABELS` 保持原樣即可,它已包含 human / easy / hard / expert / custom。
3. 若要讓「自訂」圖標更明顯,可再微調 `SVG_ICONS.custom`。
4. 測試重點:
- 設定窗口垂直選項 + 邊框高亮
- 非回合人類玩家的綠色「AI 等級圖標」按鈕
- 點擊後變藍色並顯示正確等級圖標
- 輪到 AI 時正確顯示對應等級圖標
完成以上修改後,三項需求即可全部實現。代码: 全选
這是一份明確的修改指南,透過四個區塊的替換與新增,來實現你的要求:
### 1. 修改 CSS 樣式 (替換下拉選單與新增選項樣式)
**請找到這段 CSS:**
```css
.ai-settings-grid select,
.ai-settings-grid input {
margin-top: 5px;
padding: 8px;
border-radius: 4px;
border: 1px solid #7289da;
background: #23272a;
color: white;
font-size: 1em;
}
```
**替換為:**
```css
.ai-settings-grid input {
margin-top: 5px;
padding: 8px;
border-radius: 4px;
border: 1px solid #7289da;
background: #23272a;
color: white;
font-size: 1em;
}
.ai-type-list {
display: flex;
flex-direction: column;
gap: 8px;
margin-top: 8px;
}
.ai-type-option {
display: flex;
align-items: center;
gap: 10px;
padding: 6px 10px;
border: 2px solid transparent;
border-radius: 6px;
background: rgba(255, 255, 255, 0.05);
cursor: pointer;
transition: 0.2s;
color: white;
font-size: 0.95em;
}
.ai-type-option:hover {
background: rgba(255, 255, 255, 0.1);
}
.ai-type-option.selected {
border-color: #7289da;
background: rgba(114, 137, 218, 0.15);
}
.ai-type-option svg {
width: 20px;
height: 20px;
fill: currentColor;
}
```
---
### 2. 更新 AI 設定視窗 HTML 結構
**請找到以下 `id="ai-settings-dialog"` 中的這段代碼:**
```html
<label
><span id="ai-label-p2-text">後手方 (P2)</span>
<select id="ai-p2-type" onchange="handleAITypeSelect(2)">
<option value="human">人類</option>
<option value="easy" selected>簡單AI</option>
<option value="hard">困難AI</option>
<option value="expert">專家AI</option>
<option value="custom">自訂AI</option>
</select>
</label>
<label
><span id="ai-label-p1-text">先手方 (P1)</span>
<select id="ai-p1-type" onchange="handleAITypeSelect(1)">
<option value="human" selected>人類</option>
<option value="easy">簡單AI</option>
<option value="hard">困難AI</option>
<option value="expert">專家AI</option>
<option value="custom">自訂AI</option>
</select>
</label>
```
**替換為:**
```html
<div class="ai-col">
<span id="ai-label-p2-text" style="color: #ccc; font-size: 0.9em; display: block;">後手方 (P2)</span>
<input type="hidden" id="ai-p2-type" value="easy">
<div class="ai-type-list" id="ai-list-2">
<div class="ai-type-option" data-val="human" onclick="selectAITypeOption(2, 'human')"><svg viewBox="0 0 24 24"></svg><span class="opt-label">人類</span></div>
<div class="ai-type-option" data-val="easy" onclick="selectAITypeOption(2, 'easy')"><svg viewBox="0 0 24 24"></svg><span class="opt-label">簡單AI</span></div>
<div class="ai-type-option" data-val="hard" onclick="selectAITypeOption(2, 'hard')"><svg viewBox="0 0 24 24"></svg><span class="opt-label">困難AI</span></div>
<div class="ai-type-option" data-val="expert" onclick="selectAITypeOption(2, 'expert')"><svg viewBox="0 0 24 24"></svg><span class="opt-label">專家AI</span></div>
<div class="ai-type-option" data-val="custom" onclick="selectAITypeOption(2, 'custom')"><svg viewBox="0 0 24 24"></svg><span class="opt-label">自訂AI</span></div>
</div>
</div>
<div class="ai-col">
<span id="ai-label-p1-text" style="color: #ccc; font-size: 0.9em; display: block;">先手方 (P1)</span>
<input type="hidden" id="ai-p1-type" value="human">
<div class="ai-type-list" id="ai-list-1">
<div class="ai-type-option" data-val="human" onclick="selectAITypeOption(1, 'human')"><svg viewBox="0 0 24 24"></svg><span class="opt-label">人類</span></div>
<div class="ai-type-option" data-val="easy" onclick="selectAITypeOption(1, 'easy')"><svg viewBox="0 0 24 24"></svg><span class="opt-label">簡單AI</span></div>
<div class="ai-type-option" data-val="hard" onclick="selectAITypeOption(1, 'hard')"><svg viewBox="0 0 24 24"></svg><span class="opt-label">困難AI</span></div>
<div class="ai-type-option" data-val="expert" onclick="selectAITypeOption(1, 'expert')"><svg viewBox="0 0 24 24"></svg><span class="opt-label">專家AI</span></div>
<div class="ai-type-option" data-val="custom" onclick="selectAITypeOption(1, 'custom')"><svg viewBox="0 0 24 24"></svg><span class="opt-label">自訂AI</span></div>
</div>
</div>
```
---
### 3. 修改 JS 中 AI 常數、狀態與設定更新邏輯
**請找到這段:**
```javascript
function updateAIDialogLang() {
['ai-p1-type', 'ai-p2-type'].forEach(selId => {
const sel = document.getElementById(selId);
if (!sel) return;
Array.from(sel.options).forEach(opt => {
const labels = AI_TYPE_LABELS[opt.value];
if (labels) opt.textContent = labels[currentLang];
});
});
}
```
**以及它下方一路到宣告 `const SVG_ROBOT = ...` 的這幾行,整塊替換為:**
```javascript
// 全新 AI 圖標定義
const AI_ICONS = {
human: '<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"/>',
easy: '<path d="M12 2a2 2 0 0 1 2 2c0 .74-.4 1.39-1 1.73V7h1a3 3 0 0 1 3 3v2h2v4h-2v2a3 3 0 0 1-3 3H9a3 3 0 0 1-3-3v-2H4v-4h2v-2a3 3 0 0 1 3-3h1V5.73c-.6-.34-1-.99-1-1.73a2 2 0 0 1 2-2zM9 11a1 1 0 1 0 0 2 1 1 0 0 0 0-2zm6 0a1 1 0 1 0 0 2 1 1 0 0 0 0-2z"/>',
hard: '<path d="M12,2A2,2 0 0,1 14,4C14,4.74 13.6,1.39 13,1.73V7H15A3,3 0 0,1 18,10V12H20V16H18V18A3,3 0 0,1 15,21H9A3,3 0 0,1 6,18V16H4V12H6V10A3,3 0 0,1 9,7H11V5.73C10.4,5.39 10,4.74 10,4A2,2 0 0,1 12,2M9.5,11L8,13H11L9.5,15V11M14.5,11L13,13H16L14.5,15V11Z"/>',
expert: '<path d="M12,3C7.58,3 4,4.79 4,7C4,9.21 7.58,11 12,11C16.42,11 20,9.21 20,7C20,4.79 16.42,3 12,3M4,9V12C4,14.21 7.58,16 12,16C16.42,16 20,14.21 20,12V9C20,11.21 16.42,13 12,13C7.58,13 4,11.21 4,9M4,14V17C4,19.21 7.58,21 12,21C16.42,21 20,19.21 20,17V14C20,16.21 16.42,18 12,18C7.58,18 4,16.21 4,14Z"/>',
custom: '<path d="M19.14,12.94c0.04-0.3,0.06-0.61,0.06-0.94c0-0.32-0.02-0.64-0.06-0.94l2.03-1.58c0.18-0.14,0.23-0.41,0.12-0.61 l-1.92-3.32c-0.12-0.22-0.37-0.29-0.59-0.22l-2.39,0.96c-0.5-0.38-1.03-0.7-1.62-0.94L14.4,2.81c-0.04-0.24-0.24-0.41-0.48-0.41 h-3.84c-0.24,0-0.43,0.17-0.47,0.41L9.25,5.35C8.66,5.59,8.12,5.92,7.63,6.29L5.24,5.33c-0.22-0.08-0.47,0-0.59,0.22L2.73,8.87 C2.62,9.08,2.66,9.34,2.86,9.48l2.03,1.58C4.84,11.36,4.8,11.69,4.8,12s0.02,0.64,0.06,0.94l-2.03,1.58 c-0.18,0.14-0.23,0.41-0.12,0.61l1.92,3.32c0.12,0.22,0.37,0.29,0.59,0.22l2.39-0.96c0.5,0.38,1.03,0.7,1.62,0.94l0.36,2.54 c0.05,0.24,0.24,0.41,0.48,0.41h3.84c0.24,0,0.44-0.17,0.47-0.41l0.36-2.54c0.59-0.24,1.13-0.56,1.62-0.94l2.39,0.96 c0.22,0.08,0.47,0,0.59-0.22l1.92-3.32c0.12-0.22,0.07-0.49-0.12-0.61L19.14,12.94z M12,15.6c-1.98,0-3.6-1.62-3.6-3.6 s1.62-3.6,3.6-3.6s3.6,1.62,3.6,3.6S13.98,15.6,12,15.6z"/>'
};
// 初始化所有的選項 SVG
document.querySelectorAll('.ai-type-option').forEach(el => {
let val = el.getAttribute('data-val');
if (AI_ICONS[val]) {
el.querySelector('svg').innerHTML = AI_ICONS[val];
}
});
function updateAIDialogLang() {
[1, 2].forEach(p => {
document.querySelectorAll(`#ai-list-${p} .ai-type-option`).forEach(opt => {
const val = opt.getAttribute('data-val');
const labels = AI_TYPE_LABELS[val];
if (labels) opt.querySelector('.opt-label').textContent = labels[currentLang];
});
});
}
let autoRotate = false;
let currentLang = 'zh';
let N_PIECES = 9;
let SCORING_MODE = 0;
let completedGamesNotation = '';
let arbEnded = false;
let gameHistory = [];
let historyIndex = -1;
let msgTimeout;
let gameNumber = 1;
let currentDialogMode = 'playing';
let totalScores = { 1: 0, 2: 0 };
let arbValidMoves = [];
let arbCurrentIndex = 0;
const MCTS_C = 1.414;
const CUSTOM_AI_DEFAULT = { time: 5, n: 4 };
let aiConfig = {
1: false,
2: true,
type: { 1: 'human', 2: 'easy' },
lastAIType: { 1: 'easy', 2: 'easy' }, // 新增:記憶上一次/預設使用的 AI 級別
params: {
easy: { time: 3, n: 3 },
hard: { time: 7, n: 4 },
expert: { time: 12, n: 5 },
custom: { ...CUSTOM_AI_DEFAULT }
},
settings: {
1: { time: 18, n: 6 },
2: { time: 3, n: 3 }
}
};
let aiThinking = false;
let cancelAi = false;
let lastAIYieldTime = 0;
const SVG_CHECK = '<path d="M9 16.17L4.83 12l-1.42 1.41L9 19 21 7l-1.41-1.41z" />';
```
---
### 4. 變更 AI 屬性選取與更新函式 (覆寫舊函式)
**請找到包含 `function updateAIConfig()`、`function handleAIParamInput(p)`、`function handleAITypeSelect(p)` 和 `function syncAIDialogFromConfirmed()` 的這段,將這四個函式完全替換為:**
```javascript
function selectAITypeOption(p, type) {
document.getElementById(`ai-p${p}-type`).value = type;
// 視覺化更新被選中的邊框
document.querySelectorAll(`#ai-list-${p} .ai-type-option`).forEach(el => {
el.classList.toggle('selected', el.getAttribute('data-val') === type);
});
handleAITypeSelect(p);
}
function updateAIConfig() {
[1, 2].forEach(p => {
let type = document.getElementById('ai-p' + p + '-type').value;
aiConfig.type[p] = type;
aiConfig[p] = type !== 'human';
if (type !== 'human') {
aiConfig.lastAIType[p] = type;
}
if (type === 'easy' || type === 'hard' || type === 'expert') {
let preset = aiConfig.params[type];
document.getElementById('ai-time-' + p).value = preset.time;
document.getElementById('ai-n-' + p).value = preset.n;
}
aiConfig.settings[p] = {
time: parseFloat(document.getElementById('ai-time-' + p).value) || CUSTOM_AI_DEFAULT.time,
n: parseInt(document.getElementById('ai-n-' + p).value) || CUSTOM_AI_DEFAULT.n
};
});
updateUI();
checkAndTriggerAI();
}
function detectAIPresetType(p) {
let time = parseFloat(document.getElementById('ai-time-' + p).value);
let n = parseInt(document.getElementById('ai-n-' + p).value);
for (const key of ['easy', 'hard', 'expert']) {
let preset = aiConfig.params[key];
if (time === preset.time && n === preset.n) return key;
}
return 'custom';
}
function handleAIParamInput(p) {
let detected = detectAIPresetType(p);
let hiddenInput = document.getElementById('ai-p' + p + '-type');
if (hiddenInput.value !== detected) {
hiddenInput.value = detected;
document.querySelectorAll(`#ai-list-${p} .ai-type-option`).forEach(el => {
el.classList.toggle('selected', el.getAttribute('data-val') === detected);
});
}
if (detected !== 'custom') {
aiConfig.params.custom = { ...CUSTOM_AI_DEFAULT };
}
}
function handleAITypeSelect(p) {
let type = document.getElementById('ai-p' + p + '-type').value;
if (type === 'easy' || type === 'hard' || type === 'expert') {
let preset = aiConfig.params[type];
document.getElementById('ai-time-' + p).value = preset.time;
document.getElementById('ai-n-' + p).value = preset.n;
aiConfig.params.custom = { ...CUSTOM_AI_DEFAULT };
} else if (type === 'custom') {
document.getElementById('ai-time-' + p).value = aiConfig.params.custom.time;
document.getElementById('ai-n-' + p).value = aiConfig.params.custom.n;
} else {
aiConfig.params.custom = { ...CUSTOM_AI_DEFAULT };
}
}
function syncAIDialogFromConfirmed() {
[1, 2].forEach(p => {
let val = aiConfig.type[p] || 'human';
document.getElementById('ai-p' + p + '-type').value = val;
document.querySelectorAll(`#ai-list-${p} .ai-type-option`).forEach(el => {
el.classList.toggle('selected', el.getAttribute('data-val') === val);
});
document.getElementById('ai-time-' + p).value = aiConfig.settings[p].time;
document.getElementById('ai-n-' + p).value = aiConfig.settings[p].n;
});
}
```
---
### 5. 遊戲介面上的圖標替換邏輯 (覆寫 updateUI 行為)
**請找到底下這兩個原本覆寫 `updateUI` 以及 `handleTurnTrackerClick` 的段落:**
```javascript
// 覆寫 updateUI 支援 AI 按鈕切換
const originalUpdateUI = updateUI;
updateUI = function () {
// ... 原本的整個 updateUI = function () { ... } 以及 handleTurnTrackerClick
}
// 點擊行動方的 turn-tracker 圖案,將該方切換成 AI
function handleTurnTrackerClick(p) {
// ...
}
```
**將那兩段整個替換為以下新版邏輯:**
```javascript
// 覆寫 updateUI 支援新的動態圖標與人類/AI互換機制
const originalUpdateUI = updateUI;
updateUI = function () {
originalUpdateUI();
if (currentDialogMode === 'arbitration') return;
[1, 2].forEach(p => {
const checkBtn = document.getElementById('btn-check-' + p);
const crossBtn = document.getElementById('btn-cross-' + p);
if (!checkBtn || !crossBtn) return;
let targetAI = aiConfig.lastAIType[p] || 'easy';
// 視覺圖標顯示判斷
if (aiConfig[p]) {
// 是 AI 時,顯示所選 AI 圖標,底色為藍色 (藉由 ai-active 類別)
checkBtn.innerHTML = `<svg viewBox="0 0 24 24" class="robot-icon">${AI_ICONS[aiConfig.type[p]]}</svg>`;
checkBtn.classList.add('ai-active');
} else {
if (currentPlayer !== p) {
// 人類方、非自己回合時,顯示最後選擇的(或預設的) AI 圖標,綠色底色
checkBtn.innerHTML = `<svg viewBox="0 0 24 24" class="robot-icon">${AI_ICONS[targetAI]}</svg>`;
checkBtn.classList.remove('ai-active');
} else {
// 輪到該人類方時,恢復普通的打勾圖標
checkBtn.innerHTML = `<svg viewBox="0 0 24 24">${SVG_CHECK}</svg>`;
checkBtn.classList.remove('ai-active');
}
}
// 行為與禁用狀態判斷
if (currentPlayer !== p) {
if (aiConfig[p]) {
checkBtn.classList.add('disabled');
crossBtn.classList.remove('disabled');
} else {
checkBtn.classList.remove('disabled');
crossBtn.classList.add('disabled');
}
checkBtn.onclick = e => {
if (!aiConfig[p]) {
document.getElementById(`ai-p${p}-type`).value = targetAI;
updateAIConfig();
}
};
crossBtn.onclick = e => {
if (aiConfig[p]) {
aiConfig[p] = false;
document.getElementById(`ai-p${p}-type`).value = 'human';
updateAIConfig();
}
};
} else {
checkBtn.onclick = aiConfig[p] ? null : () => actionCheck(checkBtn);
crossBtn.onclick = () => actionCross(crossBtn);
if (aiConfig[p] && aiThinking) {
crossBtn.classList.remove('disabled');
}
}
});
};
function handleTurnTrackerClick(p) {
if (currentDialogMode !== 'playing') return;
if (currentPlayer !== p) return;
if (aiThinking || aiConfig[p]) return;
let type = aiConfig.lastAIType[p] || 'easy';
document.getElementById(`ai-p${p}-type`).value = type;
updateAIConfig();
}
```