@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,315 @@
|
|
|
1
|
+
import type { CardDefinition } from "../cards/card-definition";
|
|
2
|
+
import type { CardInstance } from "../cards/card-instance";
|
|
3
|
+
import type { CardRegistry } from "../operations/card-registry";
|
|
4
|
+
import type { CardFilter, NumberFilter, PropertyFilter } from "./card-filter";
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Checks if a number matches a NumberFilter
|
|
8
|
+
* @param filter - Number filter to match against
|
|
9
|
+
* @param value - Value to check
|
|
10
|
+
* @returns true if value matches the filter
|
|
11
|
+
*/
|
|
12
|
+
export function matchesNumberFilter(
|
|
13
|
+
filter: NumberFilter,
|
|
14
|
+
value: number,
|
|
15
|
+
): boolean {
|
|
16
|
+
// Direct number comparison (exact match)
|
|
17
|
+
if (typeof filter === "number") {
|
|
18
|
+
return value === filter;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
// Object-based filters
|
|
22
|
+
if ("eq" in filter) {
|
|
23
|
+
return value === filter.eq;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
if ("gte" in filter) {
|
|
27
|
+
return value >= filter.gte;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
if ("lte" in filter) {
|
|
31
|
+
return value <= filter.lte;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
if ("gt" in filter) {
|
|
35
|
+
return value > filter.gt;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
if ("lt" in filter) {
|
|
39
|
+
return value < filter.lt;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
if ("between" in filter) {
|
|
43
|
+
const [min, max] = filter.between;
|
|
44
|
+
return value >= min && value <= max;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
return false;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Checks if a value matches a PropertyFilter
|
|
52
|
+
* @param filter - Property filter to match against
|
|
53
|
+
* @param value - Value to check
|
|
54
|
+
* @returns true if value matches the filter
|
|
55
|
+
*/
|
|
56
|
+
export function matchesPropertyFilter(
|
|
57
|
+
filter: PropertyFilter,
|
|
58
|
+
value: any,
|
|
59
|
+
): boolean {
|
|
60
|
+
// Direct value comparison (exact match)
|
|
61
|
+
if (typeof filter !== "object" || filter === null) {
|
|
62
|
+
// Handle RegExp
|
|
63
|
+
if (filter instanceof RegExp && typeof value === "string") {
|
|
64
|
+
return filter.test(value);
|
|
65
|
+
}
|
|
66
|
+
// Handle array of values (OR logic)
|
|
67
|
+
if (Array.isArray(filter)) {
|
|
68
|
+
return filter.includes(value);
|
|
69
|
+
}
|
|
70
|
+
// Exact match
|
|
71
|
+
return value === filter;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// Object-based filters
|
|
75
|
+
if ("eq" in filter) {
|
|
76
|
+
return value === filter.eq;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// Numeric filters
|
|
80
|
+
if (typeof value === "number") {
|
|
81
|
+
if ("gte" in filter) {
|
|
82
|
+
return value >= (filter as any).gte;
|
|
83
|
+
}
|
|
84
|
+
if ("lte" in filter) {
|
|
85
|
+
return value <= (filter as any).lte;
|
|
86
|
+
}
|
|
87
|
+
if ("gt" in filter) {
|
|
88
|
+
return value > (filter as any).gt;
|
|
89
|
+
}
|
|
90
|
+
if ("lt" in filter) {
|
|
91
|
+
return value < (filter as any).lt;
|
|
92
|
+
}
|
|
93
|
+
if ("between" in filter) {
|
|
94
|
+
const [min, max] = (filter as any).between;
|
|
95
|
+
return value >= min && value <= max;
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
return false;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* Checks if a card matches a CardFilter
|
|
104
|
+
* @template TGameState - The game state type
|
|
105
|
+
* @param card - Card instance to check
|
|
106
|
+
* @param filter - Filter to match against
|
|
107
|
+
* @param state - Game state for computed properties
|
|
108
|
+
* @param registry - Card definition registry
|
|
109
|
+
* @returns true if card matches the filter
|
|
110
|
+
*/
|
|
111
|
+
export function matchesFilter<
|
|
112
|
+
TGameState extends { cards: Record<string, CardInstance<any>> } = {
|
|
113
|
+
cards: Record<string, CardInstance<unknown>>;
|
|
114
|
+
},
|
|
115
|
+
>(
|
|
116
|
+
card: TGameState["cards"][string],
|
|
117
|
+
filter: CardFilter<TGameState>,
|
|
118
|
+
state: TGameState,
|
|
119
|
+
registry: CardRegistry<CardDefinition>,
|
|
120
|
+
): boolean {
|
|
121
|
+
const definition = registry.getCard(card.definitionId);
|
|
122
|
+
|
|
123
|
+
// Zone filtering
|
|
124
|
+
if (filter.zone !== undefined) {
|
|
125
|
+
if (Array.isArray(filter.zone)) {
|
|
126
|
+
if (!filter.zone.includes(card.zone)) {
|
|
127
|
+
return false;
|
|
128
|
+
}
|
|
129
|
+
} else if (card.zone !== filter.zone) {
|
|
130
|
+
return false;
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
// Owner filtering
|
|
135
|
+
if (filter.owner !== undefined) {
|
|
136
|
+
if (Array.isArray(filter.owner)) {
|
|
137
|
+
if (!filter.owner.includes(card.owner)) {
|
|
138
|
+
return false;
|
|
139
|
+
}
|
|
140
|
+
} else if (card.owner !== filter.owner) {
|
|
141
|
+
return false;
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
// Controller filtering
|
|
146
|
+
if (filter.controller !== undefined) {
|
|
147
|
+
if (Array.isArray(filter.controller)) {
|
|
148
|
+
if (!filter.controller.includes(card.controller)) {
|
|
149
|
+
return false;
|
|
150
|
+
}
|
|
151
|
+
} else if (card.controller !== filter.controller) {
|
|
152
|
+
return false;
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
// Type filtering
|
|
157
|
+
if (filter.type !== undefined && definition) {
|
|
158
|
+
if (Array.isArray(filter.type)) {
|
|
159
|
+
if (!filter.type.includes(definition.type)) {
|
|
160
|
+
return false;
|
|
161
|
+
}
|
|
162
|
+
} else if (definition.type !== filter.type) {
|
|
163
|
+
return false;
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
// Name filtering
|
|
168
|
+
if (filter.name !== undefined && definition) {
|
|
169
|
+
if (typeof filter.name === "string") {
|
|
170
|
+
if (definition.name !== filter.name) {
|
|
171
|
+
return false;
|
|
172
|
+
}
|
|
173
|
+
} else if (filter.name instanceof RegExp) {
|
|
174
|
+
if (!filter.name.test(definition.name)) {
|
|
175
|
+
return false;
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
// Generic property filtering - works for ANY card property
|
|
181
|
+
if (filter.properties !== undefined && definition) {
|
|
182
|
+
for (const [propName, propFilter] of Object.entries(filter.properties)) {
|
|
183
|
+
const propValue = (definition as any)[propName];
|
|
184
|
+
|
|
185
|
+
// Property doesn't exist on card
|
|
186
|
+
if (propValue === undefined) {
|
|
187
|
+
return false;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
if (!matchesPropertyFilter(propFilter, propValue)) {
|
|
191
|
+
return false;
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
// State filtering (from CardInstanceBase)
|
|
197
|
+
if (filter.tapped !== undefined) {
|
|
198
|
+
if (card.tapped !== filter.tapped) {
|
|
199
|
+
return false;
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
if (filter.revealed !== undefined) {
|
|
204
|
+
if (card.revealed !== filter.revealed) {
|
|
205
|
+
return false;
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
if (filter.flipped !== undefined) {
|
|
210
|
+
if (card.flipped !== filter.flipped) {
|
|
211
|
+
return false;
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
if (filter.phased !== undefined) {
|
|
216
|
+
if (card.phased !== filter.phased) {
|
|
217
|
+
return false;
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
// Composite filters - AND
|
|
222
|
+
if (filter.and !== undefined) {
|
|
223
|
+
for (const subFilter of filter.and) {
|
|
224
|
+
if (!matchesFilter(card, subFilter, state, registry)) {
|
|
225
|
+
return false;
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
// Composite filters - OR
|
|
231
|
+
if (filter.or !== undefined) {
|
|
232
|
+
let anyMatch = false;
|
|
233
|
+
for (const subFilter of filter.or) {
|
|
234
|
+
if (matchesFilter(card, subFilter, state, registry)) {
|
|
235
|
+
anyMatch = true;
|
|
236
|
+
break;
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
if (!anyMatch) {
|
|
240
|
+
return false;
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
// Composite filters - NOT
|
|
245
|
+
if (filter.not !== undefined) {
|
|
246
|
+
if (matchesFilter(card, filter.not, state, registry)) {
|
|
247
|
+
return false;
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
// Custom predicate
|
|
252
|
+
if (filter.where !== undefined) {
|
|
253
|
+
if (!filter.where(card, state)) {
|
|
254
|
+
return false;
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
return true;
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
/**
|
|
262
|
+
* Selects all cards from state that match the filter
|
|
263
|
+
* @template TGameState - The game state type
|
|
264
|
+
* @param state - Game state containing cards
|
|
265
|
+
* @param filter - Filter to apply
|
|
266
|
+
* @param registry - Card definition registry
|
|
267
|
+
* @returns Array of matching cards
|
|
268
|
+
*/
|
|
269
|
+
export function selectCards<
|
|
270
|
+
TGameState extends { cards: Record<string, CardInstance<any>> },
|
|
271
|
+
>(
|
|
272
|
+
state: TGameState,
|
|
273
|
+
filter: CardFilter<TGameState>,
|
|
274
|
+
registry: CardRegistry<CardDefinition>,
|
|
275
|
+
): TGameState["cards"][string][] {
|
|
276
|
+
const cards = Object.values(state.cards);
|
|
277
|
+
return cards.filter((card) => matchesFilter(card, filter, state, registry));
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
/**
|
|
281
|
+
* Counts cards that match the filter
|
|
282
|
+
* @template TGameState - The game state type
|
|
283
|
+
* @param state - Game state containing cards
|
|
284
|
+
* @param filter - Filter to apply
|
|
285
|
+
* @param registry - Card definition registry
|
|
286
|
+
* @returns Number of matching cards
|
|
287
|
+
*/
|
|
288
|
+
export function countCards<
|
|
289
|
+
TGameState extends { cards: Record<string, CardInstance<any>> },
|
|
290
|
+
>(
|
|
291
|
+
state: TGameState,
|
|
292
|
+
filter: CardFilter<TGameState>,
|
|
293
|
+
registry: CardRegistry<CardDefinition>,
|
|
294
|
+
): number {
|
|
295
|
+
return selectCards(state, filter, registry).length;
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
/**
|
|
299
|
+
* Checks if any card matches the filter
|
|
300
|
+
* @template TGameState - The game state type
|
|
301
|
+
* @param state - Game state containing cards
|
|
302
|
+
* @param filter - Filter to apply
|
|
303
|
+
* @param registry - Card definition registry
|
|
304
|
+
* @returns true if at least one card matches
|
|
305
|
+
*/
|
|
306
|
+
export function anyCard<
|
|
307
|
+
TGameState extends { cards: Record<string, CardInstance<any>> },
|
|
308
|
+
>(
|
|
309
|
+
state: TGameState,
|
|
310
|
+
filter: CardFilter<TGameState>,
|
|
311
|
+
registry: CardRegistry<CardDefinition>,
|
|
312
|
+
): boolean {
|
|
313
|
+
const cards = Object.values(state.cards);
|
|
314
|
+
return cards.some((card) => matchesFilter(card, filter, state, registry));
|
|
315
|
+
}
|