@pompidup/kingdomino-engine 3.3.0 → 3.5.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,8 +1,18 @@
1
+ type Result<T> = Ok<T> | Err;
2
+ interface Ok<T> {
3
+ readonly tag: "Ok";
4
+ readonly value: T;
5
+ }
6
+ interface Err {
7
+ readonly tag: "Err";
8
+ readonly error: string;
9
+ }
10
+
1
11
  /**
2
12
  * All possible ground/terrain types in Kingdomino.
3
13
  * Each type can form contiguous properties for scoring.
4
14
  */
5
- declare const GROUND: readonly ["castle", "wheat", "forest", "sea", "plain", "swamp", "mine"];
15
+ declare const GROUND: readonly ["castle", "wheat", "forest", "sea", "plain", "swamp", "mine", "grassland", "lake", "jungle", "quarry", "desert", "volcano"];
6
16
  /** Union type of all terrain types */
7
17
  type Ground = (typeof GROUND)[number];
8
18
  /** Special ground type for empty/unplaced tiles */
@@ -16,8 +26,16 @@ type Crown = number;
16
26
  type Tile = {
17
27
  /** The terrain type of this tile */
18
28
  type: Ground;
19
- /** Number of crowns (0-3), multiplier for scoring */
29
+ /** Number of crowns (0-3), multiplier for scoring. In Origins, represents fire symbols. */
20
30
  crowns: Crown;
31
+ /** Whether this tile has a construction square for buildings (Queendomino) */
32
+ hasConstructionSquare?: boolean;
33
+ /** Number of volcanic craters on this tile (Origins, 1-3). Determines fire token type. */
34
+ volcanoCraters?: number;
35
+ /** Whether this tile has a giant icon (Age of Giants, letter dominos A-F) */
36
+ hasGiant?: boolean;
37
+ /** Whether this tile has footprint icons (Age of Giants, dominos 49-54) */
38
+ hasFootprint?: boolean;
21
39
  };
22
40
  /**
23
41
  * Represents an empty/unplaced tile on the kingdom grid.
@@ -37,8 +55,10 @@ type Domino = {
37
55
  left: Tile;
38
56
  /** The right tile of the domino */
39
57
  right: Tile;
40
- /** Unique number identifying this domino (1-48 in Classic mode) */
58
+ /** Unique number identifying this domino (1-48 in Classic mode). Letter dominos use fractional numbers (A=0.01...F=0.06). */
41
59
  number: number;
60
+ /** Display letter for Age of Giants giant dominos (A-F). Undefined for numbered dominos. */
61
+ letter?: string;
42
62
  };
43
63
  /**
44
64
  * Represents a domino that has been revealed for player selection.
@@ -89,6 +109,65 @@ declare const GRIDSIZE = 9;
89
109
  */
90
110
  declare const MAX_KINGDOM_SIZE = 5;
91
111
 
