/* * 五子棋规则引擎(纯逻辑,无 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, }; });