@drmxrcy/tcg-lorcana 0.0.0-202602060544

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.
Files changed (100) hide show
  1. package/README.md +160 -0
  2. package/package.json +45 -0
  3. package/src/__tests__/integration/move-enumeration.test.ts +256 -0
  4. package/src/__tests__/rules/section-01-concepts.test.ts +426 -0
  5. package/src/__tests__/rules/section-03-gameplay.test.ts +298 -0
  6. package/src/__tests__/rules/section-04-turn-structure.test.ts +708 -0
  7. package/src/__tests__/rules/section-05-cards.test.ts +158 -0
  8. package/src/__tests__/rules/section-06-card-types.test.ts +342 -0
  9. package/src/__tests__/rules/section-07-abilities.test.ts +333 -0
  10. package/src/__tests__/rules/section-08-zones.test.ts +231 -0
  11. package/src/__tests__/rules/section-09-damage.test.ts +148 -0
  12. package/src/__tests__/rules/section-10-keywords.test.ts +469 -0
  13. package/src/__tests__/spec-01-foundation-types.test.ts +534 -0
  14. package/src/__tests__/spec-02-zones-card-states.test.ts +295 -0
  15. package/src/card-utils.ts +302 -0
  16. package/src/cards/README.md +296 -0
  17. package/src/cards/abilities/index.ts +175 -0
  18. package/src/cards/index.ts +10 -0
  19. package/src/deck-validation.ts +175 -0
  20. package/src/engine/lorcana-engine.ts +625 -0
  21. package/src/game-definition/__tests__/core-zone-integration.test.ts +553 -0
  22. package/src/game-definition/__tests__/zone-operations.test.ts +362 -0
  23. package/src/game-definition/__tests__/zones.test.ts +176 -0
  24. package/src/game-definition/definition.ts +45 -0
  25. package/src/game-definition/flow/turn-flow.ts +216 -0
  26. package/src/game-definition/index.ts +31 -0
  27. package/src/game-definition/moves/abilities/activate-ability.ts +51 -0
  28. package/src/game-definition/moves/core/__tests__/move-parameter-enumeration.test.ts +316 -0
  29. package/src/game-definition/moves/core/challenge.test.ts +545 -0
  30. package/src/game-definition/moves/core/challenge.ts +81 -0
  31. package/src/game-definition/moves/core/play-card.ts +83 -0
  32. package/src/game-definition/moves/core/quest.test.ts +448 -0
  33. package/src/game-definition/moves/core/quest.ts +49 -0
  34. package/src/game-definition/moves/debug/manual-exert.ts +36 -0
  35. package/src/game-definition/moves/effects/resolve-bag.ts +35 -0
  36. package/src/game-definition/moves/effects/resolve-effect.ts +34 -0
  37. package/src/game-definition/moves/index.ts +85 -0
  38. package/src/game-definition/moves/locations/move-character-to-location.ts +42 -0
  39. package/src/game-definition/moves/resources/put-card-into-inkwell.test.ts +462 -0
  40. package/src/game-definition/moves/resources/put-card-into-inkwell.ts +51 -0
  41. package/src/game-definition/moves/setup/alter-hand.test.ts +395 -0
  42. package/src/game-definition/moves/setup/alter-hand.ts +210 -0
  43. package/src/game-definition/moves/setup/choose-first-player.test.ts +450 -0
  44. package/src/game-definition/moves/setup/choose-first-player.ts +105 -0
  45. package/src/game-definition/moves/setup/draw-cards.ts +37 -0
  46. package/src/game-definition/moves/songs/sing-together.ts +47 -0
  47. package/src/game-definition/moves/songs/sing.ts +56 -0
  48. package/src/game-definition/moves/standard/concede.test.ts +189 -0
  49. package/src/game-definition/moves/standard/concede.ts +72 -0
  50. package/src/game-definition/moves/standard/pass-turn.ts +49 -0
  51. package/src/game-definition/setup/game-setup.ts +19 -0
  52. package/src/game-definition/trackers/tracker-config.ts +23 -0
  53. package/src/game-definition/win-conditions/lore-victory.ts +26 -0
  54. package/src/game-definition/zone-operations.ts +405 -0
  55. package/src/game-definition/zones/zone-configs.ts +59 -0
  56. package/src/game-definition/zones.ts +283 -0
  57. package/src/index.ts +189 -0
  58. package/src/operations/index.ts +7 -0
  59. package/src/operations/lorcana-operations.ts +288 -0
  60. package/src/queries/README.md +56 -0
  61. package/src/resolvers/__tests__/condition-resolver.test.ts +301 -0
  62. package/src/resolvers/condition-registry.ts +70 -0
  63. package/src/resolvers/condition-resolver.ts +85 -0
  64. package/src/resolvers/conditions/basic.ts +81 -0
  65. package/src/resolvers/conditions/card-state.ts +12 -0
  66. package/src/resolvers/conditions/comparison.ts +102 -0
  67. package/src/resolvers/conditions/existence.ts +219 -0
  68. package/src/resolvers/conditions/history.ts +68 -0
  69. package/src/resolvers/conditions/index.ts +15 -0
  70. package/src/resolvers/conditions/logical.ts +55 -0
  71. package/src/resolvers/conditions/resolution.ts +41 -0
  72. package/src/resolvers/conditions/revealed.ts +42 -0
  73. package/src/resolvers/conditions/zone.ts +84 -0
  74. package/src/setup.test.ts +18 -0
  75. package/src/targeting/__tests__/filter-resolver.test.ts +294 -0
  76. package/src/targeting/__tests__/real-cards-targeting.test.ts +303 -0
  77. package/src/targeting/__tests__/targeting-dsl.test.ts +386 -0
  78. package/src/targeting/enum-expansion.ts +387 -0
  79. package/src/targeting/filter-registry.ts +322 -0
  80. package/src/targeting/filter-resolver.ts +145 -0
  81. package/src/targeting/index.ts +91 -0
  82. package/src/targeting/lorcana-target-dsl.ts +495 -0
  83. package/src/targeting/targeting-ui.ts +407 -0
  84. package/src/testing/index.ts +14 -0
  85. package/src/testing/lorcana-test-engine.ts +813 -0
  86. package/src/types/README.md +303 -0
  87. package/src/types/__tests__/lorcana-state.test.ts +168 -0
  88. package/src/types/__tests__/move-enumeration.test.ts +179 -0
  89. package/src/types/branded-types.ts +106 -0
  90. package/src/types/game-state.ts +184 -0
  91. package/src/types/index.ts +87 -0
  92. package/src/types/keywords.ts +187 -0
  93. package/src/types/lorcana-state.ts +260 -0
  94. package/src/types/move-enumeration.ts +126 -0
  95. package/src/types/move-params.ts +216 -0
  96. package/src/validators/index.ts +7 -0
  97. package/src/validators/move-validators.ts +374 -0
  98. package/src/zones/card-state.ts +234 -0
  99. package/src/zones/index.ts +42 -0
  100. package/src/zones/zone-config.ts +150 -0