112
+ type ObjectValues<T> = T[keyof T];
113
+
114
+ /**
115
+ * A giant placed on a player's kingdom, covering a crown.
116
+ * The covered crown does not count for scoring.
117
+ */
118
+ type PlacedGiant = {
119
+ /** Position on the kingdom grid where the giant covers a crown */
120
+ position: Position;
121
+ };
122
+ /**
123
+ * All quest tile types available in Age of Giants.
124
+ */
125
+ declare const questTypes: {
126
+ /** 5pts per matching terrain tile adjacent (8-dir) to castle */
127
+ readonly localTrade: "localTrade";
128
+ /** 5pts per matching terrain tile in the 4 corners of the kingdom */
129
+ readonly kingdomBorders: "kingdomBorders";
130
+ /** 5pts if kingdom is complete (no discarded dominos) */
131
+ readonly harmony: "harmony";
132
+ /** 10pts if castle is in the center of the kingdom */
133
+ readonly middleKingdom: "middleKingdom";
134
+ /** 20pts if castle is in one of the 4 corners of the kingdom */
135
+ readonly lostCorner: "lostCorner";
136
+ /** 10pts per alignment of 3+ tiles with crowns (H/V/diagonal) */
137
+ readonly megalomania: "megalomania";
138
+ /** 10pts per property of 5+ wheat/forest/sea/plain tiles with 0 crowns */
139
+ readonly austereKing: "austereKing";
140
+ };
141
+ type QuestType = ObjectValues<typeof questTypes>;
142
+ /**
143
+ * A quest tile drawn at game start.
144
+ * Provides a specific end-of-game bonus scoring condition.
145
+ */
146
+ type QuestTile = {
147
+ /** Unique identifier for the quest tile */
148
+ id: number;
149
+ /** Type of quest determining the scoring rule */
150
+ type: QuestType;
151
+ /** Display name of the quest */
152
+ name: string;
153
+ /** Description of the scoring condition */
154
+ description: string;
155
+ /** Points awarded per match (e.g., 5 for localTrade, 10 for megalomania) */
156
+ points: number;
157
+ /** Target terrain type (only for localTrade and kingdomBorders quests) */
158
+ terrain?: Ground;
159
+ };
160
+ /**
161
+ * Complete Age of Giants-specific game state.
162
+ * Stored as an optional field on Game and only present in AoG modes.
163
+ */
164
+ type AgeOfGiantsState = {
165
+ /** Number of giant figurines remaining in the pool (starts at 6) */
166
+ giantPool: number;
167
+ /** The 2 quest tiles drawn for this game */
168
+ questTiles: QuestTile[];
169
+ };
170
+
92
171
  /**
93
172
  * Represents a lord (player token) used for turn order and domino selection.
94
173
  * In games with 2 players, each player has 2 lords.
@@ -120,8 +199,241 @@ type GameMode = {
120
199
  description: string;
121
200
  };
122
201
 
123
- type ObjectValues<T> = T[keyof T];
202
+ /**
203
+ * The three gameplay modes within Kingdomino Origins.
204
+ * Discovery is the base, Totem adds resources/totems, Tribe adds cavemen.
205
+ */
206
+ declare const originsSubModes: {
207
+ readonly discovery: "Discovery";
208
+ readonly totem: "Totem";
209
+ readonly tribe: "Tribe";
210
+ };
211
+ type OriginsSubMode = ObjectValues<typeof originsSubModes>;
212
+ /**
213
+ * Represents the pool of available fire tokens.
214
+ * Discovery Mode starts with: 5×1-fire, 4×2-fire, 1×3-fire = 10 total.
215
+ */
216
+ type FireTokenPool = {
217
+ ones: number;
218
+ twos: number;
219
+ threes: number;
220
+ };
221
+ /**
222
+ * A fire token that has been placed on a player's kingdom.
223
+ */
224
+ type PlacedFireToken = {
225
+ /** Number of fires on this token (1, 2, or 3) */
226
+ fires: 1 | 2 | 3;
227
+ /** Position on the kingdom grid */
228
+ position: Position;
229
+ };
230
+ /**
231
+ * Tracks which volcano granted the current pending fire token,
232
+ * so the placement validation knows the launch position.
233
+ */
234
+ type PendingFireToken = {
235
+ /** Number of fires on the token to place */
236
+ fires: 1 | 2 | 3;
237
+ /** Position of the volcano that granted this token */
238
+ volcanoPosition: Position;
239
+ };
240
+ /**
241
+ * Types of wooden resources in Origins.
242
+ * Each maps to a specific terrain type.
243
+ */
244
+ declare const resourceTypes: {
245
+ readonly mammoth: "mammoth";
246
+ readonly fish: "fish";
247
+ readonly mushroom: "mushroom";
248
+ readonly flint: "flint";
249
+ };
250
+ type ResourceType = ObjectValues<typeof resourceTypes>;
251
+ /**
252
+ * A wooden resource placed on a player's kingdom tile.
253
+ */
254
+ type Resource = {
255
+ /** Type of resource */
256
+ type: ResourceType;
257
+ /** Position on the kingdom grid */
258
+ position: Position;
259
+ };
260
+ /**
261
+ * Tracks which player holds each totem tile.
262
+ * null means no player currently holds that totem.
263
+ */
264
+ type TotemState = Record<ResourceType, string | null>;
265
+ /**
266
+ * Point values for each totem tile at end of game.
267
+ */
268
+ type TotemBonus = Record<ResourceType, number>;
269
+ /**
270
+ * The 7 kinds of hunter-gatherers.
271
+ * Each has a specific scoring rule based on surrounding resources.
272
+ */
273
+ declare const hunterGathererKinds: {
274
+ /** Scores per mammoth adjacent */
275
+ readonly mammothHunter: "mammothHunter";
276
+ /** Scores per fish adjacent */
277
+ readonly fisherman: "fisherman";
278
+ /** Scores per mushroom adjacent */
279
+ readonly mushroomPicker: "mushroomPicker";
280
+ /** Scores per flint adjacent */
281
+ readonly flintCollector: "flintCollector";
282
+ /** Scores per unique resource type adjacent */
283
+ readonly trapper: "trapper";
284
+ /** Scores per total resource adjacent (any type) */
285
+ readonly gatherer: "gatherer";
286
+ /** Scores per fire symbol adjacent (Fire Lady) */
287
+ readonly fireLady: "fireLady";
288
+ };
289
+ type HunterGathererKind = ObjectValues<typeof hunterGathererKinds>;
290
+ /**
291
+ * Warrior size categories with their power values.
292
+ */
293
+ declare const warriorTypes: {
294
+ readonly small: "small";
295
+ readonly amazon: "amazon";
296
+ readonly oafish: "oafish";
297
+ };
298
+ type WarriorType = ObjectValues<typeof warriorTypes>;
299
+ /**
300
+ * Power values for each warrior type.
301
+ */
302
+ declare const WARRIOR_POWER: Record<WarriorType, number>;
303
+ /**
304
+ * A hunter-gatherer caveman tile.
305
+ */
306
+ type HunterGathererTile = {
307
+ kind: "hunterGatherer";
308
+ id: number;
309
+ /** Specific hunter-gatherer type determining scoring rule */
310
+ hunterType: HunterGathererKind;
311
+ /** Points per matching adjacent resource/symbol */
312
+ pointsPerMatch: number;
313
+ };
314
+ /**
315
+ * A warrior caveman tile.
316
+ */
317
+ type WarriorTile = {
318
+ kind: "warrior";
319
+ id: number;
320
+ /** Warrior size category */
321
+ warriorType: WarriorType;
322
+ /** Combat power (1, 2, or 3) */
323
+ power: number;
324
+ };
325
+ /** Discriminated union of all caveman tile types. */
326
+ type CavemanTile = HunterGathererTile | WarriorTile;
327
+ /**
328
+ * A caveman tile placed on a player's kingdom.
329
+ */
330
+ type PlacedCaveman = {
331
+ caveman: CavemanTile;
332
+ position: Position;
333
+ };
334
+ /**
335
+ * The Cave board used in Tribe mode.
336
+ * Shows 4 visible caveman tiles and a face-down draw pile.
337
+ */
338
+ type CaveBoard = {
339
+ /** Up to 4 visible caveman tiles available for recruitment */
340
+ visible: CavemanTile[];
341
+ /** Face-down draw pile */
342
+ drawPile: CavemanTile[];
343
+ };
344
+ /**
345
+ * Complete Origins-specific game state.
346
+ * Stored as an optional field on Game and only present in Origins modes.
347
+ */
348
+ type OriginsState = {
349
+ /** Which Origins sub-mode is being played */
350
+ subMode: OriginsSubMode;
351
+ /** Available fire tokens pool */
352
+ fireTokenPool: FireTokenPool;
353
+ /** Pending fire token to place (set after placing a volcano domino) */
354
+ pendingFireToken?: PendingFireToken;
355
+ /** Totem holder state (Totem mode only) */
356
+ totems?: TotemState;
357
+ /** Cave board state (Tribe mode only) */
358
+ caveBoard?: CaveBoard;
359
+ };
360
+
361
+ /**
362
+ * Types of immediate bonuses a building can grant when constructed.
363
+ */
364
+ type ImmediateBonusType = "coins" | "tower" | "knight";
365
+ /**
366
+ * Immediate bonus granted when a building is constructed.
367
+ */
368
+ type ImmediateBonus = {
369
+ type: ImmediateBonusType;
370
+ amount: number;
371
+ };
372
+ /**
373
+ * Types of end-game scoring conditions for buildings.
374
+ */
375
+ type EndGameScoringType = "perTerrain" | "perBuilding" | "perTower" | "perCrown" | "flat";
376
+ /**
377
+ * End-game scoring rule for a building.
378
+ */
379
+ type EndGameScoring = {
380
+ type: EndGameScoringType;
381
+ /** Terrain type (only for perTerrain) */
382
+ terrain?: string;
383
+ /** Points per matching element */
384
+ points: number;
385
+ };
386
+ /**
387
+ * Represents a building tile that can be purchased and placed on a construction square.
388
+ */
389
+ type BuildingTile = {
390
+ /** Unique identifier */
391
+ id: number;
392
+ /** Display name */
393
+ name: string;
394
+ /** Cost in coins to purchase */
395
+ cost: number;
396
+ /** Immediate bonus when constructed (optional) */
397
+ immediateBonus?: ImmediateBonus;
398
+ /** Permanent crowns added to the territory */
399
+ crowns: number;
400
+ /** Towers granted by this building */
401
+ towers: number;
402
+ /** End-game scoring condition (optional) */
403
+ endGameScoring?: EndGameScoring;
404
+ };
405
+ /**
406
+ * A slot on the Builders Board that may hold a building tile.
407
+ */
408
+ type BuildingSlot = BuildingTile | null;
409
+ /**
410
+ * The Builders Board holding available building tiles for purchase.
411
+ */
412
+ type BuildersBoard = {
413
+ /** Visible building tiles available for purchase (4 slots) */
414
+ slots: BuildingSlot[];
415
+ /** Remaining building tiles in the draw pile */
416
+ drawPile: BuildingTile[];
417
+ };
418
+ /**
419
+ * A building that has been placed on a player's kingdom.
420
+ */
421
+ type PlacedBuilding = {
422
+ /** The building tile that was placed */
423
+ building: BuildingTile;
424
+ /** Position on the kingdom grid */
425
+ position: Position;
426
+ };
124
427
 
