@pompidup/kingdomino-engine 2.3.0 → 3.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.ts CHANGED
@@ -1,87 +1,201 @@
1
+ /**
2
+ * All possible ground/terrain types in Kingdomino.
3
+ * Each type can form contiguous properties for scoring.
4
+ */
1
5
  declare const GROUND: readonly ["castle", "wheat", "forest", "sea", "plain", "swamp", "mine"];
6
+ /** Union type of all terrain types */
2
7
  type Ground = (typeof GROUND)[number];
8
+ /** Special ground type for empty/unplaced tiles */
3
9
  type EmptyGround = "empty";
10
+ /** Number of crowns on a tile (0-3) */
4
11
  type Crown = number;
12
+ /**
13
+ * Represents a single tile with terrain and crowns.
14
+ * Two tiles form a domino.
15
+ */
5
16
  type Tile = {
17
+ /** The terrain type of this tile */
6
18
  type: Ground;
19
+ /** Number of crowns (0-3), multiplier for scoring */
7
20
  crowns: Crown;
8
21
  };
22
+ /**
23
+ * Represents an empty/unplaced tile on the kingdom grid.
24
+ */
9
25
  type EmptyTile = {
26
+ /** Always "empty" for unplaced tiles */
10
27
  type: EmptyGround;
28
+ /** Empty tiles have no crowns */
11
29
  crowns: 0;
12
30
  };
31
+ /**
32
+ * Represents a domino piece consisting of two connected tiles.
33
+ * Dominoes are the main game pieces that players place on their kingdoms.
34
+ */
13
35
  type Domino = {
36
+ /** The left tile of the domino */
14
37
  left: Tile;
38
+ /** The right tile of the domino */
15
39
  right: Tile;
40
+ /** Unique number identifying this domino (1-48 in Classic mode) */
16
41
  number: number;
17
42
  };
43
+ /**
44
+ * Represents a domino that has been revealed for player selection.
45
+ * Contains selection state for the current turn.
46
+ */
18
47
  type RevealsDomino = {
48
+ /** The actual domino piece */
19
49
  domino: Domino;
50
+ /** Whether this domino has been picked by a lord */
20
51
  picked: boolean;
52
+ /** ID of the lord who picked this domino, or null if unpicked */
21
53
  lordId: string | null;
54
+ /** Display position in the revealed set (for UI ordering) */
22
55
  position: number;
23
56
  };
24
57
 
58
+ /**
59
+ * Represents a position on the kingdom grid.
60
+ * Origin (0,0) is at the top-left corner.
61
+ */
25
62
  type Position = {
63
+ /** Column index (0-8, left to right) */
26
64
  x: number;
65
+ /** Row index (0-8, top to bottom) */
27
66
  y: number;
28
67
  };
29
- type Orientation = "vertical" | "horizontal";
30
- type Rotation = 0 | 180;
68
+ /**
69
+ * Rotation angle for domino placement.
70
+ * - 0: Horizontal, left tile at position, right tile to the east
71
+ * - 90: Vertical, left tile at position, right tile to the south
72
+ * - 180: Horizontal, right tile at position, left tile to the east
73
+ * - 270: Vertical, right tile at position, left tile to the south
74
+ */
75
+ type Rotation = 0 | 90 | 180 | 270;
76
+ /**
77
+ * Represents a player's kingdom as a 2D grid of tiles.
78
+ * The grid is 9x9 to allow for the 5x5 maximum kingdom size
79
+ * with the castle at any position.
80
+ */
31
81
  type Kingdom = (Tile | EmptyTile)[][];
82
+ /**
83
+ * The size of the kingdom grid (9x9).
84
+ * This allows for a 5x5 kingdom with the castle placed anywhere.
85
+ */
32
86
  declare const GRIDSIZE = 9;
33
87
 
88
+ /**
89
+ * Represents a lord (player token) used for turn order and domino selection.
90
+ * In games with 2 players, each player has 2 lords.
91
+ * In games with 3-4 players, each player has 1 lord.
92
+ */
34
93
  type Lord = {
94
+ /** Unique identifier for this lord */
35
95
  id: string;
96
+ /** ID of the player who owns this lord */
36
97
  playerId: string;
98
+ /** Whether this lord has finished their turn */
37
99
  turnEnded: boolean;
100
+ /** Whether this lord has picked a domino this round */
38
101
  hasPick: boolean;
102
+ /** Whether this lord has placed/discarded a domino this round */
39
103
  hasPlace: boolean;
104
+ /** The domino this lord picked (if any) */
40
105
  dominoPicked?: Domino;
41
106
  };
42
107
 
108
+ /**
109
+ * Represents a game mode with its configuration.
110
+ * Currently only "Classic" mode is available.
111
+ */
43
112
  type GameMode = {
113
+ /** Name of the game mode (e.g., "Classic") */
44
114
  name: string;
115
+ /** Description of the game mode */
45
116
  description: string;
46
117
  };
47
118
 
48
119
  type ObjectValues<T> = T[keyof T];
