@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.
- package/README.md +160 -0
- package/package.json +45 -0
- package/src/__tests__/integration/move-enumeration.test.ts +256 -0
- package/src/__tests__/rules/section-01-concepts.test.ts +426 -0
- package/src/__tests__/rules/section-03-gameplay.test.ts +298 -0
- package/src/__tests__/rules/section-04-turn-structure.test.ts +708 -0
- package/src/__tests__/rules/section-05-cards.test.ts +158 -0
- package/src/__tests__/rules/section-06-card-types.test.ts +342 -0
- package/src/__tests__/rules/section-07-abilities.test.ts +333 -0
- package/src/__tests__/rules/section-08-zones.test.ts +231 -0
- package/src/__tests__/rules/section-09-damage.test.ts +148 -0
- package/src/__tests__/rules/section-10-keywords.test.ts +469 -0
- package/src/__tests__/spec-01-foundation-types.test.ts +534 -0
- package/src/__tests__/spec-02-zones-card-states.test.ts +295 -0
- package/src/card-utils.ts +302 -0
- package/src/cards/README.md +296 -0
- package/src/cards/abilities/index.ts +175 -0
- package/src/cards/index.ts +10 -0
- package/src/deck-validation.ts +175 -0
- package/src/engine/lorcana-engine.ts +625 -0
- package/src/game-definition/__tests__/core-zone-integration.test.ts +553 -0
- package/src/game-definition/__tests__/zone-operations.test.ts +362 -0
- package/src/game-definition/__tests__/zones.test.ts +176 -0
- package/src/game-definition/definition.ts +45 -0
- package/src/game-definition/flow/turn-flow.ts +216 -0
- package/src/game-definition/index.ts +31 -0
- package/src/game-definition/moves/abilities/activate-ability.ts +51 -0
- package/src/game-definition/moves/core/__tests__/move-parameter-enumeration.test.ts +316 -0
- package/src/game-definition/moves/core/challenge.test.ts +545 -0
- package/src/game-definition/moves/core/challenge.ts +81 -0
- package/src/game-definition/moves/core/play-card.ts +83 -0
- package/src/game-definition/moves/core/quest.test.ts +448 -0
- package/src/game-definition/moves/core/quest.ts +49 -0
- package/src/game-definition/moves/debug/manual-exert.ts +36 -0
- package/src/game-definition/moves/effects/resolve-bag.ts +35 -0
- package/src/game-definition/moves/effects/resolve-effect.ts +34 -0
- package/src/game-definition/moves/index.ts +85 -0
- package/src/game-definition/moves/locations/move-character-to-location.ts +42 -0
- package/src/game-definition/moves/resources/put-card-into-inkwell.test.ts +462 -0
- package/src/game-definition/moves/resources/put-card-into-inkwell.ts +51 -0
- package/src/game-definition/moves/setup/alter-hand.test.ts +395 -0
- package/src/game-definition/moves/setup/alter-hand.ts +210 -0
- package/src/game-definition/moves/setup/choose-first-player.test.ts +450 -0
- package/src/game-definition/moves/setup/choose-first-player.ts +105 -0
- package/src/game-definition/moves/setup/draw-cards.ts +37 -0
- package/src/game-definition/moves/songs/sing-together.ts +47 -0
- package/src/game-definition/moves/songs/sing.ts +56 -0
- package/src/game-definition/moves/standard/concede.test.ts +189 -0
- package/src/game-definition/moves/standard/concede.ts +72 -0
- package/src/game-definition/moves/standard/pass-turn.ts +49 -0
- package/src/game-definition/setup/game-setup.ts +19 -0
- package/src/game-definition/trackers/tracker-config.ts +23 -0
- package/src/game-definition/win-conditions/lore-victory.ts +26 -0
- package/src/game-definition/zone-operations.ts +405 -0
- package/src/game-definition/zones/zone-configs.ts +59 -0
- package/src/game-definition/zones.ts +283 -0
- package/src/index.ts +189 -0
- package/src/operations/index.ts +7 -0
- package/src/operations/lorcana-operations.ts +288 -0
- package/src/queries/README.md +56 -0
- package/src/resolvers/__tests__/condition-resolver.test.ts +301 -0
- package/src/resolvers/condition-registry.ts +70 -0
- package/src/resolvers/condition-resolver.ts +85 -0
- package/src/resolvers/conditions/basic.ts +81 -0
- package/src/resolvers/conditions/card-state.ts +12 -0
- package/src/resolvers/conditions/comparison.ts +102 -0
- package/src/resolvers/conditions/existence.ts +219 -0
- package/src/resolvers/conditions/history.ts +68 -0
- package/src/resolvers/conditions/index.ts +15 -0
- package/src/resolvers/conditions/logical.ts +55 -0
- package/src/resolvers/conditions/resolution.ts +41 -0
- package/src/resolvers/conditions/revealed.ts +42 -0
- package/src/resolvers/conditions/zone.ts +84 -0
- package/src/setup.test.ts +18 -0
- package/src/targeting/__tests__/filter-resolver.test.ts +294 -0
- package/src/targeting/__tests__/real-cards-targeting.test.ts +303 -0
- package/src/targeting/__tests__/targeting-dsl.test.ts +386 -0
- package/src/targeting/enum-expansion.ts +387 -0
- package/src/targeting/filter-registry.ts +322 -0
- package/src/targeting/filter-resolver.ts +145 -0
- package/src/targeting/index.ts +91 -0
- package/src/targeting/lorcana-target-dsl.ts +495 -0
- package/src/targeting/targeting-ui.ts +407 -0
- package/src/testing/index.ts +14 -0
- package/src/testing/lorcana-test-engine.ts +813 -0
- package/src/types/README.md +303 -0
- package/src/types/__tests__/lorcana-state.test.ts +168 -0
- package/src/types/__tests__/move-enumeration.test.ts +179 -0
- package/src/types/branded-types.ts +106 -0
- package/src/types/game-state.ts +184 -0
- package/src/types/index.ts +87 -0
- package/src/types/keywords.ts +187 -0
- package/src/types/lorcana-state.ts +260 -0
- package/src/types/move-enumeration.ts +126 -0
- package/src/types/move-params.ts +216 -0
- package/src/validators/index.ts +7 -0
- package/src/validators/move-validators.ts +374 -0
- package/src/zones/card-state.ts +234 -0
- package/src/zones/index.ts +42 -0
- 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
|
+
});
|