110 lines
3.2 KiB
JavaScript
110 lines
3.2 KiB
JavaScript
/*
|
||
* 交互控制:点击落子、模式/执子切换、AI 应手、状态栏文案。
|
||
*/
|
||
(function () {
|
||
|
||
function createController(canvas, renderer) {
|
||
const E = window.GomokuEngine;
|
||
const AI = window.GomokuAI;
|
||
|
||
const statusEl = document.getElementById('status');
|
||
const modeSel = document.getElementById('mode');
|
||
const sideSel = document.getElementById('side');
|
||
const sideRow = document.getElementById('side-row');
|
||
const newBtn = document.getElementById('new-game');
|
||
|
||
let game = E.createGame();
|
||
let mode = modeSel.value; // 'ai' | 'pvp'
|
||
let humanColor = Number(sideSel.value);
|
||
let aiThinking = false;
|
||
|
||
function isHumanTurn() {
|
||
return !game.gameOver && (mode === 'pvp' || game.currentPlayer === humanColor);
|
||
}
|
||
|
||
function updateStatus() {
|
||
if (game.gameOver) {
|
||
if (game.draw) {
|
||
statusEl.textContent = '平局,棋盘已下满';
|
||
} else if (mode === 'ai') {
|
||
statusEl.textContent = game.winner === humanColor ? '你赢了!🎉' : 'AI 获胜,再接再厉';
|
||
} else {
|
||
statusEl.textContent = game.winner === E.BLACK ? '黑方获胜!' : '白方获胜!';
|
||
}
|
||
return;
|
||
}
|
||
if (aiThinking) {
|
||
statusEl.textContent = 'AI 思考中…';
|
||
return;
|
||
}
|
||
if (mode === 'ai') {
|
||
statusEl.textContent = game.currentPlayer === humanColor
|
||
? `轮到你落子(${humanColor === E.BLACK ? '黑子' : '白子'})`
|
||
: '轮到 AI 落子';
|
||
} else {
|
||
statusEl.textContent = game.currentPlayer === E.BLACK ? '轮到黑方落子' : '轮到白方落子';
|
||
}
|
||
}
|
||
|
||
function refresh(hover) {
|
||
renderer.render(game, hover || null);
|
||
updateStatus();
|
||
}
|
||
|
||
// 轮到 AI 时延迟 300ms 落子:观感自然,也给界面留出渲染时间
|
||
function maybeAIMove() {
|
||
if (mode !== 'ai' || game.gameOver || game.currentPlayer === humanColor) return;
|
||
aiThinking = true;
|
||
updateStatus();
|
||
setTimeout(() => {
|
||
const mv = AI.nextMove(game, game.currentPlayer);
|
||
E.place(game, mv.x, mv.y);
|
||
aiThinking = false;
|
||
refresh();
|
||
}, 300);
|
||
}
|
||
|
||
canvas.addEventListener('click', (e) => {
|
||
if (!isHumanTurn() || aiThinking) return;
|
||
const pos = renderer.cellFromEvent(e);
|
||
if (!pos) return;
|
||
const r = E.place(game, pos.x, pos.y);
|
||
if (!r.ok) return;
|
||
refresh();
|
||
maybeAIMove();
|
||
});
|
||
|
||
canvas.addEventListener('mousemove', (e) => {
|
||
if (!isHumanTurn() || aiThinking) {
|
||
refresh();
|
||
return;
|
||
}
|
||
refresh(renderer.cellFromEvent(e));
|
||
});
|
||
canvas.addEventListener('mouseleave', () => refresh());
|
||
|
||
function newGame() {
|
||
game = E.createGame();
|
||
aiThinking = false;
|
||
refresh();
|
||
maybeAIMove(); // 人执白时让 AI(黑)先落子
|
||
}
|
||
|
||
modeSel.addEventListener('change', () => {
|
||
mode = modeSel.value;
|
||
sideRow.style.display = mode === 'ai' ? '' : 'none';
|
||
newGame();
|
||
});
|
||
sideSel.addEventListener('change', () => {
|
||
humanColor = Number(sideSel.value);
|
||
newGame();
|
||
});
|
||
newBtn.addEventListener('click', newGame);
|
||
|
||
newGame();
|
||
return { refresh };
|
||
}
|
||
|
||
window.GomokuUI = { createController };
|
||
})();
|