2026-04-04 21:53:37 +08:00
|
|
|
|
import parts from './parts.csv';
|
|
|
|
|
|
import {createRegion, moveToRegion, Region} from "@/core/region";
|
|
|
|
|
|
import {createPartsFromTable} from "@/core/part-factory";
|
|
|
|
|
|
import {Part} from "@/core/part";
|
2026-04-06 15:47:33 +08:00
|
|
|
|
import {createPromptDef, IGameContext} from "@/core/game";
|
2026-04-04 21:53:37 +08:00
|
|
|
|
|
|
|
|
|
|
export const BOARD_SIZE = 6;
|
|
|
|
|
|
export const MAX_PIECES_PER_PLAYER = 8;
|
|
|
|
|
|
export const WIN_LENGTH = 3;
|
|
|
|
|
|
|
|
|
|
|
|
export type PlayerType = 'white' | 'black';
|
|
|
|
|
|
export type PieceType = 'kitten' | 'cat';
|
|
|
|
|
|
export type WinnerType = PlayerType | 'draw' | null;
|
|
|
|
|
|
export type RegionType = 'white' | 'black' | 'board' | '';
|
|
|
|
|
|
export type BoopPartMeta = { player: PlayerType; type: PieceType };
|
|
|
|
|
|
export type BoopPart = Part<BoopPartMeta>;
|
2026-04-06 15:47:33 +08:00
|
|
|
|
export const prompts = {
|
|
|
|
|
|
play: createPromptDef<[PlayerType, number, number, PieceType?]>(
|
|
|
|
|
|
'play <player> <row:number> <col:number> [type:string]'),
|
|
|
|
|
|
choose: createPromptDef<[PlayerType, number, number]>(
|
|
|
|
|
|
'choose <player> <row:number> <col:number>')
|
|
|
|
|
|
}
|
2026-04-04 21:53:37 +08:00
|
|
|
|
|
|
|
|
|
|
export function createInitialState() {
|
|
|
|
|
|
const pieces = createPartsFromTable(
|
|
|
|
|
|
parts,
|
|
|
|
|
|
(item, index) => `${item.player}-${item.type}-${index + 1}`,
|
|
|
|
|
|
(item) => item.count
|
|
|
|
|
|
) as Record<string, BoopPart>;
|
|
|
|
|
|
|
|
|
|
|
|
// Initialize region childIds
|
|
|
|
|
|
const whiteRegion = createRegion('white', []);
|
|
|
|
|
|
const blackRegion = createRegion('black', []);
|
|
|
|
|
|
const boardRegion = createRegion('board', [
|
|
|
|
|
|
{ name: 'x', min: 0, max: BOARD_SIZE - 1 },
|
|
|
|
|
|
{ name: 'y', min: 0, max: BOARD_SIZE - 1 },
|
|
|
|
|
|
]);
|
|
|
|
|
|
|
|
|
|
|
|
// Populate region childIds based on piece regionId
|
|
|
|
|
|
for (const part of Object.values(pieces)) {
|
|
|
|
|
|
if(part.type !== 'kitten') continue;
|
|
|
|
|
|
if (part.player === 'white' ) {
|
|
|
|
|
|
moveToRegion(part, null, whiteRegion);
|
|
|
|
|
|
} else if (part.player === 'black') {
|
|
|
|
|
|
moveToRegion(part, null, blackRegion);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return {
|
|
|
|
|
|
regions: {
|
|
|
|
|
|
white: whiteRegion,
|
|
|
|
|
|
black: blackRegion,
|
|
|
|
|
|
board: boardRegion,
|
|
|
|
|
|
} as Record<RegionType, Region>,
|
|
|
|
|
|
pieces,
|
|
|
|
|
|
currentPlayer: 'white' as PlayerType,
|
|
|
|
|
|
winner: null as WinnerType,
|
|
|
|
|
|
};
|
|
|
|
|
|
}
|
|
|
|
|
|
export type BoopState = ReturnType<typeof createInitialState>;
|
|
|
|
|
|
export type BoopGame = IGameContext<BoopState>;
|