@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,553 @@
|
|
|
1
|
+
import { describe, expect, it } from "bun:test";
|
|
2
|
+
import {
|
|
3
|
+
addCard,
|
|
4
|
+
addCardToBottom,
|
|
5
|
+
addCardToTop,
|
|
6
|
+
clearZone,
|
|
7
|
+
createCardId,
|
|
8
|
+
createPlayerId,
|
|
9
|
+
createPlayerZones,
|
|
10
|
+
createZone,
|
|
11
|
+
createZoneId,
|
|
12
|
+
getCardsInZone,
|
|
13
|
+
getTopCard,
|
|
14
|
+
getZoneSize,
|
|
15
|
+
isCardInZone,
|
|
16
|
+
moveCard,
|
|
17
|
+
removeCard,
|
|
18
|
+
} from "@drmxrcy/tcg-core";
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Task 7.2: Tests verifying lorcana zone operations work with core utilities
|
|
22
|
+
*
|
|
23
|
+
* These tests verify that core zone operations from @drmxrcy/tcg-core can handle
|
|
24
|
+
* Lorcana's zone management needs, validating the migration path.
|
|
25
|
+
*
|
|
26
|
+
* Tests cover:
|
|
27
|
+
* - Zone creation for multiple players
|
|
28
|
+
* - Card operations (add, remove, move)
|
|
29
|
+
* - Zone queries (size, cards, top card)
|
|
30
|
+
* - Ordered zone operations (top/bottom)
|
|
31
|
+
* - Real-world Lorcana scenarios (draw, play, banish, ink)
|
|
32
|
+
*/
|
|
33
|
+
|
|
34
|
+
// Test helpers
|
|
35
|
+
function createTestPlayers(...names: string[]) {
|
|
36
|
+
return names.map((name) => createPlayerId(name));
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function _createTestCards(...names: string[]) {
|
|
40
|
+
return names.map((name) => createCardId(name));
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
describe("Core Zone Integration for Lorcana", () => {
|
|
44
|
+
describe("Zone Creation", () => {
|
|
45
|
+
it("should create zones for multiple players using core utilities", () => {
|
|
46
|
+
const [player1, player2] = createTestPlayers("player1", "player2");
|
|
47
|
+
|
|
48
|
+
// Use core's createPlayerZones helper
|
|
49
|
+
const handZones = createPlayerZones([player1, player2], () =>
|
|
50
|
+
createZone({
|
|
51
|
+
id: createZoneId("hand"),
|
|
52
|
+
name: "Hand",
|
|
53
|
+
visibility: "private",
|
|
54
|
+
ordered: false,
|
|
55
|
+
}),
|
|
56
|
+
);
|
|
57
|
+
|
|
58
|
+
expect(handZones[player1]).toBeDefined();
|
|
59
|
+
expect(handZones[player2]).toBeDefined();
|
|
60
|
+
expect(handZones[player1].cards).toEqual([]);
|
|
61
|
+
expect(handZones[player2].cards).toEqual([]);
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
it("should create all 5 Lorcana zones with correct configurations", () => {
|
|
65
|
+
const [player1] = createTestPlayers("player1");
|
|
66
|
+
|
|
67
|
+
const deckZone = createZone({
|
|
68
|
+
id: createZoneId("deck"),
|
|
69
|
+
name: "Deck",
|
|
70
|
+
visibility: "secret",
|
|
71
|
+
ordered: true,
|
|
72
|
+
owner: player1,
|
|
73
|
+
faceDown: true,
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
const handZone = createZone({
|
|
77
|
+
id: createZoneId("hand"),
|
|
78
|
+
name: "Hand",
|
|
79
|
+
visibility: "private",
|
|
80
|
+
ordered: false,
|
|
81
|
+
owner: player1,
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
const playZone = createZone({
|
|
85
|
+
id: createZoneId("play"),
|
|
86
|
+
name: "Play",
|
|
87
|
+
visibility: "public",
|
|
88
|
+
ordered: false,
|
|
89
|
+
owner: player1,
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
const discardZone = createZone({
|
|
93
|
+
id: createZoneId("discard"),
|
|
94
|
+
name: "Discard",
|
|
95
|
+
visibility: "public",
|
|
96
|
+
ordered: true,
|
|
97
|
+
owner: player1,
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
const inkwellZone = createZone({
|
|
101
|
+
id: createZoneId("inkwell"),
|
|
102
|
+
name: "Inkwell",
|
|
103
|
+
visibility: "secret",
|
|
104
|
+
ordered: false,
|
|
105
|
+
owner: player1,
|
|
106
|
+
faceDown: true,
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
expect(deckZone.config.visibility).toBe("secret");
|
|
110
|
+
expect(deckZone.config.ordered).toBe(true);
|
|
111
|
+
expect(handZone.config.visibility).toBe("private");
|
|
112
|
+
expect(playZone.config.visibility).toBe("public");
|
|
113
|
+
expect(discardZone.config.ordered).toBe(true);
|
|
114
|
+
expect(inkwellZone.config.faceDown).toBe(true);
|
|
115
|
+
});
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
describe("Basic Zone Operations", () => {
|
|
119
|
+
it("should add card to zone using core addCard", () => {
|
|
120
|
+
const player1 = createPlayerId("player1");
|
|
121
|
+
const card1 = createCardId("card-1");
|
|
122
|
+
|
|
123
|
+
const handZone = createZone({
|
|
124
|
+
id: createZoneId("hand"),
|
|
125
|
+
name: "Hand",
|
|
126
|
+
visibility: "private",
|
|
127
|
+
ordered: false,
|
|
128
|
+
owner: player1,
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
const updatedHand = addCard(handZone, card1);
|
|
132
|
+
|
|
133
|
+
expect(updatedHand.cards).toEqual([card1]);
|
|
134
|
+
expect(handZone.cards).toEqual([]); // Original unchanged (immutable)
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
it("should remove card from zone using core removeCard", () => {
|
|
138
|
+
const player1 = createPlayerId("player1");
|
|
139
|
+
const card1 = createCardId("card-1");
|
|
140
|
+
const card2 = createCardId("card-2");
|
|
141
|
+
|
|
142
|
+
let handZone = createZone({
|
|
143
|
+
id: createZoneId("hand"),
|
|
144
|
+
name: "Hand",
|
|
145
|
+
visibility: "private",
|
|
146
|
+
ordered: false,
|
|
147
|
+
owner: player1,
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
handZone = addCard(handZone, card1);
|
|
151
|
+
handZone = addCard(handZone, card2);
|
|
152
|
+
|
|
153
|
+
const updatedHand = removeCard(handZone, card1);
|
|
154
|
+
|
|
155
|
+
expect(updatedHand.cards).toEqual([card2]);
|
|
156
|
+
});
|
|
157
|
+
|
|
158
|
+
it("should move card between zones using core moveCard", () => {
|
|
159
|
+
const player1 = createPlayerId("player1");
|
|
160
|
+
const card1 = createCardId("card-1");
|
|
161
|
+
|
|
162
|
+
let handZone = createZone({
|
|
163
|
+
id: createZoneId("hand"),
|
|
164
|
+
name: "Hand",
|
|
165
|
+
visibility: "private",
|
|
166
|
+
ordered: false,
|
|
167
|
+
owner: player1,
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
const playZone = createZone({
|
|
171
|
+
id: createZoneId("play"),
|
|
172
|
+
name: "Play",
|
|
173
|
+
visibility: "public",
|
|
174
|
+
ordered: false,
|
|
175
|
+
owner: player1,
|
|
176
|
+
});
|
|
177
|
+
|
|
178
|
+
handZone = addCard(handZone, card1);
|
|
179
|
+
|
|
180
|
+
const { fromZone: updatedHand, toZone: updatedPlay } = moveCard(
|
|
181
|
+
handZone,
|
|
182
|
+
playZone,
|
|
183
|
+
card1,
|
|
184
|
+
);
|
|
185
|
+
|
|
186
|
+
expect(updatedHand.cards).toEqual([]);
|
|
187
|
+
expect(updatedPlay.cards).toEqual([card1]);
|
|
188
|
+
});
|
|
189
|
+
});
|
|
190
|
+
|
|
191
|
+
describe("Zone Queries", () => {
|
|
192
|
+
it("should check if card is in zone using core isCardInZone", () => {
|
|
193
|
+
const player1 = createPlayerId("player1");
|
|
194
|
+
const card1 = createCardId("card-1");
|
|
195
|
+
const card2 = createCardId("card-2");
|
|
196
|
+
|
|
197
|
+
let handZone = createZone({
|
|
198
|
+
id: createZoneId("hand"),
|
|
199
|
+
name: "Hand",
|
|
200
|
+
visibility: "private",
|
|
201
|
+
ordered: false,
|
|
202
|
+
owner: player1,
|
|
203
|
+
});
|
|
204
|
+
|
|
205
|
+
handZone = addCard(handZone, card1);
|
|
206
|
+
|
|
207
|
+
expect(isCardInZone(handZone, card1)).toBe(true);
|
|
208
|
+
expect(isCardInZone(handZone, card2)).toBe(false);
|
|
209
|
+
});
|
|
210
|
+
|
|
211
|
+
it("should get all cards in zone using core getCardsInZone", () => {
|
|
212
|
+
const player1 = createPlayerId("player1");
|
|
213
|
+
const card1 = createCardId("card-1");
|
|
214
|
+
const card2 = createCardId("card-2");
|
|
215
|
+
const card3 = createCardId("card-3");
|
|
216
|
+
|
|
217
|
+
let handZone = createZone({
|
|
218
|
+
id: createZoneId("hand"),
|
|
219
|
+
name: "Hand",
|
|
220
|
+
visibility: "private",
|
|
221
|
+
ordered: false,
|
|
222
|
+
owner: player1,
|
|
223
|
+
});
|
|
224
|
+
|
|
225
|
+
handZone = addCard(handZone, card1);
|
|
226
|
+
handZone = addCard(handZone, card2);
|
|
227
|
+
handZone = addCard(handZone, card3);
|
|
228
|
+
|
|
229
|
+
const cards = getCardsInZone(handZone);
|
|
230
|
+
|
|
231
|
+
expect(cards).toEqual([card1, card2, card3]);
|
|
232
|
+
});
|
|
233
|
+
|
|
234
|
+
it("should get zone size using core getZoneSize", () => {
|
|
235
|
+
const player1 = createPlayerId("player1");
|
|
236
|
+
const card1 = createCardId("card-1");
|
|
237
|
+
const card2 = createCardId("card-2");
|
|
238
|
+
|
|
239
|
+
let deckZone = createZone({
|
|
240
|
+
id: createZoneId("deck"),
|
|
241
|
+
name: "Deck",
|
|
242
|
+
visibility: "secret",
|
|
243
|
+
ordered: true,
|
|
244
|
+
owner: player1,
|
|
245
|
+
});
|
|
246
|
+
|
|
247
|
+
deckZone = addCard(deckZone, card1);
|
|
248
|
+
deckZone = addCard(deckZone, card2);
|
|
249
|
+
|
|
250
|
+
expect(getZoneSize(deckZone)).toBe(2);
|
|
251
|
+
});
|
|
252
|
+
|
|
253
|
+
it("should get top card using core getTopCard", () => {
|
|
254
|
+
const player1 = createPlayerId("player1");
|
|
255
|
+
const topCard = createCardId("top-card");
|
|
256
|
+
const card2 = createCardId("card-2");
|
|
257
|
+
|
|
258
|
+
let deckZone = createZone({
|
|
259
|
+
id: createZoneId("deck"),
|
|
260
|
+
name: "Deck",
|
|
261
|
+
visibility: "secret",
|
|
262
|
+
ordered: true,
|
|
263
|
+
owner: player1,
|
|
264
|
+
});
|
|
265
|
+
|
|
266
|
+
deckZone = addCard(deckZone, topCard);
|
|
267
|
+
deckZone = addCard(deckZone, card2);
|
|
268
|
+
|
|
269
|
+
expect(getTopCard(deckZone)).toBe(topCard);
|
|
270
|
+
});
|
|
271
|
+
});
|
|
272
|
+
|
|
273
|
+
describe("Ordered Zone Operations", () => {
|
|
274
|
+
it("should add card to top of zone using core addCardToTop", () => {
|
|
275
|
+
const player1 = createPlayerId("player1");
|
|
276
|
+
const card1 = createCardId("card-1");
|
|
277
|
+
const card2 = createCardId("card-2");
|
|
278
|
+
|
|
279
|
+
let deckZone = createZone({
|
|
280
|
+
id: createZoneId("deck"),
|
|
281
|
+
name: "Deck",
|
|
282
|
+
visibility: "secret",
|
|
283
|
+
ordered: true,
|
|
284
|
+
owner: player1,
|
|
285
|
+
});
|
|
286
|
+
|
|
287
|
+
deckZone = addCard(deckZone, card1);
|
|
288
|
+
deckZone = addCardToTop(deckZone, card2);
|
|
289
|
+
|
|
290
|
+
expect(deckZone.cards[0]).toBe(card2);
|
|
291
|
+
expect(deckZone.cards[1]).toBe(card1);
|
|
292
|
+
});
|
|
293
|
+
|
|
294
|
+
it("should add card to bottom of zone using core addCardToBottom", () => {
|
|
295
|
+
const player1 = createPlayerId("player1");
|
|
296
|
+
const card1 = createCardId("card-1");
|
|
297
|
+
const card2 = createCardId("card-2");
|
|
298
|
+
|
|
299
|
+
let deckZone = createZone({
|
|
300
|
+
id: createZoneId("deck"),
|
|
301
|
+
name: "Deck",
|
|
302
|
+
visibility: "secret",
|
|
303
|
+
ordered: true,
|
|
304
|
+
owner: player1,
|
|
305
|
+
});
|
|
306
|
+
|
|
307
|
+
deckZone = addCard(deckZone, card1);
|
|
308
|
+
deckZone = addCardToBottom(deckZone, card2);
|
|
309
|
+
|
|
310
|
+
expect(deckZone.cards[0]).toBe(card1);
|
|
311
|
+
expect(deckZone.cards[1]).toBe(card2);
|
|
312
|
+
});
|
|
313
|
+
|
|
314
|
+
it("should clear zone using core clearZone", () => {
|
|
315
|
+
const player1 = createPlayerId("player1");
|
|
316
|
+
const card1 = createCardId("card-1");
|
|
317
|
+
const card2 = createCardId("card-2");
|
|
318
|
+
|
|
319
|
+
let handZone = createZone({
|
|
320
|
+
id: createZoneId("hand"),
|
|
321
|
+
name: "Hand",
|
|
322
|
+
visibility: "private",
|
|
323
|
+
ordered: false,
|
|
324
|
+
owner: player1,
|
|
325
|
+
});
|
|
326
|
+
|
|
327
|
+
handZone = addCard(handZone, card1);
|
|
328
|
+
handZone = addCard(handZone, card2);
|
|
329
|
+
|
|
330
|
+
const clearedHand = clearZone(handZone);
|
|
331
|
+
|
|
332
|
+
expect(clearedHand.cards).toEqual([]);
|
|
333
|
+
});
|
|
334
|
+
});
|
|
335
|
+
|
|
336
|
+
describe("Lorcana Scenario Tests", () => {
|
|
337
|
+
it("should handle draw card scenario (deck -> hand)", () => {
|
|
338
|
+
const player1 = createPlayerId("player1");
|
|
339
|
+
const topCard = createCardId("top-card");
|
|
340
|
+
const card2 = createCardId("card-2");
|
|
341
|
+
const card3 = createCardId("card-3");
|
|
342
|
+
|
|
343
|
+
let deckZone = createZone({
|
|
344
|
+
id: createZoneId("deck"),
|
|
345
|
+
name: "Deck",
|
|
346
|
+
visibility: "secret",
|
|
347
|
+
ordered: true,
|
|
348
|
+
owner: player1,
|
|
349
|
+
});
|
|
350
|
+
|
|
351
|
+
const handZone = createZone({
|
|
352
|
+
id: createZoneId("hand"),
|
|
353
|
+
name: "Hand",
|
|
354
|
+
visibility: "private",
|
|
355
|
+
ordered: false,
|
|
356
|
+
owner: player1,
|
|
357
|
+
});
|
|
358
|
+
|
|
359
|
+
deckZone = addCard(deckZone, topCard);
|
|
360
|
+
deckZone = addCard(deckZone, card2);
|
|
361
|
+
deckZone = addCard(deckZone, card3);
|
|
362
|
+
|
|
363
|
+
// Draw top card
|
|
364
|
+
const { fromZone: updatedDeck, toZone: updatedHand } = moveCard(
|
|
365
|
+
deckZone,
|
|
366
|
+
handZone,
|
|
367
|
+
topCard,
|
|
368
|
+
);
|
|
369
|
+
|
|
370
|
+
expect(getZoneSize(updatedDeck)).toBe(2);
|
|
371
|
+
expect(updatedHand.cards).toContain(topCard);
|
|
372
|
+
});
|
|
373
|
+
|
|
374
|
+
it("should handle play card scenario (hand -> play)", () => {
|
|
375
|
+
const player1 = createPlayerId("player1");
|
|
376
|
+
const character = createCardId("character-1");
|
|
377
|
+
|
|
378
|
+
let handZone = createZone({
|
|
379
|
+
id: createZoneId("hand"),
|
|
380
|
+
name: "Hand",
|
|
381
|
+
visibility: "private",
|
|
382
|
+
ordered: false,
|
|
383
|
+
owner: player1,
|
|
384
|
+
});
|
|
385
|
+
|
|
386
|
+
const playZone = createZone({
|
|
387
|
+
id: createZoneId("play"),
|
|
388
|
+
name: "Play",
|
|
389
|
+
visibility: "public",
|
|
390
|
+
ordered: false,
|
|
391
|
+
owner: player1,
|
|
392
|
+
});
|
|
393
|
+
|
|
394
|
+
handZone = addCard(handZone, character);
|
|
395
|
+
|
|
396
|
+
const { fromZone: updatedHand, toZone: updatedPlay } = moveCard(
|
|
397
|
+
handZone,
|
|
398
|
+
playZone,
|
|
399
|
+
character,
|
|
400
|
+
);
|
|
401
|
+
|
|
402
|
+
expect(updatedHand.cards).toEqual([]);
|
|
403
|
+
expect(updatedPlay.cards).toContain(character);
|
|
404
|
+
});
|
|
405
|
+
|
|
406
|
+
it("should handle banish scenario (play -> discard)", () => {
|
|
407
|
+
const player1 = createPlayerId("player1");
|
|
408
|
+
const character = createCardId("character-1");
|
|
409
|
+
|
|
410
|
+
let playZone = createZone({
|
|
411
|
+
id: createZoneId("play"),
|
|
412
|
+
name: "Play",
|
|
413
|
+
visibility: "public",
|
|
414
|
+
ordered: false,
|
|
415
|
+
owner: player1,
|
|
416
|
+
});
|
|
417
|
+
|
|
418
|
+
const discardZone = createZone({
|
|
419
|
+
id: createZoneId("discard"),
|
|
420
|
+
name: "Discard",
|
|
421
|
+
visibility: "public",
|
|
422
|
+
ordered: true,
|
|
423
|
+
owner: player1,
|
|
424
|
+
});
|
|
425
|
+
|
|
426
|
+
playZone = addCard(playZone, character);
|
|
427
|
+
|
|
428
|
+
const { fromZone: updatedPlay, toZone: updatedDiscard } = moveCard(
|
|
429
|
+
playZone,
|
|
430
|
+
discardZone,
|
|
431
|
+
character,
|
|
432
|
+
);
|
|
433
|
+
|
|
434
|
+
expect(updatedPlay.cards).toEqual([]);
|
|
435
|
+
expect(updatedDiscard.cards).toContain(character);
|
|
436
|
+
});
|
|
437
|
+
|
|
438
|
+
it("should handle ink card scenario (hand -> inkwell)", () => {
|
|
439
|
+
const player1 = createPlayerId("player1");
|
|
440
|
+
const inkableCard = createCardId("inkable-1");
|
|
441
|
+
|
|
442
|
+
let handZone = createZone({
|
|
443
|
+
id: createZoneId("hand"),
|
|
444
|
+
name: "Hand",
|
|
445
|
+
visibility: "private",
|
|
446
|
+
ordered: false,
|
|
447
|
+
owner: player1,
|
|
448
|
+
});
|
|
449
|
+
|
|
450
|
+
const inkwellZone = createZone({
|
|
451
|
+
id: createZoneId("inkwell"),
|
|
452
|
+
name: "Inkwell",
|
|
453
|
+
visibility: "secret",
|
|
454
|
+
ordered: false,
|
|
455
|
+
owner: player1,
|
|
456
|
+
faceDown: true,
|
|
457
|
+
});
|
|
458
|
+
|
|
459
|
+
handZone = addCard(handZone, inkableCard);
|
|
460
|
+
|
|
461
|
+
const { fromZone: updatedHand, toZone: updatedInkwell } = moveCard(
|
|
462
|
+
handZone,
|
|
463
|
+
inkwellZone,
|
|
464
|
+
inkableCard,
|
|
465
|
+
);
|
|
466
|
+
|
|
467
|
+
expect(updatedHand.cards).toEqual([]);
|
|
468
|
+
expect(updatedInkwell.cards).toContain(inkableCard);
|
|
469
|
+
});
|
|
470
|
+
|
|
471
|
+
it("should handle mulligan scenario (put cards on bottom of deck)", () => {
|
|
472
|
+
const player1 = createPlayerId("player1");
|
|
473
|
+
const mulliganCard1 = createCardId("mulligan-1");
|
|
474
|
+
const mulliganCard2 = createCardId("mulligan-2");
|
|
475
|
+
const deckCard1 = createCardId("deck-1");
|
|
476
|
+
const deckCard2 = createCardId("deck-2");
|
|
477
|
+
|
|
478
|
+
let deckZone = createZone({
|
|
479
|
+
id: createZoneId("deck"),
|
|
480
|
+
name: "Deck",
|
|
481
|
+
visibility: "secret",
|
|
482
|
+
ordered: true,
|
|
483
|
+
owner: player1,
|
|
484
|
+
});
|
|
485
|
+
|
|
486
|
+
// Deck has some cards
|
|
487
|
+
deckZone = addCard(deckZone, deckCard1);
|
|
488
|
+
deckZone = addCard(deckZone, deckCard2);
|
|
489
|
+
|
|
490
|
+
// Put mulligan cards on bottom
|
|
491
|
+
deckZone = addCardToBottom(deckZone, mulliganCard1);
|
|
492
|
+
deckZone = addCardToBottom(deckZone, mulliganCard2);
|
|
493
|
+
|
|
494
|
+
// Check order: original deck cards on top, mulligan cards on bottom
|
|
495
|
+
expect(deckZone.cards[0]).toBe(deckCard1);
|
|
496
|
+
expect(deckZone.cards[1]).toBe(deckCard2);
|
|
497
|
+
expect(deckZone.cards[2]).toBe(mulliganCard1);
|
|
498
|
+
expect(deckZone.cards[3]).toBe(mulliganCard2);
|
|
499
|
+
});
|
|
500
|
+
});
|
|
501
|
+
|
|
502
|
+
describe("Immutability Tests", () => {
|
|
503
|
+
it("should not mutate original zone when adding cards", () => {
|
|
504
|
+
const player1 = createPlayerId("player1");
|
|
505
|
+
const card1 = createCardId("card-1");
|
|
506
|
+
|
|
507
|
+
const originalZone = createZone({
|
|
508
|
+
id: createZoneId("hand"),
|
|
509
|
+
name: "Hand",
|
|
510
|
+
visibility: "private",
|
|
511
|
+
ordered: false,
|
|
512
|
+
owner: player1,
|
|
513
|
+
});
|
|
514
|
+
|
|
515
|
+
const updatedZone = addCard(originalZone, card1);
|
|
516
|
+
|
|
517
|
+
expect(originalZone.cards).toEqual([]);
|
|
518
|
+
expect(updatedZone.cards).toEqual([card1]);
|
|
519
|
+
});
|
|
520
|
+
|
|
521
|
+
it("should not mutate original zones when moving cards", () => {
|
|
522
|
+
const player1 = createPlayerId("player1");
|
|
523
|
+
const card1 = createCardId("card-1");
|
|
524
|
+
|
|
525
|
+
let handZone = createZone({
|
|
526
|
+
id: createZoneId("hand"),
|
|
527
|
+
name: "Hand",
|
|
528
|
+
visibility: "private",
|
|
529
|
+
ordered: false,
|
|
530
|
+
owner: player1,
|
|
531
|
+
});
|
|
532
|
+
|
|
533
|
+
const playZone = createZone({
|
|
534
|
+
id: createZoneId("play"),
|
|
535
|
+
name: "Play",
|
|
536
|
+
visibility: "public",
|
|
537
|
+
ordered: false,
|
|
538
|
+
owner: player1,
|
|
539
|
+
});
|
|
540
|
+
|
|
541
|
+
handZone = addCard(handZone, card1);
|
|
542
|
+
|
|
543
|
+
const originalHandCards = [...handZone.cards];
|
|
544
|
+
const originalPlayCards = [...playZone.cards];
|
|
545
|
+
|
|
546
|
+
moveCard(handZone, playZone, card1);
|
|
547
|
+
|
|
548
|
+
// Original zones unchanged
|
|
549
|
+
expect(handZone.cards).toEqual(originalHandCards);
|
|
550
|
+
expect(playZone.cards).toEqual(originalPlayCards);
|
|
551
|
+
});
|
|
552
|
+
});
|
|
553
|
+
});
|