/** * 纯规则模块:坐标换算、落子合法性、胜负判定。 * 不依赖任何 I/O,便于单独推演和测试。 */ import { BOARD_SIZE, CELL_COUNT, WIN_COUNT, type Cell, type Player } from '../shared/protocol.js' export function idx(x: number, y: number): number { return y * BOARD_SIZE + x } export function inBounds(x: number, y: number): boolean { return x >= 0 && x < BOARD_SIZE && y >= 0 && y < BOARD_SIZE } export function createBoard(): Cell[] { return new Array(CELL_COUNT).fill(0) } /** * 以最后落子点为中心,向横、竖、两条斜线四个方向统计同色连子。 * 返回该方向上的完整连子下标数组(长度 >= 5 即为获胜),否则返回 null。 */ export function findWinLine(board: Cell[], x: number, y: number, player: Player): number[] | null { const dirs: Array<[number, number]> = [ [1, 0], [0, 1], [1, 1], [1, -1], ] for (const [dx, dy] of dirs) { const line: number[] = [idx(x, y)] // 正方向延伸 for (let cx = x + dx, cy = y + dy; inBounds(cx, cy) && board[idx(cx, cy)] === player; cx += dx, cy += dy) { line.push(idx(cx, cy)) } // 反方向延伸,插到数组头部以保持连线有序 for (let cx = x - dx, cy = y - dy; inBounds(cx, cy) && board[idx(cx, cy)] === player; cx -= dx, cy -= dy) { line.unshift(idx(cx, cy)) } if (line.length >= WIN_COUNT) return line } return null } export function isEmptyCell(board: Cell[], x: number, y: number): boolean { return inBounds(x, y) && board[idx(x, y)] === 0 } export function opponent(p: Player): Player { return p === 1 ? 2 : 1 }