49
120
 
121
+ /**
122
+ * Represents a player in the game.
123
+ */
50
124
  type Player = {
125
+ /** Unique identifier for the player */
51
126
  id: string;
127
+ /** Display name of the player */
52
128
  name: string;
129
+ /** The player's kingdom grid where dominoes are placed */
53
130
  kingdom: Kingdom;
54
131
  };
132
+ /**
133
+ * Payload for creating a new player.
134
+ */
55
135
  type PlayerPayload = {
136
+ /** Display name for the player (minimum 3 characters) */
56
137
  name: string;
57
138
  };
139
+ /** Array of player creation payloads */
58
140
  type PlayersPayload = PlayerPayload[];
141
+ /** Array of players */
59
142
  type Players = Player[];
143
+ /**
144
+ * Available actions a player can perform during their turn.
145
+ */
60
146
  declare const playerActions: {
147
+ /** Place a previously picked domino on the kingdom */
61
148
  readonly placeDomino: "placeDomino";
149
+ /** Pick a domino from the revealed set */
62
150
  readonly pickDomino: "pickDomino";
151
+ /** Pass/discard when no valid placement exists */
63
152
  readonly pass: "pass";
64
153
  };
154
+ /** Union type of all player actions */
65
155
  type PlayerActions = ObjectValues<typeof playerActions>;
66
156
 
157
+ /**
158
+ * Basic rules that vary based on player count.
159
+ */
67
160
  type BasicRules = {
161
+ /** Number of lords per player */
68
162
  lords: number;
163
+ /** Maximum number of dominoes in the game */
69
164
  maxDominoes: number;
165
+ /** Number of dominoes revealed each turn */
70
166
  dominoesPerTurn: number;
167
+ /** Maximum number of turns in the game */
71
168
  maxTurns: number;
72
169
  };
170
+ /**
171
+ * Optional extra rules that modify gameplay and scoring.
172
+ */
73
173
  type ExtraRule = {
174
+ /** Name of the rule (e.g., "The middle Kingdom", "Harmony") */
74
175
  name: string;
176
+ /** Description of what the rule does */
75
177
  description: string;
178
+ /** Game modes this rule is compatible with */
76
179
  mode: GameMode[];
180
+ /** Maximum number of players for this rule (optional) */
77
181
  playersLimit?: number;
78
182
  };
183
+ /**
184
+ * Complete rules configuration loaded from data source.
185
+ */
79
186
  type Rules = {
187
+ /** Basic rules indexed by player count (2-4) */
80
188
  basic: Record<number, BasicRules>;
189
+ /** Available extra rules */
81
190
  extraRules: ExtraRule[];
82
191
  };
192
+ /**
193
+ * Rules selected for a specific game instance.
194
+ */
83
195
  type SelectedRules = {
196
+ /** The basic rules for the current player count */
84
197
  basic: BasicRules;
198
+ /** Extra rules enabled for this game */
85
199
  extra: ExtraRule[];
86
200
  };
87
201
 
@@ -95,56 +209,121 @@ interface Err {
95
209
  readonly error: string;
96
210
  }
97
211
 
212
+ /**
213
+ * Represents the complete state of a Kingdomino game.
214
+ * This is the main aggregate containing all game data.
215
+ */
98
216
  type Game = {
217
+ /** Unique identifier for the game */
99
218
  id: string;
219
+ /** Remaining dominoes in the draw pile */
100
220
  dominoes: Domino[];
221
+ /** Currently revealed dominoes available for picking */
101
222
  currentDominoes: RevealsDomino[];
223
+ /** Players participating in the game with their kingdoms */
102
224
  players: Player[];
225
+ /** Lords (player tokens) used for turn order and domino selection */
103
226
  lords: Lord[];
227
+ /** Current turn number (1-based) */
104
228
  turn: number;
229
+ /** Next required action or game step */
105
230
  nextAction: NextAction | NextStep;
231
+ /** Active rules for this game */
106
232
  rules: SelectedRules;
233
+ /** Game mode being played */
107
234
  mode: GameMode;
108
235
  };
236
+ /**
237
+ * Available game steps representing phases of game setup and completion.
238
+ */
109
239
  declare const gameSteps: {
240
+ /** Waiting for players to be added */
110
241
  readonly addPlayers: "addPlayers";
242
+ /** Optional rules selection phase */
111
243
  readonly options: "options";
244
+ /** Ready to start the game */
112
245
  readonly start: "start";
246
+ /** Game has ended, results available */
113
247
  readonly result: "result";
114
248
  };
249
+ /** Union type of all possible game steps */
115
250
  type GameSteps = ObjectValues<typeof gameSteps>;
251
+ /**
252
+ * Represents a player action that needs to be performed.
253
+ * Used during active gameplay.
254
+ */
116
255
  type NextAction = {
256
+ /** Discriminator for action type */
117
257
  type: "action";
258
+ /** ID of the lord who must perform the action */
118
259
  nextLord: string;
260
+ /** The action the lord must perform */
119
261
  nextAction: PlayerActions;
120
262
  };
