# boardgame-core A state management library for board games using [Preact Signals](https://preactjs.com/guide/v10/signals/). ## Features - **Reactive State Management**: Fine-grained reactivity powered by [@preact/signals-core](https://preactjs.com/guide/v10/signals/) - **Type Safe**: Full TypeScript support with strict mode - **Entity Collections**: Signal-backed collections for managing game pieces (cards, dice, tokens, meeples, etc.) - **Region System**: Spatial management with multi-axis positioning, alignment, and shuffling - **Command Parsing**: CLI-style command parsing with schema validation and type coercion - **Rule Engine**: Generator-based rule system with reactive context management - **Deterministic RNG**: Seeded pseudo-random number generator (Mulberry32) for reproducible game states ## Installation ```bash npm install boardgame-core ``` ## Usage ### Game Context ```ts import { createGameContext } from 'boardgame-core'; const game = createGameContext({ type: 'game' }); // Access entity collections game.parts.add({ id: 'card1', sides: 2, side: 0, region: /* ... */, position: [0] }); game.regions.add({ id: 'hand', axes: [{ name: 'slot', min: 0, max: 7, align: 'start' }], children: [] }); // Context stack for nested rule scopes game.pushContext({ type: 'combat' }); const combatCtx = game.latestContext('combat'); game.popContext(); ``` ### Parts (Cards, Dice, Tokens) ```ts import { flip, flipTo, roll, createRNG } from 'boardgame-core'; const rng = createRNG(42); flip(card); // cycle to next side flipTo(card, 0); // set to specific side roll(dice, rng); // random side using seeded RNG ``` ### Regions & Alignment ```ts import { applyAlign, shuffle } from 'boardgame-core'; // Compact cards in a hand towards the start applyAlign(handRegion); // Shuffle positions of all parts in a region shuffle(handRegion, rng); ``` ### Command Parsing ```ts import { parseCommand, parseCommandSchema, validateCommand } from 'boardgame-core'; // Parse a command string const cmd = parseCommand('move card1 hand --force -x 10'); // { name: 'move', params: ['card1', 'hand'], flags: { force: true }, options: { x: '10' } } // Define and validate against a schema const schema = parseCommandSchema('move [--force] [-x: number]'); const result = validateCommand(cmd, schema); // { valid: true } ``` ### Entity Collections ```ts import { createEntityCollection } from 'boardgame-core'; const collection = createEntityCollection(); collection.add({ id: 'a', name: 'Item A' }, { id: 'b', name: 'Item B' }); const accessor = collection.get('a'); console.log(accessor.value); // reactive access collection.remove('a'); ``` ### Rule Engine ```ts import { createRule, invokeRuleContext } from 'boardgame-core'; const myRule = createRule('drawCard', (ctx) => { // yield action types to pause and wait for external handling const action = yield 'draw'; ctx.resolution = action; }); const result = invokeRuleContext( game.pushContext.bind(game), 'drawCard', myRule({ type: 'drawCard', actions: [], handledActions: 0, invocations: [] }) ); ``` ### Random Number Generation ```ts import { createRNG } from 'boardgame-core'; const rng = createRNG(12345); rng.nextInt(6); // 0-5 rng.next(); // [0, 1) rng.next(100); // [0, 100) rng.setSeed(999); // reseed ``` ## API Reference ### Core | Export | Description | |---|---| | `createGameContext(root?)` | Create a new game context instance | | `GameContext` | The game context model class | | `Context` | Base context type | ### Parts | Export | Description | |---|---| | `Part` | Entity type representing a game piece (card, die, token, etc.) | | `flip(part)` | Cycle to the next side | | `flipTo(part, side)` | Set to a specific side | | `roll(part, rng)` | Randomize side using RNG | ### Regions | Export | Description | |---|---| | `Region` | Entity type for spatial grouping of parts | | `RegionAxis` | Axis definition with min/max/align | | `applyAlign(region)` | Compact parts according to axis alignment | | `shuffle(region, rng)` | Randomize part positions | ### Rules | Export | Description | |---|---| | `RuleContext` | Rule execution context type | | `createRule(type, fn)` | Create a rule generator factory | | `invokeRuleContext(pushContext, type, rule)` | Execute a rule with context management | ### Commands | Export | Description | |---|---| | `parseCommand(input)` | Parse a command string into a `Command` object | | `parseCommandSchema(schema)` | Parse a schema string into a `CommandSchema` | | `validateCommand(cmd, schema)` | Validate a command against a schema | ### Utilities | Export | Description | |---|---| | `createEntityCollection()` | Create a reactive entity collection | | `createRNG(seed?)` | Create a seeded RNG instance | | `Mulberry32RNG` | Mulberry32 PRNG class | ## Scripts ```bash npm run build # Build with tsup npm run test # Run tests in watch mode npm run test:run # Run tests once npm run typecheck # Type check with TypeScript ``` ## License MIT