game/js/engine.js

96 lines
2.8 KiB
JavaScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*
* 五子棋规则引擎(纯逻辑,无 DOM)
* 浏览器中挂到 window.GomokuEngine,Node 中通过 module.exports 导出供测试使用。
* 规则:无禁手自由规则,任意方向连成五子及以上即胜。
*/
(function (root, factory) {
const api = factory();
if (typeof module !== 'undefined' && module.exports) {
module.exports = api;
} else {
root.GomokuEngine = api;
}
})(typeof window !== 'undefined' ? window : globalThis, function () {
const SIZE = 15;
const EMPTY = 0;
const BLACK = 1;
const WHITE = 2;
// 横、竖、两条斜线
const DIRECTIONS = [[1, 0], [0, 1], [1, 1], [1, -1]];
function inBounds(x, y) {
return x >= 0 && x < SIZE && y >= 0 && y < SIZE;
}
function createGame() {
return {
size: SIZE,
board: Array.from({ length: SIZE }, () => new Array(SIZE).fill(EMPTY)),
currentPlayer: BLACK, // 黑先
winner: 0, // 1 黑胜,2 白胜,0 未分
draw: false,
gameOver: false,
winningLine: null, // 获胜连线的坐标数组 [{x, y}, ...]
moveHistory: [], // {x, y, player},为悔棋功能预留
};
}
// 从 (x, y) 出发沿 4 个方向数连子,返回获胜连线(≥5 个)或 null
function checkWinAt(game, x, y) {
const player = game.board[y][x];
if (player === EMPTY) return null;
for (const [dx, dy] of DIRECTIONS) {
const line = [{ x, y }];
for (let nx = x + dx, ny = y + dy; inBounds(nx, ny) && game.board[ny][nx] === player; nx += dx, ny += dy) {
line.push({ x: nx, y: ny });
}
for (let nx = x - dx, ny = y - dy; inBounds(nx, ny) && game.board[ny][nx] === player; nx -= dx, ny -= dy) {
line.unshift({ x: nx, y: ny });
}
if (line.length >= 5) return line;
}
return null;
}
function isBoardFull(game) {
return game.board.every(row => row.every(c => c !== EMPTY));
}
function place(game, x, y) {
if (game.gameOver) return { ok: false, reason: '游戏已结束' };
if (!inBounds(x, y)) return { ok: false, reason: '超出棋盘范围' };
if (game.board[y][x] !== EMPTY) return { ok: false, reason: '该位置已有棋子' };
game.board[y][x] = game.currentPlayer;
game.moveHistory.push({ x, y, player: game.currentPlayer });
const line = checkWinAt(game, x, y);
if (line) {
game.winner = game.currentPlayer;
game.winningLine = line;
game.gameOver = true;
return { ok: true, win: true };
}
if (isBoardFull(game)) {
game.draw = true;
game.gameOver = true;
return { ok: true, draw: true };
}
game.currentPlayer = game.currentPlayer === BLACK ? WHITE : BLACK;
return { ok: true };
}
return {
SIZE,
EMPTY,
BLACK,
WHITE,
createGame,
place,
checkWinAt,
inBounds,
};
});