@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.
- package/README.md +882 -0
- package/package.json +58 -0
- package/src/__tests__/alpha-clash-engine-definition.test.ts +319 -0
- package/src/__tests__/createMockAlphaClashGame.ts +462 -0
- package/src/__tests__/createMockGrandArchiveGame.ts +373 -0
- package/src/__tests__/createMockGundamGame.ts +379 -0
- package/src/__tests__/createMockLorcanaGame.ts +328 -0
- package/src/__tests__/createMockOnePieceGame.ts +429 -0
- package/src/__tests__/createMockRiftboundGame.ts +462 -0
- package/src/__tests__/grand-archive-engine-definition.test.ts +118 -0
- package/src/__tests__/gundam-engine-definition.test.ts +110 -0
- package/src/__tests__/integration-complete-game.test.ts +508 -0
- package/src/__tests__/integration-network-sync.test.ts +469 -0
- package/src/__tests__/lorcana-engine-definition.test.ts +100 -0
- package/src/__tests__/move-enumeration.test.ts +725 -0
- package/src/__tests__/multiplayer-engine.test.ts +555 -0
- package/src/__tests__/one-piece-engine-definition.test.ts +114 -0
- package/src/__tests__/riftbound-engine-definition.test.ts +124 -0
- package/src/actions/action-definition.test.ts +201 -0
- package/src/actions/action-definition.ts +122 -0
- package/src/actions/action-timing.test.ts +490 -0
- package/src/actions/action-timing.ts +257 -0
- package/src/cards/card-definition.test.ts +268 -0
- package/src/cards/card-definition.ts +27 -0
- package/src/cards/card-instance.test.ts +422 -0
- package/src/cards/card-instance.ts +49 -0
- package/src/cards/computed-properties.test.ts +530 -0
- package/src/cards/computed-properties.ts +84 -0
- package/src/cards/conditional-modifiers.test.ts +390 -0
- package/src/cards/modifiers.test.ts +286 -0
- package/src/cards/modifiers.ts +51 -0
- package/src/engine/MULTIPLAYER.md +425 -0
- package/src/engine/__tests__/rule-engine-flow.test.ts +348 -0
- package/src/engine/__tests__/rule-engine-history.test.ts +535 -0
- package/src/engine/__tests__/rule-engine-moves.test.ts +488 -0
- package/src/engine/__tests__/rule-engine.test.ts +366 -0
- package/src/engine/index.ts +14 -0
- package/src/engine/multiplayer-engine.example.ts +571 -0
- package/src/engine/multiplayer-engine.ts +409 -0
- package/src/engine/rule-engine.test.ts +286 -0
- package/src/engine/rule-engine.ts +1539 -0
- package/src/engine/tracker-system.ts +172 -0
- package/src/examples/__tests__/coin-flip-game.test.ts +641 -0
- package/src/filtering/card-filter.test.ts +230 -0
- package/src/filtering/card-filter.ts +91 -0
- package/src/filtering/card-query.test.ts +901 -0
- package/src/filtering/card-query.ts +273 -0
- package/src/filtering/filter-matching.test.ts +944 -0
- package/src/filtering/filter-matching.ts +315 -0
- package/src/flow/SERIALIZATION.md +428 -0
- package/src/flow/__tests__/flow-definition.test.ts +427 -0
- package/src/flow/__tests__/flow-manager.test.ts +756 -0
- package/src/flow/__tests__/flow-serialization.test.ts +565 -0
- package/src/flow/flow-definition.ts +453 -0
- package/src/flow/flow-manager.ts +1044 -0
- package/src/flow/index.ts +35 -0
- package/src/game-definition/__tests__/game-definition-validation.test.ts +359 -0
- package/src/game-definition/__tests__/game-definition.test.ts +291 -0
- package/src/game-definition/__tests__/move-definitions.test.ts +328 -0
- package/src/game-definition/game-definition.ts +261 -0
- package/src/game-definition/index.ts +28 -0
- package/src/game-definition/move-definitions.ts +188 -0
- package/src/game-definition/validation.ts +183 -0
- package/src/history/history-manager.test.ts +497 -0
- package/src/history/history-manager.ts +312 -0
- package/src/history/history-operations.ts +122 -0
- package/src/history/index.ts +9 -0
- package/src/history/types.ts +255 -0
- package/src/index.ts +32 -0
- package/src/logging/index.ts +27 -0
- package/src/logging/log-formatter.ts +187 -0
- package/src/logging/logger.ts +276 -0
- package/src/logging/types.ts +148 -0
- package/src/moves/create-move.test.ts +331 -0
- package/src/moves/create-move.ts +64 -0
- package/src/moves/move-enumeration.ts +228 -0
- package/src/moves/move-executor.test.ts +431 -0
- package/src/moves/move-executor.ts +195 -0
- package/src/moves/move-system.test.ts +380 -0
- package/src/moves/move-system.ts +463 -0
- package/src/moves/standard-moves.ts +231 -0
- package/src/operations/card-operations.test.ts +236 -0
- package/src/operations/card-operations.ts +116 -0
- package/src/operations/card-registry-impl.test.ts +251 -0
- package/src/operations/card-registry-impl.ts +70 -0
- package/src/operations/card-registry.test.ts +234 -0
- package/src/operations/card-registry.ts +106 -0
- package/src/operations/counter-operations.ts +152 -0
- package/src/operations/game-operations.test.ts +280 -0
- package/src/operations/game-operations.ts +140 -0
- package/src/operations/index.ts +24 -0
- package/src/operations/operations-impl.test.ts +354 -0
- package/src/operations/operations-impl.ts +468 -0
- package/src/operations/zone-operations.test.ts +295 -0
- package/src/operations/zone-operations.ts +223 -0
- package/src/rng/seeded-rng.test.ts +339 -0
- package/src/rng/seeded-rng.ts +123 -0
- package/src/targeting/index.ts +48 -0
- package/src/targeting/target-definition.test.ts +273 -0
- package/src/targeting/target-definition.ts +37 -0
- package/src/targeting/target-dsl.ts +279 -0
- package/src/targeting/target-resolver.ts +486 -0
- package/src/targeting/target-validation.test.ts +994 -0
- package/src/targeting/target-validation.ts +286 -0
- package/src/telemetry/events.ts +202 -0
- package/src/telemetry/index.ts +21 -0
- package/src/telemetry/telemetry-manager.ts +127 -0
- package/src/telemetry/types.ts +68 -0
- package/src/testing/__tests__/testing-utilities-integration.test.ts +161 -0
- package/src/testing/index.ts +88 -0
- package/src/testing/test-assertions.test.ts +341 -0
- package/src/testing/test-assertions.ts +256 -0
- package/src/testing/test-card-factory.test.ts +228 -0
- package/src/testing/test-card-factory.ts +111 -0
- package/src/testing/test-context-factory.ts +187 -0
- package/src/testing/test-end-assertions.test.ts +262 -0
- package/src/testing/test-end-assertions.ts +95 -0
- package/src/testing/test-engine-builder.test.ts +389 -0
- package/src/testing/test-engine-builder.ts +46 -0
- package/src/testing/test-flow-assertions.test.ts +284 -0
- package/src/testing/test-flow-assertions.ts +115 -0
- package/src/testing/test-player-builder.test.ts +132 -0
- package/src/testing/test-player-builder.ts +46 -0
- package/src/testing/test-replay-assertions.test.ts +356 -0
- package/src/testing/test-replay-assertions.ts +164 -0
- package/src/testing/test-rng-helpers.test.ts +260 -0
- package/src/testing/test-rng-helpers.ts +190 -0
- package/src/testing/test-state-builder.test.ts +373 -0
- package/src/testing/test-state-builder.ts +99 -0
- package/src/testing/test-zone-factory.test.ts +295 -0
- package/src/testing/test-zone-factory.ts +224 -0
- package/src/types/branded-utils.ts +54 -0
- package/src/types/branded.test.ts +175 -0
- package/src/types/branded.ts +33 -0
- package/src/types/index.ts +8 -0
- package/src/types/state.test.ts +198 -0
- package/src/types/state.ts +154 -0
- package/src/validation/card-type-guards.test.ts +242 -0
- package/src/validation/card-type-guards.ts +179 -0
- package/src/validation/index.ts +40 -0
- package/src/validation/schema-builders.test.ts +403 -0
- package/src/validation/schema-builders.ts +345 -0
- package/src/validation/type-guard-builder.test.ts +216 -0
- package/src/validation/type-guard-builder.ts +109 -0
- package/src/validation/validator-builder.test.ts +375 -0
- package/src/validation/validator-builder.ts +273 -0
- package/src/zones/index.ts +28 -0
- package/src/zones/zone-factory.test.ts +183 -0
- package/src/zones/zone-factory.ts +44 -0
- package/src/zones/zone-operations.test.ts +800 -0
- package/src/zones/zone-operations.ts +306 -0
- package/src/zones/zone-state-helpers.test.ts +337 -0
- package/src/zones/zone-state-helpers.ts +128 -0
- package/src/zones/zone-visibility.test.ts +156 -0
- package/src/zones/zone-visibility.ts +36 -0
- package/src/zones/zone.test.ts +186 -0
- 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
|
+
}
|