boardgame-core
A state management library for board games using Preact Signals.
Features
- Reactive State Management: Fine-grained reactivity powered by @preact/signals-core
- 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
npm install boardgame-core
Usage
Game Context
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)
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
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
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 <from> <to> [--force] [-x: number]');
const result = validateCommand(cmd, schema);
// { valid: true }
Entity Collections
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
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
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<T> |
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<T>() |
Create a reactive entity collection |
createRNG(seed?) |
Create a seeded RNG instance |
Mulberry32RNG |
Mulberry32 PRNG class |
Scripts
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