@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,148 @@
1
+ /**
2
+ * Section 9: Damage
3
+ *
4
+ * Tests for rules 9.1-9.4 from Disney Lorcana Comprehensive Rules (Aug 22, 2025)
5
+ * Covers damage representation, "put" vs "deal", moving damage, and leaving play.
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 9: Damage", () => {
16
+ let testEngine: LorcanaTestEngine;
17
+
18
+ beforeEach(() => {
19
+ testEngine = new LorcanaTestEngine(
20
+ { hand: 7, deck: 53, inkwell: 3 },
21
+ { hand: 7, deck: 53, inkwell: 3 },
22
+ { skipPreGame: true },
23
+ );
24
+ });
25
+
26
+ afterEach(() => {
27
+ testEngine.dispose();
28
+ });
29
+
30
+ describe("9.1. Representation of Damage", () => {
31
+ /**
32
+ * Rule 9.1.1: Damage is tracked on characters and locations using damage counters.
33
+ * Each counter represents 1 damage.
34
+ */
35
+ test.failing("Rule 9.1.1 - Damage tracked with counters", () => {
36
+ // Arrange: Create character
37
+ const character = testEngine.createCharacterInPlay(PLAYER_ONE, {
38
+ strength: 2,
39
+ willpower: 5,
40
+ });
41
+
42
+ // Initially should have 0 damage
43
+ expect(testEngine.getDamage(character)).toBe(0);
44
+
45
+ // After taking damage, counter should increase
46
+ // (need to deal damage somehow)
47
+ expect(true).toBe(false); // Will fail until damage dealing implemented
48
+ });
49
+
50
+ /**
51
+ * Damage is persistent - accumulates over the game.
52
+ */
53
+ test.failing("Rule 9.1 - Damage persists across turns", () => {
54
+ // Arrange: Deal damage to character
55
+
56
+ // Act: Pass multiple turns
57
+
58
+ // Assert: Damage should still be there
59
+ expect(true).toBe(false); // Will fail until damage persistence verified
60
+ });
61
+ });
62
+
63
+ describe("9.2. 'Put' Damage", () => {
64
+ /**
65
+ * Rule 9.2.1: "Put damage" is different from "deal damage".
66
+ * Put bypasses effects that modify dealt damage (like Resist).
67
+ */
68
+ test.failing("Rule 9.2.1 - Put damage bypasses Resist", () => {
69
+ // Arrange: Character with Resist 2
70
+
71
+ // Act: Put 3 damage on it
72
+
73
+ // Assert: Should take full 3 damage (Resist doesn't apply)
74
+ expect(true).toBe(false); // Will fail until put vs deal distinction implemented
75
+ });
76
+
77
+ /**
78
+ * "Put" also bypasses damage prevention effects.
79
+ */
80
+ test.failing("Rule 9.2 - Put damage bypasses prevention", () => {
81
+ // Arrange: Damage prevention effect active
82
+
83
+ // Act: Put damage
84
+
85
+ // Assert: Damage should still be placed
86
+ expect(true).toBe(false); // Will fail until put damage implemented
87
+ });
88
+ });
89
+
90
+ describe("9.3. Moving Damage Counters", () => {
91
+ /**
92
+ * Rule 9.3.1: Some effects can move damage counters between cards.
93
+ */
94
+ test.failing("Rule 9.3.1 - Damage counters can be moved", () => {
95
+ // Arrange: Two characters, one with damage
96
+
97
+ // Act: Move damage from one to other
98
+
99
+ // Assert: Damage should transfer
100
+ expect(true).toBe(false); // Will fail until damage movement implemented
101
+ });
102
+
103
+ /**
104
+ * Moving damage is not dealing or putting damage (doesn't trigger those effects).
105
+ */
106
+ test.failing("Rule 9.3 - Moving damage is not dealing", () => {
107
+ // Arrange: "When this character takes damage" trigger
108
+
109
+ // Act: Move damage to this character
110
+
111
+ // Assert: Trigger should NOT fire (moving isn't dealing/taking)
112
+ expect(true).toBe(false); // Will fail until damage movement semantics implemented
113
+ });
114
+ });
115
+
116
+ describe("9.4. Leaving Play", () => {
117
+ /**
118
+ * Rule 9.4.1: When a card leaves play, all damage on it is removed.
119
+ */
120
+ test.failing("Rule 9.4.1 - Damage cleared when leaving play", () => {
121
+ // Arrange: Character with 2 damage (not lethal)
122
+ const character = testEngine.createCharacterInPlay(PLAYER_ONE, {
123
+ strength: 2,
124
+ willpower: 5,
125
+ });
126
+
127
+ // Deal 2 damage somehow
128
+ // Then return character to hand
129
+
130
+ // Act: Play character again
131
+
132
+ // Assert: Should have 0 damage (cleared when left play)
133
+ expect(true).toBe(false); // Will fail until leaving play cleanup implemented
134
+ });
135
+
136
+ /**
137
+ * If a character would leave play but is prevented, damage stays.
138
+ */
139
+ test.failing("Rule 9.4 - Prevention keeps damage", () => {
140
+ // Arrange: Character with damage, replacement effect preventing leaving
141
+
142
+ // Act: Try to banish character (prevented)
143
+
144
+ // Assert: Damage should remain
145
+ expect(true).toBe(false); // Will fail until leave prevention implemented
146
+ });
147
+ });
148
+ });
@@ -0,0 +1,469 @@
1
+ /**
2
+ * Section 10: Keywords
3
+ *
4
+ * Tests for rules 10.1-10.13 from Disney Lorcana Comprehensive Rules (Aug 22, 2025)
5
+ * Covers all keywords: General, Bodyguard, Challenger, Evasive, Reckless, Resist,
6
+ * Rush, Shift, Singer, Sing Together, Support, Vanish, and Ward.
7
+ */
8
+
9
+ import { afterEach, beforeEach, describe, expect, test } from "bun:test";
10
+ import {
11
+ LorcanaTestEngine,
12
+ PLAYER_ONE,
13
+ PLAYER_TWO,
14
+ } from "../../testing/lorcana-test-engine";
15
+
16
+ describe("Section 10: Keywords", () => {
17
+ let testEngine: LorcanaTestEngine;
18
+
19
+ beforeEach(() => {
20
+ testEngine = new LorcanaTestEngine(
21
+ { hand: 7, deck: 53, inkwell: 3 },
22
+ { hand: 7, deck: 53, inkwell: 3 },
23
+ { skipPreGame: true },
24
+ );
25
+ });
26
+
27
+ afterEach(() => {
28
+ testEngine.dispose();
29
+ });
30
+
31
+ describe("10.1. General", () => {
32
+ /**
33
+ * Rule 10.1.1: Keywords are abilities represented by specific words or phrases.
34
+ */
35
+ test.failing("Rule 10.1.1 - Keywords represent abilities", () => {
36
+ // Assert: Keyword should expand to full ability rules
37
+ expect(true).toBe(false); // Will fail until keyword system implemented
38
+ });
39
+
40
+ /**
41
+ * Rule 10.1.2: Some keywords stack (add together), some don't.
42
+ */
43
+ test.failing("Rule 10.1.2 - Keyword stacking rules", () => {
44
+ // Arrange: Character with Challenger +2 from two sources
45
+
46
+ // Assert: Should have Challenger +4 (stacks)
47
+ expect(true).toBe(false); // Will fail until keyword stacking implemented
48
+ });
49
+
50
+ /**
51
+ * Rule 10.1.3: Technical definitions define exact keyword behavior.
52
+ * Reminder text is not rules text.
53
+ */
54
+ test.failing("Rule 10.1.3 - Reminder text is not rules", () => {
55
+ // Assert: Only technical definition applies, not reminder text
56
+ expect(true).toBe(false); // Will fail until keyword parsing verified
57
+ });
58
+ });
59
+
60
+ describe("10.2. Bodyguard", () => {
61
+ /**
62
+ * Rule 10.2.1: Bodyguard - When this character is exerted, opposing characters
63
+ * must challenge this character if able.
64
+ */
65
+ test.failing("Rule 10.2.1 - Bodyguard forces challenges to this character", () => {
66
+ // Arrange: Bodyguard character (exerted), other character
67
+ // Opponent has character that could challenge either
68
+
69
+ // Assert: Opponent must challenge Bodyguard
70
+ expect(true).toBe(false); // Will fail until Bodyguard implemented
71
+ });
72
+
73
+ /**
74
+ * Bodyguard only applies when exerted.
75
+ */
76
+ test.failing("Rule 10.2 - Bodyguard requires exerted state", () => {
77
+ // Arrange: Bodyguard character (ready)
78
+
79
+ // Assert: Opponent can challenge other characters
80
+ expect(true).toBe(false); // Will fail until Bodyguard state check implemented
81
+ });
82
+
83
+ /**
84
+ * Multiple Bodyguards - opponent chooses which to challenge.
85
+ */
86
+ test.failing("Rule 10.2 - Multiple Bodyguards allow choice", () => {
87
+ // Arrange: Two exerted Bodyguard characters
88
+
89
+ // Assert: Opponent can choose either
90
+ expect(true).toBe(false); // Will fail until multiple Bodyguard implemented
91
+ });
92
+ });
93
+
94
+ describe("10.3. Challenger", () => {
95
+ /**
96
+ * Rule 10.3.1: Challenger +X - This character gets +X Strength while challenging.
97
+ */
98
+ test.failing("Rule 10.3.1 - Challenger adds Strength when challenging", () => {
99
+ // Arrange: Character with Challenger +2 (base strength 3)
100
+ // Defender with 4 willpower
101
+
102
+ // Act: Challenge
103
+
104
+ // Assert: Should deal 5 damage (3 + 2 from Challenger)
105
+ expect(true).toBe(false); // Will fail until Challenger implemented
106
+ });
107
+
108
+ /**
109
+ * Rule 10.3.2: Challenger stacks - multiple sources add together.
110
+ */
111
+ test.failing("Rule 10.3.2 - Challenger stacks", () => {
112
+ // Arrange: Character with Challenger +2 and Challenger +1
113
+
114
+ // Assert: Should have Challenger +3 total
115
+ expect(true).toBe(false); // Will fail until Challenger stacking implemented
116
+ });
117
+
118
+ /**
119
+ * Challenger only applies while challenging, not when being challenged.
120
+ */
121
+ test.failing("Rule 10.3 - Challenger only when attacking", () => {
122
+ // Arrange: Challenger character being challenged
123
+
124
+ // Assert: Challenger bonus should NOT apply
125
+ expect(true).toBe(false); // Will fail until Challenger direction implemented
126
+ });
127
+ });
128
+
129
+ describe("10.4. Evasive", () => {
130
+ /**
131
+ * Rule 10.4.1: Evasive - Only characters with Evasive can challenge this character.
132
+ */
133
+ test.failing("Rule 10.4.1 - Only Evasive can challenge Evasive", () => {
134
+ // Arrange: Evasive character (exerted), non-Evasive attacker
135
+
136
+ // Act: Try to challenge Evasive character
137
+
138
+ // Assert: Should fail
139
+ expect(true).toBe(false); // Will fail until Evasive implemented
140
+ });
141
+
142
+ /**
143
+ * Evasive character can challenge Evasive character.
144
+ */
145
+ test.failing("Rule 10.4 - Evasive can challenge Evasive", () => {
146
+ // Arrange: Two Evasive characters
147
+
148
+ // Act: One challenges the other
149
+
150
+ // Assert: Should succeed
151
+ expect(true).toBe(false); // Will fail until Evasive vs Evasive implemented
152
+ });
153
+ });
154
+
155
+ describe("10.5. Reckless", () => {
156
+ /**
157
+ * Rule 10.5.1: Reckless - This character must challenge if able.
158
+ */
159
+ test.failing("Rule 10.5.1 - Reckless must challenge when possible", () => {
160
+ // Arrange: Reckless character, exerted opponent character
161
+
162
+ // Assert: Reckless character cannot quest (must challenge)
163
+ expect(true).toBe(false); // Will fail until Reckless implemented
164
+ });
165
+
166
+ /**
167
+ * Reckless only forced when a valid challenge target exists.
168
+ */
169
+ test.failing("Rule 10.5 - Reckless can quest if no targets", () => {
170
+ // Arrange: Reckless character, no exerted opponent characters
171
+
172
+ // Assert: Reckless character can quest
173
+ expect(true).toBe(false); // Will fail until Reckless target check implemented
174
+ });
175
+
176
+ /**
177
+ * Reckless character can use abilities instead of challenging.
178
+ */
179
+ test.failing("Rule 10.5 - Reckless can use abilities", () => {
180
+ // Arrange: Reckless character with activated ability, valid challenge target
181
+
182
+ // Assert: Can use ability (Reckless doesn't force challenge over abilities)
183
+ // Note: This may depend on exact ruling interpretation
184
+ expect(true).toBe(false); // Will fail until Reckless ability interaction implemented
185
+ });
186
+ });
187
+
188
+ describe("10.6. Resist", () => {
189
+ /**
190
+ * Rule 10.6.1: Resist +X - When this character takes damage, reduce it by X.
191
+ */
192
+ test.failing("Rule 10.6.1 - Resist reduces damage taken", () => {
193
+ // Arrange: Character with Resist 2, willpower 5
194
+
195
+ // Act: Deal 4 damage
196
+
197
+ // Assert: Should take 2 damage (4 - 2 Resist)
198
+ expect(true).toBe(false); // Will fail until Resist implemented
199
+ });
200
+
201
+ /**
202
+ * Rule 10.6.2: Resist stacks - multiple sources add together.
203
+ */
204
+ test.failing("Rule 10.6.2 - Resist stacks", () => {
205
+ // Arrange: Character with Resist 2 and Resist 1
206
+
207
+ // Act: Deal 5 damage
208
+
209
+ // Assert: Should take 2 damage (5 - 3 total Resist)
210
+ expect(true).toBe(false); // Will fail until Resist stacking implemented
211
+ });
212
+
213
+ /**
214
+ * Rule 10.6.3: Resist can reduce damage to 0 but not below.
215
+ */
216
+ test.failing("Rule 10.6.3 - Resist cannot make damage negative", () => {
217
+ // Arrange: Character with Resist 5
218
+
219
+ // Act: Deal 2 damage
220
+
221
+ // Assert: Should take 0 damage (not -3)
222
+ expect(true).toBe(false); // Will fail until Resist floor implemented
223
+ });
224
+
225
+ /**
226
+ * Resist only applies to dealt damage, not put damage.
227
+ */
228
+ test.failing("Rule 10.6 - Resist doesn't affect put damage", () => {
229
+ // Arrange: Character with Resist 2
230
+
231
+ // Act: Put 3 damage
232
+
233
+ // Assert: Should take 3 damage (put bypasses Resist)
234
+ expect(true).toBe(false); // Will fail until Resist vs put implemented
235
+ });
236
+ });
237
+
238
+ describe("10.7. Rush", () => {
239
+ /**
240
+ * Rule 10.7.1: Rush - This character can challenge the turn it's played.
241
+ */
242
+ test.failing("Rule 10.7.1 - Rush allows immediate challenge", () => {
243
+ // Arrange: Play Rush character
244
+
245
+ // Act: Challenge same turn
246
+
247
+ // Assert: Should succeed (no drying restriction)
248
+ expect(true).toBe(false); // Will fail until Rush implemented
249
+ });
250
+
251
+ /**
252
+ * Rush only bypasses challenge restriction, not quest restriction.
253
+ */
254
+ test.failing("Rule 10.7 - Rush doesn't allow immediate quest", () => {
255
+ // Arrange: Play Rush character
256
+
257
+ // Act: Try to quest same turn
258
+
259
+ // Assert: Should fail (Rush doesn't bypass quest drying)
260
+ expect(true).toBe(false); // Will fail until Rush limitation implemented
261
+ });
262
+ });
263
+
264
+ describe("10.8. Shift", () => {
265
+ /**
266
+ * Rule 10.8.1: Shift X (cost) - Pay X ink to play this on top of a character
267
+ * with the specified name.
268
+ */
269
+ test.failing("Rule 10.8.1 - Shift as alternate play cost", () => {
270
+ // Arrange: "Elsa - Snow Queen" in play
271
+ // Floodborn Elsa with Shift 4 in hand
272
+
273
+ // Act: Pay 4 ink to Shift onto Elsa
274
+
275
+ // Assert: Floodborn Elsa should be on top of stack
276
+ expect(true).toBe(false); // Will fail until Shift implemented
277
+ });
278
+
279
+ /**
280
+ * Rule 10.8.2: Shifted character inherits the "dry" state of the base.
281
+ */
282
+ test.failing("Rule 10.8.2 - Shift inherits dry state", () => {
283
+ // Arrange: Dry character in play
284
+
285
+ // Act: Shift onto it
286
+
287
+ // Assert: New character should also be dry (can quest/challenge)
288
+ expect(true).toBe(false); // Will fail until Shift state inheritance implemented
289
+ });
290
+
291
+ /**
292
+ * Rule 10.8.3: Shifted character inherits damage from the base.
293
+ */
294
+ test.failing("Rule 10.8.3 - Shift inherits damage", () => {
295
+ // Arrange: Character with 2 damage
296
+
297
+ // Act: Shift onto it
298
+
299
+ // Assert: New character should have 2 damage
300
+ expect(true).toBe(false); // Will fail until Shift damage inheritance implemented
301
+ });
302
+
303
+ /**
304
+ * Rule 10.8.4: When shifted character leaves play, all cards in stack leave.
305
+ */
306
+ test.failing("Rule 10.8.4 - Stack leaves play together", () => {
307
+ // Arrange: Create shift stack
308
+
309
+ // Act: Banish the top card
310
+
311
+ // Assert: All cards in stack should go to discard
312
+ expect(true).toBe(false); // Will fail until stack leaving implemented
313
+ });
314
+ });
315
+
316
+ describe("10.9. Singer", () => {
317
+ /**
318
+ * Rule 10.9.1: Singer X - This character counts as cost X for singing songs.
319
+ */
320
+ test.failing("Rule 10.9.1 - Singer allows singing songs", () => {
321
+ // Arrange: Character with Singer 5, Song with cost 4
322
+
323
+ // Act: Exert character to sing song
324
+
325
+ // Assert: Song should be played (Singer 5 >= Song cost 4)
326
+ expect(true).toBe(false); // Will fail until Singer implemented
327
+ });
328
+
329
+ /**
330
+ * Character cannot sing songs with cost higher than Singer value.
331
+ */
332
+ test.failing("Rule 10.9 - Singer limits song cost", () => {
333
+ // Arrange: Character with Singer 3, Song with cost 5
334
+
335
+ // Act: Try to sing song
336
+
337
+ // Assert: Should fail (Singer 3 < Song cost 5)
338
+ expect(true).toBe(false); // Will fail until Singer cost check implemented
339
+ });
340
+ });
341
+
342
+ describe("10.10. Sing Together", () => {
343
+ /**
344
+ * Rule 10.10.1: Sing Together X - Multiple characters can exert together
345
+ * to sing this song if their total ink costs meet or exceed X.
346
+ */
347
+ test.failing("Rule 10.10.1 - Sing Together combines character costs", () => {
348
+ // Arrange: Two characters (cost 2 each), Song with Sing Together 4
349
+
350
+ // Act: Exert both to sing
351
+
352
+ // Assert: Should succeed (2 + 2 = 4 >= required 4)
353
+ expect(true).toBe(false); // Will fail until Sing Together implemented
354
+ });
355
+
356
+ /**
357
+ * Sing Together uses character ink cost, not Singer value.
358
+ */
359
+ test.failing("Rule 10.10 - Sing Together uses ink cost", () => {
360
+ // Arrange: Two characters (cost 3 each), even if they have Singer
361
+
362
+ // Assert: Contribution is 3 + 3 = 6, not Singer values
363
+ expect(true).toBe(false); // Will fail until Sing Together cost source implemented
364
+ });
365
+ });
366
+
367
+ describe("10.11. Support", () => {
368
+ /**
369
+ * Rule 10.11.1: Support - When this character quests, choose another character.
370
+ * That character gets +X Strength this turn.
371
+ */
372
+ test.failing("Rule 10.11.1 - Support grants Strength when questing", () => {
373
+ // Arrange: Character with Support, another character
374
+
375
+ // Act: Quest with Support character
376
+
377
+ // Assert: Chosen character should have +X Strength
378
+ expect(true).toBe(false); // Will fail until Support implemented
379
+ });
380
+
381
+ /**
382
+ * Support bonus lasts until end of turn.
383
+ */
384
+ test.failing("Rule 10.11 - Support ends at end of turn", () => {
385
+ // Arrange: Use Support
386
+
387
+ // Act: End turn
388
+
389
+ // Assert: Strength bonus should end
390
+ expect(true).toBe(false); // Will fail until Support duration implemented
391
+ });
392
+ });
393
+
394
+ describe("10.12. Vanish", () => {
395
+ /**
396
+ * Rule 10.12.1: Vanish - After this character quests, return it to your hand.
397
+ */
398
+ test.failing("Rule 10.12.1 - Vanish returns to hand after quest", () => {
399
+ // Arrange: Vanish character
400
+
401
+ // Act: Quest
402
+
403
+ // Assert: Character should be in hand (gained lore first)
404
+ expect(true).toBe(false); // Will fail until Vanish implemented
405
+ });
406
+
407
+ /**
408
+ * Vanish triggers after quest completes (lore is gained first).
409
+ */
410
+ test.failing("Rule 10.12 - Vanish triggers after lore gained", () => {
411
+ // Arrange: Vanish character, note initial lore
412
+
413
+ // Act: Quest
414
+
415
+ // Assert: Lore should be gained, then character returns to hand
416
+ expect(true).toBe(false); // Will fail until Vanish timing implemented
417
+ });
418
+ });
419
+
420
+ describe("10.13. Ward", () => {
421
+ /**
422
+ * Rule 10.13.1: Ward - Opponents can't choose this character except to challenge.
423
+ */
424
+ test.failing("Rule 10.13.1 - Ward prevents opponent targeting", () => {
425
+ // Arrange: Ward character, opponent effect that chooses character
426
+
427
+ // Act: Opponent tries to target Ward character
428
+
429
+ // Assert: Should fail (can't choose)
430
+ expect(true).toBe(false); // Will fail until Ward implemented
431
+ });
432
+
433
+ /**
434
+ * Ward doesn't prevent challenges.
435
+ */
436
+ test.failing("Rule 10.13 - Ward allows challenges", () => {
437
+ // Arrange: Exerted Ward character, opponent attacker
438
+
439
+ // Act: Opponent challenges Ward character
440
+
441
+ // Assert: Should succeed (Ward doesn't block challenges)
442
+ expect(true).toBe(false); // Will fail until Ward challenge exception implemented
443
+ });
444
+
445
+ /**
446
+ * Ward doesn't prevent effects that don't "choose" (e.g., "all characters").
447
+ */
448
+ test.failing("Rule 10.13 - Ward doesn't block non-choosing effects", () => {
449
+ // Arrange: Ward character, effect that damages "all characters"
450
+
451
+ // Act: Execute effect
452
+
453
+ // Assert: Ward character should be affected
454
+ expect(true).toBe(false); // Will fail until Ward choose distinction implemented
455
+ });
456
+
457
+ /**
458
+ * Ward only protects from opponents, not owner.
459
+ */
460
+ test.failing("Rule 10.13 - Ward doesn't block owner targeting", () => {
461
+ // Arrange: Ward character, owner effect that chooses character
462
+
463
+ // Act: Owner targets their Ward character
464
+
465
+ // Assert: Should succeed (Ward only blocks opponents)
466
+ expect(true).toBe(false); // Will fail until Ward owner exception implemented
467
+ });
468
+ });
469
+ });