428
+ /**
429
+ * Represents a knight placed on a player's kingdom (Queendomino).
430
+ */
431
+ type Knight = {
432
+ /** ID of the player who owns this knight */
433
+ playerId: string;
434
+ /** Position on the kingdom grid where the knight is placed */
435
+ position: Position;
436
+ };
125
437
  /**
126
438
  * Represents a player in the game.
127
439
  */
@@ -136,6 +448,22 @@ type Player = {
136
448
  bot?: {
137
449
  strategyName: string;
138
450
  };
451
+ /** Number of coins the player has (Queendomino) */
452
+ coins?: number;
453
+ /** Number of towers the player has (Queendomino) */
454
+ towers?: number;
455
+ /** Knights placed on the kingdom (Queendomino) */
456
+ knights?: Knight[];
457
+ /** Buildings placed on the kingdom (Queendomino) */
458
+ buildings?: PlacedBuilding[];
459
+ /** Fire tokens placed on the kingdom (Origins) */
460
+ fireTokens?: PlacedFireToken[];
461
+ /** Wooden resources on the kingdom (Origins Totem/Tribe) */
462
+ resources?: Resource[];
463
+ /** Cavemen placed on the kingdom (Origins Tribe) */
464
+ cavemen?: PlacedCaveman[];
465
+ /** Giants placed on the kingdom covering crowns (Age of Giants) */
466
+ giants?: PlacedGiant[];
139
467
  };
140
468
  /**
141
469
  * Payload for creating a new player.
@@ -158,10 +486,41 @@ declare const playerActions: {
158
486
  readonly pickDomino: "pickDomino";
159
487
  /** Pass/discard when no valid placement exists */
160
488
  readonly pass: "pass";
489
+ /** Place a knight on the just-placed domino (Queendomino, optional) */
490
+ readonly placeKnight: "placeKnight";
491
+ /** Construct a building on a construction square (Queendomino, optional) */
492
+ readonly constructBuilding: "constructBuilding";
493
+ /** Use the Dragon to burn a building tile (Queendomino, optional) */
494
+ readonly useDragon: "useDragon";
495
+ /** Skip the current optional action (Queendomino/Origins/AoG) */
496
+ readonly skipOptionalAction: "skipOptionalAction";
497
+ /** Place a giant on a crown after placing a giant domino (Age of Giants, mandatory) */
498
+ readonly placeGiant: "placeGiant";
499
+ /** Send a giant to an opponent after placing a footprint domino (Age of Giants, optional) */
500
+ readonly sendGiant: "sendGiant";
501
+ /** Place a fire token after placing a volcano domino (Origins, optional) */
502
+ readonly placeFireToken: "placeFireToken";
503
+ /** Recruit a caveman from the cave board (Origins Tribe, optional) */
504
+ readonly recruitCaveman: "recruitCaveman";
161
505
  };
162
506
  /** Union type of all player actions */
163
507
  type PlayerActions = ObjectValues<typeof playerActions>;
164
508
 
509
+ /**
510
+ * Complete Queendomino-specific game state.
511
+ * This is stored as an optional field on Game and only present in QueenDomino mode.
512
+ */
513
+ type QueenDominoState = {
514
+ /** The Builders Board with available building tiles */
515
+ buildersBoard: BuildersBoard;
516
+ /** ID of the player currently hosting the Queen, or null */
517
+ queenHolderId: string | null;
518
+ /** Whether the Dragon is available for use this round */
519
+ dragonAvailable: boolean;
520
+ /** Whether the Dragon has been used this round (resets each round) */
521
+ dragonUsedThisRound: boolean;
522
+ };
523
+
165
524
  /**
166
525
  * Basic rules that vary based on player count.
167
526
  */
@@ -176,6 +535,8 @@ type BasicRules = {
176
535
  maxTurns: number;
177
536
  /** Maximum kingdom grid size (5 for standard, 7 for Mighty Duel) */
178
537
  maxKingdomSize: number;
538
+ /** Number of dominos discarded from each draw before selection (Age of Giants) */
539
+ dominoesDiscardedPerTurn?: number;
179
540
  };
