This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
@gamepark/rules-api is a TypeScript library that provides an API for implementing board game rules on the Game Park platform. It uses a material-oriented approach where game state and moves are structured around game material items (cards, tokens, dice, etc.) and their movements.
yarn build- Compile TypeScript to JavaScript (output indist/)yarn test- Run tests with Vitestyarn test:watch- Run tests in watch modeyarn test:coverage- Run tests with coverage report
To run a specific test file:
yarn test src/tests/MaterialMoney.test.tsTo run tests matching a pattern:
yarn test -t "pattern"The library implements three main approaches to building game rules:
- Basic Rules API (
Rulesclass) - Minimal API for any board game - Material Rules (
MaterialRulesclass) - Recommended material-oriented approach - Hidden/Secret Material Rules - Extensions for games with hidden information
The material approach structures games around items that exist at locations and are manipulated through moves:
MaterialGame {
players: Player[] // Player identifiers
items: Record<M, Item[]> // All game items by type
rule?: RuleStep<P, R> // Current rule step
memory: Record<any, any> // Free-form state storage
}
Rules<Game, Move, PlayerId> - Base class for all game implementations
isTurnToPlay(playerId)- Check if player is activegetLegalMoves(playerId)- List legal moves for a playerplay(move)- Execute a move and return consequencesisOver()- Check if game is finished- Supports delegation pattern to split rules into smaller parts
MaterialRules<Player, MaterialType, LocationType, RuleId> - Material-oriented rules
- Extends
Ruleswith material-specific features rules: Record<RuleId, MaterialRulesPartCreator>- Maps rule IDs to rule implementationsmaterial(type)- GetMaterialhelper for manipulating itemsmemorize(key, value, player?)- Store temporary stateremind(key, player?)- Retrieve memorized statelocationsStrategies- Define automatic positioning rules
MaterialRulesPart<Player, MaterialType, LocationType, RuleId> - Small rule segments
- Implements one specific phase/step of the game
beforeItemMove(move)- Hook before item movesafterItemMove(move)- Hook after item movesonRuleStart(move)- Called when this rule beginsonRuleEnd(move)- Called when this rule endsonCustomMove(move)- Handle custom game-specific moves
Material<Player, MaterialType, LocationType> - Immutable item manipulation helper
- Fluent API for filtering and moving items
.location(type)- Filter by location.player(id)- Filter by owner.id(value)- Filter by item ID.getItems()- Collect filtered items.moveItem(index, location)- Create move for single item.moveItems(location)- Create move for all filtered items
GameSetup<Game, Options> / MaterialGameSetup - Initialize game state
setup(options)- Create initial game state from optionssetupMaterial(options)- Create material items (override in subclass)start(options)- Start first rule (override in subclass)playMove(move)- Execute moves during setup
All moves extend MaterialMove which includes:
- ItemMove - Create, delete, move, roll, shuffle, or select items
- RuleMove - Start player turn, start simultaneous rule, end game
- CustomMove - Game-specific custom moves
- LocalMove - Client-side only moves (UI state)
Moves produce consequences - additional moves that execute automatically after the original move.
Location strategies maintain consistent item positioning:
PositiveSequenceStrategy- Items arranged in sequence without gaps (e.g., cards in hand)FillGapStrategy- Automatically fill gaps when items removed (e.g., card rivers)StackingStrategy- Items stack at same position
Define in MaterialRules.locationsStrategies: Partial<Record<MaterialType, Partial<Record<LocationType, LocationStrategy>>>>
Use memory for state that doesn't fit in items or rule properties:
memorize(key, value, player?)- Store valueremind(key, player?)- Retrieve valueforget(key, player?)- Delete value
Memory keys are typically numeric enum values.
Grid utilities (src/utils/grid.*.util.ts)
grid.util.ts- Square grid helpersgrid.hex.util.ts- Hexagonal grid coordinate systems (Axial, OddQ, EvenQ, OddR, EvenR)grid.squares.util.ts- Square grid specific utilities- Functions for neighbors, distances, coordinate conversions
Other utilities
adjacent-groups.util.ts- Find connected groups of itemsneighbors.util.ts- Get adjacent positionsmoney.util.ts- Currency/token calculationsrandom.util.ts- Random value generation
Most classes use consistent generic type parameters:
P/Player- Player identifier (number or numeric enum)M/MaterialType- Numeric enum of material typesL/LocationType- Numeric enum of location typesR/RuleId- Numeric enum of rule step identifiersId- Item identifier type (varies per item)
Tests use Vitest with Node environment. Test files are excluded from TypeScript compilation (see tsconfig.json).
Example test structure:
import { describe, expect, it } from 'vitest'
describe('FeatureName', () => {
it('should do something', () => {
// test code
})
})The framework uses delegation extensively to split complex rules into manageable parts:
Rules.delegate()- Return single delegateRules.delegates()- Return array of delegates- Default behavior calls delegates first, then own implementation
- Used for
isTurnToPlay,getLegalMoves,play,isOver
- RandomMove - Moves with unpredictable outcomes (dice rolls, shuffles)
randomize(move)- Server-side randomization before broadcastisUnpredictableMove(move, player)- Client shouldn't predict outcome- Roll moves default to 6-sided dice (0-5), override
roll()for custom dice
canUndo(action, consecutiveActions)- Determine if action can be undone- Default: cannot undo if player became active or dice rolled
moveBlocksUndo(move, player)- Check if specific move blocks undo- SelectItem moves can be undone in sequence
Uses Yarn 4.9.4. Package is scoped as @gamepark/rules-api with public access. Build runs automatically before yarn pack.