@ludus/game-engine
The core game engine package. Provides the Game<TState, TAction> interface, deterministic SeededRNG, GameLoopController, event system, and replay recording/playback.
Installation
Section titled “Installation”npm install @ludus/game-engineGame Interface
Section titled “Game Interface”See Game Interface for a full walkthrough.
import type { Game, BaseGameState, BaseAction, GameConfig, Player, SeededRNG } from '@ludus/game-engine';
interface MyState extends BaseGameState { /* ... */ }interface MyAction extends BaseAction { /* ... */ }
const myGame: Game<MyState, MyAction> = { metadata: { name: 'My Game', description: '...', playerRange: { min: 2, max: 4 }, avgDurationMinutes: 15, category: 'strategy', version: '1.0.0' }, initialize(config, players, rng) { /* ... */ }, getValidActions(state, playerId) { /* ... */ }, executeAction(state, action, rng) { /* ... */ }, isGameOver(state) { /* ... */ }, getWinner(state) { /* ... */ }, getRankings(state) { /* ... */ }, describeAction(action, state) { /* ... */ },};SeededRNG
Section titled “SeededRNG”Mulberry32 deterministic random number generator.
import { SeededRNG } from '@ludus/game-engine';
const rng = new SeededRNG(42);rng.next(); // Float in [0, 1)rng.range(1, 6); // Integer in [1, 6]rng.shuffle([1,2,3]); // Deterministic shuffle (in-place)GameLoopController
Section titled “GameLoopController”Runs a game to completion with any set of AgentAdapter implementations.
import { GameLoopController } from '@ludus/game-engine';
const controller = new GameLoopController(myGame);const result = await controller.run(agents, { seed: 42, maxTurns: 100, turnTimeoutMs: 5000 });// result: GameResult<MyState>GameResult
Section titled “GameResult”interface GameResult<TState> { gameId: string; winner: Player | null; rankings: PlayerRanking[]; replay: GameReplay; finalState: TState; totalTurns: number;}Event System
Section titled “Event System”GameEventEmitter broadcasts typed events to EventSink implementations.
import { GameEventEmitter, NoopSink } from '@ludus/game-engine';import type { EventSink, GameEvent } from '@ludus/game-engine';
const emitter = new GameEventEmitter();emitter.addSink(mySink);await emitter.emit('game_start', 'game-123', { players: ['Alice', 'Bob'] });Event Types
Section titled “Event Types”game_start · game_over · action_executed · player_eliminated · turn_skipped · turn_timeout · invalid_action
Replay System
Section titled “Replay System”ReplayRecorder
Section titled “ReplayRecorder”Records actions during a game for later playback.
ReplayPlayer
Section titled “ReplayPlayer”Replays a recorded game, verifying determinism.
import { ReplayPlayer } from '@ludus/game-engine';
const player = new ReplayPlayer(myGame);const verified = await player.verify(replay); // true if replay is deterministicGameReplay
Section titled “GameReplay”interface GameReplay { replayId: string; gameType: string; seed: number; players: Player[]; actions: ReplayEntry[]; finalRankings: PlayerRanking[]; metadata: { startedAt: number; endedAt: number; totalTurns: number; durationMs: number };}State Management
Section titled “State Management”StateManager validates that executeAction returns a new state object (not the same reference). Throws StateImmutabilityError on violation.
Error Types
Section titled “Error Types”| Error | When |
|---|---|
TurnTimeoutError | Agent exceeds turn time limit |
StateImmutabilityError | Game mutates state in-place |
InvalidActionError | Agent plays an invalid action |
NoValidActionsError | No valid actions available |
GameInitializationError | Game fails to initialize |
ReplayVerificationError | Replay doesn’t match deterministic expectation |
// Coreimport type { Game, BaseGameState, BaseAction, GameConfig, GameMetadata, Player, PlayerRanking, GameResult, GameReplay, ReplayEntry, DecisionContext, AgentAdapter } from '@ludus/game-engine';
// Eventsimport type { GameEvent, GameEventType, EventSink } from '@ludus/game-engine';