@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,188 @@
1
+ import type { Draft } from "immer";
2
+ import type { MoveContext, NormalizeParams } from "../moves/move-system";
3
+
4
+ /**
5
+ * Game Move Definition
6
+ *
7
+ * Task 10.5, 10.6: Implement GameMoveDefinition with reducer and optional condition
8
+ *
9
+ * Declarative definition of a single move/action in the game.
10
+ * Each move has:
11
+ * - A reducer function (required) - executes the move with typed parameters
12
+ * - An optional condition function - validates if move is legal
13
+ * - Optional metadata - for categorization, UI, etc.
14
+ *
15
+ * @template TState - Game state type
16
+ * @template TParams - Move-specific parameter type (from TMoves[MoveName])
17
+ * @template TCardMeta - Card metadata type (for zone/card operations)
18
+ * @template TCardDefinition - Card definition type (for registry access)
19
+ */
20
+ export type GameMoveDefinition<
21
+ TState,
22
+ TParams = any,
23
+ TCardMeta = any,
24
+ TCardDefinition = any,
25
+ > = {
26
+ /**
27
+ * Move reducer - executes the move using Immer draft
28
+ *
29
+ * Task 10.6: Reducer with Immer draft pattern
30
+ *
31
+ * Pure function that mutates the draft to update state.
32
+ * Immer converts mutations into immutable updates.
33
+ *
34
+ * The context includes fully-typed parameters specific to this move.
35
+ *
36
+ * @param draft - Immer draft (mutable proxy) of game state
37
+ * @param context - Move context (player, typed params, targets, timestamp, zones, cards, registry)
38
+ *
39
+ * @example
40
+ * ```typescript
41
+ * // For a move: playCard: { cardId: string; alternativeCost?: AlternativeCost }
42
+ * reducer: (draft, context) => {
43
+ * const { cardId, alternativeCost } = context.params; // ✅ Fully typed!
44
+ * // Implementation...
45
+ * }
46
+ * ```
47
+ */
48
+ reducer: (
49
+ draft: Draft<TState>,
50
+ context: MoveContext<NormalizeParams<TParams>, TCardMeta, TCardDefinition>,
51
+ ) => void;
52
+
53
+ /**
54
+ * Move condition - validates if move is legal
55
+ *
56
+ * Task 10.6: Optional condition for validation
57
+ *
58
+ * Pure predicate checked BEFORE reducer execution.
59
+ * If returns false, move is rejected without state changes.
60
+ *
61
+ * The context includes fully-typed parameters specific to this move.
62
+ *
63
+ * @param state - Current game state (readonly)
64
+ * @param context - Move context (player, typed params, targets, timestamp, zones, cards, registry)
65
+ * @returns True if move is legal, false otherwise
66
+ *
67
+ * @example
68
+ * ```typescript
69
+ * // For a move: playCard: { cardId: string; alternativeCost?: AlternativeCost }
70
+ * condition: (state, context) => {
71
+ * const { cardId, alternativeCost } = context.params; // ✅ Fully typed!
72
+ * // Validation logic...
73
+ * }
74
+ * ```
75
+ */
76
+ condition?: (
77
+ state: TState,
78
+ context: MoveContext<NormalizeParams<TParams>, TCardMeta, TCardDefinition>,
79
+ ) => boolean | import("../moves/move-system").ConditionFailure;
80
+
81
+ /**
82
+ * Parameter enumerator (for move enumeration system)
83
+ *
84
+ * Optional function to generate candidate parameter combinations.
85
+ * Used by RuleEngine.enumerateMoves() to discover available moves for AI/UI.
86
+ * Each parameter combination returned will be validated against the move's condition.
87
+ *
88
+ * If not provided, move will still appear in enumeration results
89
+ * but will indicate that parameters are required.
90
+ *
91
+ * @example
92
+ * ```typescript
93
+ * enumerator: (state, context) => {
94
+ * // Get all cards in player's hand
95
+ * const handCards = context.zones.getCardsInZone('hand', context.playerId);
96
+ * // Generate parameter for each card
97
+ * return handCards.map(cardId => ({ cardId }));
98
+ * }
99
+ * ```
100
+ */
101
+ enumerator?: (
102
+ state: TState,
103
+ context: import("../moves/move-enumeration").MoveEnumerationContext<
104
+ TCardMeta,
105
+ TCardDefinition
106
+ >,
107
+ ) => TParams[];
108
+
109
+ /**
110
+ * Optional metadata
111
+ *
112
+ * For categorization, UI display, AI hints, etc.
113
+ * Not used by engine, but available to game implementations.
114
+ */
115
+ metadata?: {
116
+ /** Move category (e.g., 'combat', 'resource', 'draw') */
117
+ category?: string;
118
+ /** Tags for filtering/searching */
119
+ tags?: string[];
120
+ /** Custom metadata */
121
+ [key: string]: unknown;
122
+ };
123
+ };
124
+
125
+ /**
126
+ * Game Move Definitions - Exhaustive mapping of moves with type-safe parameters
127
+ *
128
+ * Task 10.6: GameMoveDefinitions type with exhaustive mapping
129
+ *
130
+ * Maps each move name in TMoves to its GameMoveDefinition with the correct parameter type.
131
+ * TypeScript enforces that:
132
+ * - All moves in TMoves have definitions
133
+ * - No extra moves are defined
134
+ * - Each definition has the correct parameter type (TMoves[K])
135
+ * - Reducers and conditions receive fully-typed parameters via context.params
136
+ *
137
+ * @template TState - Game state type
138
+ * @template TMoves - Record of move names to parameter types
139
+ * @template TCardMeta - Card metadata type
140
+ * @template TCardDefinition - Card definition type
141
+ *
142
+ * @example
143
+ * ```typescript
144
+ * type MyMoves = {
145
+ * playCard: { cardId: string; cost?: number };
146
+ * quest: { cardId: string };
147
+ * pass: void;
148
+ * };
149
+ *
150
+ * const moves: GameMoveDefinitions<GameState, MyMoves> = {
151
+ * playCard: {
152
+ * condition: (state, context) => {
153
+ * const { cardId, cost } = context.params; // ✅ Typed as { cardId: string; cost?: number }
154
+ * return true;
155
+ * },
156
+ * reducer: (draft, context) => {
157
+ * const { cardId, cost } = context.params; // ✅ Typed as { cardId: string; cost?: number }
158
+ * // Implementation...
159
+ * }
160
+ * },
161
+ * quest: {
162
+ * reducer: (draft, context) => {
163
+ * const { cardId } = context.params; // ✅ Typed as { cardId: string }
164
+ * // Implementation...
165
+ * }
166
+ * },
167
+ * pass: {
168
+ * reducer: (draft, context) => {
169
+ * // context.params is {} (empty object)
170
+ * // Implementation...
171
+ * }
172
+ * }
173
+ * };
174
+ * ```
175
+ */
176
+ export type GameMoveDefinitions<
177
+ TState,
178
+ TMoves extends Record<string, any>,
179
+ TCardMeta = any,
180
+ TCardDefinition = any,
181
+ > = {
182
+ [K in keyof TMoves]: GameMoveDefinition<
183
+ TState,
184
+ TMoves[K], // ✅ Each move gets its specific parameter type!
185
+ TCardMeta,
186
+ TCardDefinition
187
+ >;
188
+ };
@@ -0,0 +1,183 @@
1
+ import { z } from "zod";
2
+ import type { GameDefinition } from "./game-definition";
3
+
4
+ /**
5
+ * Game Definition Validation Result
6
+ *
7
+ * Result of validating a GameDefinition.
8
+ */
9
+ export type GameDefinitionValidationResult =
10
+ | { success: true }
11
+ | { success: false; error: string; errors?: string[] };
12
+
13
+ /**
14
+ * Zod schema for GameDefinition validation
15
+ *
16
+ * Task 10.14: Implement Zod schema for GameDefinition validation
17
+ *
18
+ * Runtime validation to ensure GameDefinition is well-formed.
19
+ * Checks:
20
+ * - Required fields are present
21
+ * - Types are correct (functions, numbers, strings)
22
+ * - Constraints are met (minPlayers <= maxPlayers, etc.)
23
+ */
24
+ const GameDefinitionSchema = z.object({
25
+ // Name validation
26
+ name: z.string().min(1, "Game name must not be empty"),
27
+
28
+ // Setup function validation
29
+ setup: z.function().args(z.array(z.string())).returns(z.record(z.any())),
30
+
31
+ // Moves validation
32
+ moves: z.record(
33
+ z.object({
34
+ reducer: z.function(),
35
+ condition: z.function().optional(),
36
+ metadata: z.record(z.any()).optional(),
37
+ }),
38
+ ),
39
+
40
+ // Optional flow validation
41
+ flow: z
42
+ .object({
43
+ initial: z.string(),
44
+ states: z.record(z.any()),
45
+ hooks: z
46
+ .object({
47
+ onBegin: z.function().optional(),
48
+ onEnd: z.function().optional(),
49
+ })
50
+ .optional(),
51
+ })
52
+ .optional(),
53
+
54
+ // Optional endIf validation
55
+ endIf: z
56
+ .function()
57
+ .args(z.any())
58
+ .returns(
59
+ z.union([
60
+ z.object({ winner: z.string(), reason: z.string() }),
61
+ z.undefined(),
62
+ ]),
63
+ )
64
+ .optional(),
65
+
66
+ // Optional playerView validation
67
+ playerView: z
68
+ .function()
69
+ .args(z.any(), z.string())
70
+ .returns(z.any())
71
+ .optional(),
72
+ });
73
+
74
+ /**
75
+ * Validate GameDefinition
76
+ *
77
+ * Task 10.13, 10.14: Validate GameDefinition using Zod schema
78
+ *
79
+ * Performs comprehensive validation:
80
+ * 1. Schema validation (types, required fields)
81
+ * 3. Move definition validation (each move has reducer)
82
+ * 4. Function signature validation
83
+ *
84
+ * @param definition - GameDefinition to validate
85
+ * @returns GameDefinitionValidationResult with success flag and error details
86
+ */
87
+ export function validateGameDefinition<
88
+ TState,
89
+ TMoves extends Record<string, any>,
90
+ >(definition: GameDefinition<TState, TMoves>): GameDefinitionValidationResult {
91
+ const errors: string[] = [];
92
+
93
+ try {
94
+ // Run Zod schema validation
95
+ GameDefinitionSchema.parse(definition);
96
+ } catch (error) {
97
+ if (error instanceof z.ZodError) {
98
+ // Collect all Zod validation errors
99
+ for (const issue of error.issues) {
100
+ const path = issue.path.join(".");
101
+ errors.push(`${path}: ${issue.message}`);
102
+ }
103
+ } else {
104
+ errors.push("Unknown validation error");
105
+ }
106
+ }
107
+
108
+ // Validate that setup is a function
109
+ if (typeof definition.setup !== "function") {
110
+ errors.push("setup must be a function");
111
+ }
112
+
113
+ // Validate that moves exist and have reducers
114
+ if (!definition.moves || typeof definition.moves !== "object") {
115
+ errors.push("moves must be an object");
116
+ } else {
117
+ for (const [moveName, moveDef] of Object.entries(definition.moves)) {
118
+ if (!moveDef || typeof moveDef !== "object") {
119
+ errors.push(`Move "${moveName}" must be an object`);
120
+ continue;
121
+ }
122
+
123
+ if (typeof moveDef.reducer !== "function") {
124
+ errors.push(`Move "${moveName}" must have a reducer function`);
125
+ }
126
+
127
+ if (moveDef.condition && typeof moveDef.condition !== "function") {
128
+ errors.push(`Move "${moveName}" condition must be a function`);
129
+ }
130
+ }
131
+ }
132
+
133
+ // Validate optional endIf
134
+ if (definition.endIf && typeof definition.endIf !== "function") {
135
+ errors.push("endIf must be a function");
136
+ }
137
+
138
+ // Validate optional playerView
139
+ if (definition.playerView && typeof definition.playerView !== "function") {
140
+ errors.push("playerView must be a function");
141
+ }
142
+
143
+ // Validate optional flow
144
+ if (definition.flow) {
145
+ if (typeof definition.flow !== "object") {
146
+ errors.push("flow must be an object");
147
+ } else {
148
+ // Flow validation - supports both simplified (turn) and full (gameSegments) syntax
149
+ const flow = definition.flow as any;
150
+
151
+ // Check for simplified syntax (turn property)
152
+ const hasSimplifiedSyntax = "turn" in flow && flow.turn;
153
+
154
+ // Check for full syntax (gameSegments property)
155
+ const hasFullSyntax = "gameSegments" in flow && flow.gameSegments;
156
+
157
+ if (!(hasSimplifiedSyntax || hasFullSyntax)) {
158
+ errors.push(
159
+ "flow must have either 'turn' property (simplified) or 'gameSegments' property (full syntax)",
160
+ );
161
+ } else if (hasSimplifiedSyntax) {
162
+ // Validate simplified syntax
163
+ if (typeof flow.turn !== "object") {
164
+ errors.push("flow.turn must be an object");
165
+ } else if (!flow.turn.phases || typeof flow.turn.phases !== "object") {
166
+ errors.push("flow.turn must have phases object");
167
+ }
168
+ }
169
+ // Full syntax validation could be added here if needed
170
+ }
171
+ }
172
+
173
+ // Return result
174
+ if (errors.length > 0) {
175
+ return {
176
+ success: false,
177
+ error: errors.join("; "),
178
+ errors,
179
+ };
180
+ }
181
+
182
+ return { success: true };
183
+ }