@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,230 @@
1
+ import { describe, expect, it } from "bun:test";
2
+ import { createPlayerId, createZoneId } from "../types";
3
+ import type { CardFilter, NumberFilter } from "./card-filter";
4
+
5
+ describe("Card Filter Types", () => {
6
+ describe("CardFilter", () => {
7
+ it("should support zone filtering with single zone", () => {
8
+ const filter: CardFilter = {
9
+ zone: createZoneId("play"),
10
+ };
11
+
12
+ expect(filter.zone).toBe(createZoneId("play"));
13
+ });
14
+
15
+ it("should support zone filtering with multiple zones", () => {
16
+ const filter: CardFilter = {
17
+ zone: [createZoneId("hand"), createZoneId("graveyard")],
18
+ };
19
+
20
+ expect(Array.isArray(filter.zone)).toBe(true);
21
+ expect(filter.zone).toHaveLength(2);
22
+ });
23
+
24
+ it("should support owner filtering with single player", () => {
25
+ const playerId = createPlayerId("player-1");
26
+ const filter: CardFilter = {
27
+ owner: playerId,
28
+ };
29
+
30
+ expect(filter.owner).toBe(playerId);
31
+ });
32
+
33
+ it("should support owner filtering with multiple players", () => {
34
+ const filter: CardFilter = {
35
+ owner: [createPlayerId("player-1"), createPlayerId("player-2")],
36
+ };
37
+
38
+ expect(Array.isArray(filter.owner)).toBe(true);
39
+ expect(filter.owner).toHaveLength(2);
40
+ });
41
+
42
+ it("should support controller filtering", () => {
43
+ const playerId = createPlayerId("player-1");
44
+ const filter: CardFilter = {
45
+ controller: playerId,
46
+ };
47
+
48
+ expect(filter.controller).toBe(playerId);
49
+ });
50
+
51
+ it("should support type filtering with single type", () => {
52
+ const filter: CardFilter = {
53
+ type: "creature",
54
+ };
55
+
56
+ expect(filter.type).toBe("creature");
57
+ });
58
+
59
+ it("should support type filtering with multiple types", () => {
60
+ const filter: CardFilter = {
61
+ type: ["creature", "artifact"],
62
+ };
63
+
64
+ expect(Array.isArray(filter.type)).toBe(true);
65
+ expect(filter.type).toHaveLength(2);
66
+ });
67
+
68
+ it("should support name filtering with string", () => {
69
+ const filter: CardFilter = {
70
+ name: "Lightning Bolt",
71
+ };
72
+
73
+ expect(filter.name).toBe("Lightning Bolt");
74
+ });
75
+
76
+ it("should support name filtering with RegExp", () => {
77
+ const filter: CardFilter = {
78
+ name: /Lightning.*/,
79
+ };
80
+
81
+ expect(filter.name).toBeInstanceOf(RegExp);
82
+ });
83
+
84
+ it("should support generic property filtering", () => {
85
+ const filter: CardFilter = {
86
+ properties: {
87
+ baseCost: { gte: 3 },
88
+ basePower: { gt: 5 },
89
+ },
90
+ };
91
+
92
+ expect(filter.properties).toBeDefined();
93
+ expect(filter.properties?.baseCost).toEqual({ gte: 3 });
94
+ expect(filter.properties?.basePower).toEqual({ gt: 5 });
95
+ });
96
+
97
+ it("should support tapped state filtering", () => {
98
+ const filter: CardFilter = {
99
+ tapped: true,
100
+ };
101
+
102
+ expect(filter.tapped).toBe(true);
103
+ });
104
+
105
+ it("should support revealed state filtering", () => {
106
+ const filter: CardFilter = {
107
+ revealed: true,
108
+ };
109
+
110
+ expect(filter.revealed).toBe(true);
111
+ });
112
+
113
+ it("should support flipped state filtering", () => {
114
+ const filter: CardFilter = {
115
+ flipped: true,
116
+ };
117
+
118
+ expect(filter.flipped).toBe(true);
119
+ });
120
+
121
+ it("should support phased state filtering", () => {
122
+ const filter: CardFilter = {
123
+ phased: false,
124
+ };
125
+
126
+ expect(filter.phased).toBe(false);
127
+ });
128
+
129
+ it("should support composite AND filters", () => {
130
+ const filter: CardFilter = {
131
+ and: [{ type: "creature" }, { tapped: false }],
132
+ };
133
+
134
+ expect(Array.isArray(filter.and)).toBe(true);
135
+ expect(filter.and).toHaveLength(2);
136
+ });
137
+
138
+ it("should support composite OR filters", () => {
139
+ const filter: CardFilter = {
140
+ or: [
141
+ { zone: createZoneId("hand") },
142
+ { zone: createZoneId("graveyard") },
143
+ ],
144
+ };
145
+
146
+ expect(Array.isArray(filter.or)).toBe(true);
147
+ expect(filter.or).toHaveLength(2);
148
+ });
149
+
150
+ it("should support NOT filters", () => {
151
+ const filter: CardFilter = {
152
+ not: { type: "land" },
153
+ };
154
+
155
+ expect(filter.not).toBeDefined();
156
+ expect(filter.not?.type).toBe("land");
157
+ });
158
+
159
+ it("should support custom where predicate", () => {
160
+ const filter: CardFilter = {
161
+ where: (card, _state) => card.tapped === true,
162
+ };
163
+
164
+ expect(typeof filter.where).toBe("function");
165
+ });
166
+
167
+ it("should support combining multiple filter properties", () => {
168
+ const filter: CardFilter = {
169
+ zone: createZoneId("play"),
170
+ type: "creature",
171
+ controller: createPlayerId("player-1"),
172
+ tapped: false,
173
+ properties: {
174
+ basePower: { gte: 3 },
175
+ },
176
+ };
177
+
178
+ expect(filter.zone).toBeDefined();
179
+ expect(filter.type).toBe("creature");
180
+ expect(filter.controller).toBeDefined();
181
+ expect(filter.tapped).toBe(false);
182
+ expect(filter.properties?.basePower).toEqual({ gte: 3 });
183
+ });
184
+ });
185
+
186
+ describe("NumberFilter", () => {
187
+ it("should support exact number match", () => {
188
+ const filter: NumberFilter = 5;
189
+
190
+ expect(filter).toBe(5);
191
+ expect(typeof filter).toBe("number");
192
+ });
193
+
194
+ it("should support eq (equal) filter", () => {
195
+ const filter: NumberFilter = { eq: 5 };
196
+
197
+ expect(filter).toEqual({ eq: 5 });
198
+ });
199
+
200
+ it("should support gte (greater than or equal) filter", () => {
201
+ const filter: NumberFilter = { gte: 3 };
202
+
203
+ expect(filter).toEqual({ gte: 3 });
204
+ });
205
+
206
+ it("should support lte (less than or equal) filter", () => {
207
+ const filter: NumberFilter = { lte: 7 };
208
+
209
+ expect(filter).toEqual({ lte: 7 });
210
+ });
211
+
212
+ it("should support gt (greater than) filter", () => {
213
+ const filter: NumberFilter = { gt: 10 };
214
+
215
+ expect(filter).toEqual({ gt: 10 });
216
+ });
217
+
218
+ it("should support lt (less than) filter", () => {
219
+ const filter: NumberFilter = { lt: 2 };
220
+
221
+ expect(filter).toEqual({ lt: 2 });
222
+ });
223
+
224
+ it("should support between (range) filter", () => {
225
+ const filter: NumberFilter = { between: [2, 5] };
226
+
227
+ expect(filter).toEqual({ between: [2, 5] });
228
+ });
229
+ });
230
+ });
@@ -0,0 +1,91 @@
1
+ import type { CardInstance } from "../cards/card-instance";
2
+ import type { PlayerId, ZoneId } from "../types";
3
+
4
+ /**
5
+ * Number filter for numerical comparisons
6
+ * Can be a direct number (exact match) or an object with comparison operators
7
+ */
8
+ export type NumberFilter =
9
+ | number // exact match
10
+ | { eq: number } // equal
11
+ | { gte: number } // greater than or equal
12
+ | { lte: number } // less than or equal
13
+ | { gt: number } // greater than
14
+ | { lt: number } // less than
15
+ | { between: [number, number] }; // range [min, max]
16
+
17
+ /**
18
+ * String filter for text comparisons
19
+ */
20
+ export type StringFilter = string | RegExp | string[];
21
+
22
+ /**
23
+ * Generic property filter that can match any value type
24
+ */
25
+ export type PropertyFilter<T = any> =
26
+ | T // exact match
27
+ | { eq: T } // equal
28
+ | (T extends number ? NumberFilter : never) // numeric operations only for numbers
29
+ | (T extends string ? StringFilter : never); // string operations only for strings
30
+
31
+ /**
32
+ * Core card filter - only properties ALL cards have
33
+ * All filter properties are optional and combined with AND logic
34
+ * @template TGameState - The game state type
35
+ */
36
+ export type CardFilter<TGameState = unknown> = {
37
+ // Universal card properties (all TCGs have these)
38
+ /** Filter by zone(s) where the card is located */
39
+ zone?: ZoneId | ZoneId[];
40
+
41
+ /** Filter by card owner(s) */
42
+ owner?: PlayerId | PlayerId[];
43
+
44
+ /** Filter by card controller(s) */
45
+ controller?: PlayerId | PlayerId[];
46
+
47
+ /** Filter by card type(s) - all games have card types */
48
+ type?: string | string[];
49
+
50
+ /** Filter by card name (exact string or regex pattern) */
51
+ name?: string | RegExp;
52
+
53
+ // State filtering (from CardInstanceBase)
54
+ /** Filter by tapped/exhausted state */
55
+ tapped?: boolean;
56
+
57
+ /** Filter by revealed state */
58
+ revealed?: boolean;
59
+
60
+ /** Filter by flipped/face-down state */
61
+ flipped?: boolean;
62
+
63
+ /** Filter by phased state */
64
+ phased?: boolean;
65
+
66
+ // Extensible property filtering
67
+ /**
68
+ * Filter by properties from the card definition
69
+ * Allows filtering on any game-specific property like:
70
+ * - MTG: { basePower: 5, baseToughness: { gte: 3 } }
71
+ * - Pokemon: { hp: { gte: 100 }, weakness: "Fire" }
72
+ * - Lorcana: { inkCost: { lte: 3 }, strength: 2 }
73
+ */
74
+ properties?: Record<string, PropertyFilter>;
75
+
76
+ // Composite filters
77
+ /** All filters must match (AND logic) */
78
+ and?: CardFilter<TGameState>[];
79
+
80
+ /** At least one filter must match (OR logic) */
81
+ or?: CardFilter<TGameState>[];
82
+
83
+ /** Filter must NOT match (NOT logic) */
84
+ not?: CardFilter<TGameState>;
85
+
86
+ // Custom predicates
87
+ /** Custom filter function for complex logic */
88
+ where?: TGameState extends { cards: Record<string, infer TCard> }
89
+ ? (card: TCard, state: TGameState) => boolean
90
+ : (card: CardInstance<unknown>, state: TGameState) => boolean;
91
+ };