263
+ /**
264
+ * Represents a game phase transition.
265
+ * Used during setup and game end.
266
+ */
121
267
  type NextStep = {
268
+ /** Discriminator for step type */
122
269
  type: "step";
270
+ /** The current game step/phase */
123
271
  step: GameSteps;
124
272
  };
273
+ /** Game state where a player action is required */
125
274
  type GameWithNextAction = Game & {
126
275
  nextAction: NextAction;
127
276
  };
277
+ /** Game state where a phase transition is occurring */
128
278
  type GameWithNextStep = Game & {
129
279
  nextAction: NextStep;
130
280
  };
281
+ /** Union of all possible game states */
131
282
  type GameState = GameWithNextAction | GameWithNextStep;
283
+ /** Result wrapper for game state operations */
132
284
  type GameStateResult = Result<GameState>;
285
+ /**
286
+ * Type guard to check if the game requires a player action.
287
+ * @param game - The game state to check
288
+ * @returns True if the game is waiting for a player action
289
+ */
133
290
  declare const isGameWithNextAction: (game: GameWithNextAction | GameWithNextStep) => game is GameWithNextAction;
291
+ /**
292
+ * Type guard to check if the game is in a phase transition.
293
+ * @param game - The game state to check
294
+ * @returns True if the game is transitioning between phases
295
+ */
134
296
  declare const isGameWithNextStep: (game: GameWithNextAction | GameWithNextStep) => game is GameWithNextStep;
297
+ /**
298
+ * Represents the score calculation for a kingdom.
299
+ */
135
300
  type Score = {
301
+ /** Total points earned */
136
302
  points: number;
303
+ /** Size of the largest contiguous property */
137
304
  maxPropertiesSize: number;
305
+ /** Total number of crowns in the kingdom */
138
306
  totalCrowns: number;
139
307
  };
308
+ /**
309
+ * Score result for a specific player.
310
+ */
140
311
  type ScoreResult = {
312
+ /** Player's unique identifier */
141
313
  playerId: string;
314
+ /** Player's display name */
142
315
  playerName: string;
316
+ /** Detailed score breakdown */
143
317
  details: Score;
144
318
  };
319
+ /**
320
+ * Final result including ranking position.
321
+ */
145
322
  type FinalResult = ScoreResult & {
323
+ /** Player's final position (1 = winner) */
146
324
  position: number;
147
325
  };
326
+ /** Game state with final results calculated */
148
327
  type GameWithResults = Game & {
149
328
  result: FinalResult[];
150
329
  };
@@ -184,7 +363,6 @@ type PlaceDominoCommand = {
184
363
  game: GameState;
185
364
  lordId: string;
186
365
  position: Position;
187
- orientation: Orientation;
188
366
  rotation: Rotation;
189
367
  };
190
368
 
@@ -201,30 +379,549 @@ type CalculateScoreCommand = {
201
379
  kingdom: Kingdom;
202
380
  };
203
381
 