@@ -0,0 +1,298 @@
1
+ /**
2
+ * Section 3: Gameplay
3
+ *
4
+ * Tests for rules 3.1-3.2 from Disney Lorcana Comprehensive Rules (Aug 22, 2025)
5
+ * Covers starting and ending a game.
6
+ */
7
+
8
+ import { afterEach, beforeEach, describe, expect, test } from "bun:test";
9
+ import {
10
+ LorcanaTestEngine,
11
+ PLAYER_ONE,
12
+ PLAYER_TWO,
13
+ } from "../../testing/lorcana-test-engine";
14
+
15
+ describe("Section 3: Gameplay", () => {
16
+ describe("3.1. Starting a Game", () => {
17
+ /**
18
+ * Rule 3.1.1: Starting a game involves several steps that all players follow.
19
+ * Once these steps are completed, the game is considered to be started.
20
+ */
21
+ test.failing("Rule 3.1.1 - Game start requires completing all setup steps", () => {
22
+ // Arrange: Create engine without skipping pre-game
23
+ const testEngine = new LorcanaTestEngine(
24
+ { hand: 0, deck: 60 },
25
+ { hand: 0, deck: 60 },
26
+ { skipPreGame: false },
27
+ );
28
+
29
+ try {
30
+ // Assert: Game should require setup steps before main game
31
+ const segment = testEngine.getGameSegment();
32
+ expect(segment).toBe("startingAGame");
33
+ expect(true).toBe(false); // Will fail until fully verified
34
+ } finally {
35
+ testEngine.dispose();
36
+ }
37
+ });
38
+
39
+ /**
40
+ * Rule 3.1.2: First, use a method for randomly determining who chooses
41
+ * who is the starting player. If this is next in a best-of series,
42
+ * the losing player of the previous game chooses.
43
+ */
44
+ test.failing("Rule 3.1.2 - Random determination of starting player chooser", () => {
45
+ const testEngine = new LorcanaTestEngine(
46
+ { hand: 0, deck: 60 },
47
+ { hand: 0, deck: 60 },
48
+ { skipPreGame: false },
49
+ );
50
+
51
+ try {
52
+ // Assert: One player should be designated to choose first player
53
+ const ctx = testEngine.getCtx();
54
+ expect(ctx.choosingFirstPlayer).toBeDefined();
55
+ expect(true).toBe(false); // Will fail until fully verified
56
+ } finally {
57
+ testEngine.dispose();
58
+ }
59
+ });
60
+
61
+ /**
62
+ * Rule 3.1.3: Second, each player randomizes (shuffles) their deck.
63
+ * Each player must offer an opposing player a chance to cut their deck.
64
+ */
65
+ test.failing("Rule 3.1.3 - Decks must be shuffled at game start", () => {
66
+ // Arrange: Create two games with same seed to verify shuffle occurs
67
+ const testEngine = new LorcanaTestEngine(
68
+ { hand: 0, deck: 60 },
69
+ { hand: 0, deck: 60 },
70
+ { skipPreGame: false, seed: "test-seed" },
71
+ );
72
+
73
+ try {
74
+ // Assert: Deck should be randomized
75
+ // (Hard to verify without comparing to unshuffled state)
76
+ expect(true).toBe(false); // Will fail until shuffle verification implemented
77
+ } finally {
78
+ testEngine.dispose();
79
+ }
80
+ });
81
+
82
+ /**
83
+ * Rule 3.1.4: Third, each player begins the game with 0 lore.
84
+ */
85
+ test.failing("Rule 3.1.4 - Players start with 0 lore", () => {
86
+ const testEngine = new LorcanaTestEngine(
87
+ { hand: 7, deck: 53 },
88
+ { hand: 7, deck: 53 },
89
+ { skipPreGame: true },
90
+ );
91
+
92
+ try {
93
+ // Assert: Both players should start with 0 lore
94
+ expect(testEngine.getLore(PLAYER_ONE)).toBe(0);
95
+ expect(testEngine.getLore(PLAYER_TWO)).toBe(0);
96
+ // This might actually pass - update assertion once verified
97
+ expect(true).toBe(false);
98
+ } finally {
99
+ testEngine.dispose();
100
+ }
101
+ });
102
+
103
+ /**
104
+ * Rule 3.1.5: Fourth, each player draws 7 cards.
105
+ */
106
+ test.failing("Rule 3.1.5 - Players draw 7 cards at start", () => {
107
+ const testEngine = new LorcanaTestEngine(
108
+ { hand: 0, deck: 60 },
109
+ { hand: 0, deck: 60 },
110
+ { skipPreGame: false },
111
+ );
112
+
113
+ try {
114
+ // After setup, each player should have 7 cards
115
+ // Would need to complete setup first
116
+ expect(true).toBe(false); // Will fail until draw step implemented
117
+ } finally {
118
+ testEngine.dispose();
119
+ }
120
+ });
121
+
122
+ /**
123
+ * Rule 3.1.6: Fifth, players may alter their hands (mulligan),
124
+ * beginning with the starting player.
125
+ */
126
+ test.failing("Rule 3.1.6 - Mulligan in starting player order", () => {
127
+ const testEngine = new LorcanaTestEngine(
128
+ { hand: 7, deck: 53 },
129
+ { hand: 7, deck: 53 },
130
+ { skipPreGame: false },
131
+ );
132
+
133
+ try {
134
+ // Complete first player selection
135
+ const ctx = testEngine.getCtx();
136
+ testEngine.changeActivePlayer(ctx.choosingFirstPlayer || PLAYER_ONE);
137
+ testEngine.chooseWhoGoesFirst(PLAYER_ONE);
138
+
139
+ // Now mulligan phase - starting player should go first
140
+ // Assert: Mulligan order follows starting player
141
+ expect(true).toBe(false); // Will fail until mulligan order verified
142
+ } finally {
143
+ testEngine.dispose();
144
+ }
145
+ });
146
+
147
+ /**
148
+ * Rule 3.1.6.1: Step 1 - The player selects any number of cards from their hand
149
+ * and places them on the bottom of their deck without revealing them.
150
+ */
151
+ test.failing("Rule 3.1.6.1 - Mulligan puts cards on bottom of deck", () => {
152
+ const testEngine = new LorcanaTestEngine(
153
+ { hand: 7, deck: 53 },
154
+ { hand: 7, deck: 53 },
155
+ { skipPreGame: false },
156
+ );
157
+
158
+ try {
159
+ // Complete setup to mulligan phase
160
+ const ctx = testEngine.getCtx();
161
+ testEngine.changeActivePlayer(ctx.choosingFirstPlayer || PLAYER_ONE);
162
+ testEngine.chooseWhoGoesFirst(PLAYER_ONE);
163
+ testEngine.changeActivePlayer(PLAYER_ONE);
164
+
165
+ // Get initial hand
166
+ const initialHand = testEngine.getZone("hand", PLAYER_ONE);
167
+ const cardsToMulligan = initialHand.slice(0, 3);
168
+
169
+ // Mulligan those cards
170
+ testEngine.alterHand(cardsToMulligan);
171
+
172
+ // Assert: Cards should be on bottom of deck (not revealed)
173
+ expect(true).toBe(false); // Will fail until mulligan bottom placement verified
174
+ } finally {
175
+ testEngine.dispose();
176
+ }
177
+ });
178
+
179
+ /**
180
+ * Rule 3.1.6.2: Step 2 - The player draws until they have 7 cards in hand.
181
+ */
182
+ test.failing("Rule 3.1.6.2 - Draw back up to 7 after mulligan", () => {
183
+ const testEngine = new LorcanaTestEngine(
184
+ { hand: 7, deck: 53 },
185
+ { hand: 7, deck: 53 },
186
+ { skipPreGame: false },
187
+ );
188
+
189
+ try {
190
+ // Complete setup and mulligan
191
+ const ctx = testEngine.getCtx();
192
+ testEngine.changeActivePlayer(ctx.choosingFirstPlayer || PLAYER_ONE);
193
+ testEngine.chooseWhoGoesFirst(PLAYER_ONE);
194
+ testEngine.changeActivePlayer(PLAYER_ONE);
195
+
196
+ const initialHand = testEngine.getZone("hand", PLAYER_ONE);
197
+ testEngine.alterHand(initialHand.slice(0, 3)); // Mulligan 3 cards
198
+
199
+ // Assert: Hand should be back to 7 cards
200
+ const finalHand = testEngine.getZone("hand", PLAYER_ONE);
201
+ expect(finalHand).toHaveLength(7);
202
+ expect(true).toBe(false); // Will fail until mulligan draw verified
203
+ } finally {
204
+ testEngine.dispose();
205
+ }
206
+ });
207
+
208
+ /**
209
+ * Rule 3.1.6.4: Step 4 - Each player who altered their hand by 1 or more cards
210
+ * shuffles their deck.
211
+ */
212
+ test.failing("Rule 3.1.6.4 - Deck shuffled after mulligan", () => {
213
+ // Assert: Deck should be shuffled if mulligan occurred
214
+ expect(true).toBe(false); // Will fail until mulligan shuffle implemented
215
+ });
216
+
217
+ /**
218
+ * Rule 3.1.7: Once all players have altered or chosen not to alter their hand,
219
+ * the game officially starts with the starting player's Beginning Phase.
220
+ */
221
+ test.failing("Rule 3.1.7 - Game starts after all mulligans complete", () => {
222
+ const testEngine = new LorcanaTestEngine(
223
+ { hand: 7, deck: 53 },
224
+ { hand: 7, deck: 53 },
225
+ { skipPreGame: false },
226
+ );
227
+
228
+ try {
229
+ // Complete all setup
230
+ const ctx = testEngine.getCtx();
231
+ testEngine.changeActivePlayer(ctx.choosingFirstPlayer || PLAYER_ONE);
232
+ testEngine.chooseWhoGoesFirst(PLAYER_ONE);
233
+ testEngine.changeActivePlayer(PLAYER_ONE);
234
+ testEngine.alterHand([]);
235
+ testEngine.changeActivePlayer(PLAYER_TWO);
236
+ testEngine.alterHand([]);
237
+
238
+ // Assert: Game should now be in mainGame segment
239
+ const segment = testEngine.getGameSegment();
240
+ expect(segment).toBe("mainGame");
241
+ expect(true).toBe(false); // Will fail until transition verified
242
+ } finally {
243
+ testEngine.dispose();
244
+ }
245
+ });
246
+ });
247
+
248
+ describe("3.2. Ending a Game", () => {
249
+ let testEngine: LorcanaTestEngine;
250
+
251
+ beforeEach(() => {
252
+ testEngine = new LorcanaTestEngine(
253
+ { hand: 7, deck: 53, inkwell: 0 },
254
+ { hand: 7, deck: 53, inkwell: 0 },
255
+ { skipPreGame: true },
256
+ );
257
+ });
258
+
259
+ afterEach(() => {
260
+ testEngine.dispose();
261
+ });
262
+
263
+ /**
264
+ * Rule 3.2.1.1: When a player reaches 20 lore, they win the game.
265
+ */
266
+ test.failing("Rule 3.2.1.1 - 20 lore wins the game", () => {
267
+ // Arrange: Set up scenario to reach 20 lore
268
+ // Create many characters and quest repeatedly
269
+
270
+ // Act: Quest until 20 lore reached
271
+
272
+ // Assert: Game should end with that player winning
273
+ expect(true).toBe(false); // Will fail until win condition implemented
274
+ });
275
+
276
+ /**
277
+ * Rule 3.2.1.2: If a player attempted to draw from a deck with no cards
278
+ * since the last game state check, that player loses the game.
279
+ * All cards in play and abilities from that player are removed.
280
+ */
281
+ test.failing("Rule 3.2.1.2 - Empty deck draw loses the game", () => {
282
+ // Arrange: Would need to set up scenario where deck is empty
283
+
284
+ // Act: Attempt to draw from empty deck
285
+
286
+ // Assert: Player should lose, their cards/effects removed
287
+ expect(true).toBe(false); // Will fail until deck-out loss implemented
288
+ });
289
+
290
+ /**
291
+ * Rule 3.2.1.3: If a player is the last person left in a game, they win.
292
+ */
293
+ test.failing("Rule 3.2.1.3 - Last player standing wins", () => {
294
+ // In 2-player, if one player loses (e.g., deck-out), other wins
295
+ expect(true).toBe(false); // Will fail until last player win implemented
296
+ });
297
+ });
298
+ });