refactor: update api

This commit is contained in:
2026-04-06 11:31:51 +08:00
parent 0e7aaea7da
commit fa104d10f2
12 changed files with 165 additions and 229 deletions
+23 -24
View File
@@ -24,7 +24,7 @@ export const registry = createGameCommandRegistry<BoopState>();
/**
* 放置棋子到棋盘
*/
async function place(game: BoopGame, row: number, col: number, player: PlayerType, type: PieceType) {
async function handlePlace(game: BoopGame, row: number, col: number, player: PlayerType, type: PieceType) {
const value = game.value;
// 从玩家supply中找到对应类型的棋子
const part = findPartInRegion(game, player, type);
@@ -43,12 +43,12 @@ async function place(game: BoopGame, row: number, col: number, player: PlayerTyp
return { row, col, player, type, partId };
}
const placeCommand = registry.register( 'place <row:number> <col:number> <player> <type>', place);
const place = registry.register( 'place <row:number> <col:number> <player> <type>', handlePlace);
/**
* 执行boop - 推动周围棋子
*/
async function boop(game: BoopGame, row: number, col: number, type: PieceType) {
async function handleBoop(game: BoopGame, row: number, col: number, type: PieceType) {
const booped: string[] = [];
const toRemove = new Set<string>();
@@ -95,12 +95,12 @@ async function boop(game: BoopGame, row: number, col: number, type: PieceType) {
return { booped };
}
const boopCommand = registry.register('boop <row:number> <col:number> <type>', boop);
const boop = registry.register('boop <row:number> <col:number> <type>', handleBoop);
/**
* 检查是否有玩家获胜(三个猫连线)
*/
async function checkWin(game: BoopGame) {
async function handleCheckWin(game: BoopGame) {
for(const line of getLineCandidates()){
let whites = 0;
let blacks = 0;
@@ -119,12 +119,12 @@ async function checkWin(game: BoopGame) {
}
return null;
}
const checkWinCommand = registry.register('check-win', checkWin);
const checkWin = registry.register('check-win', handleCheckWin);
/**
* 检查并执行小猫升级(三个小猫连线变成猫)
*/
async function checkGraduates(game: BoopGame){
async function handleCheckGraduates(game: BoopGame){
const toUpgrade = new Set<string>();
for(const line of getLineCandidates()){
let whites = 0;
@@ -155,12 +155,12 @@ async function checkGraduates(game: BoopGame){
}
});
}
const checkGraduatesCommand = registry.register('check-graduates', checkGraduates);
const checkGraduates = registry.register('check-graduates', handleCheckGraduates);
async function setup(game: BoopGame) {
export async function start(game: BoopGame) {
while (true) {
const currentPlayer = game.value.currentPlayer;
const { winner } = await turnCommand(game, currentPlayer);
const { winner } = await turn(game, currentPlayer);
await game.produceAsync((state: BoopState) => {
state.winner = winner;
@@ -173,9 +173,8 @@ async function setup(game: BoopGame) {
return game.value;
}
registry.register('setup', setup);
async function checkFullBoard(game: BoopGame, turnPlayer: PlayerType){
async function handleCheckFullBoard(game: BoopGame, turnPlayer: PlayerType){
// 检查8-piece规则: 如果玩家所有8个棋子都在棋盘上且没有获胜,强制升级一个小猫
const playerPieces = Object.values(game.value.pieces).filter(
(p: BoopPart) => p.player === turnPlayer && p.regionId === 'board'
@@ -186,8 +185,7 @@ async function checkFullBoard(game: BoopGame, turnPlayer: PlayerType){
const partId = await game.prompt(
'play <player> <row:number> <col:number> [type:string]',
(command: Command) => {
const [player, row, col] = command.params as [PlayerType, number, number];
(player: PlayerType, row: number, col: number, type?: PieceType) => {
if (player !== turnPlayer) {
throw `无效的玩家: ${player},期望的是 ${turnPlayer}`;
}
@@ -211,12 +209,12 @@ async function checkFullBoard(game: BoopGame, turnPlayer: PlayerType){
moveToRegion(cat || part, null, state.regions[turnPlayer]);
});
}
const checkFullBoard = registry.register('check-full-board', handleCheckFullBoard);
async function turn(game: BoopGame, turnPlayer: PlayerType) {
async function handleTurn(game: BoopGame, turnPlayer: PlayerType) {
const {row, col, type} = await game.prompt(
'play <player> <row:number> <col:number> [type:string]',
(command: Command) => {
const [player, row, col, type] = command.params as [PlayerType, number, number, PieceType?];
(player: PlayerType, row: number, col: number, type?: PieceType) => {
const pieceType = type === 'cat' ? 'cat' : 'kitten';
if (player !== turnPlayer) {
@@ -239,17 +237,18 @@ async function turn(game: BoopGame, turnPlayer: PlayerType) {
);
const pieceType = type === 'cat' ? 'cat' : 'kitten';
await placeCommand(game, row, col, turnPlayer, pieceType);
await boopCommand(game, row, col, pieceType);
const winner = await checkWinCommand(game);
await place(game, row, col, turnPlayer, pieceType);
await boop(game, row, col, pieceType);
const winner = await checkWin(game);
if(winner) return { winner: winner as WinnerType };
await checkGraduatesCommand(game);
await checkFullBoard(game, turnPlayer);
await checkGraduates(game);
await handleCheckFullBoard(game, turnPlayer);
return { winner: null };
}
const turnCommand = registry.register('turn <player>', turn);
export const commands = {
const turn = registry.register('turn <player>', handleTurn);
export const prompts = {
play: (player: PlayerType, row: number, col: number, type?: PieceType) => {
if (type) {
return `play ${player} ${row} ${col} ${type}`;
+5 -5
View File
@@ -1,6 +1,6 @@
import type { BoopState } from '@/game';
import { GameHostScene } from 'boardgame-phaser';
import { commands } from '@/game';
import { prompts } from '@/game';
import { BoardRenderer } from './BoardRenderer';
import { createPieceSpawner } from './PieceSpawner';
import { SupplyUI } from './SupplyUI';
@@ -73,7 +73,7 @@ export class GameScene extends GameHostScene<BoopState> {
private handleCellClick(row: number, col: number): void {
const selectedType = this.pieceTypeSelector.getSelectedType();
const cmd = commands.play(this.state.currentPlayer, row, col, selectedType);
const cmd = prompts.play(this.state.currentPlayer, row, col, selectedType);
const error = this.gameHost.onInput(cmd);
if (error) {
this.errorOverlay.show(error);
@@ -82,7 +82,7 @@ export class GameScene extends GameHostScene<BoopState> {
private handlePieceClick(row: number, col: number): void {
// 棋盘满时,点击棋子触发升级
const cmd = commands.play(this.state.currentPlayer, row, col);
const cmd = prompts.play(this.state.currentPlayer, row, col);
const error = this.gameHost.onInput(cmd);
if (error) {
this.errorOverlay.show(error);
@@ -90,10 +90,10 @@ export class GameScene extends GameHostScene<BoopState> {
}
private startGame(): void {
this.gameHost.setup('setup');
this.gameHost.start();
}
private restartGame(): void {
this.gameHost.setup('setup');
this.gameHost.start();
}
}
+2 -8
View File
@@ -14,19 +14,13 @@ export default function App<TState extends Record<string, unknown>>(props: { gam
const scene = useComputed(() => new props.gameScene());
const handleReset = async () => {
gameHost.value.gameHost.setup('setup').then(result => {
if(!result.success) {
console.error(result.error);
}else{
console.log('Game finished!', result.result);
}
});
gameHost.value.gameHost.start();
};
const label = useComputed(() => gameHost.value.gameHost.status.value === 'running' ? 'Restart' : 'Start');
return (
<div className="flex flex-col h-screen">
<div className="flex-1 relative">
<div className="flex-1 flex relative justify-center items-center">
<PhaserGame config={{ width: 640, height: 750 }}>
<PhaserScene sceneKey="GameScene" scene={scene.value} autoStart data={gameHost.value} />
</PhaserGame>