@drmxrcy/tcg-core 0.0.0-202602060542

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 (157) hide show
  1. package/README.md +882 -0
  2. package/package.json +58 -0
  3. package/src/__tests__/alpha-clash-engine-definition.test.ts +319 -0
  4. package/src/__tests__/createMockAlphaClashGame.ts +462 -0
  5. package/src/__tests__/createMockGrandArchiveGame.ts +373 -0
  6. package/src/__tests__/createMockGundamGame.ts +379 -0
  7. package/src/__tests__/createMockLorcanaGame.ts +328 -0
  8. package/src/__tests__/createMockOnePieceGame.ts +429 -0
  9. package/src/__tests__/createMockRiftboundGame.ts +462 -0
  10. package/src/__tests__/grand-archive-engine-definition.test.ts +118 -0
  11. package/src/__tests__/gundam-engine-definition.test.ts +110 -0
  12. package/src/__tests__/integration-complete-game.test.ts +508 -0
  13. package/src/__tests__/integration-network-sync.test.ts +469 -0
  14. package/src/__tests__/lorcana-engine-definition.test.ts +100 -0
  15. package/src/__tests__/move-enumeration.test.ts +725 -0
  16. package/src/__tests__/multiplayer-engine.test.ts +555 -0
  17. package/src/__tests__/one-piece-engine-definition.test.ts +114 -0
  18. package/src/__tests__/riftbound-engine-definition.test.ts +124 -0
  19. package/src/actions/action-definition.test.ts +201 -0
  20. package/src/actions/action-definition.ts +122 -0
  21. package/src/actions/action-timing.test.ts +490 -0
  22. package/src/actions/action-timing.ts +257 -0
  23. package/src/cards/card-definition.test.ts +268 -0
  24. package/src/cards/card-definition.ts +27 -0
  25. package/src/cards/card-instance.test.ts +422 -0
  26. package/src/cards/card-instance.ts +49 -0
  27. package/src/cards/computed-properties.test.ts +530 -0
  28. package/src/cards/computed-properties.ts +84 -0
  29. package/src/cards/conditional-modifiers.test.ts +390 -0
  30. package/src/cards/modifiers.test.ts +286 -0
  31. package/src/cards/modifiers.ts +51 -0
  32. package/src/engine/MULTIPLAYER.md +425 -0
  33. package/src/engine/__tests__/rule-engine-flow.test.ts +348 -0
  34. package/src/engine/__tests__/rule-engine-history.test.ts +535 -0
  35. package/src/engine/__tests__/rule-engine-moves.test.ts +488 -0
  36. package/src/engine/__tests__/rule-engine.test.ts +366 -0
  37. package/src/engine/index.ts +14 -0
  38. package/src/engine/multiplayer-engine.example.ts +571 -0
  39. package/src/engine/multiplayer-engine.ts +409 -0
  40. package/src/engine/rule-engine.test.ts +286 -0
  41. package/src/engine/rule-engine.ts +1539 -0
  42. package/src/engine/tracker-system.ts +172 -0
  43. package/src/examples/__tests__/coin-flip-game.test.ts +641 -0
  44. package/src/filtering/card-filter.test.ts +230 -0
  45. package/src/filtering/card-filter.ts +91 -0
  46. package/src/filtering/card-query.test.ts +901 -0
  47. package/src/filtering/card-query.ts +273 -0
  48. package/src/filtering/filter-matching.test.ts +944 -0
  49. package/src/filtering/filter-matching.ts +315 -0
  50. package/src/flow/SERIALIZATION.md +428 -0
  51. package/src/flow/__tests__/flow-definition.test.ts +427 -0
  52. package/src/flow/__tests__/flow-manager.test.ts +756 -0
  53. package/src/flow/__tests__/flow-serialization.test.ts +565 -0
  54. package/src/flow/flow-definition.ts +453 -0
  55. package/src/flow/flow-manager.ts +1044 -0
  56. package/src/flow/index.ts +35 -0
  57. package/src/game-definition/__tests__/game-definition-validation.test.ts +359 -0
  58. package/src/game-definition/__tests__/game-definition.test.ts +291 -0
  59. package/src/game-definition/__tests__/move-definitions.test.ts +328 -0
  60. package/src/game-definition/game-definition.ts +261 -0
  61. package/src/game-definition/index.ts +28 -0
  62. package/src/game-definition/move-definitions.ts +188 -0
  63. package/src/game-definition/validation.ts +183 -0
  64. package/src/history/history-manager.test.ts +497 -0
  65. package/src/history/history-manager.ts +312 -0
  66. package/src/history/history-operations.ts +122 -0
  67. package/src/history/index.ts +9 -0
  68. package/src/history/types.ts +255 -0
  69. package/src/index.ts +32 -0
  70. package/src/logging/index.ts +27 -0
  71. package/src/logging/log-formatter.ts +187 -0
  72. package/src/logging/logger.ts +276 -0
  73. package/src/logging/types.ts +148 -0
  74. package/src/moves/create-move.test.ts +331 -0
  75. package/src/moves/create-move.ts +64 -0
  76. package/src/moves/move-enumeration.ts +228 -0
  77. package/src/moves/move-executor.test.ts +431 -0
  78. package/src/moves/move-executor.ts +195 -0
  79. package/src/moves/move-system.test.ts +380 -0
  80. package/src/moves/move-system.ts +463 -0
  81. package/src/moves/standard-moves.ts +231 -0
  82. package/src/operations/card-operations.test.ts +236 -0
  83. package/src/operations/card-operations.ts +116 -0
  84. package/src/operations/card-registry-impl.test.ts +251 -0
  85. package/src/operations/card-registry-impl.ts +70 -0
  86. package/src/operations/card-registry.test.ts +234 -0
  87. package/src/operations/card-registry.ts +106 -0
  88. package/src/operations/counter-operations.ts +152 -0
  89. package/src/operations/game-operations.test.ts +280 -0
  90. package/src/operations/game-operations.ts +140 -0
  91. package/src/operations/index.ts +24 -0
  92. package/src/operations/operations-impl.test.ts +354 -0
  93. package/src/operations/operations-impl.ts +468 -0
  94. package/src/operations/zone-operations.test.ts +295 -0
  95. package/src/operations/zone-operations.ts +223 -0
  96. package/src/rng/seeded-rng.test.ts +339 -0
  97. package/src/rng/seeded-rng.ts +123 -0
  98. package/src/targeting/index.ts +48 -0
  99. package/src/targeting/target-definition.test.ts +273 -0
  100. package/src/targeting/target-definition.ts +37 -0
  101. package/src/targeting/target-dsl.ts +279 -0
  102. package/src/targeting/target-resolver.ts +486 -0
  103. package/src/targeting/target-validation.test.ts +994 -0
  104. package/src/targeting/target-validation.ts +286 -0
  105. package/src/telemetry/events.ts +202 -0
  106. package/src/telemetry/index.ts +21 -0
  107. package/src/telemetry/telemetry-manager.ts +127 -0
  108. package/src/telemetry/types.ts +68 -0
  109. package/src/testing/__tests__/testing-utilities-integration.test.ts +161 -0
  110. package/src/testing/index.ts +88 -0
  111. package/src/testing/test-assertions.test.ts +341 -0
  112. package/src/testing/test-assertions.ts +256 -0
  113. package/src/testing/test-card-factory.test.ts +228 -0
  114. package/src/testing/test-card-factory.ts +111 -0
  115. package/src/testing/test-context-factory.ts +187 -0
  116. package/src/testing/test-end-assertions.test.ts +262 -0
  117. package/src/testing/test-end-assertions.ts +95 -0
  118. package/src/testing/test-engine-builder.test.ts +389 -0
  119. package/src/testing/test-engine-builder.ts +46 -0
  120. package/src/testing/test-flow-assertions.test.ts +284 -0
  121. package/src/testing/test-flow-assertions.ts +115 -0
  122. package/src/testing/test-player-builder.test.ts +132 -0
  123. package/src/testing/test-player-builder.ts +46 -0
  124. package/src/testing/test-replay-assertions.test.ts +356 -0
  125. package/src/testing/test-replay-assertions.ts +164 -0
  126. package/src/testing/test-rng-helpers.test.ts +260 -0
  127. package/src/testing/test-rng-helpers.ts +190 -0
  128. package/src/testing/test-state-builder.test.ts +373 -0
  129. package/src/testing/test-state-builder.ts +99 -0
  130. package/src/testing/test-zone-factory.test.ts +295 -0
  131. package/src/testing/test-zone-factory.ts +224 -0
  132. package/src/types/branded-utils.ts +54 -0
  133. package/src/types/branded.test.ts +175 -0
  134. package/src/types/branded.ts +33 -0
  135. package/src/types/index.ts +8 -0
  136. package/src/types/state.test.ts +198 -0
  137. package/src/types/state.ts +154 -0
  138. package/src/validation/card-type-guards.test.ts +242 -0
  139. package/src/validation/card-type-guards.ts +179 -0
  140. package/src/validation/index.ts +40 -0
  141. package/src/validation/schema-builders.test.ts +403 -0
  142. package/src/validation/schema-builders.ts +345 -0
  143. package/src/validation/type-guard-builder.test.ts +216 -0
  144. package/src/validation/type-guard-builder.ts +109 -0
  145. package/src/validation/validator-builder.test.ts +375 -0
  146. package/src/validation/validator-builder.ts +273 -0
  147. package/src/zones/index.ts +28 -0
  148. package/src/zones/zone-factory.test.ts +183 -0
  149. package/src/zones/zone-factory.ts +44 -0
  150. package/src/zones/zone-operations.test.ts +800 -0
  151. package/src/zones/zone-operations.ts +306 -0
  152. package/src/zones/zone-state-helpers.test.ts +337 -0
  153. package/src/zones/zone-state-helpers.ts +128 -0
  154. package/src/zones/zone-visibility.test.ts +156 -0
  155. package/src/zones/zone-visibility.ts +36 -0
  156. package/src/zones/zone.test.ts +186 -0
  157. package/src/zones/zone.ts +66 -0