382
+ /**
383
+ * Command to get all valid placements for a domino on a kingdom.
384
+ */
385
+ type GetValidPlacementsCommand = {
386
+ /** The kingdom grid to check placements on */
387
+ kingdom: Kingdom;
388
+ /** The domino to find placements for */
389
+ domino: Domino;
390
+ };
391
+
392
+ /**
393
+ * Command to check if a domino can be placed anywhere on a kingdom.
394
+ */
395
+ type CanPlaceDominoCommand = {
396
+ /** The kingdom grid to check */
397
+ kingdom: Kingdom;
398
+ /** The domino to check placement for */
399
+ domino: Domino;
400
+ };
401
+
402
+ /**
403
+ * Command to serialize a game state to JSON.
404
+ */
405
+ type SerializeGameCommand = {
406
+ /** The game state to serialize */
407
+ game: GameState;
408
+ };
409
+ /**
410
+ * Command to deserialize a game state from JSON.
411
+ */
412
+ type DeserializeGameCommand = {
413
+ /** The JSON string to deserialize */
414
+ json: string;
415
+ };
416
+
417
+ /**
418
+ * Represents a valid placement position with rotation.
419
+ */
420
+ type ValidPlacement = {
421
+ position: Position;
422
+ rotation: Rotation;
423
+ };
424
+
425
+ /**
426
+ * The main interface for interacting with the Kingdomino game engine.
427
+ *
428
+ * This interface provides all methods needed to manage a complete game lifecycle,
429
+ * from creation to scoring. Each method follows a command pattern and returns
430
+ * an updated game state.
431
+ *
432
+ * @example
433
+ * ```typescript
434
+ * import { createGameEngine, isGameWithNextAction } from '@pompidup/kingdomino-engine';
435
+ *
436
+ * const engine = createGameEngine({});
437
+ * let game = engine.createGame({ mode: 'Classic' });
438
+ * game = engine.addPlayers({ game, players: ['Alice', 'Bob'] });
439
+ * game = engine.startGame({ game });
440
+ *
441
+ * while (isGameWithNextAction(game)) {
442
+ * // Handle player actions...
443
+ * }
444
+ *
445
+ * const results = engine.getResults({ game });
446
+ * ```
447
+ */
204
448
  type GameEngine = {
449
+ /**
450
+ * Retrieves all available game modes.
451
+ * @param command - Empty command object
452
+ * @returns Array of available game modes (e.g., 'Classic')
453
+ */
205
454
  getModes: (command: GetModesCommand) => GameMode[];
455
+ /**
456
+ * Retrieves available extra rules for a specific mode and player count.
457
+ * @param command - Contains mode name and number of players
458
+ * @returns Array of applicable extra rules
459
+ */
206
460
  getExtraRules: (command: GetExtraRulesCommand) => ExtraRule[];
461
+ /**
462
+ * Creates a new game instance with the specified mode.
463
+ * This is the first step in setting up a game.
464
+ * @param command - Contains the game mode name
465
+ * @returns Game state with next step 'addPlayers'
466
+ */
207
467
  createGame: (command: CreateGameCommand) => GameWithNextStep;
468
+ /**
469
+ * Adds players to the game and initializes their kingdoms.
470
+ * Each player receives an empty kingdom with a castle at the center.
471
+ * @param command - Contains game state and array of player names (2-4 players)
472
+ * @returns Game state with next step 'options' or 'start'
473
+ * @throws Error if player count is invalid or names are too short
474
+ */
208
475
  addPlayers: (command: AddPlayersCommand) => GameWithNextStep;
476
+ /**
477
+ * Adds optional extra rules to modify gameplay.
478
+ * @param command - Contains game state and array of extra rule names
479
+ * @returns Game state with next step 'start'
480
+ */
209
481
  addExtraRules: (command: AddExtraRulesCommand) => GameWithNextStep;
482
+ /**
483
+ * Starts the game, initializing lords, shuffling dominoes, and revealing the first set.
484
+ * @param command - Contains the game state
485
+ * @returns Game state with first player action required
486
+ */
210
487
  startGame: (command: StartGameCommand) => GameWithNextAction;
488
+ /**
489
+ * Allows a lord to choose a domino from the currently revealed set.
490
+ * The chosen domino is reserved for that lord to place later.
491
+ * @param command - Contains game state, lord ID, and domino number to pick
492
+ * @returns Updated game state with next action
493
+ */
211
494
  chooseDomino: (command: ChooseDominoCommand) => GameWithNextAction;
495
+ /**
496
+ * Places a domino on the lord's kingdom at the specified position and rotation.
497
+ * The domino must be adjacent to an existing tile and match terrain types.
498
+ * @param command - Contains game state, lord ID, position {x, y}, and rotation (0, 90, 180, 270)
499
+ * @returns Updated game state (may transition to 'result' step if game ends)
500
+ * @throws Error if placement is invalid
501
+ */
212
502
  placeDomino: (command: PlaceDominoCommand) => GameState;
503
+ /**
504
+ * Discards a domino when no valid placement exists.
505
+ * The lord forfeits placing the domino this turn.
506
+ * @param command - Contains game state and lord ID
507
+ * @returns Updated game state with next action
508
+ */
213
509
  discardDomino: (command: DiscardDominoCommand) => GameState;
510
+ /**
511
+ * Calculates and returns final results after the game ends.
512
+ * Applies extra rule bonuses and ranks players.
513
+ * @param command - Contains the final game state
514
+ * @returns Game state with complete results including positions
515
+ */
214
516
  getResults: (command: GetResultCommand) => GameWithResults;
517
+ /**
518
+ * Calculates the score for a single kingdom.
519
+ * Useful for showing live scores during gameplay.
520
+ * @param command - Contains the kingdom grid to score
521
+ * @returns Score with points, largest property size, and total crowns
522
+ */
215
523
  calculateScore: (command: CalculateScoreCommand) => Score;
524
+ /**
525
+ * Finds all valid placements for a domino on a kingdom.
526
+ * Useful for AI players or showing available moves to human players.
527
+ * @param command - Contains kingdom and domino to check
528
+ * @returns Array of valid position/rotation combinations
529
+ */
530
+ getValidPlacements: (command: GetValidPlacementsCommand) => ValidPlacement[];
531
+ /**
532
+ * Checks if a domino can be placed anywhere on a kingdom.
533
+ * Quick check before deciding to discard.
534
+ * @param command - Contains kingdom and domino to check
535
+ * @returns True if at least one valid placement exists
536
+ */
537
+ canPlaceDomino: (command: CanPlaceDominoCommand) => boolean;
538
+ /**
539
+ * Serializes the game state to a JSON string for persistence.
540
+ * @param command - Contains the game state to serialize
541
+ * @returns JSON string representation of the game
542
+ */
543
+ serialize: (command: SerializeGameCommand) => string;
544
+ /**
545
+ * Deserializes a JSON string back to a game state.
546
+ * @param command - Contains the JSON string to parse
547
+ * @returns The restored game state
548
+ * @throws Error if the JSON is invalid or incompatible
549
+ */
550
+ deserialize: (command: DeserializeGameCommand) => GameState;
216
551
  };
