162 lines
3.5 KiB
TypeScript
162 lines
3.5 KiB
TypeScript
/**
|
||
* 井字棋游戏状态扩展
|
||
*/
|
||
|
||
/**
|
||
* 玩家类型
|
||
*/
|
||
export type Player = 'X' | 'O';
|
||
|
||
/**
|
||
* 单元格状态
|
||
*/
|
||
export interface CellState {
|
||
/** 单元格 ID(如 A1, B2, C3) */
|
||
id: string;
|
||
/** 行索引 (0-2) */
|
||
row: number;
|
||
/** 列索引 (0-2) */
|
||
col: number;
|
||
/** 当前玩家标记,null 表示空 */
|
||
player: Player | null;
|
||
}
|
||
|
||
/**
|
||
* 井字棋游戏元数据
|
||
*/
|
||
export interface TicTacToeMetadata {
|
||
/** 当前玩家 */
|
||
currentPlayer: Player;
|
||
/** 游戏是否结束 */
|
||
gameEnded: boolean;
|
||
/** 获胜者,null 表示平局或未结束 */
|
||
winner: Player | null;
|
||
/** 获胜的组合(如果有) */
|
||
winningCombination?: string[];
|
||
/** 游戏历史 */
|
||
moveHistory: MoveRecord[];
|
||
/** 总回合数 */
|
||
totalMoves: number;
|
||
}
|
||
|
||
/**
|
||
* 移动记录
|
||
*/
|
||
export interface MoveRecord {
|
||
/** 移动的玩家 */
|
||
player: Player;
|
||
/** 移动的单元格 ID */
|
||
cellId: string;
|
||
/** 移动时间戳 */
|
||
timestamp: number;
|
||
}
|
||
|
||
/**
|
||
* 获胜组合类型
|
||
*/
|
||
export type WinningLine =
|
||
| { type: 'row'; index: number }
|
||
| { type: 'column'; index: number }
|
||
| { type: 'diagonal'; direction: 'main' | 'anti' };
|
||
|
||
/**
|
||
* 井字棋棋盘配置
|
||
*/
|
||
export interface TicTacToeBoardConfig {
|
||
/** 棋盘大小(默认 3x3) */
|
||
size: number;
|
||
/** 单元格 ID 前缀 */
|
||
cellIdPrefix: string;
|
||
}
|
||
|
||
/**
|
||
* 默认的 3x3 棋盘配置
|
||
*/
|
||
export const DEFAULT_BOARD_CONFIG: TicTacToeBoardConfig = {
|
||
size: 3,
|
||
cellIdPrefix: 'cell',
|
||
};
|
||
|
||
/**
|
||
* 获取单元格 ID
|
||
*/
|
||
export function getCellId(row: number, col: number, prefix: string = 'cell'): string {
|
||
const rowLabel = String.fromCharCode('A'.charCodeAt(0) + row);
|
||
return `${prefix}-${rowLabel}${col + 1}`;
|
||
}
|
||
|
||
/**
|
||
* 解析单元格 ID
|
||
*/
|
||
export function parseCellId(cellId: string): { row: number; col: number } | null {
|
||
const match = cellId.match(/^cell-([A-Z])(\d+)$/);
|
||
if (!match) return null;
|
||
return {
|
||
row: match[1].charCodeAt(0) - 'A'.charCodeAt(0),
|
||
col: parseInt(match[2], 10) - 1,
|
||
};
|
||
}
|
||
|
||
/**
|
||
* 检查是否是有效的单元格 ID
|
||
*/
|
||
export function isValidCellId(cellId: string, size: number = 3): boolean {
|
||
const parsed = parseCellId(cellId);
|
||
if (!parsed) return false;
|
||
return parsed.row >= 0 && parsed.row < size && parsed.col >= 0 && parsed.col < size;
|
||
}
|
||
|
||
/**
|
||
* 生成所有单元格 ID
|
||
*/
|
||
export function getAllCellIds(size: number = 3, prefix: string = 'cell'): string[] {
|
||
const cells: string[] = [];
|
||
for (let row = 0; row < size; row++) {
|
||
for (let col = 0; col < size; col++) {
|
||
cells.push(getCellId(row, col, prefix));
|
||
}
|
||
}
|
||
return cells;
|
||
}
|
||
|
||
/**
|
||
* 获取所有可能的获胜组合
|
||
*/
|
||
export function getWinningCombinations(size: number = 3): string[][] {
|
||
const combinations: string[][] = [];
|
||
|
||
// 行
|
||
for (let row = 0; row < size; row++) {
|
||
const rowCells: string[] = [];
|
||
for (let col = 0; col < size; col++) {
|
||
rowCells.push(getCellId(row, col));
|
||
}
|
||
combinations.push(rowCells);
|
||
}
|
||
|
||
// 列
|
||
for (let col = 0; col < size; col++) {
|
||
const colCells: string[] = [];
|
||
for (let row = 0; row < size; row++) {
|
||
colCells.push(getCellId(row, col));
|
||
}
|
||
combinations.push(colCells);
|
||
}
|
||
|
||
// 主对角线
|
||
const mainDiagonal: string[] = [];
|
||
for (let i = 0; i < size; i++) {
|
||
mainDiagonal.push(getCellId(i, i));
|
||
}
|
||
combinations.push(mainDiagonal);
|
||
|
||
// 反对角线
|
||
const antiDiagonal: string[] = [];
|
||
for (let i = 0; i < size; i++) {
|
||
antiDiagonal.push(getCellId(i, size - 1 - i));
|
||
}
|
||
combinations.push(antiDiagonal);
|
||
|
||
return combinations;
|
||
}
|