@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
package/README.md ADDED
@@ -0,0 +1,882 @@
1
+ # @drmxrcy/tcg-core
2
+
3
+ > Production-ready game engine for trading card games and turn-based strategy games
4
+
5
+ **@drmxrcy/tcg-core** is a declarative, type-safe game engine built with **Immer** for immutable state management and **delta synchronization** for multiplayer games. It provides a complete framework for building complex card games with deterministic gameplay, network synchronization, and time-travel debugging.
6
+
7
+ ## Features
8
+
9
+ - **🎮 Declarative Game Definition** - Define your game rules declaratively with TypeScript
10
+ - **🔄 Immutable State Management** - Powered by Immer for structural sharing and performance
11
+ - **🌐 Network Synchronization** - Delta patches enable server-authoritative multiplayer
12
+ - **🎲 Deterministic RNG** - Seeded random number generation for replay and testing
13
+ - **⏮️ Time-Travel Debugging** - Complete undo/redo with history replay
14
+ - **👁️ Player Views** - Automatic information hiding for multiplayer games
15
+ - **📊 Flow Orchestration** - Optional turn/phase/segment management
16
+ - **🎯 Type Safety** - Full TypeScript support with branded types
17
+ - **🧪 Test-Driven** - 95%+ test coverage with real engine instances
18
+ - **🏗️ Zone Management** - Comprehensive zone operations for card locations
19
+ - **🔍 Testing Utilities** - Complete TDD toolkit with assertions and factories
20
+ - **🛠️ Card Tooling** - Reusable infrastructure for parsers, generators, and validators
21
+ - **✅ Validation System** - Type guards and runtime validators for data integrity
22
+ - **📝 Logging System** - Structured logging with configurable verbosity levels
23
+ - **📡 Telemetry System** - Event-based telemetry for analytics and debugging
24
+
25
+ ## Quick Start
26
+
27
+ ### Installation
28
+
29
+ ```bash
30
+ bun add @drmxrcy/tcg-core
31
+ # or
32
+ npm install @drmxrcy/tcg-core
33
+ ```
34
+
35
+ ### Create Your First Game
36
+
37
+ ```typescript
38
+ import { RuleEngine, createPlayerId } from "@drmxrcy/tcg-core";
39
+ import type { GameDefinition } from "@drmxrcy/tcg-core";
40
+
41
+ // 1. Define your game state
42
+ type CoinFlipState = {
43
+ players: Array<{
44
+ id: string;
45
+ name: string;
46
+ score: number;
47
+ }>;
48
+ currentPlayerIndex: number;
49
+ turnNumber: number;
50
+ phase: "flip" | "ended";
51
+ };
52
+
53
+ // 2. Define your moves
54
+ type CoinFlipMoves = {
55
+ flipCoin: Record<string, never>;
56
+ endTurn: Record<string, never>;
57
+ };
58
+
59
+ // 3. Create game definition
60
+ const gameDefinition: GameDefinition<CoinFlipState, CoinFlipMoves> = {
61
+ name: "Coin Flip",
62
+ setup: (players) => ({
63
+ players: players.map((p) => ({
64
+ id: p.id,
65
+ name: p.name || "Player",
66
+ score: 0,
67
+ })),
68
+ currentPlayerIndex: 0,
69
+ turnNumber: 1,
70
+ phase: "flip",
71
+ }),
72
+ moves: {
73
+ flipCoin: {
74
+ reducer: (draft, context) => {
75
+ // Use deterministic RNG
76
+ const isHeads = context.rng?.flipCoin() ?? Math.random() >= 0.5;
77
+
78
+ if (isHeads) {
79
+ const player = draft.players[draft.currentPlayerIndex];
80
+ if (player) {
81
+ player.score += 1;
82
+ }
83
+ }
84
+ },
85
+ },
86
+ endTurn: {
87
+ reducer: (draft) => {
88
+ draft.currentPlayerIndex =
89
+ (draft.currentPlayerIndex + 1) % draft.players.length;
90
+ draft.turnNumber += 1;
91
+ },
92
+ },
93
+ },
94
+ endIf: (state) => {
95
+ const winner = state.players.find((p) => p.score >= 3);
96
+ return winner
97
+ ? { winner: winner.id, reason: "Reached 3 points" }
98
+ : undefined;
99
+ },
100
+ };
101
+
102
+ // 4. Create engine and play
103
+ const players = [
104
+ { id: createPlayerId("p1"), name: "Alice" },
105
+ { id: createPlayerId("p2"), name: "Bob" },
106
+ ];
107
+
108
+ const engine = new RuleEngine(gameDefinition, players, {
109
+ seed: "game-123", // Deterministic seed
110
+ });
111
+
112
+ // Execute moves
113
+ const result = engine.executeMove("flipCoin", {
114
+ playerId: createPlayerId("p1"),
115
+ });
116
+
117
+ if (result.success) {
118
+ console.log("Move successful!");
119
+
120
+ // Check if game ended
121
+ const gameEnd = engine.checkGameEnd();
122
+ if (gameEnd) {
123
+ console.log(`Winner: ${gameEnd.winner}`);
124
+ }
125
+ }
126
+ ```
127
+
128
+ ## Core Concepts
129
+
130
+ ### 1. GameDefinition
131
+
132
+ The heart of your game. Defines setup, moves, flow, and end conditions.
133
+
134
+ ```typescript
135
+ const gameDefinition: GameDefinition<TState, TMoves> = {
136
+ name: "My Game",
137
+
138
+ // Setup: Create initial state
139
+ setup: (players) => ({
140
+ players: players.map(p => ({ id: p.id, hand: [], deck: [] })),
141
+ turn: 1,
142
+ }),
143
+
144
+ // Moves: Define all possible actions
145
+ moves: {
146
+ drawCard: {
147
+ condition: (state, context) => {
148
+ // Optional: check if move is legal
149
+ return state.currentPlayer === context.playerId;
150
+ },
151
+ reducer: (draft, context) => {
152
+ // Modify state using Immer draft
153
+ const player = draft.players.find(p => p.id === context.playerId);
154
+ const card = draft.deck.pop();
155
+ if (card) player.hand.push(card);
156
+ },
157
+ },
158
+ },
159
+
160
+ // Optional: Game end condition
161
+ endIf: (state) => {
162
+ const winner = state.players.find(p => p.score >= 10);
163
+ return winner ? { winner: winner.id, reason: "Score limit" } : undefined;
164
+ },
165
+
166
+ // Optional: Filter state for each player
167
+ playerView: (state, playerId) => ({
168
+ ...state,
169
+ players: state.players.map(p => ({
170
+ ...p,
171
+ hand: p.id === playerId ? p.hand : [], // Hide opponent hands
172
+ })),
173
+ }),
174
+ };
175
+ ```
176
+
177
+ ### 2. RuleEngine
178
+
179
+ The game engine that executes moves, manages state, and provides game services.
180
+
181
+ ```typescript
182
+ // Create engine
183
+ const engine = new RuleEngine(gameDefinition, players, {
184
+ seed: "deterministic-seed", // Optional: for deterministic gameplay
185
+ });
186
+
187
+ // Execute moves
188
+ const result = engine.executeMove("playCard", {
189
+ playerId: createPlayerId("p1"),
190
+ data: { cardId: "card-123" },
191
+ });
192
+
193
+ // Get current state
194
+ const state = engine.getState();
195
+
196
+ // Get player-specific view
197
+ const playerView = engine.getPlayerView(createPlayerId("p1"));
198
+
199
+ // Time travel
200
+ engine.undo(); // Undo last move
201
+ engine.redo(); // Redo undone move
202
+
203
+ // Replay from history
204
+ const finalState = engine.replay();
205
+
206
+ // Network sync
207
+ const patches = engine.getPatches(); // Get all patches
208
+ engine.applyPatches(patches); // Apply patches from server
209
+
210
+ // Check game end
211
+ const gameEnd = engine.checkGameEnd();
212
+ ```
213
+
214
+ ### 3. Move System
215
+
216
+ Moves are the only way to modify game state. Each move has:
217
+
218
+ - **Condition** (optional): Determines if move is legal
219
+ - **Reducer**: Updates state using Immer draft
220
+ - **Context**: Provides playerId, data, targets, and RNG
221
+
222
+ ```typescript
223
+ const moves: GameMoveDefinitions<GameState, GameMoves> = {
224
+ playCard: {
225
+ // Optional condition
226
+ condition: (state, context) => {
227
+ const player = state.players.find(p => p.id === context.playerId);
228
+ return player?.hand.includes(context.data?.cardId as string) ?? false;
229
+ },
230
+
231
+ // Required reducer
232
+ reducer: (draft, context) => {
233
+ const player = draft.players.find(p => p.id === context.playerId);
234
+ const cardId = context.data?.cardId as string;
235
+
236
+ // Remove from hand
237
+ const index = player.hand.indexOf(cardId);
238
+ player.hand.splice(index, 1);
239
+
240
+ // Add to field
241
+ draft.field.push(cardId);
242
+
243
+ // Use RNG for random effects
244
+ if (context.rng?.flipCoin()) {
245
+ player.life += 1;
246
+ }
247
+ },
248
+ },
249
+ };
250
+ ```
251
+
252
+ ### 4. Flow Management (Optional)
253
+
254
+ For games with structured turns/phases/segments:
255
+
256
+ ```typescript
257
+ const flow: FlowDefinition<GameState> = {
258
+ turn: {
259
+ onBegin: (context) => {
260
+ context.state.phase = "draw";
261
+ },
262
+ phases: {
263
+ draw: { order: 0, next: "main" },
264
+ main: { order: 1, next: "end" },
265
+ end: {
266
+ order: 2,
267
+ next: undefined, // End of turn
268
+ onEnd: (context) => {
269
+ // Next player's turn
270
+ context.state.currentPlayer =
271
+ (context.state.currentPlayer + 1) % context.state.players.length;
272
+ },
273
+ },
274
+ },
275
+ },
276
+ };
277
+ ```
278
+
279
+ ## Multiplayer Pattern
280
+
281
+ **@drmxrcy/tcg-core** is designed for server-authoritative multiplayer:
282
+
283
+ ```typescript
284
+ // SERVER
285
+ const serverEngine = new RuleEngine(gameDefinition, players, {
286
+ seed: "server-seed",
287
+ });
288
+
289
+ // Client sends move
290
+ socket.on("move", (moveId, context) => {
291
+ const result = serverEngine.executeMove(moveId, context);
292
+
293
+ if (result.success) {
294
+ // Broadcast patches to all clients
295
+ io.emit("patches", result.patches);
296
+ } else {
297
+ // Send error to client
298
+ socket.emit("error", result.error);
299
+ }
300
+ });
301
+
302
+ // CLIENTS
303
+ const clientEngine = new RuleEngine(gameDefinition, players, {
304
+ seed: "client-seed",
305
+ });
306
+
307
+ // Apply patches from server
308
+ socket.on("patches", (patches) => {
309
+ clientEngine.applyPatches(patches);
310
+ updateUI(clientEngine.getState());
311
+ });
312
+ ```
313
+
314
+ ## Deterministic Replay
315
+
316
+ Games are fully deterministic when using seeded RNG:
317
+
318
+ ```typescript
319
+ // Game 1
320
+ const engine1 = new RuleEngine(gameDefinition, players, { seed: "test" });
321
+ engine1.executeMove("drawCard", { playerId: "p1" });
322
+ const state1 = engine1.getState();
323
+
324
+ // Game 2 - Same seed = Same outcome
325
+ const engine2 = new RuleEngine(gameDefinition, players, { seed: "test" });
326
+ engine2.executeMove("drawCard", { playerId: "p1" });
327
+ const state2 = engine2.getState();
328
+
329
+ // States are identical
330
+ console.log(state1 === state2); // true
331
+
332
+ // Replay from history
333
+ const replayedState = engine1.replay();
334
+ console.log(replayedState === state1); // true
335
+ ```
336
+
337
+ ## Advanced Features
338
+
339
+ ### Branded Types
340
+
341
+ Safe type wrappers prevent ID mixups:
342
+
343
+ ```typescript
344
+ import { createPlayerId, createCardId, createZoneId } from "@drmxrcy/tcg-core";
345
+
346
+ const playerId = createPlayerId("p1"); // PlayerId type
347
+ const cardId = createCardId("c1"); // CardId type
348
+ const zoneId = createZoneId("hand"); // ZoneId type
349
+
350
+ // Type error: can't pass CardId where PlayerId expected ✅
351
+ engine.executeMove("draw", { playerId: cardId }); // ❌ Type error
352
+ ```
353
+
354
+ ### Zone Management
355
+
356
+ Built-in zone system for card locations:
357
+
358
+ ```typescript
359
+ import { createZone, moveCard } from "@drmxrcy/tcg-core";
360
+
361
+ const deck = createZone({
362
+ id: createZoneId("deck"),
363
+ visibility: "secret", // Hidden from all players
364
+ ordered: true,
365
+ });
366
+
367
+ const hand = createZone({
368
+ id: createZoneId("hand"),
369
+ visibility: "owner", // Visible to owner only
370
+ maxSize: 7,
371
+ });
372
+
373
+ // Move cards between zones
374
+ moveCard(state, cardId, sourcezone, destZone);
375
+ ```
376
+
377
+ ### Card Filtering DSL
378
+
379
+ Query cards with a fluent API:
380
+
381
+ ```typescript
382
+ import { selectCards } from "@drmxrcy/tcg-core";
383
+
384
+ // Find all creatures with power >= 3
385
+ const creatures = selectCards(state, {
386
+ and: [
387
+ { type: "creature" },
388
+ { power: { gte: 3 } },
389
+ { zone: createZoneId("field") },
390
+ ],
391
+ });
392
+
393
+ // Fluent builder
394
+ const cards = new CardQuery(state)
395
+ .inZone(createZoneId("hand"))
396
+ .withType("spell")
397
+ .withCost({ lte: 3 })
398
+ .build();
399
+ ```
400
+
401
+ ### Targeting System
402
+
403
+ Define complex targeting requirements:
404
+
405
+ ```typescript
406
+ const targetDefinition: TargetDefinition = {
407
+ min: 1,
408
+ max: 1,
409
+ filter: {
410
+ type: "creature",
411
+ zone: createZoneId("field"),
412
+ },
413
+ restrictions: ["not-self", "not-controller"],
414
+ };
415
+
416
+ // Validate targets
417
+ const isValid = validateTargetSelection(
418
+ state,
419
+ targetDefinition,
420
+ selectedTargets,
421
+ );
422
+ ```
423
+
424
+ ## Logging & Telemetry
425
+
426
+ Production-grade logging and telemetry for debugging, transparency, and analytics.
427
+
428
+ ### Logging System
429
+
430
+ Structured logging with zero-overhead SILENT mode:
431
+
432
+ ```typescript
433
+ import { RuleEngine, LogLevel } from '@drmxrcy/tcg-core';
434
+
435
+ const engine = new RuleEngine(gameDefinition, players, {
436
+ seed: 'game-123',
437
+ logger: {
438
+ level: 'DEVELOPER', // SILENT, NORMAL_PLAYER, ADVANCED_PLAYER, DEVELOPER
439
+ pretty: true // Human-readable output
440
+ }
441
+ });
442
+
443
+ // Access logger for custom logging
444
+ const logger = engine.getLogger();
445
+ logger.info('Custom event', { eventId: 'custom-123', data: { value: 42 } });
446
+
447
+ // Create child loggers for subsystems
448
+ const aiLogger = logger.child('ai');
449
+ aiLogger.debug('Evaluating move options', { count: 12 });
450
+ ```
451
+
452
+ **Verbosity Levels:**
453
+ - `SILENT` (0): No logging - zero overhead
454
+ - `NORMAL_PLAYER` (INFO): Basic game events
455
+ - `ADVANCED_PLAYER` (DEBUG): Detailed game mechanics
456
+ - `DEVELOPER` (TRACE): Full internal details
457
+
458
+ **Learn more:** [Logging Guide](./docs/LOGGING.md)
459
+
460
+ ### Telemetry System
461
+
462
+ Event-based telemetry for tracking player actions and engine events:
463
+
464
+ ```typescript
465
+ const engine = new RuleEngine(gameDefinition, players, {
466
+ seed: 'game-123',
467
+ telemetry: {
468
+ enabled: true,
469
+ hooks: {
470
+ onPlayerAction: (event) => {
471
+ analytics.track('game.move', {
472
+ moveId: event.moveId,
473
+ playerId: event.playerId,
474
+ duration: event.duration
475
+ });
476
+ },
477
+ onStateChange: (event) => {
478
+ database.savePatches(event.patches);
479
+ },
480
+ onEngineError: (event) => {
481
+ errorReporter.captureException(event.error, event.context);
482
+ }
483
+ }
484
+ }
485
+ });
486
+
487
+ // EventEmitter style
488
+ const telemetry = engine.getTelemetry();
489
+ telemetry.on('playerAction', (event) => {
490
+ console.log(`Move: ${event.moveId}, Result: ${event.result}`);
491
+ });
492
+ ```
493
+
494
+ **Event Types:**
495
+ - `PlayerActionEvent`: Move execution tracking
496
+ - `StateChangeEvent`: State mutations with patches
497
+ - `RuleEvaluationEvent`: Condition checks
498
+ - `FlowTransitionEvent`: Phase/turn/segment changes
499
+ - `EngineErrorEvent`: Error tracking
500
+ - `PerformanceEvent`: Performance metrics
501
+
502
+ **Learn more:** [Telemetry Guide](./docs/TELEMETRY.md)
503
+
504
+ ## Testing Utilities
505
+
506
+ Comprehensive testing utilities for TDD workflow:
507
+
508
+ ```typescript
509
+ import {
510
+ createTestEngine,
511
+ expectMoveSuccess,
512
+ expectStateProperty,
513
+ createTestCard,
514
+ withSeed,
515
+ } from '@drmxrcy/tcg-core/testing';
516
+
517
+ // Create test engine with deterministic seed
518
+ const engine = createTestEngine(gameDefinition, players, { seed: 'test' });
519
+
520
+ // Test move execution
521
+ expectMoveSuccess(engine, 'playCard', {
522
+ playerId: 'p1',
523
+ data: { cardId: 'card-123' }
524
+ });
525
+
526
+ // Verify state changes
527
+ expectStateProperty(engine, 'players[0].hand.length', 6);
528
+ expectStateProperty(engine, 'field.length', 1);
529
+
530
+ // Test with deterministic RNG
531
+ const shuffled = withSeed('test-seed', (rng) => {
532
+ return rng.shuffle([1, 2, 3, 4, 5]);
533
+ });
534
+
535
+ // Create test data
536
+ const card = createTestCard({ type: 'creature', basePower: 3 });
537
+ const deck = createTestDeck(['card1', 'card2', 'card3'], 'player1');
538
+ ```
539
+
540
+ **Learn more:** [Testing Utilities Guide](./docs/guides/testing-utilities.md)
541
+
542
+ ## Card Tooling
543
+
544
+ Build card management pipelines with reusable infrastructure:
545
+
546
+ ```typescript
547
+ import {
548
+ CardParser,
549
+ CardGenerator,
550
+ FileWriter,
551
+ formatTypeScript,
552
+ generateVariableName,
553
+ } from '@drmxrcy/tcg-core/tooling';
554
+
555
+ // Extend CardParser for game-specific parsing
556
+ class MyCardParser extends CardParser<string, MyCard> {
557
+ protected doParse(text: string): ParserResult<MyCard> {
558
+ // Parse logic here
559
+ return { success: true, data: card, warnings: [] };
560
+ }
561
+ }
562
+
563
+ // Extend CardGenerator for code generation
564
+ class MyCardGenerator extends CardGenerator<MyCard> {
565
+ protected generateContent(card: MyCard): string {
566
+ return `export const ${generateVariableName(card.name)} = ${JSON.stringify(card)};`;
567
+ }
568
+
569
+ protected generateFileName(card: MyCard): string {
570
+ return `${card.name.toLowerCase()}.ts`;
571
+ }
572
+ }
573
+
574
+ // Use file utilities
575
+ const writer = new FileWriter('./cards');
576
+ const formatted = await formatTypeScript(code);
577
+ await writer.write('card.ts', formatted);
578
+ ```
579
+
580
+ **Learn more:** [Card Tooling Guide](./docs/guides/card-tooling.md)
581
+
582
+ ## Validation Utilities
583
+
584
+ Type-safe runtime validation with type guards and validators:
585
+
586
+ ```typescript
587
+ import {
588
+ createTypeGuard,
589
+ isCardOfType,
590
+ ValidatorBuilder,
591
+ combineTypeGuards,
592
+ } from '@drmxrcy/tcg-core/validation';
593
+
594
+ // Type guards for filtering
595
+ const isCreature = isCardOfType('creature');
596
+ const creatures = cards.filter(isCreature);
597
+
598
+ // Complex filtering
599
+ const isRareLegendary = combineTypeGuards([
600
+ isCardOfType('creature'),
601
+ isCardWithField('rarity', 'rare'),
602
+ isCardWithField('legendary', true),
603
+ ]);
604
+
605
+ // Runtime validation
606
+ const validator = new ValidatorBuilder<CardData>()
607
+ .required('name', 'Name is required')
608
+ .type('cost', 'number', 'Cost must be a number')
609
+ .min('cost', 0, 'Cost must be non-negative')
610
+ .max('cost', 10, 'Cost cannot exceed 10')
611
+ .custom('power', (power) => power > 0, 'Power must be positive')
612
+ .build();
613
+
614
+ const result = validator.validate(cardData);
615
+ if (!result.success) {
616
+ console.error('Validation errors:', result.errors);
617
+ }
618
+ ```
619
+
620
+ **Learn more:** [Validation Guide](./docs/guides/validation.md)
621
+
622
+ ## Comprehensive Zone Operations
623
+
624
+ Complete zone management utilities:
625
+
626
+ ```typescript
627
+ import {
628
+ createZone,
629
+ addCard,
630
+ removeCard,
631
+ moveCard,
632
+ draw,
633
+ shuffle,
634
+ mill,
635
+ search,
636
+ peek,
637
+ findCardInZones,
638
+ createPlayerZones,
639
+ moveCardInState,
640
+ } from '@drmxrcy/tcg-core';
641
+
642
+ // Basic operations
643
+ let deck = createZone(config, [card1, card2, card3]);
644
+ deck = addCardToTop(deck, card4);
645
+ deck = shuffle(deck, 'game-seed-123');
646
+
647
+ // Draw cards
648
+ const { fromZone, toZone, drawnCards } = draw(deck, hand, 3);
649
+
650
+ // Search zones
651
+ const creatures = search(zone, (cardId) => {
652
+ const card = getCard(cardId);
653
+ return card.type === 'creature';
654
+ });
655
+
656
+ // State helpers
657
+ const hands = createPlayerZones(playerIds, () => createZone(handConfig));
658
+ const newState = moveCardInState(state, 'hand', 'graveyard', cardId);
659
+ ```
660
+
661
+ **Learn more:** [Zone Operations Guide](./docs/guides/zone-operations.md)
662
+
663
+ ## API Reference
664
+
665
+ ### RuleEngine
666
+
667
+ ```typescript
668
+ class RuleEngine<TState, TMoves> {
669
+ constructor(
670
+ gameDefinition: GameDefinition<TState, TMoves>,
671
+ players: Player[],
672
+ options?: { seed?: string }
673
+ );
674
+
675
+ // State access
676
+ getState(): TState;
677
+ getPlayerView(playerId: PlayerId): TState;
678
+
679
+ // Move execution
680
+ executeMove(moveId: string, context: MoveContext): MoveExecutionResult;
681
+ canExecuteMove(moveId: string, context: MoveContext): boolean;
682
+ getValidMoves(playerId: PlayerId): string[];
683
+
684
+ // History
685
+ getHistory(): readonly HistoryEntry[];
686
+ undo(): boolean;
687
+ redo(): boolean;
688
+ replay(upToIndex?: number): TState;
689
+
690
+ // Network sync
691
+ getPatches(sinceIndex?: number): Patch[];
692
+ applyPatches(patches: Patch[]): void;
693
+
694
+ // Game services
695
+ getRNG(): SeededRNG;
696
+ getFlowManager(): FlowManager<TState> | undefined;
697
+ checkGameEnd(): GameEndResult | undefined;
698
+ }
699
+ ```
700
+
701
+ ### GameDefinition
702
+
703
+ ```typescript
704
+ type GameDefinition<TState, TMoves> = {
705
+ name: string;
706
+ setup: (players: Player[]) => TState;
707
+ moves: GameMoveDefinitions<TState, TMoves>;
708
+ flow?: FlowDefinition<TState>;
709
+ endIf?: (state: TState) => GameEndResult | undefined;
710
+ playerView?: (state: TState, playerId: PlayerId) => TState;
711
+ };
712
+ ```
713
+
714
+ ### MoveContext
715
+
716
+ ```typescript
717
+ type MoveContext = {
718
+ playerId: PlayerId;
719
+ sourceCardId?: CardId;
720
+ targets?: string[][];
721
+ data?: Record<string, unknown>;
722
+ timestamp?: number;
723
+ rng?: SeededRNG;
724
+ };
725
+ ```
726
+
727
+ ## Examples
728
+
729
+ See the `examples/` directory for complete game implementations:
730
+
731
+ - **Coin Flip Game** - Simple example validating the framework
732
+ - **Rock Paper Scissors** - Turn-based game with flow management
733
+ - **Card Battle** - Full card game with zones, targeting, and combat
734
+
735
+ ## Testing
736
+
737
+ @drmxrcy/tcg-core is built with Test-Driven Development:
738
+
739
+ ```bash
740
+ # Run all tests
741
+ bun test
742
+
743
+ # Run specific test
744
+ bun test src/engine/__tests__/rule-engine.test.ts
745
+
746
+ # Watch mode
747
+ bun test --watch
748
+ ```
749
+
750
+ ## Architecture
751
+
752
+ ```
753
+ @drmxrcy/tcg-core
754
+ ├── engine/ # RuleEngine - Main orchestration
755
+ ├── game-definition/ # GameDefinition types and validation
756
+ ├── moves/ # Move system and execution
757
+ ├── flow/ # Turn/phase/segment management
758
+ ├── zones/ # Zone management system
759
+ ├── cards/ # Card instances and modifiers
760
+ ├── filtering/ # Card query DSL
761
+ ├── targeting/ # Targeting system
762
+ ├── rng/ # Seeded random number generation
763
+ ├── logging/ # Structured logging system
764
+ ├── telemetry/ # Event-based telemetry
765
+ ├── delta-sync/ # Patch utilities
766
+ ├── testing/ # Testing utilities (@drmxrcy/tcg-core/testing)
767
+ ├── tooling/ # Card tooling infrastructure (@drmxrcy/tcg-core/tooling)
768
+ ├── validation/ # Type guards and validators (@drmxrcy/tcg-core/validation)
769
+ └── types/ # Branded types and utilities
770
+ ```
771
+
772
+ ## Documentation
773
+
774
+ ### Guides
775
+
776
+ - **[Logging Guide](./docs/LOGGING.md)** - Structured logging system
777
+ - **[Telemetry Guide](./docs/TELEMETRY.md)** - Event-based telemetry
778
+ - **[Zone Operations Guide](./docs/guides/zone-operations.md)** - Comprehensive zone management utilities
779
+ - **[Testing Utilities Guide](./docs/guides/testing-utilities.md)** - TDD workflow and test patterns
780
+ - **[Card Tooling Guide](./docs/guides/card-tooling.md)** - Building card management pipelines
781
+ - **[Validation Guide](./docs/guides/validation.md)** - Type guards and runtime validation
782
+
783
+ ### Examples
784
+
785
+ - **[Zone Management Examples](./docs/examples/zone-management.ts)** - Runnable zone operation examples
786
+ - **[Test Patterns Examples](./docs/examples/test-patterns.ts)** - Complete testing examples
787
+ - **[Card Parser Extension](./docs/examples/card-parser-extension.ts)** - Extending CardParser
788
+ - **[Custom Validator](./docs/examples/custom-validator.ts)** - Building validators
789
+
790
+ ## Performance
791
+
792
+ - **Immutable Updates**: Immer provides structural sharing for efficient updates
793
+ - **Delta Sync**: Only transmit state changes, not entire state
794
+ - **Deterministic**: Seeded RNG enables caching and replay
795
+ - **Type Safety**: Zero runtime overhead for branded types
796
+
797
+ ## Move Enumeration
798
+
799
+ Discover all available moves with their valid parameters for AI agents and UI components:
800
+
801
+ ```typescript
802
+ // Get all valid moves with parameters
803
+ const moves = engine.enumerateMoves(playerId, {
804
+ validOnly: true,
805
+ includeMetadata: true
806
+ });
807
+
808
+ for (const move of moves) {
809
+ console.log(`Move: ${move.metadata?.displayName}`);
810
+ console.log(` Params:`, move.params);
811
+
812
+ // Execute the move
813
+ if (move.isValid) {
814
+ engine.executeMove(move.moveId, {
815
+ playerId: move.playerId,
816
+ params: move.params
817
+ });
818
+ }
819
+ }
820
+ ```
821
+
822
+ **Define enumerators in move definitions:**
823
+
824
+ ```typescript
825
+ const playCardMove: MoveDefinition<GameState, PlayCardParams> = {
826
+ id: 'play-card',
827
+ name: 'Play Card',
828
+
829
+ // Enumerate all cards in hand
830
+ enumerator: (state, context) => {
831
+ const handCards = context.zones.getCardsInZone('hand', context.playerId);
832
+ return handCards.map(cardId => ({ cardId }));
833
+ },
834
+
835
+ condition: (state, context) => {
836
+ // Validate the move
837
+ return isCardPlayable(state, context.params.cardId);
838
+ },
839
+
840
+ reducer: (draft, context) => {
841
+ // Execute the move
842
+ playCard(draft, context.params.cardId);
843
+ }
844
+ };
845
+ ```
846
+
847
+ **Perfect for:**
848
+ - 🤖 AI agents that need to explore all possible moves
849
+ - 🎮 UI components building dynamic action menus
850
+ - 📊 Game analysis and move tree exploration
851
+ - 🔍 Debugging and testing game states
852
+
853
+ **Learn more:** [Move Enumeration Guide](./docs/guides/move-enumeration.md)
854
+
855
+ ## Roadmap
856
+
857
+ - [x] Move enumeration system for AI and UI
858
+ - [ ] WebSocket transport layer
859
+ - [ ] React hooks for UI integration
860
+ - [ ] Vue composables
861
+ - [ ] Matchmaking service
862
+ - [ ] Tournament system
863
+ - [ ] Spectator mode
864
+ - [ ] Replay viewer
865
+
866
+ ## Contributing
867
+
868
+ We welcome contributions! See [CONTRIBUTING.md](../../CONTRIBUTING.md) for guidelines.
869
+
870
+ ## License
871
+
872
+ MIT © The Card Goat Team
873
+
874
+ ## Related Packages
875
+
876
+ - **@drmxrcy/tcg-lorcana** - Disney Lorcana TCG implementation
877
+ - **@drmxrcy/tcg-server** - Authoritative game server
878
+ - **@drmxrcy/tcg-client** - Client SDK for web/mobile
879
+
880
+ ---
881
+
882
+ **Built with ❤️ for trading card game developers**