@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,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
+ }