180
541
  /**
181
542
  * Optional extra rules that modify gameplay and scoring.
@@ -196,6 +557,8 @@ type ExtraRule = {
196
557
  type Rules = {
197
558
  /** Basic rules indexed by player count (1-4) */
198
559
  basic: Record<number, BasicRules>;
560
+ /** Age of Giants basic rules indexed by player count (2-5) */
561
+ aogBasic?: Record<number, BasicRules>;
199
562
  /** Available extra rules */
200
563
  extraRules: ExtraRule[];
201
564
  };
@@ -209,16 +572,6 @@ type SelectedRules = {
209
572
  extra: ExtraRule[];
210
573
  };
211
574
 
212
- type Result<T> = Ok<T> | Err;
213
- interface Ok<T> {
214
- readonly tag: "Ok";
215
- readonly value: T;
216
- }
217
- interface Err {
218
- readonly tag: "Err";
219
- readonly error: string;
220
- }
221
-
222
575
  /**
223
576
  * Represents the complete state of a Kingdomino game.
224
577
  * This is the main aggregate containing all game data.
@@ -244,6 +597,12 @@ type Game = {
244
597
  mode: GameMode;
245
598
  /** Optional seed for deterministic shuffle (replays, sharing) */
246
599
  seed?: string;
600
+ /** Queendomino-specific state (only present in QueenDomino mode) */
601
+ queendomino?: QueenDominoState;
602
+ /** Origins-specific state (only present in KingdominoOrigins modes) */
603
+ origins?: OriginsState;
604
+ /** Age of Giants-specific state (only present in AgeOfGiants modes) */
605
+ ageOfGiants?: AgeOfGiantsState;
247
606
  };
248
607
  /**
249
608
  * Available game steps representing phases of game setup and completion.
@@ -340,6 +699,29 @@ type GameWithResults = Game & {
340
699
  result: FinalResult[];
341
700
  };
342
701
 
702
+ type AddExtraRulesCommand = {
703
+ game: GameState;
704
+ extraRules: string[];
705
+ };
706
+
707
+ /**
708
+ * Input for a single player: either a name string (human) or an object with bot config.
709
+ */
710
+ type PlayerInput = string | {
711
+ name: string;
712
+ bot?: {
713
+ strategyName: string;
714
+ };
715
+ };
716
+ type AddPlayersCommand = {
717
+ game: GameState;
718
+ players: PlayerInput[];
719
+ };
720
+
721
+ type CalculateScoreCommand = {
722
+ kingdom: Kingdom;
723
+ };
724
+
343
725
  /**
344
726
  * Severity level for a validation issue.
345
727
  * - "error": The state is corrupted and cannot be used safely.
@@ -360,39 +742,16 @@ type ValidationIssue = {
360
742
  severity: ValidationSeverity;
361
743
  };
362
744
 
363
- type GetModesCommand = Record<string, never>;
364
-
365
- type GetExtraRulesCommand = {
366
- mode: GameMode["name"];
367
- players: number;
368
- };
369
-
370
- type CreateGameCommand = {
371
- mode: string;
372
- seed?: string;
373
- };
374
-
375
745
  /**
376
- * Input for a single player: either a name string (human) or an object with bot config.
746
+ * Command to check if a domino can be placed anywhere on a kingdom.
377
747
  */
