diff --git a/doc/how_to_add_games.md b/doc/how_to_add_games.md index 1e466f9..ad97de2 100644 --- a/doc/how_to_add_games.md +++ b/doc/how_to_add_games.md @@ -15,22 +15,28 @@ src/games// └── Game.test.ts # Unit tests (write BEFORE implementation) ``` -### Registration (src/registry.ts) +### Self-Registration (index.ts) + +Games are auto-discovered - no need to edit `registry.ts`. Just export a `definition` object: ```typescript -import * as MyGame from './games/my-game'; -import MyGameRules from './games/my-game/rules.md?raw'; +// src/games/my-game/index.ts +import type { GameDefinition } from '../../types'; +import { MyGame as game } from './Game'; +import { Board } from './Board'; +import rules from './rules.md?raw'; + +export type { MyGameState } from './Game'; -// Add to games object: -'my-game': { - game: MyGame.game, - Board: MyGame.Board, +export const definition: GameDefinition = { + game, + Board, name: 'My Game', description: 'Short description', minPlayers: 2, maxPlayers: 2, - rules: MyGameRules, -}, + rules, +}; ``` --- @@ -243,18 +249,32 @@ ai: { **Commit**: `feat(my-game): add AI support with move enumeration (Phase 7)` -### Phase 8: Registration & Exports +### Phase 8: Exports & Self-Registration -Create `index.ts`: +Create `index.ts` with the full game definition (games are auto-discovered): ```typescript -export { MyGame as game, type MyGameState } from './Game'; -export { Board } from './Board'; +import type { GameDefinition } from '../../types'; +import { MyGame as game } from './Game'; +import { Board } from './Board'; +import rules from './rules.md?raw'; + +export type { MyGameState } from './Game'; + +export const definition: GameDefinition = { + game, + Board, + name: 'My Game', + description: 'Short description', + minPlayers: 2, + maxPlayers: 2, + rules, +}; ``` -Update `src/registry.ts` (see Quick Reference above). +No need to edit `registry.ts` - games are auto-discovered via `import.meta.glob()`. -**Commit**: `feat(my-game): register game in registry (Phase 8)` +**Commit**: `feat(my-game): add game exports and definition (Phase 8)` --- @@ -455,9 +475,9 @@ G.cells[posToIndex(newPos)] = 'piece'; G.piecePosition = newPos; ``` -### 4. Not registering in registry.ts +### 4. Missing or incorrect definition export -The game won't appear in the UI until added to `registry.ts`. +The game won't appear in the UI unless `index.ts` exports a `definition` object of type `GameDefinition`. Ensure all required fields are present. --- diff --git a/src/games/mecha-duel/index.ts b/src/games/mecha-duel/index.ts index 9c29dc0..5d19771 100644 --- a/src/games/mecha-duel/index.ts +++ b/src/games/mecha-duel/index.ts @@ -1,2 +1,16 @@ -export { MechaDuel as game, type MechaDuelState } from './Game'; -export { Board } from './Board'; +import type { GameDefinition } from '../../types'; +import { MechaDuel as game } from './Game'; +import { Board } from './Board'; +import rules from './rules.md?raw'; + +export type { MechaDuelState } from './Game'; + +export const definition: GameDefinition = { + game, + Board, + name: 'Mecha Duel', + description: 'Strategic mecha combat with committed attacks', + minPlayers: 2, + maxPlayers: 2, + rules, +}; diff --git a/src/games/rock-paper-scissors/index.ts b/src/games/rock-paper-scissors/index.ts index 20f2b94..c567eab 100644 --- a/src/games/rock-paper-scissors/index.ts +++ b/src/games/rock-paper-scissors/index.ts @@ -1,2 +1,16 @@ -export { RPS as game, type RPSState } from './Game'; -export { Board } from './Board'; +import type { GameDefinition } from '../../types'; +import { RPS as game } from './Game'; +import { Board } from './Board'; +import rules from './rules.md?raw'; + +export type { RPSState } from './Game'; + +export const definition: GameDefinition = { + game, + Board, + name: 'Rock Paper Scissors', + description: 'Best of three', + minPlayers: 2, + maxPlayers: 2, + rules, +}; diff --git a/src/games/tic-tac-toe/index.ts b/src/games/tic-tac-toe/index.ts index 9652068..9f03b60 100644 --- a/src/games/tic-tac-toe/index.ts +++ b/src/games/tic-tac-toe/index.ts @@ -1,2 +1,16 @@ -export { TicTacToe as game, type TicTacToeState } from './Game'; -export { Board } from './Board'; +import type { GameDefinition } from '../../types'; +import { TicTacToe as game } from './Game'; +import { Board } from './Board'; +import rules from './rules.md?raw'; + +export type { TicTacToeState } from './Game'; + +export const definition: GameDefinition = { + game, + Board, + name: 'Tic-Tac-Toe', + description: 'Classic 3x3 grid game', + minPlayers: 2, + maxPlayers: 2, + rules, +}; diff --git a/src/registry.ts b/src/registry.ts index c85a215..0ae2a49 100644 --- a/src/registry.ts +++ b/src/registry.ts @@ -1,71 +1,49 @@ -import type { Game } from 'boardgame.io'; -import type { BoardProps } from 'boardgame.io/react'; -import type { ComponentType } from 'react'; +import type { GameDefinition } from './types'; -import * as TicTacToe from './games/tic-tac-toe'; -import TicTacToeRules from './games/tic-tac-toe/rules.md?raw'; - -import * as RockPaperScissors from './games/rock-paper-scissors'; -import RockPaperScissorsRules from './games/rock-paper-scissors/rules.md?raw'; - -import * as MechaDuel from './games/mecha-duel'; -import MechaDuelRules from './games/mecha-duel/rules.md?raw'; - -export interface GameDefinition { - game: Game; - Board: ComponentType; - name: string; - description: string; - minPlayers: number; - maxPlayers: number; - rules: string; -} +export type { GameDefinition } from './types'; /* * ============================================================================ * ADDING A NEW GAME * ============================================================================ - * 1. Create a new folder in src/games// - * 2. Add the following files: - * - Game.ts : boardgame.io game definition - * - Board.tsx : React component for the game board - * - index.ts : exports { game, Board } - * - rules.md : Markdown file with game rules - * 3. Import the game and rules below: - * import * as MyGame from './games/my-game'; - * import MyGameRules from './games/my-game/rules.md?raw'; - * 4. Add an entry to the `games` object below + * Games are auto-discovered from src/games// directories. + * + * To add a new game, create a folder with these files: + * - Game.ts : boardgame.io game definition + * - Board.tsx : React component for the game board + * - index.ts : exports a `definition` object of type GameDefinition + * - rules.md : Markdown file with game rules + * + * Example index.ts: + * import type { GameDefinition } from '../../types'; + * import { MyGame as game } from './Game'; + * import { Board } from './Board'; + * import rules from './rules.md?raw'; + * + * export const definition: GameDefinition = { + * game, + * Board, + * name: 'My Game', + * description: 'A fun game', + * minPlayers: 2, + * maxPlayers: 4, + * rules, + * }; * ============================================================================ */ -export const games: Record = { - 'tic-tac-toe': { - game: TicTacToe.game, - Board: TicTacToe.Board, - name: 'Tic-Tac-Toe', - description: 'Classic 3x3 grid game', - minPlayers: 2, - maxPlayers: 2, - rules: TicTacToeRules, - }, - 'rock-paper-scissors': { - game: RockPaperScissors.game, - Board: RockPaperScissors.Board, - name: 'Rock Paper Scissors', - description: 'Best of three', - minPlayers: 2, - maxPlayers: 2, - rules: RockPaperScissorsRules, - }, - 'mecha-duel': { - game: MechaDuel.game, - Board: MechaDuel.Board, - name: 'Mecha Duel', - description: 'Strategic mecha combat with committed attacks', - minPlayers: 2, - maxPlayers: 2, - rules: MechaDuelRules, - }, -}; +const gameModules = import.meta.glob<{ definition: GameDefinition }>( + './games/*/index.ts', + { eager: true } +); + +export const games: Record = {}; + +for (const [path, module] of Object.entries(gameModules)) { + const id = path.match(/\.\/games\/([^/]+)\//)?.[1]; + if (id && module.definition) { + games[id] = module.definition; + } +} export const gameIds = Object.keys(games); diff --git a/src/types.ts b/src/types.ts new file mode 100644 index 0000000..ac59c39 --- /dev/null +++ b/src/types.ts @@ -0,0 +1,13 @@ +import type { Game } from 'boardgame.io'; +import type { BoardProps } from 'boardgame.io/react'; +import type { ComponentType } from 'react'; + +export interface GameDefinition { + game: Game; + Board: ComponentType; + name: string; + description: string; + minPlayers: number; + maxPlayers: number; + rules: string; +}