217
552
 
218
553
  type ShuffleMethod = <T>(array: T[]) => T[];
219
554
 
220
555
  type UuidMethod = () => string;
221
556
 
557
+ type Logger = Readonly<{
558
+ info: (message: string) => void;
559
+ error: (message: string) => void;
560
+ }>;
561
+
562
+ /**
563
+ * Configuration options for the game engine.
564
+ */
222
565
  type EngineConfig = {
566
+ /** Custom UUID generation method. Defaults to crypto.randomUUID() */
223
567
  uuidMethod?: UuidMethod;
568
+ /** Custom shuffle method. Defaults to Fisher-Yates algorithm */
224
569
  shuffleMethod?: ShuffleMethod;
570
+ /** Enable console logging. Defaults to false */
225
571
  logging?: boolean;
572
+ /** Custom logger instance. When provided, takes precedence over 'logging' option */
573
+ logger?: Logger;
226
574
  };
227
575
 
576
+ /**
577
+ * Error codes for domain-level errors.
578
+ * These codes can be used for i18n and programmatic error handling.
579
+ */
580
+ declare const ErrorCode: {
581
+ readonly INVALID_STEP: "INVALID_STEP";
582
+ readonly STEP_EXECUTION_FAILED: "STEP_EXECUTION_FAILED";
583
+ readonly ACTION_EXECUTION_FAILED: "ACTION_EXECUTION_FAILED";
584
+ readonly LORD_NOT_FOUND: "LORD_NOT_FOUND";
585
+ readonly PLAYER_NOT_FOUND: "PLAYER_NOT_FOUND";
586
+ readonly DOMINO_NOT_FOUND: "DOMINO_NOT_FOUND";
587
+ readonly MODE_NOT_FOUND: "MODE_NOT_FOUND";
588
+ readonly INVALID_PLAYER_COUNT: "INVALID_PLAYER_COUNT";
589
+ readonly INVALID_PLAYER_NAME: "INVALID_PLAYER_NAME";
590
+ readonly INVALID_PLACEMENT: "INVALID_PLACEMENT";
591
+ readonly PLACEMENT_NOT_EMPTY: "PLACEMENT_NOT_EMPTY";
592
+ readonly PLACEMENT_NOT_ADJACENT: "PLACEMENT_NOT_ADJACENT";
593
+ readonly PLACEMENT_INVALID_TERRAIN: "PLACEMENT_INVALID_TERRAIN";
594
+ readonly PLACEMENT_OUT_OF_BOUNDS: "PLACEMENT_OUT_OF_BOUNDS";
595
+ readonly NOT_YOUR_TURN: "NOT_YOUR_TURN";
596
+ readonly CANNOT_PICK: "CANNOT_PICK";
597
+ readonly CANNOT_PLACE: "CANNOT_PLACE";
598
+ readonly DOMINO_ALREADY_PICKED: "DOMINO_ALREADY_PICKED";
599
+ };
600
+ type ErrorCodeType = (typeof ErrorCode)[keyof typeof ErrorCode];
601
+ /**
602
+ * Structured domain error with code and context.
603
+ */
604
+ interface DomainError {
605
+ /** Error code for programmatic handling */
606
+ code: ErrorCodeType;
607
+ /** Human-readable error message */
608
+ message: string;
609
+ /** Optional context data for debugging */
610
+ context?: Record<string, unknown>;
611
+ }
612
+ /**
613
+ * Creates a domain error object.
614
+ */
615
+ declare const createDomainError: (code: ErrorCodeType, message: string, context?: Record<string, unknown>) => DomainError;
616
+ /**
617
+ * Base class for domain errors thrown by handlers.
618
+ */
619
+ declare class DomainException extends Error {
620
+ readonly code: ErrorCodeType;
621
+ readonly context?: Record<string, unknown>;
622
+ constructor(error: DomainError);
623
+ }
624
+ /**
625
+ * Error thrown when the game is in an invalid step for the requested action.
626
+ */
627
+ declare class InvalidStepError extends DomainException {
628
+ constructor(message: string, context?: Record<string, unknown>);
629
+ }
630
+ /**
631
+ * Error thrown when a step execution fails.
632
+ */
633
+ declare class StepExecutionError extends DomainException {
634
+ constructor(message: string, context?: Record<string, unknown>);
635
+ }
636
+ /**
637
+ * Error thrown when an action execution fails.
638
+ */
639
+ declare class ActionExecutionError extends DomainException {
640
+ constructor(message: string, code?: ErrorCodeType, context?: Record<string, unknown>);
641
+ }
642
+ /**
643
+ * Error thrown when a required entity is not found.
644
+ */
645
+ declare class NotFoundError extends DomainException {
646
+ constructor(message: string, code?: ErrorCodeType, context?: Record<string, unknown>);
647
+ }
648
+ /**
649
+ * Error thrown when validation fails.
650
+ */
651
+ declare class ValidationError extends DomainException {
652
+ constructor(message: string, code: ErrorCodeType, context?: Record<string, unknown>);
653
+ }
654
+
655
+ /**
656
+ * All possible game event types.
657
+ */
658
+ declare const GameEventType: {
659
+ readonly GAME_CREATED: "game:created";
660
+ readonly PLAYERS_ADDED: "game:playersAdded";
661
+ readonly GAME_STARTED: "game:started";
662
+ readonly DOMINO_PICKED: "domino:picked";
663
+ readonly DOMINO_PLACED: "domino:placed";
664
+ readonly DOMINO_DISCARDED: "domino:discarded";
665
+ readonly TURN_CHANGED: "turn:changed";
666
+ readonly GAME_ENDED: "game:ended";
667
+ };
668
+ type GameEventTypeValue = (typeof GameEventType)[keyof typeof GameEventType];
669
+ /**
670
+ * Base event structure with common properties.
671
+ */
672
+ interface BaseEvent {
673
+ /** Event type identifier */
674
+ type: GameEventTypeValue;
675
+ /** Timestamp when the event occurred */
676
+ timestamp: string;
677
+ /** Game ID this event belongs to */
678
+ gameId: string;
679
+ }
680
+ /**
681
+ * Event emitted when a new game is created.
682
+ */
683
+ interface GameCreatedEvent extends BaseEvent {
684
+ type: typeof GameEventType.GAME_CREATED;
685
+ mode: string;
686
+ }
687
+ /**
688
+ * Event emitted when players are added to the game.
689
+ */
690
+ interface PlayersAddedEvent extends BaseEvent {
691
+ type: typeof GameEventType.PLAYERS_ADDED;
692
+ playerNames: string[];
693
+ playerCount: number;
694
+ }
695
+ /**
696
+ * Event emitted when the game starts.
697
+ */
698
+ interface GameStartedEvent extends BaseEvent {
699
+ type: typeof GameEventType.GAME_STARTED;
700
+ firstLordId: string;
701
+ }
702
+ /**
703
+ * Event emitted when a lord picks a domino.
704
+ */
705
+ interface DominoPickedEvent extends BaseEvent {
706
+ type: typeof GameEventType.DOMINO_PICKED;
707
+ lordId: string;
708
+ playerId: string;
709
+ domino: Domino;
710
+ }
711
+ /**
712
+ * Event emitted when a lord places a domino.
713
+ */
714
+ interface DominoPlacedEvent extends BaseEvent {
715
+ type: typeof GameEventType.DOMINO_PLACED;
716
+ lordId: string;
717
+ playerId: string;
718
+ domino: Domino;
719
+ position: Position;
720
+ rotation: Rotation;
721
+ }
722
+ /**
723
+ * Event emitted when a lord discards a domino.
724
+ */
725
+ interface DominoDiscardedEvent extends BaseEvent {
726
+ type: typeof GameEventType.DOMINO_DISCARDED;
727
+ lordId: string;
728
+ playerId: string;
729
+ domino: Domino;
730
+ }
731
+ /**
732
+ * Event emitted when the turn changes.
733
+ */
734
+ interface TurnChangedEvent extends BaseEvent {
735
+ type: typeof GameEventType.TURN_CHANGED;
736
+ previousTurn: number;
737
+ newTurn: number;
738
+ nextLordId: string;
739
+ }
740
+ /**
741
+ * Event emitted when the game ends.
742
+ */
743
+ interface GameEndedEvent extends BaseEvent {
744
+ type: typeof GameEventType.GAME_ENDED;
745
+ results: FinalResult[];
746
+ winner: {
747
+ playerId: string;
748
+ playerName: string;
749
+ score: number;
750
+ };
751
+ }
752
+ /**
753
+ * Union type of all game events.
754
+ */
755
+ type GameEvent = GameCreatedEvent | PlayersAddedEvent | GameStartedEvent | DominoPickedEvent | DominoPlacedEvent | DominoDiscardedEvent | TurnChangedEvent | GameEndedEvent;
756
+ /**
757
+ * Event listener function type.
758
+ */
759
+ type GameEventListener<T extends GameEvent = GameEvent> = (event: T) => void;
760
+ /**
761
+ * Event emitter for game events.
762
+ * Allows subscribing to specific event types or all events.
763
+ */
764
+ declare class GameEventEmitter {
765
+ private listeners;
766
+ /**
767
+ * Subscribe to a specific event type.
768
+ *
769
+ * @param eventType - The event type to listen for, or "*" for all events
770
+ * @param listener - The callback function to invoke
771
+ * @returns Unsubscribe function
772
+ */
773
+ on<T extends GameEvent>(eventType: T["type"] | "*", listener: GameEventListener<T>): () => void;
774
+ /**
775
+ * Emit an event to all registered listeners.
776
+ *
777
+ * @param event - The event to emit
778
+ */
779
+ emit(event: GameEvent): void;
780
+ /**
781
+ * Remove all listeners for a specific event type or all listeners.
782
+ *
783
+ * @param eventType - Optional event type to clear, clears all if not provided
784
+ */
785
+ clear(eventType?: GameEventTypeValue | "*"): void;
786
+ }
787
+ /**
788
+ * Creates an event with common properties filled in.
789
+ */
790
+ declare const createEvent: <T extends GameEvent>(gameId: string, type: T["type"], data: Omit<T, "type" | "timestamp" | "gameId">) => T;
791
+
792
+ /**
793
+ * Translation keys for internationalization support.
794
+ * Use these keys instead of hardcoded strings for translatable content.
795
+ */
796
+ declare const TranslationKey: {
797
+ readonly ERROR_INVALID_STEP: "error.invalidStep";
798
+ readonly ERROR_LORD_NOT_FOUND: "error.lordNotFound";
799
+ readonly ERROR_PLAYER_NOT_FOUND: "error.playerNotFound";
800
+ readonly ERROR_DOMINO_NOT_FOUND: "error.dominoNotFound";
801
+ readonly ERROR_INVALID_PLACEMENT: "error.invalidPlacement";
802
+ readonly ERROR_PLACEMENT_NOT_EMPTY: "error.placementNotEmpty";
803
+ readonly ERROR_PLACEMENT_NOT_ADJACENT: "error.placementNotAdjacent";
804
+ readonly ERROR_PLACEMENT_INVALID_TERRAIN: "error.placementInvalidTerrain";
805
+ readonly ERROR_PLACEMENT_OUT_OF_BOUNDS: "error.placementOutOfBounds";
806
+ readonly ERROR_NOT_YOUR_TURN: "error.notYourTurn";
807
+ readonly ERROR_CANNOT_PICK: "error.cannotPick";
808
+ readonly ERROR_CANNOT_PLACE: "error.cannotPlace";
809
+ readonly ERROR_DOMINO_ALREADY_PICKED: "error.dominoAlreadyPicked";
810
+ readonly ERROR_INVALID_PLAYER_COUNT: "error.invalidPlayerCount";
811
+ readonly ERROR_INVALID_PLAYER_NAME: "error.invalidPlayerName";
812
+ readonly MODE_CLASSIC: "mode.classic";
813
+ readonly RULE_MIDDLE_KINGDOM: "rule.middleKingdom";
814
+ readonly RULE_HARMONY: "rule.harmony";
815
+ readonly GROUND_CASTLE: "ground.castle";
816
+ readonly GROUND_WHEAT: "ground.wheat";
817
+ readonly GROUND_FOREST: "ground.forest";
818
+ readonly GROUND_SEA: "ground.sea";
819
+ readonly GROUND_PLAIN: "ground.plain";
820
+ readonly GROUND_SWAMP: "ground.swamp";
821
+ readonly GROUND_MINE: "ground.mine";
822
+ readonly PHASE_ADD_PLAYERS: "phase.addPlayers";
823
+ readonly PHASE_OPTIONS: "phase.options";
824
+ readonly PHASE_START: "phase.start";
825
+ readonly PHASE_RESULT: "phase.result";
826
+ readonly ACTION_PICK_DOMINO: "action.pickDomino";
827
+ readonly ACTION_PLACE_DOMINO: "action.placeDomino";
828
+ readonly ACTION_PASS: "action.pass";
829
+ };
830
+ type TranslationKeyType = (typeof TranslationKey)[keyof typeof TranslationKey];
831
+ /**
832
+ * Default English translations.
833
+ */
834
+ declare const defaultTranslations: Record<TranslationKeyType, string>;
835
+ /**
836
+ * Translator interface for custom translations.
837
+ */
838
+ interface Translator {
839
+ /**
840
+ * Translate a key to the current language.
841
+ * @param key - The translation key
842
+ * @param params - Optional parameters for interpolation
843
+ * @returns The translated string
844
+ */
845
+ t(key: TranslationKeyType, params?: Record<string, string | number>): string;
846
+ }
847
+ /**
848
+ * Creates a translator with the provided translations.
849
+ * Falls back to default English translations for missing keys.
850
+ *
851
+ * @param translations - Custom translations to use
852
+ * @returns A translator instance
853
+ */
854
+ declare const createTranslator: (translations?: Partial<Record<TranslationKeyType, string>>) => Translator;
855
+ /**
856
+ * Default translator using English translations.
857
+ */
858
+ declare const defaultTranslator: Translator;
859
+
860
+ /**
861
+ * Serializes a game state to a JSON string.
862
+ * The serialized string includes version information for future compatibility.
863
+ *
864
+ * @param game - The game state to serialize
865
+ * @returns JSON string representation of the game
866
+ */
867
+ declare const serializeGame: (game: GameState) => string;
868
+ /**
869
+ * Deserializes a JSON string back to a game state.
870
+ * Validates the structure and version before returning.
871
+ *
872
+ * @param json - The JSON string to deserialize
873
+ * @returns Result containing the game state or an error
874
+ */
875
+ declare const deserializeGame: (json: string) => Result<GameState>;
876
+ /**
877
+ * Creates a save point for the current game state.
878
+ * Includes metadata for debugging and restore purposes.
879
+ */
880
+ interface GameSavePoint {
881
+ /** Serialized game data */
882
+ serialized: string;
883
+ /** When the save was created */
884
+ createdAt: string;
885
+ /** Game ID for reference */
886
+ gameId: string;
887
+ /** Current turn number */
888
+ turn: number;
889
+ }
890
+ /**
891
+ * Creates a save point from a game state.
892
+ *
893
+ * @param game - The game state to save
894
+ * @returns A save point object with metadata
895
+ */
896
+ declare const createSavePoint: (game: GameState) => GameSavePoint;
897
+ /**
898
+ * Restores a game state from a save point.
899
+ *
900
+ * @param savePoint - The save point to restore from
901
+ * @returns Result containing the restored game state or an error
902
+ */
903
+ declare const restoreFromSavePoint: (savePoint: GameSavePoint) => Result<GameState>;
904
+
905
+ /**
906
+ * Creates a new Kingdomino game engine instance.
907
+ *
908
+ * @param config - Configuration options for the engine
909
+ * @returns A fully configured GameEngine instance
910
+ *
911
+ * @example
912
+ * ```typescript
913
+ * // Basic usage
914
+ * const engine = createGameEngine({});
915
+ *
916
+ * // With logging
917
+ * const engine = createGameEngine({ logging: true });
918
+ *
919
+ * // With custom shuffle for testing
920
+ * const engine = createGameEngine({
921
+ * shuffleMethod: (array) => array, // No shuffle
922
+ * });
923
+ * ```
924
+ */
228
925
  declare const createGameEngine: (config: Partial<EngineConfig>) => GameEngine;
