Skip to content

@ludus/game-engine

The core game engine package. Provides the Game<TState, TAction> interface, deterministic SeededRNG, GameLoopController, event system, and replay recording/playback.

Terminal window
npm install @ludus/game-engine

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) { /* ... */ },
};

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)

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>
interface GameResult<TState> {
gameId: string;
winner: Player | null;
rankings: PlayerRanking[];
replay: GameReplay;
finalState: TState;
totalTurns: number;
}

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'] });

game_start · game_over · action_executed · player_eliminated · turn_skipped · turn_timeout · invalid_action

Records actions during a game for later playback.

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 deterministic
interface GameReplay {
replayId: string;
gameType: string;
seed: number;
players: Player[];
actions: ReplayEntry[];
finalRankings: PlayerRanking[];
metadata: { startedAt: number; endedAt: number; totalTurns: number; durationMs: number };
}

StateManager validates that executeAction returns a new state object (not the same reference). Throws StateImmutabilityError on violation.

ErrorWhen
TurnTimeoutErrorAgent exceeds turn time limit
StateImmutabilityErrorGame mutates state in-place
InvalidActionErrorAgent plays an invalid action
NoValidActionsErrorNo valid actions available
GameInitializationErrorGame fails to initialize
ReplayVerificationErrorReplay doesn’t match deterministic expectation
// Core
import type { Game, BaseGameState, BaseAction, GameConfig, GameMetadata, Player, PlayerRanking, GameResult, GameReplay, ReplayEntry, DecisionContext, AgentAdapter } from '@ludus/game-engine';
// Events
import type { GameEvent, GameEventType, EventSink } from '@ludus/game-engine';