@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,426 @@
1
+ /**
2
+ * Section 1: Concepts
3
+ *
4
+ * Tests for rules 1.1-1.9 from Disney Lorcana Comprehensive Rules (Aug 22, 2025)
5
+ * Note: Section 1.10 (Multiplayer Games) is skipped per plan - focusing on 2-player rules only
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 1: Concepts", () => {
16
+ let testEngine: LorcanaTestEngine;
17
+
18
+ beforeEach(() => {
19
+ testEngine = new LorcanaTestEngine(
20
+ { hand: 7, deck: 53, inkwell: 0 },
21
+ { hand: 7, deck: 53, inkwell: 0 },
22
+ { skipPreGame: true },
23
+ );
24
+ });
25
+
26
+ afterEach(() => {
27
+ testEngine.dispose();
28
+ });
29
+
30
+ describe("1.1. General", () => {
31
+ /**
32
+ * Rule 1.1.3: The Disney Lorcana TCG is a game played with two or more people.
33
+ * Each player needs a deck of Disney Lorcana cards that they'll use in the game.
34
+ */
35
+ test.failing("Rule 1.1.3 - Game requires two or more players with decks", () => {
36
+ // Arrange: Game is already set up with two players
37
+ const state = testEngine.getState();
38
+
39
+ // Assert: Both players should exist in the game
40
+ expect(Object.keys(state.external.loreScores)).toHaveLength(2);
41
+ expect(true).toBe(false); // Will fail until fully implemented
42
+ });
43
+
44
+ /**
45
+ * Rule 1.1.4: Each player needs a way to track their lore totals and mark damage
46
+ * on characters and locations.
47
+ */
48
+ test.failing("Rule 1.1.4 - Game tracks lore totals for each player", () => {
49
+ // Arrange: Game state
50
+ const state = testEngine.getState();
51
+
52
+ // Assert: Lore tracking should exist for both players
53
+ expect(state.external.loreScores[PLAYER_ONE]).toBeDefined();
54
+ expect(state.external.loreScores[PLAYER_TWO]).toBeDefined();
55
+ expect(true).toBe(false); // Will fail until fully implemented
56
+ });
57
+
58
+ /**
59
+ * Rule 1.1.6: Some cards include reminder text set in italics.
60
+ * Reminder text isn't rules text. It's only a memory aid.
61
+ */
62
+ test.failing("Rule 1.1.6 - Reminder text is not rules text", () => {
63
+ // This is a card definition/parsing rule - test that reminder text
64
+ // is stripped from ability resolution
65
+ expect(true).toBe(false); // Will fail until implemented
66
+ });
67
+ });
68
+
69
+ describe("1.2. Golden Rules", () => {
70
+ /**
71
+ * Rule 1.2.1: If the text of a card contradicts a game rule, the card effect
72
+ * supersedes that rule.
73
+ *
74
+ * Example: The game doesn't allow a character to challenge a ready character,
75
+ * but a player has a character with an ability that reads, "This character can
76
+ * challenge ready characters." The ability overrides the game rule.
77
+ */
78
+ test.failing("Rule 1.2.1 - Card effect supersedes game rule", () => {
79
+ // Arrange: Create a character with ability to challenge ready characters
80
+ const attacker = testEngine.createCharacterInPlay(PLAYER_ONE, {
81
+ strength: 3,
82
+ willpower: 4,
83
+ });
84
+ const readyDefender = testEngine.createCharacterInPlay(PLAYER_TWO, {
85
+ strength: 2,
86
+ willpower: 3,
87
+ });
88
+ // TODO: Add ability "This character can challenge ready characters"
89
+
90
+ // Act: Attempt to challenge the ready defender
91
+ // The base game rule should prevent this, but the ability overrides it
92
+
93
+ // Assert: Challenge should succeed due to card ability
94
+ expect(true).toBe(false); // Will fail until ability system implemented
95
+ });
96
+
97
+ /**
98
+ * Rule 1.2.2: If a rule or effect prevents something from happening, that rule
99
+ * or effect supersedes other rules and effects that allow it to happen.
100
+ *
101
+ * Example: An effect says that players can't play actions. Another effect
102
+ * instructs a player they may play an action for free. That player still
103
+ * can't play an action.
104
+ */
105
+ test.failing("Rule 1.2.2 - Can't beats can", () => {
106
+ // Arrange: Set up a scenario where one effect prevents actions
107
+ // and another allows playing actions for free
108
+
109
+ // Act: Try to play an action while prevented
110
+
111
+ // Assert: Action should not be playable
112
+ expect(true).toBe(false); // Will fail until effect system implemented
113
+ });
114
+
115
+ /**
116
+ * Rule 1.2.3: Do as much as you can - If an effect tells a player to do something,
117
+ * the player does as much as possible even if some part of that effect can't be done.
118
+ *
119
+ * Example: Strike a Good Match has an effect "Draw 2 cards, then choose and discard a card."
120
+ * If an effect prevents drawing, player still must discard a card.
121
+ */
122
+ test.failing("Rule 1.2.3 - Do as much as you can", () => {
123
+ // Arrange: Set up effect that draws cards and discards
124
+ // with draw being prevented
125
+
126
+ // Act: Execute the effect
127
+
128
+ // Assert: Discard should still happen even though draw was prevented
129
+ expect(true).toBe(false); // Will fail until effect system implemented
130
+ });
131
+
132
+ /**
133
+ * Rule 1.2.4: Choices that are made as a part of an effect are made as the effect
134
+ * is resolving and not as part of playing the card or using the ability.
135
+ *
136
+ * Example: Let the Storm Rage On reads "Deal 2 damage to chosen character. Draw a card."
137
+ * The choice of character happens as the effect resolves, not when playing the card.
138
+ */
139
+ test.failing("Rule 1.2.4 - Choices made during resolution, not when playing", () => {
140
+ // Arrange: Play a card that requires choosing a target
141
+
142
+ // Act: Resolve the effect
143
+
144
+ // Assert: Target choice should be made during resolution
145
+ expect(true).toBe(false); // Will fail until effect system implemented
146
+ });
147
+ });
148
+
149
+ describe("1.3. Active Player", () => {
150
+ /**
151
+ * Rule 1.3.1: When a player starts their turn, they become the active player.
152
+ * When a player ends their turn, they're no longer the active player.
153
+ */
154
+ test.failing("Rule 1.3.1 - Active player changes on turn start/end", () => {
155
+ // Arrange: Get initial turn player
156
+ const initialPlayer = testEngine.getTurnPlayer();
157
+
158
+ // Act: Pass turn
159
+ testEngine.passTurn();
160
+
161
+ // Assert: Active player should have changed
162
+ const newPlayer = testEngine.getTurnPlayer();
163
+ expect(newPlayer).not.toBe(initialPlayer);
164
+ expect(true).toBe(false); // Will fail until fully verified
165
+ });
166
+ });
167
+
168
+ describe("1.4. Opponent", () => {
169
+ /**
170
+ * Rule 1.4.1: Anyone a player is playing against is their opponent.
171
+ */
172
+ test.failing("Rule 1.4.1 - Opponent identification", () => {
173
+ // Arrange: Get player one's view
174
+
175
+ // Assert: Player two should be identified as opponent
176
+ expect(true).toBe(false); // Will fail until opponent system implemented
177
+ });
178
+
179
+ /**
180
+ * Rule 1.4.2: Teammates are identified before the game starts.
181
+ * Teammates aren't opponents to one another.
182
+ */
183
+ test.failing("Rule 1.4.2 - Teammates are not opponents", () => {
184
+ // Note: This is for team formats - skip detailed implementation for 2-player focus
185
+ expect(true).toBe(false); // Placeholder
186
+ });
187
+ });
188
+
189
+ describe("1.5. Playing Cards", () => {
190
+ /**
191
+ * Rule 1.5.1: Players can play a card whenever they're the active player
192
+ * and there are no effects to resolve. To play a card, the player reveals
193
+ * it from their hand and pays the cost.
194
+ */
195
+ test.failing("Rule 1.5.1 - Only active player can play cards when no effects resolving", () => {
196
+ // Arrange: Ensure player one is active
197
+ testEngine.changeActivePlayer(PLAYER_ONE);
198
+
199
+ // Act: Player two tries to play a card
200
+ // This should fail
201
+
202
+ // Assert: Non-active player cannot play cards
203
+ expect(true).toBe(false); // Will fail until turn order enforced
204
+ });
205
+ });
206
+
207
+ describe("1.6. Types of Abilities", () => {
208
+ /**
209
+ * Rule 1.6.1.1: Keywords are words or shortened phrases that represent
210
+ * a larger ability or abilities.
211
+ */
212
+ test.failing("Rule 1.6.1.1 - Keywords represent larger abilities", () => {
213
+ // Arrange: Create character with a keyword (e.g., Bodyguard)
214
+
215
+ // Assert: Keyword should expand to full ability effect
216
+ expect(true).toBe(false); // Will fail until keyword system implemented
217
+ });
218
+
219
+ /**
220
+ * Rule 1.6.1.2: Triggered abilities continuously look for a specific condition
221
+ * and have an effect when that condition is met.
222
+ */
223
+ test.failing("Rule 1.6.1.2 - Triggered abilities fire on condition", () => {
224
+ // Arrange: Create character with triggered ability
225
+ // e.g., "When this character quests, draw a card"
226
+
227
+ // Act: Quest with the character
228
+
229
+ // Assert: Triggered ability should fire
230
+ expect(true).toBe(false); // Will fail until trigger system implemented
231
+ });
232
+
233
+ /**
234
+ * Rule 1.6.1.3: Activated abilities have a cost and an effect that occurs
235
+ * if that cost is paid.
236
+ */
237
+ test.failing("Rule 1.6.1.3 - Activated abilities require cost payment", () => {
238
+ // Arrange: Create character with activated ability
239
+ // e.g., "{exert}, 2 ink: Draw 2 cards"
240
+
241
+ // Act: Pay cost and use ability
242
+
243
+ // Assert: Ability effect should occur after cost paid
244
+ expect(true).toBe(false); // Will fail until activated abilities implemented
245
+ });
246
+
247
+ /**
248
+ * Rule 1.6.1.4: Static abilities are effects that are continuously active,
249
+ * either for a fixed length of time or for as long as the card generating
250
+ * the effect is in play.
251
+ */
252
+ test.failing("Rule 1.6.1.4 - Static abilities continuously active", () => {
253
+ // Arrange: Create character with static ability
254
+ // e.g., "Your other characters get +1 strength"
255
+
256
+ // Assert: Effect should apply to other characters while card is in play
257
+ expect(true).toBe(false); // Will fail until static abilities implemented
258
+ });
259
+
260
+ /**
261
+ * Rule 1.6.1.5: Replacement effects are generated by some abilities.
262
+ * These replace one effect with another.
263
+ */
264
+ test.failing("Rule 1.6.1.5 - Replacement effects replace other effects", () => {
265
+ // Arrange: Set up replacement effect
266
+ // e.g., "If this character would be banished, return it to hand instead"
267
+
268
+ // Act: Trigger the original effect (banish)
269
+
270
+ // Assert: Replacement effect should occur instead
271
+ expect(true).toBe(false); // Will fail until replacement effects implemented
272
+ });
273
+
274
+ /**
275
+ * Rule 1.6.2: Whenever an effect would affect multiple players at the same time,
276
+ * the active player resolves that effect first, then in turn order each other
277
+ * player resolves that effect.
278
+ */
279
+ test.failing("Rule 1.6.2 - Multi-player effects resolve in turn order", () => {
280
+ // Arrange: Set up effect that affects all players
281
+ // e.g., "Each player draws a card"
282
+
283
+ // Act: Resolve the effect
284
+
285
+ // Assert: Active player resolves first, then other players in turn order
286
+ expect(true).toBe(false); // Will fail until multi-player effect resolution implemented
287
+ });
288
+ });
289
+
290
+ describe("1.7. The Bag", () => {
291
+ /**
292
+ * Rule 1.7.1: The bag is the zone where triggered abilities wait to resolve.
293
+ * It's not a physical zone but a way to picture the process.
294
+ */
295
+ test.failing("Rule 1.7.1 - Triggered abilities queue in the bag", () => {
296
+ // Arrange: Create multiple triggered abilities that fire simultaneously
297
+
298
+ // Act: Trigger condition occurs
299
+
300
+ // Assert: Both abilities should be in the bag waiting to resolve
301
+ expect(true).toBe(false); // Will fail until bag system implemented
302
+ });
303
+
304
+ /**
305
+ * Rule 1.7.2: It's possible for both the active player and their opponent(s)
306
+ * to add triggered abilities to the bag at the same time.
307
+ */
308
+ test.failing("Rule 1.7.2 - Both players can add to bag simultaneously", () => {
309
+ // Arrange: Create triggered abilities for both players
310
+
311
+ // Act: Trigger condition that fires both abilities
312
+
313
+ // Assert: Both abilities should be in the bag
314
+ expect(true).toBe(false); // Will fail until bag system implemented
315
+ });
316
+ });
317
+
318
+ describe("1.8. Players' Cards", () => {
319
+ /**
320
+ * Rule 1.8.1: Cards a player brings to the table in their deck are their cards,
321
+ * and that player makes any decisions necessary for the card and its effects.
322
+ */
323
+ test.failing("Rule 1.8.1 - Card owner makes decisions for their cards", () => {
324
+ // Arrange: Card with decision point
325
+
326
+ // Assert: Owner should be the one making decisions
327
+ expect(true).toBe(false); // Will fail until ownership system verified
328
+ });
329
+
330
+ /**
331
+ * Rule 1.8.2: When a card refers to "his," "her," "its," or "their" player,
332
+ * it's referring to the person who played the card.
333
+ */
334
+ test.failing("Rule 1.8.2 - His/her/its/their refers to card's controller", () => {
335
+ // Arrange: Card with ability referencing "its player"
336
+
337
+ // Assert: Reference should point to the controller
338
+ expect(true).toBe(false); // Will fail until text parsing implemented
339
+ });
340
+
341
+ /**
342
+ * Rule 1.8.3: When a card refers to "you," "your," or "yours," it's referring
343
+ * to the player of the card, even if the ability was granted by an opposing effect.
344
+ */
345
+ test.failing("Rule 1.8.3 - You/your/yours refers to card's player", () => {
346
+ // Arrange: Card with "your" reference
347
+ // Even if ability granted by opponent
348
+
349
+ // Assert: "Your" should refer to the card's player
350
+ expect(true).toBe(false); // Will fail until text parsing implemented
351
+ });
352
+ });
353
+
354
+ describe("1.9. Game State Check", () => {
355
+ /**
356
+ * Rule 1.9.1.1: If a player has 20 or more lore, that player wins the game.
357
+ */
358
+ test.failing("Rule 1.9.1.1 - 20 lore wins the game", () => {
359
+ // Arrange: Set up player with characters to quest
360
+
361
+ // Act: Quest until reaching 20 lore
362
+
363
+ // Assert: Game should end with that player winning
364
+ expect(true).toBe(false); // Will fail until win condition implemented
365
+ });
366
+
367
+ /**
368
+ * Rule 1.9.1.2: If a player attempted to draw from a deck with no cards
369
+ * since the last game state check, that player loses the game.
370
+ */
371
+ test.failing("Rule 1.9.1.2 - Empty deck draw loses the game", () => {
372
+ // Arrange: Empty a player's deck
373
+ // This would require test setup to have empty deck
374
+
375
+ // Act: Attempt to draw
376
+
377
+ // Assert: Player should lose
378
+ expect(true).toBe(false); // Will fail until loss condition implemented
379
+ });
380
+
381
+ /**
382
+ * Rule 1.9.1.3: If a character or location has damage equal to or greater than
383
+ * its Willpower, that character or location is banished.
384
+ */
385
+ test.failing("Rule 1.9.1.3 - Damage >= Willpower banishes character", () => {
386
+ // Arrange: Create character with willpower 3
387
+ const character = testEngine.createCharacterInPlay(PLAYER_ONE, {
388
+ strength: 2,
389
+ willpower: 3,
390
+ });
391
+
392
+ // Act: Deal 3 or more damage to character
393
+ // (would need damage dealing mechanism)
394
+
395
+ // Assert: Character should be banished (moved to discard)
396
+ expect(true).toBe(false); // Will fail until damage system fully implemented
397
+ });
398
+
399
+ /**
400
+ * Rule 1.9.2: A game state check is made at the end of every step,
401
+ * after any action or ability is finished resolving, and after each
402
+ * effect in the bag is finished resolving.
403
+ */
404
+ test.failing("Rule 1.9.2 - Game state check timing", () => {
405
+ // Arrange: Set up scenario where game state check matters
406
+
407
+ // Act: Complete an action/step
408
+
409
+ // Assert: Game state check should have occurred
410
+ expect(true).toBe(false); // Will fail until game state check timing implemented
411
+ });
412
+
413
+ /**
414
+ * Rule 1.9.2.1: Any required actions generated from a game state check
415
+ * happen in turn order. If a player would win and lose at the same time,
416
+ * that player wins.
417
+ */
418
+ test.failing("Rule 1.9.2.1 - Win/lose simultaneously means win", () => {
419
+ // Arrange: Set up scenario where player wins and loses at same time
420
+ // e.g., reach 20 lore same time as deck empties
421
+
422
+ // Assert: Player should win
423
+ expect(true).toBe(false); // Will fail until win/lose resolution implemented
424
+ });
425
+ });
426
+ });