229
926
 
230
- export { type AddExtraRulesCommand, type AddPlayersCommand, type BasicRules, type CalculateScoreCommand, type ChooseDominoCommand, type CreateGameCommand, type Crown, type DiscardDominoCommand, type Domino, type EmptyGround, type EmptyTile, type ExtraRule, type FinalResult, GRIDSIZE, GROUND, type Game, type GameEngine, type GameMode, type GameState, type GameStateResult, type GameSteps, type GameWithNextAction, type GameWithNextStep, type GameWithResults, type GetExtraRulesCommand, type GetModesCommand, type GetResultCommand, type Ground, type Kingdom, type Lord, type NextAction, type NextStep, type ObjectValues, type Orientation, type PlaceDominoCommand, type Player, type PlayerActions, type PlayerPayload, type Players, type PlayersPayload, type Position, type RevealsDomino, type Rotation, type Rules, type Score, type ScoreResult, type SelectedRules, type ShuffleMethod, type StartGameCommand, type Tile, type UuidMethod, createGameEngine, gameSteps, isGameWithNextAction, isGameWithNextStep, playerActions };
927
+ export { ActionExecutionError, type AddExtraRulesCommand, type AddPlayersCommand, type BasicRules, type CalculateScoreCommand, type CanPlaceDominoCommand, type ChooseDominoCommand, type CreateGameCommand, type Crown, type DeserializeGameCommand, type DiscardDominoCommand, type DomainError, DomainException, type Domino, type DominoDiscardedEvent, type DominoPickedEvent, type DominoPlacedEvent, type EmptyGround, type EmptyTile, type EngineConfig, ErrorCode, type ErrorCodeType, type ExtraRule, type FinalResult, GRIDSIZE, GROUND, type Game, type GameCreatedEvent, type GameEndedEvent, type GameEngine, type GameEvent, GameEventEmitter, type GameEventListener, GameEventType, type GameEventTypeValue, type GameMode, type GameSavePoint, type GameStartedEvent, type GameState, type GameStateResult, type GameSteps, type GameWithNextAction, type GameWithNextStep, type GameWithResults, type GetExtraRulesCommand, type GetModesCommand, type GetResultCommand, type GetValidPlacementsCommand, type Ground, InvalidStepError, type Kingdom, type Lord, type NextAction, type NextStep, NotFoundError, type ObjectValues, type PlaceDominoCommand, type Player, type PlayerActions, type PlayerPayload, type Players, type PlayersAddedEvent, type PlayersPayload, type Position, type RevealsDomino, type Rotation, type Rules, type Score, type ScoreResult, type SelectedRules, type SerializeGameCommand, type ShuffleMethod, type StartGameCommand, StepExecutionError, type Tile, TranslationKey, type TranslationKeyType, type Translator, type TurnChangedEvent, type UuidMethod, type ValidPlacement, ValidationError, createDomainError, createEvent, createGameEngine, createSavePoint, createTranslator, defaultTranslations, defaultTranslator, deserializeGame, gameSteps, isGameWithNextAction, isGameWithNextStep, playerActions, restoreFromSavePoint, serializeGame };