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