Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
54 changes: 37 additions & 17 deletions doc/how_to_add_games.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,22 +15,28 @@ src/games/<game-name>/
└── 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,
};
```

---
Expand Down Expand Up @@ -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)`

---

Expand Down Expand Up @@ -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.

---

Expand Down
18 changes: 16 additions & 2 deletions src/games/mecha-duel/index.ts
Original file line number Diff line number Diff line change
@@ -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,
};
18 changes: 16 additions & 2 deletions src/games/rock-paper-scissors/index.ts
Original file line number Diff line number Diff line change
@@ -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,
};
18 changes: 16 additions & 2 deletions src/games/tic-tac-toe/index.ts
Original file line number Diff line number Diff line change
@@ -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,
};
98 changes: 38 additions & 60 deletions src/registry.ts
Original file line number Diff line number Diff line change
@@ -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<BoardProps>;
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/<game-name>/
* 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/<game-name>/ 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<string, GameDefinition> = {
'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<string, GameDefinition> = {};

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);
13 changes: 13 additions & 0 deletions src/types.ts
Original file line number Diff line number Diff line change
@@ -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<BoardProps>;
name: string;
description: string;
minPlayers: number;
maxPlayers: number;
rules: string;
}