@@ -0,0 +1,295 @@
1
+ import { describe, expect, it } from "bun:test";
2
+ import type { CardId, PlayerId, ZoneId } from "../types";
3
+ import type { ZoneOperations } from "./zone-operations";
4
+
5
+ describe("ZoneOperations Interface", () => {
6
+ // Mock implementation for testing the interface structure
7
+ const createMockZoneOperations = (): ZoneOperations => {
8
+ const zones: Record<string, { cardIds: string[] }> = {
9
+ deck: { cardIds: ["card-1", "card-2", "card-3"] },
10
+ hand: { cardIds: ["card-4"] },
11
+ play: { cardIds: [] },
12
+ };
13
+
14
+ return {
15
+ moveCard: (args) => {
16
+ const { cardId, targetZoneId } = args;
17
+ // Find and remove from source
18
+ for (const zoneId in zones) {
19
+ const index = zones[zoneId].cardIds.indexOf(cardId);
20
+ if (index !== -1) {
21
+ zones[zoneId].cardIds.splice(index, 1);
22
+ break;
23
+ }
24
+ }
25
+ // Add to target
26
+ if (!zones[targetZoneId]) {
27
+ zones[targetZoneId] = { cardIds: [] };
28
+ }
29
+ if (args.position === "top") {
30
+ zones[targetZoneId].cardIds.unshift(cardId);
31
+ } else if (args.position === "bottom" || args.position === undefined) {
32
+ zones[targetZoneId].cardIds.push(cardId);
33
+ } else if (typeof args.position === "number") {
34
+ zones[targetZoneId].cardIds.splice(args.position, 0, cardId);
35
+ }
36
+ },
37
+
38
+ getCardsInZone: (zoneId, ownerId?) => {
39
+ // Return a copy to avoid mutation issues
40
+ return [...(zones[zoneId]?.cardIds || [])] as unknown as CardId[];
41
+ },
42
+
43
+ shuffleZone: (zoneId, ownerId?) => {
44
+ // Mock shuffle - just reverse for testing
45
+ if (zones[zoneId]) {
46
+ zones[zoneId].cardIds.reverse();
47
+ }
48
+ },
49
+
50
+ getCardZone: (cardId) => {
51
+ for (const zoneId in zones) {
52
+ if (zones[zoneId].cardIds.includes(cardId)) {
53
+ return zoneId as ZoneId;
54
+ }
55
+ }
56
+ return undefined;
57
+ },
58
+
59
+ drawCards: (params) => {
60
+ const { from, to, count, playerId } = params;
61
+ const drawn: CardId[] = [];
62
+ const sourceZone = zones[from];
63
+ if (sourceZone) {
64
+ for (let i = 0; i < count; i++) {
65
+ const cardId = sourceZone.cardIds.shift();
66
+ if (cardId) {
67
+ drawn.push(cardId as CardId);
68
+ if (!zones[to]) zones[to] = { cardIds: [] };
69
+ zones[to].cardIds.push(cardId);
70
+ }
71
+ }
72
+ }
73
+ return drawn;
74
+ },
75
+
76
+ mulligan: (params) => {
77
+ const { hand, deck, drawCount, playerId } = params;
78
+ // Move all cards from hand to deck
79
+ if (zones[hand]) {
80
+ zones[deck].cardIds.push(...zones[hand].cardIds);
81
+ zones[hand].cardIds = [];
82
+ }
83
+ // Shuffle (reverse for testing)
84
+ zones[deck].cardIds.reverse();
85
+ // Draw new cards
86
+ for (let i = 0; i < drawCount; i++) {
87
+ const cardId = zones[deck].cardIds.shift();
88
+ if (cardId) {
89
+ zones[hand].cardIds.push(cardId);
90
+ }
91
+ }
92
+ },
93
+
94
+ bulkMove: (params) => {
95
+ const { from, to, count, playerId, position } = params;
96
+ const moved: CardId[] = [];
97
+ const sourceZone = zones[from];
98
+ if (sourceZone) {
99
+ for (let i = 0; i < count; i++) {
100
+ const cardId = sourceZone.cardIds.shift();
101
+ if (cardId) {
102
+ moved.push(cardId as CardId);
103
+ if (!zones[to]) zones[to] = { cardIds: [] };
104
+ if (position === "top") {
105
+ zones[to].cardIds.unshift(cardId);
106
+ } else {
107
+ zones[to].cardIds.push(cardId);
108
+ }
109
+ }
110
+ }
111
+ }
112
+ return moved;
113
+ },
114
+
115
+ createDeck: (params) => {
116
+ const { zoneId, playerId, cardCount, shuffle } = params;
117
+ const created: CardId[] = [];
118
+ if (!zones[zoneId]) zones[zoneId] = { cardIds: [] };
119
+ for (let i = 0; i < cardCount; i++) {
120
+ const cardId = `card-${Date.now()}-${i}` as CardId;
121
+ created.push(cardId);
122
+ zones[zoneId].cardIds.push(cardId);
123
+ }
124
+ if (shuffle) {
125
+ zones[zoneId].cardIds.reverse(); // Mock shuffle
126
+ }
127
+ return created;
128
+ },
129
+ };
130
+ };
131
+
132
+ describe("moveCard", () => {
133
+ it("should move a card to target zone", () => {
134
+ const ops = createMockZoneOperations();
135
+
136
+ ops.moveCard({
137
+ cardId: "card-1" as CardId,
138
+ targetZoneId: "hand" as ZoneId,
139
+ });
140
+
141
+ const deckCards = ops.getCardsInZone("deck" as ZoneId);
142
+ const handCards = ops.getCardsInZone("hand" as ZoneId);
143
+
144
+ expect(deckCards).not.toContain("card-1");
145
+ expect(handCards).toContain("card-1");
146
+ });
147
+
148
+ it("should add card to top when position is 'top'", () => {
149
+ const ops = createMockZoneOperations();
150
+
151
+ ops.moveCard({
152
+ cardId: "card-1" as CardId,
153
+ targetZoneId: "hand" as ZoneId,
154
+ position: "top",
155
+ });
156
+
157
+ const handCards = ops.getCardsInZone("hand" as ZoneId);
158
+ expect(handCards[0]).toBe("card-1" as unknown as CardId);
159
+ });
160
+
161
+ it("should add card to bottom when position is 'bottom'", () => {
162
+ const ops = createMockZoneOperations();
163
+
164
+ ops.moveCard({
165
+ cardId: "card-1" as CardId,
166
+ targetZoneId: "hand" as ZoneId,
167
+ position: "bottom",
168
+ });
169
+
170
+ const handCards = ops.getCardsInZone("hand" as ZoneId);
171
+ expect(handCards[handCards.length - 1]).toBe(
172
+ "card-1" as unknown as CardId,
173
+ );
174
+ });
175
+
176
+ it("should insert card at specific position when position is a number", () => {
177
+ const ops = createMockZoneOperations();
178
+
179
+ ops.moveCard({
180
+ cardId: "card-4" as CardId,
181
+ targetZoneId: "deck" as ZoneId,
182
+ position: 1,
183
+ });
184
+
185
+ const deckCards = ops.getCardsInZone("deck" as ZoneId);
186
+ expect(deckCards[1]).toBe("card-4" as unknown as CardId);
187
+ });
188
+ });
189
+
190
+ describe("getCardsInZone", () => {
191
+ it("should return all cards in a zone", () => {
192
+ const ops = createMockZoneOperations();
193
+
194
+ const deckCards = ops.getCardsInZone("deck" as ZoneId);
195
+
196
+ expect(deckCards).toHaveLength(3);
197
+ expect(deckCards).toContain("card-1");
198
+ expect(deckCards).toContain("card-2");
199
+ expect(deckCards).toContain("card-3");
200
+ });
201
+
202
+ it("should return empty array for empty zone", () => {
203
+ const ops = createMockZoneOperations();
204
+
205
+ const playCards = ops.getCardsInZone("play" as ZoneId);
206
+
207
+ expect(playCards).toHaveLength(0);
208
+ });
209
+
210
+ it("should accept optional ownerId parameter for player-specific zones", () => {
211
+ const ops = createMockZoneOperations();
212
+
213
+ // This just tests the signature works
214
+ const cards = ops.getCardsInZone(
215
+ "hand" as ZoneId,
216
+ "player-1" as unknown as PlayerId,
217
+ );
218
+
219
+ expect(cards).toBeDefined();
220
+ });
221
+ });
222
+
223
+ describe("shuffleZone", () => {
224
+ it("should shuffle cards in a zone", () => {
225
+ const ops = createMockZoneOperations();
226
+
227
+ const beforeShuffle = ops.getCardsInZone("deck" as ZoneId);
228
+ ops.shuffleZone("deck" as ZoneId);
229
+ const afterShuffle = ops.getCardsInZone("deck" as ZoneId);
230
+
231
+ // Mock implementation reverses, so order should be different
232
+ expect(afterShuffle).toHaveLength(beforeShuffle.length);
233
+ expect(afterShuffle[0]).not.toBe(beforeShuffle[0]);
234
+ });
235
+
236
+ it("should accept optional ownerId parameter for player-specific zones", () => {
237
+ const ops = createMockZoneOperations();
238
+
239
+ // This just tests the signature works
240
+ ops.shuffleZone("deck" as ZoneId, "player-1" as unknown as PlayerId);
241
+
242
+ expect(true).toBe(true); // Just verify it doesn't throw
243
+ });
244
+ });
245
+
246
+ describe("getCardZone", () => {
247
+ it("should return the zone containing a card", () => {
248
+ const ops = createMockZoneOperations();
249
+
250
+ const zone = ops.getCardZone("card-2" as CardId);
251
+
252
+ expect(zone).toBe("deck" as ZoneId);
253
+ });
254
+
255
+ it("should return undefined when card is not in any zone", () => {
256
+ const ops = createMockZoneOperations();
257
+
258
+ const zone = ops.getCardZone("nonexistent-card" as CardId);
259
+
260
+ expect(zone).toBeUndefined();
261
+ });
262
+ });
263
+
264
+ describe("Type Safety", () => {
265
+ it("should enforce CardId type for card identifiers", () => {
266
+ const ops = createMockZoneOperations();
267
+
268
+ // This is a compile-time test - it should type-check correctly
269
+ const zone = ops.getCardZone("card-1" as unknown as CardId);
270
+
271
+ expect(zone).toBeDefined();
272
+ });
273
+
274
+ it("should enforce ZoneId type for zone identifiers", () => {
275
+ const ops = createMockZoneOperations();
276
+
277
+ // This is a compile-time test - it should type-check correctly
278
+ const cards = ops.getCardsInZone("deck" as unknown as ZoneId);
279
+
280
+ expect(cards).toBeDefined();
281
+ });
282
+
283
+ it("should enforce PlayerId type for owner identifiers", () => {
284
+ const ops = createMockZoneOperations();
285
+
286
+ // This is a compile-time test - it should type-check correctly
287
+ const cards = ops.getCardsInZone(
288
+ "hand" as unknown as ZoneId,
289
+ "player-1" as unknown as PlayerId,
290
+ );
291
+
292
+ expect(cards).toBeDefined();
293
+ });
294
+ });
295
+ });
@@ -0,0 +1,223 @@
1
+ import type { CardId, PlayerId, ZoneId } from "../types";
2
+
3
+ /**
4
+ * Zone Operations Interface
5
+ *
6
+ * Provides API for manipulating cards between zones without directly mutating internal state.
7
+ * These operations are the only way for moves to interact with the framework's zone management.
8
+ *
9
+ * All zone operations maintain consistency:
10
+ * - Cards can only be in one zone at a time
11
+ * - Moving a card automatically removes it from its current zone
12
+ * - Zone order is preserved for ordered zones (like decks)
13
+ */
14
+ export interface ZoneOperations {
15
+ /**
16
+ * Move a card from its current zone to a target zone
17
+ *
18
+ * @param args - Move card arguments
19
+ * @param args.cardId - ID of the card to move
20
+ * @param args.targetZoneId - ID of the zone to move the card to
21
+ * @param args.position - Where to place the card in the target zone:
22
+ * - 'top': Add to the beginning (index 0)
23
+ * - 'bottom': Add to the end (default)
24
+ * - number: Insert at specific index
25
+ *
26
+ * @example
27
+ * ```typescript
28
+ * // Draw a card (deck -> hand)
29
+ * zones.moveCard({
30
+ * cardId: 'card-1',
31
+ * targetZoneId: 'hand',
32
+ * position: 'bottom'
33
+ * });
34
+ *
35
+ * // Put card on top of deck
36
+ * zones.moveCard({
37
+ * cardId: 'card-2',
38
+ * targetZoneId: 'deck',
39
+ * position: 'top'
40
+ * });
41
+ * ```
42
+ */
43
+ moveCard(args: {
44
+ cardId: CardId;
45
+ targetZoneId: ZoneId;
46
+ position?: "top" | "bottom" | number;
47
+ }): void;
48
+
49
+ /**
50
+ * Get all cards in a zone
51
+ *
52
+ * @param zoneId - ID of the zone to query
53
+ * @param ownerId - Optional player ID to filter by owner (for player-specific zones)
54
+ * @returns Array of card IDs in the zone (in order for ordered zones)
55
+ *
56
+ * @example
57
+ * ```typescript
58
+ * // Get all cards in a player's hand
59
+ * const handCards = zones.getCardsInZone('hand', 'player-1');
60
+ *
61
+ * // Get all cards in shared play area
62
+ * const playCards = zones.getCardsInZone('play');
63
+ * ```
64
+ */
65
+ getCardsInZone(zoneId: ZoneId, ownerId?: PlayerId): CardId[];
66
+
67
+ /**
68
+ * Shuffle cards in a zone
69
+ *
70
+ * @param zoneId - ID of the zone to shuffle
71
+ * @param ownerId - Optional player ID for player-specific zones
72
+ *
73
+ * @example
74
+ * ```typescript
75
+ * // Shuffle a player's deck
76
+ * zones.shuffleZone('deck', 'player-1');
77
+ * ```
78
+ */
79
+ shuffleZone(zoneId: ZoneId, ownerId?: PlayerId): void;
80
+
81
+ /**
82
+ * Get the zone containing a card
83
+ *
84
+ * @param cardId - ID of the card to find
85
+ * @returns Zone ID if card is in a zone, undefined otherwise
86
+ *
87
+ * @example
88
+ * ```typescript
89
+ * const zone = zones.getCardZone('card-1');
90
+ * if (zone === 'hand') {
91
+ * // Card is in hand
92
+ * }
93
+ * ```
94
+ */
95
+ getCardZone(cardId: CardId): ZoneId | undefined;
96
+
97
+ /**
98
+ * Draw cards from one zone to another
99
+ *
100
+ * High-level utility that moves multiple cards from source to target zone.
101
+ * Commonly used for drawing cards from deck to hand.
102
+ *
103
+ * @param params - Draw parameters
104
+ * @param params.from - Source zone ID (e.g., 'deck')
105
+ * @param params.to - Target zone ID (e.g., 'hand')
106
+ * @param params.count - Number of cards to draw
107
+ * @param params.playerId - Player ID for player-specific zones
108
+ * @returns Array of card IDs that were drawn
109
+ *
110
+ * @example
111
+ * ```typescript
112
+ * // Draw 5 cards from deck to hand
113
+ * const drawnCards = zones.drawCards({
114
+ * from: 'deck',
115
+ * to: 'hand',
116
+ * count: 5,
117
+ * playerId: 'player-1'
118
+ * });
119
+ * ```
120
+ */
121
+ drawCards(params: {
122
+ from: ZoneId;
123
+ to: ZoneId;
124
+ count: number;
125
+ playerId: PlayerId;
126
+ }): CardId[];
127
+
128
+ /**
129
+ * Perform mulligan - shuffle hand back into deck and redraw
130
+ *
131
+ * High-level utility for standard mulligan operation:
132
+ * 1. Move all cards from hand to deck
133
+ * 2. Shuffle deck
134
+ * 3. Draw the specified number of cards
135
+ *
136
+ * @param params - Mulligan parameters
137
+ * @param params.hand - Hand zone ID
138
+ * @param params.deck - Deck zone ID
139
+ * @param params.drawCount - Number of cards to draw after shuffle
140
+ * @param params.playerId - Player ID
141
+ *
142
+ * @example
143
+ * ```typescript
144
+ * // Mulligan: shuffle hand back and draw 7 new cards
145
+ * zones.mulligan({
146
+ * hand: 'hand',
147
+ * deck: 'deck',
148
+ * drawCount: 7,
149
+ * playerId: 'player-1'
150
+ * });
151
+ * ```
152
+ */
153
+ mulligan(params: {
154
+ hand: ZoneId;
155
+ deck: ZoneId;
156
+ drawCount: number;
157
+ playerId: PlayerId;
158
+ }): void;
159
+
160
+ /**
161
+ * Move multiple cards in bulk
162
+ *
163
+ * High-level utility for moving many cards at once.
164
+ * Commonly used for setup operations like placing shields.
165
+ *
166
+ * @param params - Bulk move parameters
167
+ * @param params.from - Source zone ID
168
+ * @param params.to - Target zone ID
169
+ * @param params.count - Number of cards to move
170
+ * @param params.playerId - Player ID
171
+ * @param params.position - Where to place cards ('top' or 'bottom', default 'bottom')
172
+ * @returns Array of card IDs that were moved
173
+ *
174
+ * @example
175
+ * ```typescript
176
+ * // Move 6 cards from deck to shields
177
+ * zones.bulkMove({
178
+ * from: 'deck',
179
+ * to: 'shieldSection',
180
+ * count: 6,
181
+ * playerId: 'player-1'
182
+ * });
183
+ * ```
184
+ */
185
+ bulkMove(params: {
186
+ from: ZoneId;
187
+ to: ZoneId;
188
+ count: number;
189
+ playerId: PlayerId;
190
+ position?: "top" | "bottom";
191
+ }): CardId[];
192
+
193
+ /**
194
+ * Create a deck with placeholder cards
195
+ *
196
+ * High-level utility for game setup. Creates card instances and
197
+ * places them in the specified zone.
198
+ *
199
+ * @param params - Deck creation parameters
200
+ * @param params.zoneId - Zone to place the cards in
201
+ * @param params.playerId - Owner of the cards
202
+ * @param params.cardCount - Number of cards to create
203
+ * @param params.shuffle - Whether to shuffle after creation (default false)
204
+ * @returns Array of created card IDs
205
+ *
206
+ * @example
207
+ * ```typescript
208
+ * // Create and shuffle a 50-card deck
209
+ * zones.createDeck({
210
+ * zoneId: 'deck',
211
+ * playerId: 'player-1',
212
+ * cardCount: 50,
213
+ * shuffle: true
214
+ * });
215
+ * ```
216
+ */
217
+ createDeck(params: {
218
+ zoneId: ZoneId;
219
+ playerId: PlayerId;
220
+ cardCount: number;
221
+ shuffle?: boolean;
222
+ }): CardId[];
223
+ }