@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 { createCardId, createPlayerId } from "../types";
3
+ import type { Zone } from "../zones/zone";
4
+ import {
5
+ createTestDeck,
6
+ createTestGraveyard,
7
+ createTestHand,
8
+ createTestPlayArea,
9
+ createTestZone,
10
+ } from "./test-zone-factory";
11
+
12
+ describe("test-zone-factory", () => {
13
+ describe("createTestZone", () => {
14
+ it("should create zone with default values", () => {
15
+ const zone = createTestZone();
16
+
17
+ expect(zone.config.id).toBeDefined();
18
+ expect(zone.config.name).toBeDefined();
19
+ expect(zone.config.visibility).toBeDefined();
20
+ expect(zone.config.ordered).toBeDefined();
21
+ expect(zone.cards).toEqual([]);
22
+ });
23
+
24
+ it("should override config properties", () => {
25
+ const zone = createTestZone({
26
+ name: "Custom Zone",
27
+ visibility: "secret",
28
+ ordered: true,
29
+ });
30
+
31
+ expect(zone.config.name).toBe("Custom Zone");
32
+ expect(zone.config.visibility).toBe("secret");
33
+ expect(zone.config.ordered).toBe(true);
34
+ });
35
+
36
+ it("should include initial cards", () => {
37
+ const cards = [
38
+ createCardId("card1"),
39
+ createCardId("card2"),
40
+ createCardId("card3"),
41
+ ];
42
+
43
+ const zone = createTestZone({}, cards);
44
+
45
+ expect(zone.cards).toEqual(cards);
46
+ expect(zone.cards.length).toBe(3);
47
+ });
48
+
49
+ it("should generate unique zone IDs", () => {
50
+ const zone1 = createTestZone();
51
+ const zone2 = createTestZone();
52
+ const zone3 = createTestZone();
53
+
54
+ expect(zone1.config.id).not.toBe(zone2.config.id);
55
+ expect(zone2.config.id).not.toBe(zone3.config.id);
56
+ });
57
+
58
+ it("should support maxSize", () => {
59
+ const zone = createTestZone({ maxSize: 10 });
60
+
61
+ expect(zone.config.maxSize).toBe(10);
62
+ });
63
+
64
+ it("should support owner", () => {
65
+ const playerId = createPlayerId("p1");
66
+ const zone = createTestZone({ owner: playerId });
67
+
68
+ expect(zone.config.owner).toBe(playerId);
69
+ });
70
+
71
+ it("should support faceDown", () => {
72
+ const zone = createTestZone({ faceDown: true });
73
+
74
+ expect(zone.config.faceDown).toBe(true);
75
+ });
76
+
77
+ it("should create valid Zone type", () => {
78
+ const zone = createTestZone();
79
+
80
+ // TypeScript type check
81
+ const validate = (z: Zone) => z;
82
+ expect(() => validate(zone)).not.toThrow();
83
+ });
84
+ });
85
+
86
+ describe("createTestDeck", () => {
87
+ it("should create deck zone with appropriate defaults", () => {
88
+ const deck = createTestDeck();
89
+
90
+ expect(deck.config.name).toContain("Deck");
91
+ expect(deck.config.visibility).toBe("secret");
92
+ expect(deck.config.ordered).toBe(true);
93
+ expect(deck.config.faceDown).toBe(true);
94
+ });
95
+
96
+ it("should include cards when provided", () => {
97
+ const cards = [
98
+ createCardId("card1"),
99
+ createCardId("card2"),
100
+ createCardId("card3"),
101
+ ];
102
+
103
+ const deck = createTestDeck(cards);
104
+
105
+ expect(deck.cards).toEqual(cards);
106
+ });
107
+
108
+ it("should support owner", () => {
109
+ const playerId = createPlayerId("p1");
110
+ const deck = createTestDeck([], playerId);
111
+
112
+ expect(deck.config.owner).toBe(playerId);
113
+ });
114
+
115
+ it("should maintain card order", () => {
116
+ const cards = [
117
+ createCardId("card1"),
118
+ createCardId("card2"),
119
+ createCardId("card3"),
120
+ ];
121
+
122
+ const deck = createTestDeck(cards);
123
+
124
+ // Order should be preserved
125
+ expect(deck.cards[0]).toBe(cards[0]);
126
+ expect(deck.cards[1]).toBe(cards[1]);
127
+ expect(deck.cards[2]).toBe(cards[2]);
128
+ });
129
+ });
130
+
131
+ describe("createTestHand", () => {
132
+ it("should create hand zone with appropriate defaults", () => {
133
+ const hand = createTestHand();
134
+
135
+ expect(hand.config.name).toContain("Hand");
136
+ expect(hand.config.visibility).toBe("private");
137
+ expect(hand.config.ordered).toBe(false);
138
+ expect(hand.config.faceDown).toBe(false);
139
+ });
140
+
141
+ it("should include cards when provided", () => {
142
+ const cards = [createCardId("card1"), createCardId("card2")];
143
+
144
+ const hand = createTestHand(cards);
145
+
146
+ expect(hand.cards).toEqual(cards);
147
+ });
148
+
149
+ it("should support owner", () => {
150
+ const playerId = createPlayerId("p1");
151
+ const hand = createTestHand([], playerId);
152
+
153
+ expect(hand.config.owner).toBe(playerId);
154
+ });
155
+ });
156
+
157
+ describe("createTestPlayArea", () => {
158
+ it("should create play area with appropriate defaults", () => {
159
+ const playArea = createTestPlayArea();
160
+
161
+ expect(playArea.config.name).toContain("Play Area");
162
+ expect(playArea.config.visibility).toBe("public");
163
+ expect(playArea.config.ordered).toBe(false);
164
+ expect(playArea.config.faceDown).toBe(false);
165
+ });
166
+
167
+ it("should include cards when provided", () => {
168
+ const cards = [createCardId("card1"), createCardId("card2")];
169
+
170
+ const playArea = createTestPlayArea(cards);
171
+
172
+ expect(playArea.cards).toEqual(cards);
173
+ });
174
+
175
+ it("should support owner", () => {
176
+ const playerId = createPlayerId("p1");
177
+ const playArea = createTestPlayArea([], playerId);
178
+
179
+ expect(playArea.config.owner).toBe(playerId);
180
+ });
181
+ });
182
+
183
+ describe("createTestGraveyard", () => {
184
+ it("should create graveyard with appropriate defaults", () => {
185
+ const graveyard = createTestGraveyard();
186
+
187
+ expect(graveyard.config.name).toContain("Graveyard");
188
+ expect(graveyard.config.visibility).toBe("public");
189
+ expect(graveyard.config.ordered).toBe(true);
190
+ expect(graveyard.config.faceDown).toBe(false);
191
+ });
192
+
193
+ it("should include cards when provided", () => {
194
+ const cards = [createCardId("card1"), createCardId("card2")];
195
+
196
+ const graveyard = createTestGraveyard(cards);
197
+
198
+ expect(graveyard.cards).toEqual(cards);
199
+ });
200
+
201
+ it("should support owner", () => {
202
+ const playerId = createPlayerId("p1");
203
+ const graveyard = createTestGraveyard([], playerId);
204
+
205
+ expect(graveyard.config.owner).toBe(playerId);
206
+ });
207
+
208
+ it("should maintain card order", () => {
209
+ const cards = [
210
+ createCardId("card1"),
211
+ createCardId("card2"),
212
+ createCardId("card3"),
213
+ ];
214
+
215
+ const graveyard = createTestGraveyard(cards);
216
+
217
+ // Order should be preserved (cards go on top)
218
+ expect(graveyard.cards[0]).toBe(cards[0]);
219
+ expect(graveyard.cards[1]).toBe(cards[1]);
220
+ expect(graveyard.cards[2]).toBe(cards[2]);
221
+ });
222
+ });
223
+
224
+ describe("integration", () => {
225
+ it("should create complete player zone setup", () => {
226
+ const playerId = createPlayerId("p1");
227
+
228
+ // Create all zones for a player
229
+ const deckCards = [
230
+ createCardId("card1"),
231
+ createCardId("card2"),
232
+ createCardId("card3"),
233
+ ];
234
+ const handCards = [createCardId("card4"), createCardId("card5")];
235
+
236
+ const deck = createTestDeck(deckCards, playerId);
237
+ const hand = createTestHand(handCards, playerId);
238
+ const playArea = createTestPlayArea([], playerId);
239
+ const graveyard = createTestGraveyard([], playerId);
240
+
241
+ // Verify all zones created correctly
242
+ expect(deck.config.owner).toBe(playerId);
243
+ expect(hand.config.owner).toBe(playerId);
244
+ expect(playArea.config.owner).toBe(playerId);
245
+ expect(graveyard.config.owner).toBe(playerId);
246
+
247
+ // Verify zones have correct visibility
248
+ expect(deck.config.visibility).toBe("secret");
249
+ expect(hand.config.visibility).toBe("private");
250
+ expect(playArea.config.visibility).toBe("public");
251
+ expect(graveyard.config.visibility).toBe("public");
252
+
253
+ // Verify cards distributed correctly
254
+ expect(deck.cards.length).toBe(3);
255
+ expect(hand.cards.length).toBe(2);
256
+ expect(playArea.cards.length).toBe(0);
257
+ expect(graveyard.cards.length).toBe(0);
258
+ });
259
+
260
+ it("should work with zone operations", () => {
261
+ const deck = createTestDeck([
262
+ createCardId("card1"),
263
+ createCardId("card2"),
264
+ createCardId("card3"),
265
+ ]);
266
+
267
+ // Test that zone factory creates zones compatible with zone operations
268
+ expect(deck.cards.length).toBe(3);
269
+ expect(deck.config.ordered).toBe(true);
270
+
271
+ // Simulate drawing a card
272
+ const drawnCard = deck.cards.pop();
273
+ expect(drawnCard).toBe(createCardId("card3"));
274
+ expect(deck.cards.length).toBe(2);
275
+ });
276
+
277
+ it("should create zones for multiple players", () => {
278
+ const p1 = createPlayerId("p1");
279
+ const p2 = createPlayerId("p2");
280
+
281
+ const p1Deck = createTestDeck(
282
+ [createCardId("p1-card1"), createCardId("p1-card2")],
283
+ p1,
284
+ );
285
+ const p2Deck = createTestDeck(
286
+ [createCardId("p2-card1"), createCardId("p2-card2")],
287
+ p2,
288
+ );
289
+
290
+ expect(p1Deck.config.owner).toBe(p1);
291
+ expect(p2Deck.config.owner).toBe(p2);
292
+ expect(p1Deck.config.id).not.toBe(p2Deck.config.id);
293
+ });
294
+ });
295
+ });
@@ -0,0 +1,224 @@
1
+ import type { CardId, PlayerId } from "../types";
2
+ import { createZoneId } from "../types";
3
+ import type { CardZoneConfig, Zone } from "../zones/zone";
4
+
5
+ /**
6
+ * Test Zone Factory
7
+ *
8
+ * Factory functions for creating test zones
9
+ */
10
+
11
+ let zoneCounter = 0;
12
+
13
+ /**
14
+ * Create a test zone with optional configuration and cards
15
+ *
16
+ * Generates a zone with sensible defaults that can be customized.
17
+ * Each zone gets a unique ID automatically.
18
+ *
19
+ * @param configOverrides - Optional config properties to override
20
+ * @param cards - Optional initial cards
21
+ * @returns Zone for testing
22
+ *
23
+ * @example
24
+ * ```typescript
25
+ * // Create default zone
26
+ * const zone = createTestZone();
27
+ *
28
+ * // Create zone with cards
29
+ * const deck = createTestZone(
30
+ * { name: 'Deck', visibility: 'secret', ordered: true },
31
+ * ['card1', 'card2', 'card3']
32
+ * );
33
+ *
34
+ * // Create player-owned zone
35
+ * const hand = createTestZone({
36
+ * name: 'Hand',
37
+ * owner: 'player1',
38
+ * visibility: 'private'
39
+ * });
40
+ * ```
41
+ */
42
+ export function createTestZone(
43
+ configOverrides?: Partial<CardZoneConfig>,
44
+ cards: CardId[] = [],
45
+ ): Zone {
46
+ const id = createZoneId(`test-zone-${zoneCounter++}`);
47
+
48
+ const config: CardZoneConfig = {
49
+ id,
50
+ name: `Test Zone ${zoneCounter}`,
51
+ visibility: "public",
52
+ ordered: false,
53
+ ...configOverrides,
54
+ };
55
+
56
+ return {
57
+ config,
58
+ cards: [...cards],
59
+ };
60
+ }
61
+
62
+ /**
63
+ * Create a test deck zone
64
+ *
65
+ * Creates a zone with typical deck properties:
66
+ * - Secret visibility (no one can see cards)
67
+ * - Ordered (card order matters)
68
+ * - Face down
69
+ *
70
+ * @param cards - Optional initial cards
71
+ * @param owner - Optional owner player ID
72
+ * @returns Deck zone
73
+ *
74
+ * @example
75
+ * ```typescript
76
+ * const deck = createTestDeck(
77
+ * ['card1', 'card2', 'card3'],
78
+ * 'player1'
79
+ * );
80
+ * ```
81
+ */
82
+ export function createTestDeck(cards: CardId[] = [], owner?: PlayerId): Zone {
83
+ const id = createZoneId(`test-deck-${zoneCounter++}`);
84
+
85
+ return {
86
+ config: {
87
+ id,
88
+ name: owner ? `${owner} Deck` : "Test Deck",
89
+ visibility: "secret",
90
+ ordered: true,
91
+ faceDown: true,
92
+ owner,
93
+ },
94
+ cards: [...cards],
95
+ };
96
+ }
97
+
98
+ /**
99
+ * Create a test hand zone
100
+ *
101
+ * Creates a zone with typical hand properties:
102
+ * - Private visibility (owner can see, opponents cannot)
103
+ * - Unordered (card order doesn't matter)
104
+ * - Face up
105
+ *
106
+ * @param cards - Optional initial cards
107
+ * @param owner - Optional owner player ID
108
+ * @returns Hand zone
109
+ *
110
+ * @example
111
+ * ```typescript
112
+ * const hand = createTestHand(
113
+ * ['card1', 'card2'],
114
+ * 'player1'
115
+ * );
116
+ * ```
117
+ */
118
+ export function createTestHand(cards: CardId[] = [], owner?: PlayerId): Zone {
119
+ const id = createZoneId(`test-hand-${zoneCounter++}`);
120
+
121
+ return {
122
+ config: {
123
+ id,
124
+ name: owner ? `${owner} Hand` : "Test Hand",
125
+ visibility: "private",
126
+ ordered: false,
127
+ faceDown: false,
128
+ owner,
129
+ },
130
+ cards: [...cards],
131
+ };
132
+ }
133
+
134
+ /**
135
+ * Create a test play area zone
136
+ *
137
+ * Creates a zone with typical play area properties:
138
+ * - Public visibility (everyone can see)
139
+ * - Unordered (card order doesn't matter)
140
+ * - Face up
141
+ *
142
+ * @param cards - Optional initial cards
143
+ * @param owner - Optional owner player ID
144
+ * @returns Play area zone
145
+ *
146
+ * @example
147
+ * ```typescript
148
+ * const playArea = createTestPlayArea(
149
+ * ['creature1', 'creature2'],
150
+ * 'player1'
151
+ * );
152
+ * ```
153
+ */
154
+ export function createTestPlayArea(
155
+ cards: CardId[] = [],
156
+ owner?: PlayerId,
157
+ ): Zone {
158
+ const id = createZoneId(`test-play-area-${zoneCounter++}`);
159
+
160
+ return {
161
+ config: {
162
+ id,
163
+ name: owner ? `${owner} Play Area` : "Test Play Area",
164
+ visibility: "public",
165
+ ordered: false,
166
+ faceDown: false,
167
+ owner,
168
+ },
169
+ cards: [...cards],
170
+ };
171
+ }
172
+
173
+ /**
174
+ * Create a test graveyard zone
175
+ *
176
+ * Creates a zone with typical graveyard properties:
177
+ * - Public visibility (everyone can see)
178
+ * - Ordered (card order matters - cards go on top)
179
+ * - Face up
180
+ *
181
+ * @param cards - Optional initial cards
182
+ * @param owner - Optional owner player ID
183
+ * @returns Graveyard zone
184
+ *
185
+ * @example
186
+ * ```typescript
187
+ * const graveyard = createTestGraveyard(
188
+ * ['dead-card1', 'dead-card2'],
189
+ * 'player1'
190
+ * );
191
+ * ```
192
+ */
193
+ export function createTestGraveyard(
194
+ cards: CardId[] = [],
195
+ owner?: PlayerId,
196
+ ): Zone {
197
+ const id = createZoneId(`test-graveyard-${zoneCounter++}`);
198
+
199
+ return {
200
+ config: {
201
+ id,
202
+ name: owner ? `${owner} Graveyard` : "Test Graveyard",
203
+ visibility: "public",
204
+ ordered: true,
205
+ faceDown: false,
206
+ owner,
207
+ },
208
+ cards: [...cards],
209
+ };
210
+ }
211
+
212
+ /**
213
+ * Reset the zone counter (useful for deterministic test IDs)
214
+ *
215
+ * @example
216
+ * ```typescript
217
+ * resetZoneCounter();
218
+ * const zone1 = createTestZone(); // test-zone-0
219
+ * const zone2 = createTestZone(); // test-zone-1
220
+ * ```
221
+ */
222
+ export function resetZoneCounter(): void {
223
+ zoneCounter = 0;
224
+ }
@@ -0,0 +1,54 @@
1
+ import { nanoid } from "nanoid";
2
+ import type { CardId, GameId, PlayerId, ZoneId } from "./branded";
3
+
4
+ /**
5
+ * Creates a CardId from a string or generates a new unique ID
6
+ * @param id - Optional ID string. If not provided, generates a new unique ID
7
+ * @returns A branded CardId
8
+ */
9
+ export function createCardId(): CardId;
10
+ export function createCardId(id: string): CardId;
11
+ export function createCardId(id?: string): CardId {
12
+ // Type assertion is acceptable here as this is the only way to create branded types
13
+ // biome-ignore lint/suspicious/noExplicitAny: Required for branded type creation
14
+ return (id ?? nanoid()) as any;
15
+ }
16
+
17
+ /**
18
+ * Creates a PlayerId from a string or generates a new unique ID
19
+ * @param id - Optional ID string. If not provided, generates a new unique ID
20
+ * @returns A branded PlayerId
21
+ */
22
+ export function createPlayerId(): PlayerId;
23
+ export function createPlayerId(id: string): PlayerId;
24
+ export function createPlayerId(id?: string): PlayerId {
25
+ // Type assertion is acceptable here as this is the only way to create branded types
26
+ // biome-ignore lint/suspicious/noExplicitAny: Required for branded type creation
27
+ return (id ?? nanoid()) as any;
28
+ }
29
+
30
+ /**
31
+ * Creates a GameId from a string or generates a new unique ID
32
+ * @param id - Optional ID string. If not provided, generates a new unique ID
33
+ * @returns A branded GameId
34
+ */
35
+ export function createGameId(): GameId;
36
+ export function createGameId(id: string): GameId;
37
+ export function createGameId(id?: string): GameId {
38
+ // Type assertion is acceptable here as this is the only way to create branded types
39
+ // biome-ignore lint/suspicious/noExplicitAny: Required for branded type creation
40
+ return (id ?? nanoid()) as any;
41
+ }
42
+
43
+ /**
44
+ * Creates a ZoneId from a string or generates a new unique ID
45
+ * @param id - Optional ID string. If not provided, generates a new unique ID
46
+ * @returns A branded ZoneId
47
+ */
48
+ export function createZoneId(): ZoneId;
49
+ export function createZoneId(id: string): ZoneId;
50
+ export function createZoneId(id?: string): ZoneId {
51
+ // Type assertion is acceptable here as this is the only way to create branded types
52
+ // biome-ignore lint/suspicious/noExplicitAny: Required for branded type creation
53
+ return (id ?? nanoid()) as any;
54
+ }