fix: api change for bop
This commit is contained in:
parent
61857b8256
commit
de4e83e4ea
|
|
@ -1,14 +1,15 @@
|
||||||
import {
|
import {
|
||||||
BOARD_SIZE,
|
BOARD_SIZE,
|
||||||
BoopState,
|
BoopState,
|
||||||
|
BoopPart,
|
||||||
PieceType,
|
PieceType,
|
||||||
PlayerType,
|
PlayerType,
|
||||||
WinnerType,
|
WinnerType,
|
||||||
WIN_LENGTH,
|
WIN_LENGTH,
|
||||||
MAX_PIECES_PER_PLAYER, BoopGame
|
MAX_PIECES_PER_PLAYER,
|
||||||
|
BoopGame,
|
||||||
} from "./data";
|
} from "./data";
|
||||||
import {createGameCommandRegistry} from "@/core/game";
|
import {createGameCommandRegistry, Command, moveToRegion} from "boardgame-core";
|
||||||
import {moveToRegion} from "@/core/region";
|
|
||||||
import {
|
import {
|
||||||
findPartAtPosition,
|
findPartAtPosition,
|
||||||
findPartInRegion,
|
findPartInRegion,
|
||||||
|
|
@ -34,7 +35,7 @@ async function place(game: BoopGame, row: number, col: number, player: PlayerTyp
|
||||||
|
|
||||||
const partId = part.id;
|
const partId = part.id;
|
||||||
|
|
||||||
await game.produceAsync(state => {
|
await game.produceAsync((state: BoopState) => {
|
||||||
// 将棋子从supply移动到棋盘
|
// 将棋子从supply移动到棋盘
|
||||||
const part = state.pieces[partId];
|
const part = state.pieces[partId];
|
||||||
moveToRegion(part, state.regions[player], state.regions.board, [row, col]);
|
moveToRegion(part, state.regions[player], state.regions.board, [row, col]);
|
||||||
|
|
@ -50,7 +51,7 @@ const placeCommand = registry.register( 'place <row:number> <col:number> <player
|
||||||
async function boop(game: BoopGame, row: number, col: number, type: PieceType) {
|
async function boop(game: BoopGame, row: number, col: number, type: PieceType) {
|
||||||
const booped: string[] = [];
|
const booped: string[] = [];
|
||||||
|
|
||||||
await game.produceAsync(state => {
|
await game.produceAsync((state: BoopState) => {
|
||||||
// 按照远离放置位置的方向推动
|
// 按照远离放置位置的方向推动
|
||||||
for (const [dr, dc] of getNeighborPositions()) {
|
for (const [dr, dc] of getNeighborPositions()) {
|
||||||
const nr = row + dr;
|
const nr = row + dr;
|
||||||
|
|
@ -133,7 +134,7 @@ async function checkGraduates(game: BoopGame){
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
await game.produceAsync(state => {
|
await game.produceAsync((state: BoopState) => {
|
||||||
for(const partId of toUpgrade){
|
for(const partId of toUpgrade){
|
||||||
const part = state.pieces[partId];
|
const part = state.pieces[partId];
|
||||||
const [row, col] = part.position;
|
const [row, col] = part.position;
|
||||||
|
|
@ -153,7 +154,7 @@ async function setup(game: BoopGame) {
|
||||||
const turnOutput = await turnCommand(game, currentPlayer);
|
const turnOutput = await turnCommand(game, currentPlayer);
|
||||||
if (!turnOutput.success) throw new Error(turnOutput.error);
|
if (!turnOutput.success) throw new Error(turnOutput.error);
|
||||||
|
|
||||||
await game.produceAsync(state => {
|
await game.produceAsync((state: BoopState) => {
|
||||||
state.winner = turnOutput.result.winner;
|
state.winner = turnOutput.result.winner;
|
||||||
if (!state.winner) {
|
if (!state.winner) {
|
||||||
state.currentPlayer = state.currentPlayer === 'white' ? 'black' : 'white';
|
state.currentPlayer = state.currentPlayer === 'white' ? 'black' : 'white';
|
||||||
|
|
@ -169,15 +170,15 @@ registry.register('setup', setup);
|
||||||
async function checkFullBoard(game: BoopGame, turnPlayer: PlayerType){
|
async function checkFullBoard(game: BoopGame, turnPlayer: PlayerType){
|
||||||
// 检查8-piece规则: 如果玩家所有8个棋子都在棋盘上且没有获胜,强制升级一个小猫
|
// 检查8-piece规则: 如果玩家所有8个棋子都在棋盘上且没有获胜,强制升级一个小猫
|
||||||
const playerPieces = Object.values(game.value.pieces).filter(
|
const playerPieces = Object.values(game.value.pieces).filter(
|
||||||
p => p.player === turnPlayer && p.regionId === 'board'
|
(p: BoopPart) => p.player === turnPlayer && p.regionId === 'board'
|
||||||
);
|
);
|
||||||
if(playerPieces.length < MAX_PIECES_PER_PLAYER || game.value.winner !== null){
|
if(playerPieces.length < MAX_PIECES_PER_PLAYER || game.value.winner !== null){
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const partId = await game.prompt(
|
const partId = await game.prompt(
|
||||||
'choose <player> <row:number> <col:number>',
|
'play <player> <row:number> <col:number> [type:string]',
|
||||||
(command) => {
|
(command: Command) => {
|
||||||
const [player, row, col] = command.params as [PlayerType, number, number];
|
const [player, row, col] = command.params as [PlayerType, number, number];
|
||||||
if (player !== turnPlayer) {
|
if (player !== turnPlayer) {
|
||||||
throw `Invalid player: ${player}. Expected ${turnPlayer}.`;
|
throw `Invalid player: ${player}. Expected ${turnPlayer}.`;
|
||||||
|
|
@ -185,17 +186,17 @@ async function checkFullBoard(game: BoopGame, turnPlayer: PlayerType){
|
||||||
if (!isInBounds(row, col)) {
|
if (!isInBounds(row, col)) {
|
||||||
throw `Invalid position: (${row}, ${col}). Must be between 0 and ${BOARD_SIZE - 1}.`;
|
throw `Invalid position: (${row}, ${col}). Must be between 0 and ${BOARD_SIZE - 1}.`;
|
||||||
}
|
}
|
||||||
|
|
||||||
const part = findPartAtPosition(game, row, col);
|
const part = findPartAtPosition(game, row, col);
|
||||||
if (!part || part.player !== turnPlayer) {
|
if (!part || part.player !== turnPlayer) {
|
||||||
throw `No ${player} piece at (${row}, ${col}).`;
|
throw `No ${player} piece at (${row}, ${col}).`;
|
||||||
}
|
}
|
||||||
|
|
||||||
return part.id;
|
return part.id;
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
await game.produceAsync(state => {
|
await game.produceAsync((state: BoopState) => {
|
||||||
const part = state.pieces[partId];
|
const part = state.pieces[partId];
|
||||||
moveToRegion(part, state.regions.board, null);
|
moveToRegion(part, state.regions.board, null);
|
||||||
const cat = findPartInRegion(state, '', 'cat');
|
const cat = findPartInRegion(state, '', 'cat');
|
||||||
|
|
@ -206,7 +207,7 @@ async function checkFullBoard(game: BoopGame, turnPlayer: PlayerType){
|
||||||
async function turn(game: BoopGame, turnPlayer: PlayerType) {
|
async function turn(game: BoopGame, turnPlayer: PlayerType) {
|
||||||
const {row, col, type} = await game.prompt(
|
const {row, col, type} = await game.prompt(
|
||||||
'play <player> <row:number> <col:number> [type:string]',
|
'play <player> <row:number> <col:number> [type:string]',
|
||||||
(command) => {
|
(command: Command) => {
|
||||||
const [player, row, col, type] = command.params as [PlayerType, number, number, PieceType?];
|
const [player, row, col, type] = command.params as [PlayerType, number, number, PieceType?];
|
||||||
const pieceType = type === 'cat' ? 'cat' : 'kitten';
|
const pieceType = type === 'cat' ? 'cat' : 'kitten';
|
||||||
|
|
||||||
|
|
@ -239,4 +240,9 @@ async function turn(game: BoopGame, turnPlayer: PlayerType) {
|
||||||
await checkFullBoard(game, turnPlayer);
|
await checkFullBoard(game, turnPlayer);
|
||||||
return { winner: null };
|
return { winner: null };
|
||||||
}
|
}
|
||||||
const turnCommand = registry.register('turn <player>', turn);
|
const turnCommand = registry.register('turn <player>', turn);
|
||||||
|
export const commands = {
|
||||||
|
play: (player: PlayerType, row: number, col: number, type: PieceType) => {
|
||||||
|
return `play ${player} ${row} ${col} ${type}`;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
@ -1,8 +1,8 @@
|
||||||
import parts from './parts.csv';
|
import parts from './parts.csv';
|
||||||
import {createRegion, moveToRegion, Region} from "@/core/region";
|
import {createRegion, moveToRegion, Region} from "boardgame-core";
|
||||||
import {createPartsFromTable} from "@/core/part-factory";
|
import {createPartsFromTable} from "boardgame-core";
|
||||||
import {Part} from "@/core/part";
|
import {Part} from "boardgame-core";
|
||||||
import {IGameContext} from "@/core/game";
|
import {IGameContext} from "boardgame-core";
|
||||||
|
|
||||||
export const BOARD_SIZE = 6;
|
export const BOARD_SIZE = 6;
|
||||||
export const MAX_PIECES_PER_PLAYER = 8;
|
export const MAX_PIECES_PER_PLAYER = 8;
|
||||||
|
|
@ -18,8 +18,8 @@ export type BoopPart = Part<BoopPartMeta>;
|
||||||
export function createInitialState() {
|
export function createInitialState() {
|
||||||
const pieces = createPartsFromTable(
|
const pieces = createPartsFromTable(
|
||||||
parts,
|
parts,
|
||||||
(item, index) => `${item.player}-${item.type}-${index + 1}`,
|
(item: {player: string, type: string}, index: number) => `${item.player}-${item.type}-${index + 1}`,
|
||||||
(item) => item.count
|
(item: {count: number}) => item.count
|
||||||
) as Record<string, BoopPart>;
|
) as Record<string, BoopPart>;
|
||||||
|
|
||||||
// Initialize region childIds
|
// Initialize region childIds
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,7 @@
|
||||||
PlayerType,
|
PlayerType,
|
||||||
RegionType,
|
RegionType,
|
||||||
WIN_LENGTH
|
WIN_LENGTH
|
||||||
} from "@/samples/boop/data";
|
} from "./data";
|
||||||
|
|
||||||
const DIRS = [
|
const DIRS = [
|
||||||
[0, 1],
|
[0, 1],
|
||||||
|
|
@ -55,7 +55,7 @@ export function findPartInRegion(ctx: BoopGame | BoopState, regionId: keyof Boop
|
||||||
if(!regionId){
|
if(!regionId){
|
||||||
return Object.values(state.pieces).find(part => match(regionId, part, type, player)) || null;
|
return Object.values(state.pieces).find(part => match(regionId, part, type, player)) || null;
|
||||||
}
|
}
|
||||||
const id = state.regions[regionId].childIds.find(id => match(regionId, state.pieces[id], type, player));
|
const id = state.regions[regionId].childIds.find((id: string) => match(regionId, state.pieces[id], type, player));
|
||||||
return id ? state.pieces[id] || null : null;
|
return id ? state.pieces[id] || null : null;
|
||||||
}
|
}
|
||||||
function match(regionId: RegionType, part: BoopPart, type: PieceType, player?: PlayerType){
|
function match(regionId: RegionType, part: BoopPart, type: PieceType, player?: PlayerType){
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,5 @@
|
||||||
import Phaser from 'phaser';
|
import Phaser from 'phaser';
|
||||||
import type { BoopState, PlayerType } from '@/game';
|
import type { BoopState, PlayerType, BoopPart } from '@/game';
|
||||||
import type { ReadonlySignal } from '@preact/signals-core';
|
|
||||||
|
|
||||||
const BOARD_SIZE = 6;
|
const BOARD_SIZE = 6;
|
||||||
const CELL_SIZE = 80;
|
const CELL_SIZE = 80;
|
||||||
|
|
@ -70,18 +69,29 @@ export class BoardRenderer {
|
||||||
}).setOrigin(0.5);
|
}).setOrigin(0.5);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private countPieces(state: BoopState, player: PlayerType) {
|
||||||
|
const pieces = Object.values(state.pieces);
|
||||||
|
const playerPieces = pieces.filter((p: BoopPart) => p.player === player);
|
||||||
|
|
||||||
|
const kittensInSupply = playerPieces.filter((p: BoopPart) => p.type === 'kitten' && p.regionId === player).length;
|
||||||
|
const catsInSupply = playerPieces.filter((p: BoopPart) => p.type === 'cat' && p.regionId === player).length;
|
||||||
|
const piecesOnBoard = playerPieces.filter((p: BoopPart) => p.regionId === 'board').length;
|
||||||
|
|
||||||
|
return { kittensInSupply, catsInSupply, piecesOnBoard };
|
||||||
|
}
|
||||||
|
|
||||||
updateTurnText(player: PlayerType, state: BoopState): void {
|
updateTurnText(player: PlayerType, state: BoopState): void {
|
||||||
const current = player === 'white' ? state.players.white : state.players.black;
|
const { kittensInSupply, catsInSupply } = this.countPieces(state, player);
|
||||||
const catsAvailable = current.catPool.remaining() + current.graduatedCount;
|
|
||||||
|
|
||||||
this.turnText.setText(
|
this.turnText.setText(
|
||||||
`${player.toUpperCase()}'s turn | Kittens: ${current.kittenPool.remaining()} | Cats: ${catsAvailable}`
|
`${player.toUpperCase()}'s turn | Kittens: ${kittensInSupply} | Cats: ${catsInSupply}`
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
setupInput(
|
setupInput(
|
||||||
state: ReadonlySignal<BoopState>,
|
getState: () => BoopState,
|
||||||
onCellClick: (row: number, col: number) => void
|
onCellClick: (row: number, col: number) => void,
|
||||||
|
checkWinner: () => boolean
|
||||||
): void {
|
): void {
|
||||||
for (let row = 0; row < BOARD_SIZE; row++) {
|
for (let row = 0; row < BOARD_SIZE; row++) {
|
||||||
for (let col = 0; col < BOARD_SIZE; col++) {
|
for (let col = 0; col < BOARD_SIZE; col++) {
|
||||||
|
|
@ -91,8 +101,9 @@ export class BoardRenderer {
|
||||||
const zone = this.scene.add.zone(x, y, CELL_SIZE, CELL_SIZE).setInteractive();
|
const zone = this.scene.add.zone(x, y, CELL_SIZE, CELL_SIZE).setInteractive();
|
||||||
|
|
||||||
zone.on('pointerdown', () => {
|
zone.on('pointerdown', () => {
|
||||||
const isOccupied = !!state.value.board.partMap[`${row},${col}`];
|
const state = getState();
|
||||||
if (!isOccupied && !state.value.winner) {
|
const isOccupied = !!state.regions.board.partMap[`${row},${col}`];
|
||||||
|
if (!isOccupied && !checkWinner()) {
|
||||||
onCellClick(row, col);
|
onCellClick(row, col);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -30,9 +30,11 @@ export class GameScene extends GameHostScene<BoopState> {
|
||||||
this.disposables.add(createPieceSpawner(this));
|
this.disposables.add(createPieceSpawner(this));
|
||||||
|
|
||||||
// 设置输入处理
|
// 设置输入处理
|
||||||
this.boardRenderer.setupInput(this.gameHost.state, (row, col) => {
|
this.boardRenderer.setupInput(
|
||||||
this.handleCellClick(row, col);
|
() => this.state,
|
||||||
});
|
(row, col) => this.handleCellClick(row, col),
|
||||||
|
() => !!this.state.winner
|
||||||
|
);
|
||||||
|
|
||||||
// 监听状态变化
|
// 监听状态变化
|
||||||
this.addEffect(() => {
|
this.addEffect(() => {
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
import Phaser from 'phaser';
|
import Phaser from 'phaser';
|
||||||
import type { BoopState, BoopPart } from '@/game/boop';
|
import type { BoopState, BoopPart } from '@/game';
|
||||||
import { GameHostScene, spawnEffect, type Spawner } from 'boardgame-phaser';
|
import { GameHostScene, spawnEffect, type Spawner } from 'boardgame-phaser';
|
||||||
import { BOARD_OFFSET, CELL_SIZE } from './BoardRenderer';
|
import { BOARD_OFFSET, CELL_SIZE } from './BoardRenderer';
|
||||||
|
|
||||||
|
|
@ -37,7 +37,7 @@ class BoopPartSpawner implements Spawner<BoopPart, Phaser.GameObjects.Container>
|
||||||
|
|
||||||
const container = this.scene.add.container(x, y);
|
const container = this.scene.add.container(x, y);
|
||||||
|
|
||||||
const isCat = part.pieceType === 'cat';
|
const isCat = part.type === 'cat';
|
||||||
const baseColor = part.player === 'white' ? 0xffffff : 0x333333;
|
const baseColor = part.player === 'white' ? 0xffffff : 0x333333;
|
||||||
const strokeColor = part.player === 'white' ? 0x000000 : 0xffffff;
|
const strokeColor = part.player === 'white' ? 0x000000 : 0xffffff;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
import Phaser from 'phaser';
|
import Phaser from 'phaser';
|
||||||
import type { BoopState, PlayerType, PieceType } from '@/game/boop';
|
import type { BoopState, PlayerType, PieceType, BoopPart } from '@/game';
|
||||||
import { BOARD_OFFSET, CELL_SIZE, BOARD_SIZE } from './BoardRenderer';
|
import { BOARD_OFFSET, CELL_SIZE, BOARD_SIZE } from './BoardRenderer';
|
||||||
|
|
||||||
export class PieceTypeSelector {
|
export class PieceTypeSelector {
|
||||||
|
|
@ -58,23 +58,36 @@ export class PieceTypeSelector {
|
||||||
return this.selectedType;
|
return this.selectedType;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private countPieces(state: BoopState, player: PlayerType) {
|
||||||
|
const pieces = Object.values(state.pieces);
|
||||||
|
const playerPieces = pieces.filter((p: BoopPart) => p.player === player);
|
||||||
|
|
||||||
|
const kittensInSupply = playerPieces.filter((p: BoopPart) => p.type === 'kitten' && p.regionId === player).length;
|
||||||
|
const catsInSupply = playerPieces.filter((p: BoopPart) => p.type === 'cat' && p.regionId === player).length;
|
||||||
|
const catsOnBoard = playerPieces.filter((p: BoopPart) => p.type === 'cat' && p.regionId === 'board').length;
|
||||||
|
|
||||||
|
return { kittensInSupply, catsInSupply, catsOnBoard };
|
||||||
|
}
|
||||||
|
|
||||||
update(state: BoopState): void {
|
update(state: BoopState): void {
|
||||||
const currentPlayer = state.players[state.currentPlayer];
|
const currentPlayer = state.currentPlayer;
|
||||||
const kittenAvailable = currentPlayer.kittenPool.remaining() > 0;
|
const { kittensInSupply, catsInSupply, catsOnBoard } = this.countPieces(state, currentPlayer);
|
||||||
const catsAvailable = currentPlayer.catPool.remaining() + currentPlayer.graduatedCount > 0;
|
|
||||||
|
const kittenAvailable = kittensInSupply > 0;
|
||||||
|
const catsAvailable = catsInSupply + catsOnBoard > 0;
|
||||||
|
|
||||||
this.updateButton(
|
this.updateButton(
|
||||||
this.kittenButton,
|
this.kittenButton,
|
||||||
kittenAvailable,
|
kittenAvailable,
|
||||||
this.selectedType === 'kitten',
|
this.selectedType === 'kitten',
|
||||||
`🐾 小猫 (${currentPlayer.kittenPool.remaining()})`
|
`🐾 小猫 (${kittensInSupply})`
|
||||||
);
|
);
|
||||||
|
|
||||||
this.updateButton(
|
this.updateButton(
|
||||||
this.catButton,
|
this.catButton,
|
||||||
catsAvailable,
|
catsAvailable,
|
||||||
this.selectedType === 'cat',
|
this.selectedType === 'cat',
|
||||||
`🐱 大猫 (${currentPlayer.catPool.remaining() + currentPlayer.graduatedCount})`
|
`🐱 大猫 (${catsInSupply + catsOnBoard})`
|
||||||
);
|
);
|
||||||
|
|
||||||
// 自动切换到可用类型
|
// 自动切换到可用类型
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
import Phaser from 'phaser';
|
import Phaser from 'phaser';
|
||||||
import type { BoopState, PlayerType } from '@/game/boop';
|
import type { BoopState, PlayerType, BoopPart } from '@/game';
|
||||||
import { BOARD_OFFSET, CELL_SIZE, BOARD_SIZE } from './BoardRenderer';
|
import { BOARD_OFFSET, CELL_SIZE, BOARD_SIZE } from './BoardRenderer';
|
||||||
|
|
||||||
export class SupplyUI {
|
export class SupplyUI {
|
||||||
|
|
@ -39,19 +39,30 @@ export class SupplyUI {
|
||||||
this.blackContainer.setDepth(100);
|
this.blackContainer.setDepth(100);
|
||||||
}
|
}
|
||||||
|
|
||||||
update(state: BoopState): void {
|
private countPieces(state: BoopState, player: PlayerType) {
|
||||||
const white = state.players.white;
|
const pieces = Object.values(state.pieces);
|
||||||
const black = state.players.black;
|
const playerPieces = pieces.filter((p: BoopPart) => p.player === player);
|
||||||
|
|
||||||
|
const kittensInSupply = playerPieces.filter((p: BoopPart) => p.type === 'kitten' && p.regionId === player).length;
|
||||||
|
const catsInSupply = playerPieces.filter((p: BoopPart) => p.type === 'cat' && p.regionId === player).length;
|
||||||
|
const catsOnBoard = playerPieces.filter((p: BoopPart) => p.type === 'cat' && p.regionId === 'board').length;
|
||||||
|
|
||||||
|
return { kittensInSupply, catsInSupply, catsOnBoard };
|
||||||
|
}
|
||||||
|
|
||||||
const whiteCatsAvailable = white.catPool.remaining() + white.graduatedCount;
|
update(state: BoopState): void {
|
||||||
const blackCatsAvailable = black.catPool.remaining() + black.graduatedCount;
|
const white = this.countPieces(state, 'white');
|
||||||
|
const black = this.countPieces(state, 'black');
|
||||||
|
|
||||||
|
const whiteCatsAvailable = white.catsInSupply + white.catsOnBoard;
|
||||||
|
const blackCatsAvailable = black.catsInSupply + black.catsOnBoard;
|
||||||
|
|
||||||
this.whiteText.setText(
|
this.whiteText.setText(
|
||||||
`⚪ WHITE\n🐾 ${white.kittenPool.remaining()} | 🐱 ${whiteCatsAvailable}`
|
`⚪ WHITE\n🐾 ${white.kittensInSupply} | 🐱 ${whiteCatsAvailable}`
|
||||||
);
|
);
|
||||||
|
|
||||||
this.blackText.setText(
|
this.blackText.setText(
|
||||||
`⚫ BLACK\n🐾 ${black.kittenPool.remaining()} | 🐱 ${blackCatsAvailable}`
|
`⚫ BLACK\n🐾 ${black.kittensInSupply} | 🐱 ${blackCatsAvailable}`
|
||||||
);
|
);
|
||||||
|
|
||||||
this.updateHighlight(state.currentPlayer);
|
this.updateHighlight(state.currentPlayer);
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
import Phaser from 'phaser';
|
import Phaser from 'phaser';
|
||||||
import type { BoopState, WinnerType } from '@/game/boop';
|
import type { BoopState, WinnerType } from '@/game';
|
||||||
import { BOARD_OFFSET, CELL_SIZE, BOARD_SIZE } from './BoardRenderer';
|
import { BOARD_OFFSET, CELL_SIZE, BOARD_SIZE } from './BoardRenderer';
|
||||||
|
|
||||||
export class WinnerOverlay {
|
export class WinnerOverlay {
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue