From 2c84728628f08935ff54f329d341395451d5b6fd Mon Sep 17 00:00:00 2001 From: Claude Date: Sat, 31 Jan 2026 22:05:49 +0000 Subject: [PATCH] docs: add Mecha Duel rules clarifications and implementation plan - Update rules.md with clarifications: - 8x8 board only - KingStep can destroy pawns - Multiple knights can be executed - Knight validity checked at execution time - Pawn destruction rules clarified - Create code_plan.md with phased implementation plan - Tests will be written before implementation (TDD) https://claude.ai/code/session_01G2NbWZYWsFUY3KPhwErBwc --- src/games/mecha-duel/code_plan.md | 513 ++++++++++++++++++++++++++++++ src/games/mecha-duel/rules.md | 55 +++- 2 files changed, 551 insertions(+), 17 deletions(-) create mode 100644 src/games/mecha-duel/code_plan.md diff --git a/src/games/mecha-duel/code_plan.md b/src/games/mecha-duel/code_plan.md new file mode 100644 index 0000000..6274fa9 --- /dev/null +++ b/src/games/mecha-duel/code_plan.md @@ -0,0 +1,513 @@ +# Mecha Duel - Implementation Plan + +## Overview + +Implement the Mecha Duel game following the boardgame.io patterns established in the codebase. Each phase should be committed separately. + +## File Structure + +``` +src/games/mecha-duel/ +├── Game.ts # boardgame.io game definition & logic +├── Board.tsx # React component for UI +├── index.ts # Module exports +├── rules.md # Game rules (already exists) +├── code_plan.md # This file +├── scenarios.ts # Scenario definitions (pawn layouts) +└── Game.test.ts # Unit tests +``` + +--- + +## Phase 1: Core Types & State + +**File:** `Game.ts` + +### Types to Define + +```typescript +// Board coordinates +type Position = { x: number; y: number }; + +// Piece types for attacks +type PieceType = 'bishop' | 'rook' | 'knight' | 'queen'; + +// Piece lifecycle states +type PieceState = 'ready' | 'committed' | 'exhausted'; + +// A single piece in player's supply +interface Piece { + type: PieceType; + state: PieceState; + id: number; // Unique identifier within player's supply +} + +// A committed piece on the board +interface CommittedPiece { + pieceId: number; // Reference to piece in supply + playerId: string; // '0' or '1' + type: PieceType; + target: Position; // Committed target square +} + +// Cell contents +type CellContent = 'pawn' | 'king0' | 'king1' | null; + +// Main game state +interface MechaDuelState { + cells: CellContent[]; // 8x8 = 64 cells, row-major order + kings: { + '0': Position | null; // null if destroyed + '1': Position | null; + }; + pieces: { + '0': Piece[]; // Player 0's piece supply + '1': Piece[]; // Player 1's piece supply + }; + committedPieces: CommittedPiece[]; +} +``` + +### Helper Functions + +- `posToIndex(pos: Position): number` - Convert x,y to cell index +- `indexToPos(index: number): Position` - Convert cell index to x,y +- `isValidPos(pos: Position): boolean` - Check if position is on board + +### Commit + +- Define empty game shell with types +- Export state interface + +--- + +## Phase 2: Board Setup & Scenarios + +**Files:** `Game.ts`, `scenarios.ts` + +### Scenarios Module + +```typescript +// scenarios.ts +interface Scenario { + name: string; + description: string; + pawns: Position[]; // Pawn positions +} + +export const scenarios: Record = { + simple: { + name: 'Simple', + description: 'A basic symmetric layout with minimal pawns', + pawns: [ + // Symmetric pawn placement for 8x8 + ], + }, +}; + +export const defaultScenario = 'simple'; +``` + +### Setup Function + +```typescript +setup: () => ({ + cells: initializeBoardWithPawns(scenarios[defaultScenario].pawns), + kings: { + '0': { x: 4, y: 0 }, // Player 0 King at row 0 + '1': { x: 4, y: 7 }, // Player 1 King at row 7 + }, + pieces: { + '0': createInitialPieces(), // 2B, 2R, 2N, 1Q all Ready + '1': createInitialPieces(), + }, + committedPieces: [], +}) +``` + +### Simple Scenario Design (8x8) + +``` +Row 7: [ ][ ][ ][ ][K1][ ][ ][ ] ← Player 1's King +Row 6: [ ][ ][ ][ ][ ][ ][ ][ ] +Row 5: [ ][ ][P][ ][ ][P][ ][ ] ← Pawns +Row 4: [ ][ ][ ][ ][ ][ ][ ][ ] +Row 3: [ ][ ][ ][ ][ ][ ][ ][ ] +Row 2: [ ][ ][P][ ][ ][P][ ][ ] ← Pawns +Row 1: [ ][ ][ ][ ][ ][ ][ ][ ] +Row 0: [ ][ ][ ][ ][K0][ ][ ][ ] ← Player 0's King + 0 1 2 3 4 5 6 7 +``` + +### Commit + +- Scenarios module with simple scenario +- Setup function initializing board state + +--- + +## Phase 8: Unit Tests (Write Tests First) + +**File:** `Game.test.ts` + +Write tests BEFORE implementing moves. Tests define expected behavior. + +### Test Categories + +#### 1. Position Helpers +```typescript +describe('position helpers', () => { + it('converts position to index correctly'); + it('converts index to position correctly'); + it('validates board positions'); +}); +``` + +#### 2. Commit Validation +```typescript +describe('isValidCommitSquare', () => { + describe('bishop commits', () => { + it('allows diagonal squares from King'); + it('rejects non-diagonal squares'); + it('rejects squares blocked by pawns'); // If applicable + }); + + describe('rook commits', () => { + it('allows orthogonal squares from King'); + it('rejects non-orthogonal squares'); + }); + + describe('knight commits', () => { + it('allows L-shaped squares from King'); + it('rejects non-L-shaped squares'); + }); + + describe('queen commits', () => { + it('allows diagonal and orthogonal squares'); + }); +}); +``` + +#### 3. Strike Resolution +```typescript +describe('resolveStrike', () => { + it('destroys first pawn in path'); + it('destroys opponent King in path'); + it('misses if no target in path'); + it('stops at first obstacle'); + it('ray-casts from current King position'); +}); +``` + +#### 4. Knight Resolution +```typescript +describe('resolveKnight', () => { + it('moves King to committed square'); + it('fails if square not valid L-jump from current position'); + it('destroys pawn at landing square'); + it('destroys opponent King at landing square'); +}); +``` + +#### 5. KingStep Resolution +```typescript +describe('resolveKingStep', () => { + it('moves King one square'); + it('allows all 8 directions'); + it('destroys pawn when entering pawn square'); + it('destroys opponent King when stepping on'); + it('rejects moves off board'); +}); +``` + +#### 6. Piece State Transitions +```typescript +describe('piece state transitions', () => { + it('commit changes piece from Ready to Committed'); + it('execute changes piece from Committed to Exhausted'); + it('pass changes Committed to Exhausted'); + it('pass changes Exhausted to Ready'); +}); +``` + +#### 7. Win Conditions +```typescript +describe('win conditions', () => { + it('detects win when opponent King destroyed by Strike'); + it('detects win when opponent King destroyed by Knight'); + it('detects win when opponent King destroyed by KingStep'); + it('returns no winner when both Kings alive'); +}); +``` + +### Commit + +- Full test suite with pending/skeleton tests +- Tests define expected behavior for Phase 3 + +--- + +## Phase 3: Game Moves Implementation + +**File:** `Game.ts` + +### Move: `commit` + +```typescript +commit: ({ G, playerID }, pieceId: number, target: Position) => { + // 1. Validate piece exists and is Ready + // 2. Validate target is valid for piece type from King position + // 3. Update piece state to Committed + // 4. Add to committedPieces array +} +``` + +### Move: `execute` + +```typescript +execute: ({ G, playerID }, pieceIds: number[]) => { + // 1. Validate all pieces are Committed and belong to player + // 2. Separate into strikes and knights + // 3. Resolve all strikes (simultaneously) + // - Ray-cast from King toward each target + // - Destroy first obstacle (pawn or opponent King) + // 4. Resolve knights sequentially + // - Validate L-jump from current King position + // - Move King, destroy any pawn at destination + // 5. Mark executed pieces as Exhausted + // 6. Mark unchosen Committed pieces as Exhausted +} +``` + +### Move: `kingStep` + +```typescript +kingStep: ({ G, playerID }, direction: Position) => { + // 1. Validate direction is 1 square (orthogonal/diagonal) + // 2. Calculate target position + // 3. Validate target is on board + // 4. Move King, destroy pawn if present + // 5. Check if opponent King destroyed + // 6. Mark all Committed pieces as Exhausted +} +``` + +### Move: `pass` + +```typescript +pass: ({ G, playerID }) => { + // 1. Mark all Committed pieces as Exhausted + // 2. Mark all Exhausted pieces as Ready +} +``` + +### Helper Functions + +- `isValidBishopTarget(kingPos, target): boolean` +- `isValidRookTarget(kingPos, target): boolean` +- `isValidKnightTarget(kingPos, target): boolean` +- `isValidQueenTarget(kingPos, target): boolean` +- `raycast(from, toward, cells): Position | null` - Find first hit +- `isValidKnightJump(from, to): boolean` + +### Commit + +- All move implementations +- All helper functions +- Tests should pass + +--- + +## Phase 4: Win Condition Logic + +**File:** `Game.ts` + +### endIf Implementation + +```typescript +endIf: ({ G }) => { + if (G.kings['0'] === null) { + return { winner: '1' }; + } + if (G.kings['1'] === null) { + return { winner: '0' }; + } + return undefined; // Game continues +} +``` + +### Commit + +- Win condition checks +- endIf function + +--- + +## Phase 5: Board UI + +**File:** `Board.tsx` + +### Component Structure + +```tsx +function Board({ G, ctx, moves, playerID }: BoardProps) { + // State for UI interaction + const [selectedAction, setSelectedAction] = useState(null); + const [selectedPiece, setSelectedPiece] = useState(null); + const [selectedPiecesToExecute, setSelectedPiecesToExecute] = useState([]); + + return ( +
+

Mecha Duel

+ + + setSelectedAction('commit')} + onExecute={() => setSelectedAction('execute')} + onKingStep={() => setSelectedAction('kingstep')} + onPass={() => moves.pass()} + /> + +
+ ); +} +``` + +### Sub-components + +1. **StatusDisplay** - Current player, turn info, winner +2. **GameBoard** - 8x8 grid with Kings, Pawns, committed pieces +3. **ActionPanel** - Buttons for Commit/Execute/KingStep/Pass +4. **PieceSupply** - Shows player's pieces with states + +### Visual Elements + +- Grid with coordinates (a-h, 1-8 or 0-7) +- Kings shown with distinct colors per player +- Pawns as obstacles +- Committed pieces shown on target squares with indicators +- Valid move highlights when selecting + +### Commit + +- Board component with full UI +- Styling for game elements + +--- + +## Phase 6: AI Support + +**File:** `Game.ts` + +### ai.enumerate Implementation + +```typescript +ai: { + enumerate: (G: MechaDuelState, ctx: Ctx) => { + const moves: Array<{ move: string; args: unknown[] }> = []; + const playerID = ctx.currentPlayer; + const kingPos = G.kings[playerID as '0' | '1']; + + if (!kingPos) return moves; // King destroyed, no moves + + // Enumerate all valid Commit moves + for (const piece of G.pieces[playerID]) { + if (piece.state === 'ready') { + for (const target of getValidCommitTargets(piece.type, kingPos, G)) { + moves.push({ move: 'commit', args: [piece.id, target] }); + } + } + } + + // Enumerate Execute combinations + const committedPieces = G.committedPieces.filter(p => p.playerId === playerID); + if (committedPieces.length > 0) { + // All non-empty subsets of committed pieces + for (const subset of powerSet(committedPieces)) { + if (subset.length > 0) { + moves.push({ move: 'execute', args: [subset.map(p => p.pieceId)] }); + } + } + } + + // Enumerate KingStep moves (8 directions) + for (const dir of DIRECTIONS) { + const target = { x: kingPos.x + dir.x, y: kingPos.y + dir.y }; + if (isValidPos(target)) { + moves.push({ move: 'kingStep', args: [dir] }); + } + } + + // Pass is always valid + moves.push({ move: 'pass', args: [] }); + + return moves; + }, +} +``` + +### Commit + +- AI enumerate function +- Helper functions for move enumeration + +--- + +## Phase 7: Registration + +**File:** `src/registry.ts` + +### Updates + +```typescript +import * as MechaDuel from './games/mecha-duel'; +import MechaDuelRules from './games/mecha-duel/rules.md?raw'; + +export const games: Record = { + // ... existing games + 'mecha-duel': { + game: MechaDuel.game, + Board: MechaDuel.Board, + name: 'Mecha Duel', + description: 'Strategic mecha combat with committed attacks', + minPlayers: 2, + maxPlayers: 2, + rules: MechaDuelRules, + }, +}; +``` + +**File:** `src/games/mecha-duel/index.ts` + +```typescript +export { MechaDuel as game, type MechaDuelState } from './Game'; +export { Board } from './Board'; +``` + +### Commit + +- Index exports +- Registry entry +- Game playable in UI + +--- + +## Implementation Order Summary + +1. **Phase 1:** Core Types & State → Commit +2. **Phase 2:** Board Setup & Scenarios → Commit +3. **Phase 8:** Unit Tests (skeleton/pending) → Commit +4. **Phase 3:** Game Moves Implementation → Commit (tests pass) +5. **Phase 4:** Win Condition Logic → Commit +6. **Phase 5:** Board UI → Commit +7. **Phase 6:** AI Support → Commit +8. **Phase 7:** Registration → Commit + +--- + +## Notes + +- Use MCTSBot for AI (turn-based game) +- Follow existing patterns from tic-tac-toe and rock-paper-scissors +- Keep UI simple but functional +- Ensure all tests pass before moving to next phase diff --git a/src/games/mecha-duel/rules.md b/src/games/mecha-duel/rules.md index e38e622..9e3143d 100644 --- a/src/games/mecha-duel/rules.md +++ b/src/games/mecha-duel/rules.md @@ -1,9 +1,9 @@ -# Mecha Game - Version ALPHA Rules +# Mecha Duel - Version ALPHA Rules ## Setup -- 8×12 board (or 8×8 for faster games) -- Each player starts with King on opposite ends -- Scenario determines pawn placement (static obstacles/stakes) +- 8×8 board +- Each player starts with King on opposite ends (row 0 and row 7) +- Scenario determines pawn placement (static obstacles) - Each player has piece supply: 2 Bishops, 2 Rooks, 2 Knights, 1 Queen (all start Ready) ## Piece States @@ -18,21 +18,24 @@ ## Turn Structure On your turn, choose ONE action: -1. **Commit:** Place one Ready piece on a board square showing direction/target - - Bishops: diagonal lines from King - - Rooks: orthogonal lines from King - - Knights: L-shaped landing square from King - - Queen: diagonal or orthogonal line from King +1. **Commit:** Place one Ready piece on a target square + - Bishops: any square on a diagonal line from King's current position + - Rooks: any square on an orthogonal line from King's current position + - Knights: any square an L-shaped jump from King's current position + - Queen: any square on a diagonal or orthogonal line from King's current position + - The committed piece marks the target; the attack direction is determined at execution time 2. **Execute:** Choose one or more Committed pieces to resolve - - All Strikes (Bishop/Rook/Queen) resolve from King's current position - - Strike destroys first piece on its line (opponent's King or pawn) - - Knights jump King to committed square - - Resolve all chosen Strikes, then Knight (if chosen) + - All Strikes (Bishop/Rook/Queen) resolve from King's position at execution time + - Strike ray-casts from King toward the committed square, destroying first piece hit (opponent's King or pawn) + - Knights jump King to committed square (must be valid L-jump from current King position at execution time) + - Knight landing on a pawn destroys the pawn + - Resolve all chosen Strikes first, then all Knights in order chosen - Executed pieces → Exhausted - Unchosen Committed pieces → Exhausted (discarded) -3. **KingStep:** Move King 1 square (cannot enter pawn squares) +3. **KingStep:** Move King 1 square orthogonally or diagonally + - Can enter pawn squares, destroying the pawn - Destroys opponent's King if stepped on - All Committed pieces → Exhausted (discarded) @@ -41,11 +44,29 @@ On your turn, choose ONE action: - All Exhausted pieces → Ready ## Key Mechanics -- Pawns block King movement and stop Strike attacks (first obstacle hit) -- Commitments lock to board squares when placed -- King can only be in one place (can only execute one Knight per turn) +- Pawns stop Strike attacks (first obstacle hit destroys the pawn) +- Commit squares are chosen at commit time; attack validity/direction computed at execution time +- Knights: committed square must be a valid L-jump from King's position at execution time (not commit time) +- Multiple Knights can be executed in one turn; they resolve sequentially, each jumping King to its committed square - All pieces follow cooldown cycle: Ready → Committed → Exhausted → (Pass) → Ready +--- + +## Clarifications (Implementation Notes) + +1. **Commit Targeting:** When committing a piece, you select a target square. For Strikes (Bishop/Rook/Queen), the attack ray-casts from the King's position at execution time toward that target square. This means the King can move (via earlier Knight execution) before a Strike resolves. + +2. **Multiple Knights:** You may execute multiple Knights in one Execute action. They resolve sequentially—each jumps the King to its committed square. The second Knight's committed square must be valid from the King's new position after the first Knight resolves. + +3. **Knight Validity:** A Knight's committed square is only validated at execution time. If the King has moved such that the committed square is no longer a valid L-jump, that Knight cannot be executed. + +4. **Pawn Destruction:** Pawns can be destroyed by: + - Strike attacks (first piece in the ray path) + - Knight landing on a pawn square + - KingStep entering a pawn square + +5. **Execution Order:** When executing, all Strikes resolve first (simultaneously), then Knights resolve in the order chosen by the player. + ---- Author: Dev Purkayastha, 2026 \ No newline at end of file