378
- type PlayerInput = string | {
379
- name: string;
380
- bot?: {
381
- strategyName: string;
382
- };
383
- };
384
- type AddPlayersCommand = {
385
- game: GameState;
386
- players: PlayerInput[];
387
- };
388
-
389
- type AddExtraRulesCommand = {
390
- game: GameState;
391
- extraRules: string[];
392
- };
393
-
394
- type StartGameCommand = {
395
- game: GameState;
748
+ type CanPlaceDominoCommand = {
749
+ /** The kingdom grid to check */
750
+ kingdom: Kingdom;
751
+ /** The domino to check placement for */
752
+ domino: Domino;
753
+ /** Maximum kingdom size (default: 5, use 7 for Mighty Duel) */
754
+ maxKingdomSize?: number;
396
755
  };
397
756
 
398
757
  type ChooseDominoCommand = {
@@ -401,11 +760,16 @@ type ChooseDominoCommand = {
401
760
  lordId: string;
402
761
  };
403
762
 
404
- type PlaceDominoCommand = {
405
- game: GameState;
763
+ type ConstructBuildingCommand = {
764
+ game: GameWithNextAction;
406
765
  lordId: string;
766
+ buildingId: number;
407
767
  position: Position;
408
- rotation: Rotation;
768
+ };
769
+
770
+ type CreateGameCommand = {
771
+ mode: string;
772
+ seed?: string;
409
773
  };
410
774
 
411
775
  type DiscardDominoCommand = {
@@ -413,12 +777,19 @@ type DiscardDominoCommand = {
413
777
  lordId: string;
414
778
  };
415
779
 
416
- type GetResultCommand = {
417
- game: GameState;
780
+ type GetDynastyResultCommand = {
781
+ games: GameWithResults[];
418
782
  };
419
783
 
420
- type CalculateScoreCommand = {
421
- kingdom: Kingdom;
784
+ type GetExtraRulesCommand = {
785
+ mode: GameMode["name"];
786
+ players: number;
787
+ };
788
+
789
+ type GetModesCommand = Record<string, never>;
790
+
791
+ type GetResultCommand = {
792
+ game: GameState;
422
793
  };
423
794
 
424
795
  /**
@@ -433,16 +804,51 @@ type GetValidPlacementsCommand = {
433
804
  maxKingdomSize?: number;
434
805
  };
435
806
 
436
- /**
437
- * Command to check if a domino can be placed anywhere on a kingdom.
438
- */
439
- type CanPlaceDominoCommand = {
440
- /** The kingdom grid to check */
441
- kingdom: Kingdom;
442
- /** The domino to check placement for */
443
- domino: Domino;
444
- /** Maximum kingdom size (default: 5, use 7 for Mighty Duel) */
445
- maxKingdomSize?: number;
807
+ type PlaceDominoCommand = {
808
+ game: GameState;
809
+ lordId: string;
810
+ position: Position;
811
+ rotation: Rotation;
812
+ };
813
+
814
+ type PlaceFireTokenCommand = {
815
+ game: GameWithNextAction;
816
+ lordId: string;
817
+ position: Position;
818
+ };
819
+
820
+ type PlaceGiantCommand = {
821
+ game: GameWithNextAction;
822
+ lordId: string;
823
+ position: Position;
824
+ };
825
+
826
+ type PlaceKnightCommand = {
827
+ game: GameWithNextAction;
828
+ lordId: string;
829
+ position: Position;
830
+ };
831
+
832
+ type RecruitCavemanCommand = {
833
+ game: GameWithNextAction;
834
+ lordId: string;
835
+ /** ID of the caveman tile to recruit */
836
+ cavemanId: number;
837
+ /** Position on the kingdom to place the caveman */
838
+ position: Position;
839
+ /** Positions of resources to spend as payment */
840
+ resourcePositions: Position[];
841
+ };
842
+
843
+ type SendGiantCommand = {
844
+ game: GameWithNextAction;
845
+ lordId: string;
846
+ /** Index of the giant to send from the current player's giants array */
847
+ giantIndex: number;
848
+ /** ID of the opponent player to receive the giant */
849
+ targetPlayerId: string;
850
+ /** Position on the opponent's kingdom where the giant will cover a crown */
851
+ targetCrownPosition: Position;
446
852
  };
447
853
 
448
854
  /**
@@ -460,16 +866,19 @@ type DeserializeGameCommand = {
460
866
  json: string;
461
867
  };
462
868
 
463
- type GetDynastyResultCommand = {
464
- games: GameWithResults[];
869
+ type SkipOptionalActionCommand = {
870
+ game: GameWithNextAction;
871
+ lordId: string;
465
872
  };
466
873
 
467
- /**
468
- * Represents a valid placement position with rotation.
469
- */
470
- type ValidPlacement = {
471
- position: Position;
472
- rotation: Rotation;
874
+ type StartGameCommand = {
875
+ game: GameState;
876
+ };
877
+
878
+ type UseDragonCommand = {
879
+ game: GameWithNextAction;
880
+ lordId: string;
881
+ buildingId: number;
473
882
  };
474
883
 
475
884
  type DynastyResult = {
@@ -480,6 +889,14 @@ type DynastyResult = {
480
889
  position: number;
481
890
  };
482
891
 
892
+ /**
893
+ * Represents a valid placement position with rotation.
894
+ */
895
+ type ValidPlacement = {
896
+ position: Position;
897
+ rotation: Rotation;
898
+ };
899
+
483
900
  /**
484
901
  * The main interface for interacting with the Kingdomino game engine.
485
902
  *
@@ -613,6 +1030,56 @@ type GameEngine = {
613
1030
  * @returns Ranked dynasty results per player
614
1031
  */
615
1032
  getDynastyResults: (command: GetDynastyResultCommand) => DynastyResult[];
1033
+ /**
1034
+ * Places a giant on a crown in the lord's kingdom (Age of Giants).
1035
+ * Mandatory after placing a giant domino (A-F). The giant covers the crown for scoring.
1036
+ * @param command - Contains game state, lord ID, and crown position
1037
+ * @returns Updated game state with next action
1038
+ */
1039
+ placeGiant: (command: PlaceGiantCommand) => GameState;
1040
+ /**
1041
+ * Sends a giant from the lord's kingdom to an opponent's kingdom (Age of Giants).
1042
+ * Optional after placing a footprint domino (49-54). The opponent chooses which crown is covered.
1043
+ * @param command - Contains game state, lord ID, giant index, target player ID, and target crown position
1044
+ * @returns Updated game state with next action
1045
+ */
1046
+ sendGiant: (command: SendGiantCommand) => GameState;
1047
+ /**
1048
+ * Places a knight on the lord's kingdom at the specified position (Queendomino).
1049
+ * @param command - Contains game state, lord ID, and position
1050
+ * @returns Updated game state with next action
1051
+ */
1052
+ placeKnight: (command: PlaceKnightCommand) => GameState;
1053
+ /**
1054
+ * Constructs a building on the lord's kingdom (Queendomino).
1055
+ * @param command - Contains game state, lord ID, building ID, and position
1056
+ * @returns Updated game state with next action
1057
+ */
1058
+ constructBuilding: (command: ConstructBuildingCommand) => GameState;
1059
+ /**
1060
+ * Uses the dragon to destroy a building on the builders board (Queendomino).
1061
+ * @param command - Contains game state, lord ID, and building ID
1062
+ * @returns Updated game state with next action
1063
+ */
1064
+ useDragon: (command: UseDragonCommand) => GameState;
1065
+ /**
1066
+ * Places a fire token on the lord's kingdom after placing a volcano domino (Origins).
1067
+ * @param command - Contains game state, lord ID, and target position
1068
+ * @returns Updated game state with next action
1069
+ */
1070
+ placeFireToken: (command: PlaceFireTokenCommand) => GameState;
1071
+ /**
1072
+ * Recruits a caveman from the cave board and places it on the lord's kingdom (Origins).
1073
+ * @param command - Contains game state, lord ID, caveman ID, position, and resource positions to spend
1074
+ * @returns Updated game state with next action
1075
+ */
1076
+ recruitCaveman: (command: RecruitCavemanCommand) => GameState;
1077
+ /**
1078
+ * Skips the current optional action (Queendomino/Origins).
1079
+ * @param command - Contains game state and lord ID
1080
+ * @returns Updated game state with next action
1081
+ */
1082
+ skipOptionalAction: (command: SkipOptionalActionCommand) => GameState;
616
1083
  };
617
1084
 
618
1085
  /**
@@ -641,6 +1108,27 @@ declare const ErrorCode: {
641
1108
  readonly DOMINO_ALREADY_PICKED: "DOMINO_ALREADY_PICKED";
642
1109
  readonly UNDO_UNAVAILABLE: "UNDO_UNAVAILABLE";
643
1110
  readonly REDO_UNAVAILABLE: "REDO_UNAVAILABLE";
1111
+ readonly CANNOT_PLACE_KNIGHT: "CANNOT_PLACE_KNIGHT";
1112
+ readonly MAX_KNIGHTS_REACHED: "MAX_KNIGHTS_REACHED";
1113
+ readonly KNIGHT_TERRITORY_OCCUPIED: "KNIGHT_TERRITORY_OCCUPIED";
1114
+ readonly CANNOT_AFFORD_BUILDING: "CANNOT_AFFORD_BUILDING";
1115
+ readonly NO_CONSTRUCTION_SQUARE: "NO_CONSTRUCTION_SQUARE";
1116
+ readonly DRAGON_UNAVAILABLE: "DRAGON_UNAVAILABLE";
1117
+ readonly QUEEN_HOLDER_CANNOT_USE_DRAGON: "QUEEN_HOLDER_CANNOT_USE_DRAGON";
1118
+ readonly BUILDING_NOT_FOUND: "BUILDING_NOT_FOUND";
1119
+ readonly INVALID_OPTIONAL_ACTION: "INVALID_OPTIONAL_ACTION";
1120
+ readonly GIANT_POOL_EMPTY: "GIANT_POOL_EMPTY";
1121
+ readonly GIANT_NOT_FOUND: "GIANT_NOT_FOUND";
1122
+ readonly INVALID_GIANT_PLACEMENT: "INVALID_GIANT_PLACEMENT";
1123
+ readonly NOT_AOG_MODE: "NOT_AOG_MODE";
1124
+ readonly FIRE_TOKEN_POOL_EMPTY: "FIRE_TOKEN_POOL_EMPTY";
1125
+ readonly FIRE_TOKEN_INVALID_POSITION: "FIRE_TOKEN_INVALID_POSITION";
1126
+ readonly NO_PENDING_FIRE_TOKEN: "NO_PENDING_FIRE_TOKEN";
1127
+ readonly CANNOT_RECRUIT_CAVEMAN: "CANNOT_RECRUIT_CAVEMAN";
1128
+ readonly INSUFFICIENT_RESOURCES: "INSUFFICIENT_RESOURCES";
1129
+ readonly INVALID_CAVEMAN_PLACEMENT: "INVALID_CAVEMAN_PLACEMENT";
1130
+ readonly CAVEMAN_NOT_FOUND: "CAVEMAN_NOT_FOUND";
1131
+ readonly NOT_TRIBE_MODE: "NOT_TRIBE_MODE";
644
1132
  };
645
1133
  type ErrorCodeType = (typeof ErrorCode)[keyof typeof ErrorCode];
646
1134
  /**
@@ -752,6 +1240,27 @@ declare const TranslationKey: {
752
1240
  readonly ACTION_PICK_DOMINO: "action.pickDomino";
753
1241
  readonly ACTION_PLACE_DOMINO: "action.placeDomino";
754
1242
  readonly ACTION_PASS: "action.pass";
1243
+ readonly ERROR_CANNOT_PLACE_KNIGHT: "error.cannotPlaceKnight";
1244
+ readonly ERROR_MAX_KNIGHTS_REACHED: "error.maxKnightsReached";
1245
+ readonly ERROR_KNIGHT_TERRITORY_OCCUPIED: "error.knightTerritoryOccupied";
1246
+ readonly ERROR_CANNOT_AFFORD_BUILDING: "error.cannotAffordBuilding";
1247
+ readonly ERROR_NO_CONSTRUCTION_SQUARE: "error.noConstructionSquare";
1248
+ readonly ERROR_DRAGON_UNAVAILABLE: "error.dragonUnavailable";
1249
+ readonly ERROR_QUEEN_HOLDER_CANNOT_USE_DRAGON: "error.queenHolderCannotUseDragon";
1250
+ readonly ERROR_BUILDING_NOT_FOUND: "error.buildingNotFound";
1251
+ readonly ERROR_INVALID_OPTIONAL_ACTION: "error.invalidOptionalAction";
1252
+ readonly ERROR_GIANT_POOL_EMPTY: "error.giantPoolEmpty";
1253
+ readonly ERROR_GIANT_NOT_FOUND: "error.giantNotFound";
1254
+ readonly ERROR_INVALID_GIANT_PLACEMENT: "error.invalidGiantPlacement";
1255
+ readonly ERROR_NOT_AOG_MODE: "error.notAogMode";
1256
+ readonly ERROR_FIRE_TOKEN_POOL_EMPTY: "error.fireTokenPoolEmpty";
1257
+ readonly ERROR_FIRE_TOKEN_INVALID_POSITION: "error.fireTokenInvalidPosition";
1258
+ readonly ERROR_NO_PENDING_FIRE_TOKEN: "error.noPendingFireToken";
1259
+ readonly ERROR_CANNOT_RECRUIT_CAVEMAN: "error.cannotRecruitCaveman";
1260
+ readonly ERROR_INSUFFICIENT_RESOURCES: "error.insufficientResources";
1261
+ readonly ERROR_INVALID_CAVEMAN_PLACEMENT: "error.invalidCavemanPlacement";
1262
+ readonly ERROR_CAVEMAN_NOT_FOUND: "error.cavemanNotFound";
1263
+ readonly ERROR_NOT_TRIBE_MODE: "error.notTribeMode";
755
1264
  };
756
1265
  type TranslationKeyType = (typeof TranslationKey)[keyof typeof TranslationKey];
757
1266
  /**
@@ -934,6 +1443,45 @@ type GameEventCallbacks = {
934
1443
  onGameEnd?: (event: {
935
1444
  game: GameState;
936
1445
  }) => void;
1446
+ /** Called after a lord places a knight on their kingdom (QueenDomino) */
1447
+ onKnightPlaced?: (event: {
1448
+ game: GameState;
1449
+ lordId: string;
1450
+ }) => void;
1451
+ /** Called after a lord constructs a building (QueenDomino) */
1452
+ onBuildingConstructed?: (event: {
1453
+ game: GameState;
1454
+ lordId: string;
1455
+ buildingId: number;
1456
+ }) => void;
1457
+ /** Called after a lord uses the dragon to destroy a building (QueenDomino) */
1458
+ onDragonUsed?: (event: {
1459
+ game: GameState;
1460
+ lordId: string;
1461
+ buildingId: number;
1462
+ }) => void;
1463
+ /** Called after a lord places a fire token on their kingdom (Origins) */
1464
+ onFireTokenPlaced?: (event: {
1465
+ game: GameState;
1466
+ lordId: string;
1467
+ }) => void;
1468
+ /** Called after a lord recruits a caveman from the cave board (Origins Tribe) */
1469
+ onCavemanRecruited?: (event: {
1470
+ game: GameState;
1471
+ lordId: string;
1472
+ cavemanId: number;
1473
+ }) => void;
1474
+ /** Called after a lord places a giant on a crown in their kingdom */
1475
+ onGiantPlaced?: (event: {
1476
+ game: GameState;
1477
+ lordId: string;
1478
+ }) => void;
1479
+ /** Called after a lord sends a giant to an opponent's kingdom */
1480
+ onGiantSent?: (event: {
1481
+ game: GameState;
1482
+ lordId: string;
1483
+ targetPlayerId: string;
1484
+ }) => void;
937
1485
  };
938
1486
  /**
939
1487
  * Wraps a GameEngine to emit event callbacks after each state-changing method.
@@ -988,6 +1536,14 @@ type EngineConfig = {
988
1536
  */
989
1537
  declare const validateGameState: (game: Game) => ValidationIssue[];
990
1538
 
1539
+ type BuildingsRepository = {
1540
+ getAll: () => BuildingTile[];
1541
+ };
1542
+
1543
+ type CavemenRepository = {
1544
+ getAll: () => CavemanTile[];
1545
+ };
1546
+
991
1547
  type DominoesRepository = {
992
1548
  getForMode: (mode: GameMode) => Domino[] | [];
993
1549
  };
@@ -996,6 +1552,10 @@ type ModeRepository = {
996
1552
  getAvailables: () => GameMode[];
997
1553
  };
998
1554
 
1555
+ type QuestTilesRepository = {
1556
+ getAll: () => QuestTile[];
1557
+ };
1558
+
999
1559
  type RuleRepository = {
1000
1560
  getAll: () => Rules;
1001
1561
  getAllExtra: () => ExtraRule[];
@@ -1093,10 +1653,12 @@ declare const getStrategy: (name: string, custom?: Record<string, BotStrategy>)
1093
1653
  */
1094
1654
  declare const getStrategyNames: () => string[];
1095
1655
 
1656
+ type ConstructBuildingUseCase = (game: GameWithNextAction, lordId: string, buildingId: number, position: Position) => GameStateResult;
1657
+
1096
1658
  /**
1097
1659
  * The types of state-changing actions that can be recorded.
1098
1660
  */
1099
- type ActionType = "createGame" | "addPlayers" | "addExtraRules" | "startGame" | "chooseDomino" | "placeDomino" | "discardDomino";
1661
+ type ActionType = "createGame" | "addPlayers" | "addExtraRules" | "startGame" | "chooseDomino" | "placeDomino" | "discardDomino" | "placeGiant" | "sendGiant";
1100
1662
  /**
1101
1663
  * Payload types for each action, containing only the command parameters
1102
1664
  * (excluding the game state, which is too large to store).
@@ -1125,6 +1687,16 @@ type ActionPayloadMap = {
1125
1687
  discardDomino: {
1126
1688
  lordId: string;
1127
1689
  };
1690
+ placeGiant: {
1691
+ lordId: string;
1692
+ position: Position;
1693
+ };
1694
+ sendGiant: {
1695
+ lordId: string;
1696
+ giantIndex: number;
1697
+ targetPlayerId: string;
1698
+ targetCrownPosition: Position;
1699
+ };
1128
1700
  };
1129
1701
  /**
1130
1702
  * A single recorded action in the game log.
@@ -1290,6 +1862,16 @@ declare const getHistorySize: (history: GameHistory) => {
1290
1862
  future: number;
1291
1863
  };
1292
1864
 
1865
+ type PlaceFireTokenUseCase = (game: GameWithNextAction, lordId: string, position: Position) => GameStateResult;
1866
+
1867
+ type PlaceGiantUseCase = (game: GameWithNextAction, lordId: string, position: Position) => GameStateResult;
1868
+
1869
+ type PlaceKnightUseCase = (game: GameWithNextAction, lordId: string, position: Position) => GameStateResult;
1870
+
1871
+ type RecruitCavemanUseCase = (game: GameWithNextAction, lordId: string, cavemanId: number, position: Position, resourcePositions: Position[]) => GameStateResult;
1872
+
1873
+ type SendGiantUseCase = (game: GameWithNextAction, lordId: string, giantIndex: number, targetPlayerId: string, targetCrownPosition: Position) => GameStateResult;
1874
+
1293
1875
  /**
1294
1876
  * Serializes a game state to a JSON string.
1295
1877
  * The serialized string includes version information for future compatibility.
@@ -1335,6 +1917,10 @@ declare const createSavePoint: (game: GameState) => GameSavePoint;
1335
1917
  */
1336
1918
  declare const restoreFromSavePoint: (savePoint: GameSavePoint) => Result<GameState>;
1337
1919
 
1920
+ type SkipOptionalActionUseCase = (game: GameWithNextAction, lordId: string) => GameStateResult;
1921
+
1922
+ type UseDragonUseCase = (game: GameWithNextAction, lordId: string, buildingId: number) => GameStateResult;
1923
+
1338
1924
  /**
1339
1925
  * Creates a new Kingdomino game engine instance.
1340
1926
  *
@@ -1360,4 +1946,4 @@ declare const restoreFromSavePoint: (savePoint: GameSavePoint) => Result<GameSta
1360
1946
  */
1361
1947
  declare const createGameEngine: (config: Partial<EngineConfig>) => GameEngine;
1362
1948
 
1363
- export { type ActionEntry, ActionExecutionError, type ActionPayloadMap, type ActionType, type AddExtraRulesCommand, type AddPlayersCommand, type BasicRules, type BotStrategy, type CalculateScoreCommand, type CanPlaceDominoCommand, type ChooseDominoCommand, type CreateGameCommand, type Crown, type DebugLogEntry, type DebugLogLevel, type DebugLogger, type DebugOptions, type DeserializeGameCommand, type DiscardDominoCommand, type DomainError, DomainException, type Domino, type DominoesRepository, type DynastyResult, type EmptyGround, type EmptyTile, type EngineConfig, ErrorCode, type ErrorCodeType, type ExtraRule, type FinalResult, GRIDSIZE, GROUND, type Game, type GameActionLog, type GameEngine, type GameEventCallbacks, type GameHistory, type GameMode, type GameSavePoint, type GameState, type GameStateResult, type GameStateSummary, type GameSteps, type GameWithNextAction, type GameWithNextStep, type GameWithResults, type GetDynastyResultCommand, type GetExtraRulesCommand, type GetModesCommand, type GetResultCommand, type GetValidPlacementsCommand, type Ground, InvalidStepError, type Kingdom, type Logger, type Lord, MAX_KINGDOM_SIZE, type ModeRepository, type NextAction, type NextStep, NotFoundError, type ObjectValues, type PickContext, type PlaceContext, type PlaceDominoCommand, type Player, type PlayerActions, type PlayerInput, type PlayerPayload, type Players, type PlayersPayload, type Position, RedoError, type RevealsDomino, type Rotation, type RuleRepository, type Rules, type Score, type ScoreResult, type SelectedRules, type SerializeGameCommand, type ShuffleMethod, type StartGameCommand, StepExecutionError, type StrategyName, type Tile, TranslationKey, type TranslationKeyType, type Translator, UndoError, type UuidMethod, type ValidPlacement, ValidationError, type ValidationIssue, type ValidationSeverity, advancedStrategy, appendAction, canRedo, canUndo, clearHistory, createActionLog, createDomainError, createGameEngine, createGameHistory, createSavePoint, createTranslator, defaultTranslations, defaultTranslator, deserializeGame, errorCodeToTranslationKey, expertStrategy, gameSteps, getActions, getActionsByTurn, getActionsByType, getHistorySize, getStrategy, getStrategyNames, greedyStrategy, isBotTurn, isGameWithNextAction, isGameWithNextStep, playBotTurn, playBotTurns, playerActions, pushState, randomStrategy, redo, replayActions, restoreFromSavePoint, serializeGame, summarizeGameState, translateErrorCode, undo, validateGameState, wrapWithActionLog, wrapWithDebug, wrapWithEvents };
1949
+ export { type ActionEntry, ActionExecutionError, type ActionPayloadMap, type ActionType, type AddExtraRulesCommand, type AddPlayersCommand, type AgeOfGiantsState, type BasicRules, type BotStrategy, type BuildersBoard, type BuildingSlot, type BuildingTile, type BuildingsRepository, type CalculateScoreCommand, type CanPlaceDominoCommand, type CaveBoard, type CavemanTile, type CavemenRepository, type ChooseDominoCommand, type ConstructBuildingCommand, type ConstructBuildingUseCase, type CreateGameCommand, type Crown, type DebugLogEntry, type DebugLogLevel, type DebugLogger, type DebugOptions, type DeserializeGameCommand, type DiscardDominoCommand, type DomainError, DomainException, type Domino, type DominoesRepository, type DynastyResult, type EmptyGround, type EmptyTile, type EndGameScoring, type EndGameScoringType, type EngineConfig, ErrorCode, type ErrorCodeType, type ExtraRule, type FinalResult, type FireTokenPool, GRIDSIZE, GROUND, type Game, type GameActionLog, type GameEngine, type GameEventCallbacks, type GameHistory, type GameMode, type GameSavePoint, type GameState, type GameStateResult, type GameStateSummary, type GameSteps, type GameWithNextAction, type GameWithNextStep, type GameWithResults, type GetDynastyResultCommand, type GetExtraRulesCommand, type GetModesCommand, type GetResultCommand, type GetValidPlacementsCommand, type Ground, type HunterGathererKind, type HunterGathererTile, type ImmediateBonus, type ImmediateBonusType, InvalidStepError, type Kingdom, type Knight, type Logger, type Lord, MAX_KINGDOM_SIZE, type ModeRepository, type NextAction, type NextStep, NotFoundError, type ObjectValues, type OriginsState, type OriginsSubMode, type PendingFireToken, type PickContext, type PlaceContext, type PlaceDominoCommand, type PlaceFireTokenCommand, type PlaceFireTokenUseCase, type PlaceGiantCommand, type PlaceGiantUseCase, type PlaceKnightCommand, type PlaceKnightUseCase, type PlacedBuilding, type PlacedCaveman, type PlacedFireToken, type PlacedGiant, type Player, type PlayerActions, type PlayerInput, type PlayerPayload, type Players, type PlayersPayload, type Position, type QueenDominoState, type QuestTile, type QuestTilesRepository, type QuestType, type RecruitCavemanCommand, type RecruitCavemanUseCase, RedoError, type Resource, type ResourceType, type RevealsDomino, type Rotation, type RuleRepository, type Rules, type Score, type ScoreResult, type SelectedRules, type SendGiantCommand, type SendGiantUseCase, type SerializeGameCommand, type ShuffleMethod, type SkipOptionalActionCommand, type SkipOptionalActionUseCase, type StartGameCommand, StepExecutionError, type StrategyName, type Tile, type TotemBonus, type TotemState, TranslationKey, type TranslationKeyType, type Translator, UndoError, type UseDragonCommand, type UseDragonUseCase, type UuidMethod, type ValidPlacement, ValidationError, type ValidationIssue, type ValidationSeverity, WARRIOR_POWER, type WarriorTile, type WarriorType, advancedStrategy, appendAction, canRedo, canUndo, clearHistory, createActionLog, createDomainError, createGameEngine, createGameHistory, createSavePoint, createTranslator, defaultTranslations, defaultTranslator, deserializeGame, errorCodeToTranslationKey, expertStrategy, gameSteps, getActions, getActionsByTurn, getActionsByType, getHistorySize, getStrategy, getStrategyNames, greedyStrategy, hunterGathererKinds, isBotTurn, isGameWithNextAction, isGameWithNextStep, originsSubModes, playBotTurn, playBotTurns, playerActions, pushState, questTypes, randomStrategy, redo, replayActions, resourceTypes, restoreFromSavePoint, serializeGame, summarizeGameState, translateErrorCode, undo, validateGameState, warriorTypes, wrapWithActionLog, wrapWithDebug, wrapWithEvents };