@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,328 @@
1
+ import { describe, expect, it } from "bun:test";
2
+ import { produce } from "immer";
3
+ import type { MoveContext } from "../../moves/move-system";
4
+ import { createMockContext } from "../../testing/test-context-factory";
5
+ import type {
6
+ GameMoveDefinition,
7
+ GameMoveDefinitions,
8
+ } from "../move-definitions";
9
+
10
+ /**
11
+ * Test suite for GameMoveDefinitions type system
12
+ *
13
+ * Task 10.5, 10.6: Write tests for moves mapping
14
+ *
15
+ * Validates:
16
+ * - GameMoveDefinition structure
17
+ * - GameMoveDefinitions exhaustive mapping
18
+ * - Type safety for move reducers
19
+ * - Conditional move execution
20
+ */
21
+
22
+ type CounterState = {
23
+ count: number;
24
+ player: string;
25
+ locked: boolean;
26
+ };
27
+
28
+ type CounterMoves = {
29
+ increment: { amount: number };
30
+ decrement: { amount: number };
31
+ reset: Record<string, never>;
32
+ setPlayer: { playerId: string };
33
+ };
34
+
35
+ describe("GameMoveDefinitions - Type System", () => {
36
+ describe("GameMoveDefinition structure", () => {
37
+ it("should create a valid GameMoveDefinition with reducer only", () => {
38
+ const incrementMove: GameMoveDefinition<CounterState> = {
39
+ reducer: (draft, context) => {
40
+ const amount = (context.params?.amount as number) || 1;
41
+ draft.count += amount;
42
+ },
43
+ };
44
+
45
+ expect(incrementMove.reducer).toBeFunction();
46
+ expect(incrementMove.condition).toBeUndefined();
47
+ });
48
+
49
+ it("should create a GameMoveDefinition with condition and reducer", () => {
50
+ const incrementMove: GameMoveDefinition<CounterState> = {
51
+ condition: (state) => !state.locked,
52
+ reducer: (draft, context) => {
53
+ const amount = (context.params?.amount as number) || 1;
54
+ draft.count += amount;
55
+ },
56
+ };
57
+
58
+ expect(incrementMove.condition).toBeFunction();
59
+ expect(incrementMove.reducer).toBeFunction();
60
+ });
61
+ });
62
+
63
+ describe("GameMoveDefinitions exhaustive mapping", () => {
64
+ it("should map all moves in TMoves type", () => {
65
+ const moves: GameMoveDefinitions<CounterState, CounterMoves> = {
66
+ increment: {
67
+ reducer: (draft, context) => {
68
+ const amount = (context.params?.amount as number) || 1;
69
+ draft.count += amount;
70
+ },
71
+ },
72
+ decrement: {
73
+ reducer: (draft, context) => {
74
+ const amount = (context.params?.amount as number) || 1;
75
+ draft.count -= amount;
76
+ },
77
+ },
78
+ reset: {
79
+ reducer: (draft) => {
80
+ draft.count = 0;
81
+ },
82
+ },
83
+ setPlayer: {
84
+ reducer: (draft, context) => {
85
+ draft.player = (context.params?.playerId as string) || "";
86
+ },
87
+ },
88
+ };
89
+
90
+ expect(Object.keys(moves)).toEqual([
91
+ "increment",
92
+ "decrement",
93
+ "reset",
94
+ "setPlayer",
95
+ ]);
96
+ });
97
+
98
+ it("should enforce that all move names have definitions", () => {
99
+ // TypeScript will error if any move is missing
100
+ const moves: GameMoveDefinitions<CounterState, CounterMoves> = {
101
+ increment: {
102
+ reducer: (draft) => {
103
+ draft.count += 1;
104
+ },
105
+ },
106
+ decrement: {
107
+ reducer: (draft) => {
108
+ draft.count -= 1;
109
+ },
110
+ },
111
+ reset: {
112
+ reducer: (draft) => {
113
+ draft.count = 0;
114
+ },
115
+ },
116
+ setPlayer: {
117
+ reducer: (draft) => {
118
+ draft.player = "test";
119
+ },
120
+ },
121
+ };
122
+
123
+ // All moves should be present
124
+ expect(moves.increment).toBeDefined();
125
+ expect(moves.decrement).toBeDefined();
126
+ expect(moves.reset).toBeDefined();
127
+ expect(moves.setPlayer).toBeDefined();
128
+ });
129
+ });
130
+
131
+ describe("move reducer execution", () => {
132
+ it("should execute reducer with Immer draft", () => {
133
+ const incrementMove: GameMoveDefinition<CounterState> = {
134
+ reducer: (draft, context) => {
135
+ const amount = (context.params?.amount as number) || 1;
136
+ draft.count += amount;
137
+ },
138
+ };
139
+
140
+ const initialState: CounterState = {
141
+ count: 5,
142
+ player: "p1",
143
+ locked: false,
144
+ };
145
+
146
+ const context: MoveContext = createMockContext({
147
+ playerId: "p1" as any,
148
+ params: { amount: 3 },
149
+ });
150
+
151
+ const newState = produce(initialState, (draft) => {
152
+ incrementMove.reducer(draft, context);
153
+ });
154
+
155
+ expect(newState.count).toBe(8);
156
+ expect(initialState.count).toBe(5); // Original unchanged
157
+ });
158
+
159
+ it("should support complex state modifications", () => {
160
+ const complexMove: GameMoveDefinition<CounterState> = {
161
+ reducer: (draft, context) => {
162
+ draft.count += 1;
163
+ draft.player = context.playerId;
164
+ draft.locked = true;
165
+ },
166
+ };
167
+
168
+ const initialState: CounterState = {
169
+ count: 0,
170
+ player: "",
171
+ locked: false,
172
+ };
173
+
174
+ const context: MoveContext = createMockContext({
175
+ playerId: "p1" as any,
176
+ params: {},
177
+ });
178
+
179
+ const newState = produce(initialState, (draft) => {
180
+ complexMove.reducer(draft, context);
181
+ });
182
+
183
+ expect(newState).toEqual({
184
+ count: 1,
185
+ player: "p1",
186
+ locked: true,
187
+ });
188
+ });
189
+ });
190
+
191
+ describe("move conditions", () => {
192
+ it("should evaluate condition before executing move", () => {
193
+ const lockedMove: GameMoveDefinition<CounterState> = {
194
+ condition: (state) => !state.locked,
195
+ reducer: (draft) => {
196
+ draft.count += 1;
197
+ },
198
+ };
199
+
200
+ const unlockedState: CounterState = {
201
+ count: 0,
202
+ player: "p1",
203
+ locked: false,
204
+ };
205
+
206
+ const lockedState: CounterState = {
207
+ count: 0,
208
+ player: "p1",
209
+ locked: true,
210
+ };
211
+
212
+ const context: MoveContext = createMockContext({
213
+ playerId: "p1" as any,
214
+ params: {},
215
+ });
216
+
217
+ // Should pass condition
218
+ expect(lockedMove.condition?.(unlockedState, context)).toBe(true);
219
+
220
+ // Should fail condition
221
+ expect(lockedMove.condition?.(lockedState, context)).toBe(false);
222
+ });
223
+
224
+ it("should support complex conditions", () => {
225
+ const conditionalMove: GameMoveDefinition<CounterState> = {
226
+ condition: (state, context) => {
227
+ return (
228
+ !state.locked &&
229
+ state.count < 10 &&
230
+ state.player === context.playerId
231
+ );
232
+ },
233
+ reducer: (draft) => {
234
+ draft.count += 1;
235
+ },
236
+ };
237
+
238
+ const validState: CounterState = {
239
+ count: 5,
240
+ player: "p1",
241
+ locked: false,
242
+ };
243
+
244
+ const context: MoveContext = createMockContext({
245
+ playerId: "p1" as any,
246
+ params: {},
247
+ });
248
+
249
+ expect(conditionalMove.condition?.(validState, context)).toBe(true);
250
+
251
+ // Wrong player
252
+ expect(
253
+ conditionalMove.condition?.(
254
+ validState,
255
+ createMockContext({
256
+ playerId: "p2" as any,
257
+ params: {},
258
+ }),
259
+ ),
260
+ ).toBe(false);
261
+
262
+ // Locked
263
+ expect(
264
+ conditionalMove.condition?.({ ...validState, locked: true }, context),
265
+ ).toBe(false);
266
+
267
+ // Count too high
268
+ expect(
269
+ conditionalMove.condition?.({ ...validState, count: 10 }, context),
270
+ ).toBe(false);
271
+ });
272
+ });
273
+
274
+ describe("type safety", () => {
275
+ it("should preserve state type through reducer", () => {
276
+ const typedMove: GameMoveDefinition<CounterState> = {
277
+ reducer: (draft) => {
278
+ // TypeScript should know draft is CounterState
279
+ draft.count += 1;
280
+ draft.player = "updated";
281
+ draft.locked = true;
282
+
283
+ // These should cause TypeScript errors:
284
+ // draft.nonExistent = true;
285
+ // draft.count = "string";
286
+ },
287
+ };
288
+
289
+ expect(typedMove.reducer).toBeFunction();
290
+ });
291
+
292
+ it("should preserve state type through condition", () => {
293
+ const typedMove: GameMoveDefinition<CounterState> = {
294
+ condition: (state) => {
295
+ // TypeScript should know state is CounterState
296
+ return state.count < 10 && !state.locked;
297
+
298
+ // This should cause TypeScript error:
299
+ // return state.nonExistent;
300
+ },
301
+ reducer: (draft) => {
302
+ draft.count += 1;
303
+ },
304
+ };
305
+
306
+ expect(typedMove.condition).toBeFunction();
307
+ });
308
+ });
309
+
310
+ describe("metadata support", () => {
311
+ it("should support optional metadata", () => {
312
+ const metadataMove: GameMoveDefinition<CounterState> = {
313
+ reducer: (draft) => {
314
+ draft.count += 1;
315
+ },
316
+ metadata: {
317
+ category: "counter",
318
+ tags: ["increment", "basic"],
319
+ priority: 1,
320
+ },
321
+ };
322
+
323
+ expect(metadataMove.metadata?.category).toBe("counter");
324
+ expect(metadataMove.metadata?.tags).toEqual(["increment", "basic"]);
325
+ expect(metadataMove.metadata?.priority).toBe(1);
326
+ });
327
+ });
328
+ });
@@ -0,0 +1,261 @@
1
+ import type { FlowDefinition } from "../flow";
2
+ import type { TelemetryHooks } from "../telemetry";
3
+ import type { CardZoneConfig } from "../zones";
4
+ import type { GameMoveDefinitions } from "./move-definitions";
5
+
6
+ /**
7
+ * Player information for game setup
8
+ */
9
+ export type Player = {
10
+ /** Unique player identifier */
11
+ id: string;
12
+ /** Optional player name for display */
13
+ name?: string;
14
+ };
15
+
16
+ /**
17
+ * Game end result
18
+ *
19
+ * Returned by endIf when the game has ended.
20
+ */
21
+ export type GameEndResult = {
22
+ /** Winner identifier (player ID or special value like 'draw') */
23
+ winner: string;
24
+ /** Reason for game end (for display/logging) */
25
+ reason: string;
26
+ /** Additional metadata about the game end */
27
+ metadata?: Record<string, unknown>;
28
+ };
29
+
30
+ /**
31
+ * GameDefinition Type System
32
+ *
33
+ * Task 10.2: Implement GameDefinition<TState, TMoves> type
34
+ *
35
+ * The core declarative game definition with full type safety.
36
+ * Generic over:
37
+ * - TState: Game state shape (game-specific logic state)
38
+ * - TMoves: Available moves (record of move names to move arg types)
39
+ * - TCardDefinition: Static card definition type
40
+ * - TCardMeta: Dynamic card metadata type
41
+ *
42
+ * @example
43
+ * ```typescript
44
+ * type MyGameState = {
45
+ * players: Player[];
46
+ * currentPlayer: number;
47
+ * };
48
+ *
49
+ * type MyMoves = {
50
+ * playCard: { cardId: string };
51
+ * pass: {};
52
+ * };
53
+ *
54
+ * type MyCardDef = {
55
+ * id: string;
56
+ * name: string;
57
+ * cost: number;
58
+ * };
59
+ *
60
+ * type MyCardMeta = {
61
+ * damage?: number;
62
+ * tapped?: boolean;
63
+ * };
64
+ *
65
+ * const game: GameDefinition<MyGameState, MyMoves, MyCardDef, MyCardMeta> = {
66
+ * name: 'My Card Game',
67
+ * zones: {
68
+ * hand: { id: 'hand', name: 'Hand', visibility: 'private', ordered: false },
69
+ * deck: { id: 'deck', name: 'Deck', visibility: 'secret', ordered: true },
70
+ * },
71
+ * setup: (players) => ({
72
+ * players,
73
+ * currentPlayer: 0,
74
+ * }),
75
+ * moves: {
76
+ * playCard: {
77
+ * condition: (state, context) => true,
78
+ * reducer: (draft, context) => { ... }
79
+ * },
80
+ * pass: {
81
+ * reducer: (draft) => { ... }
82
+ * }
83
+ * }
84
+ * };
85
+ * ```
86
+ */
87
+ export type GameDefinition<
88
+ TState,
89
+ TMoves extends Record<string, any>,
90
+ TCardDefinition = any,
91
+ TCardMeta = any,
92
+ > = {
93
+ /**
94
+ * Game name for identification and display
95
+ *
96
+ * Task 10.2: Required field
97
+ */
98
+ name: string;
99
+
100
+ /**
101
+ * Zone configuration (optional, but recommended for card games)
102
+ *
103
+ * Defines all zones used in the game.
104
+ * The framework will manage card locations and zone state internally.
105
+ *
106
+ * If zones are not provided, games must manage their own zone/card logic.
107
+ * This field enables the framework's internal zone management system.
108
+ *
109
+ * @example
110
+ * ```typescript
111
+ * zones: {
112
+ * hand: { id: 'hand', name: 'Hand', visibility: 'private', ordered: false },
113
+ * deck: { id: 'deck', name: 'Deck', visibility: 'secret', ordered: true },
114
+ * play: { id: 'play', name: 'Play Area', visibility: 'public', ordered: false },
115
+ * graveyard: { id: 'graveyard', name: 'Graveyard', visibility: 'public', ordered: false },
116
+ * }
117
+ * ```
118
+ */
119
+ zones?: Record<string, CardZoneConfig>;
120
+
121
+ /**
122
+ * Card definitions (optional)
123
+ *
124
+ * Map of card definition ID -> card data.
125
+ * Can be loaded dynamically or provided upfront.
126
+ *
127
+ * @example
128
+ * ```typescript
129
+ * cards: {
130
+ * 'pikachu': { id: 'pikachu', name: 'Pikachu', hp: 60, type: 'electric' },
131
+ * 'charizard': { id: 'charizard', name: 'Charizard', hp: 150, type: 'fire' },
132
+ * }
133
+ * ```
134
+ */
135
+ cards?: Record<string, TCardDefinition>;
136
+
137
+ /**
138
+ * Setup function - creates initial game state
139
+ *
140
+ * Task 10.4: Setup function signature
141
+ *
142
+ * Must be pure and deterministic:
143
+ * - Same players -> same initial state
144
+ * - No side effects
145
+ * - No randomness (use RNG in moves instead)
146
+ *
147
+ * @param players - Array of players in the game
148
+ * @returns Initial game state
149
+ */
150
+ setup: (players: Player[]) => TState;
151
+
152
+ /**
153
+ * Moves definition - exhaustive mapping of move names to move definitions
154
+ *
155
+ * Task 10.6: GameMoveDefinitions type with exhaustive mapping
156
+ *
157
+ * Each key in TMoves must have a corresponding GameMoveDefinition.
158
+ * Type system enforces this at compile time.
159
+ *
160
+ * Moves receive context with zones, cards operations API, and card registry.
161
+ */
162
+ moves: GameMoveDefinitions<TState, TMoves, TCardMeta, TCardDefinition>;
163
+
164
+ /**
165
+ * Flow definition (optional) - XState-based turn/phase/step orchestration
166
+ *
167
+ * Task 10.8: Flow configuration validation
168
+ *
169
+ * If omitted, game has no built-in flow control.
170
+ * Games can still progress through moves, but no automatic phase transitions.
171
+ */
172
+ flow?: FlowDefinition<TState>;
173
+
174
+ /**
175
+ * Tracker configuration (optional)
176
+ *
177
+ * Defines boolean flags that auto-reset at turn/phase boundaries.
178
+ * Eliminates boilerplate for "hasDrawnThisTurn", "hasPlayedResourceThisTurn", etc.
179
+ *
180
+ * @example
181
+ * ```typescript
182
+ * trackers: {
183
+ * perTurn: ['hasDrawn', 'hasPlayedResource', 'hasAttacked'],
184
+ * perPhase: {
185
+ * main: ['hasPlayedCard']
186
+ * },
187
+ * perPlayer: true // Default: true
188
+ * }
189
+ * ```
190
+ */
191
+ trackers?: {
192
+ /** Trackers that reset at the end of each turn */
193
+ perTurn?: string[];
194
+ /** Trackers that reset at the end of specific phases */
195
+ perPhase?: Record<string, string[]>;
196
+ /** Whether trackers are per-player or global (default: true) */
197
+ perPlayer?: boolean;
198
+ };
199
+
200
+ /**
201
+ * Game end condition (optional)
202
+ *
203
+ * Task 10.10: EndIf evaluation logic
204
+ *
205
+ * Checked after every move execution.
206
+ * If returns a GameEndResult, the game ends.
207
+ * If returns undefined, game continues.
208
+ *
209
+ * @param state - Current game state
210
+ * @returns GameEndResult if game ended, undefined otherwise
211
+ */
212
+ endIf?: (state: TState) => GameEndResult | undefined;
213
+
214
+ /**
215
+ * Player view filter (optional)
216
+ *
217
+ * Task 10.12: PlayerView function signature
218
+ *
219
+ * Filters game state to hide private information from a player.
220
+ * If omitted, all players see complete state.
221
+ *
222
+ * Must be pure and deterministic:
223
+ * - Same state + playerId -> same filtered state
224
+ * - No side effects
225
+ *
226
+ * @param state - Complete game state
227
+ * @param playerId - Player requesting the view
228
+ * @returns Filtered state for this player
229
+ */
230
+ playerView?: (state: TState, playerId: string) => TState;
231
+
232
+ /**
233
+ * Telemetry hooks (optional)
234
+ *
235
+ * Callbacks for tracking game events and player actions.
236
+ * Hooks registered here are automatically subscribed when the engine starts.
237
+ *
238
+ * Use cases:
239
+ * - Analytics tracking
240
+ * - Error reporting
241
+ * - Custom logging
242
+ * - Performance monitoring
243
+ *
244
+ * @example
245
+ * ```typescript
246
+ * telemetryHooks: {
247
+ * onPlayerAction: (event) => {
248
+ * analytics.track('game.move', {
249
+ * moveId: event.moveId,
250
+ * playerId: event.playerId,
251
+ * duration: event.duration
252
+ * });
253
+ * },
254
+ * onEngineError: (event) => {
255
+ * errorReporter.captureException(event.error, event.context);
256
+ * }
257
+ * }
258
+ * ```
259
+ */
260
+ telemetryHooks?: TelemetryHooks;
261
+ };
@@ -0,0 +1,28 @@
1
+ /**
2
+ * Game Definition Module
3
+ *
4
+ * Task 10: GameDefinition Type System
5
+ *
6
+ * Provides declarative game definition pattern with full type safety.
7
+ * Exports:
8
+ * - GameDefinition type (core definition)
9
+ * - MoveDefinition and MoveDefinitions types
10
+ * - Validation utilities
11
+ * - Supporting types (Player, GameEndResult, FlowDefinition)
12
+ */
13
+
14
+ export type {
15
+ GameDefinition,
16
+ GameEndResult,
17
+ Player,
18
+ } from "./game-definition";
19
+
20
+ export type {
21
+ GameMoveDefinition,
22
+ GameMoveDefinitions,
23
+ } from "./move-definitions";
24
+
25
+ export {
26
+ type GameDefinitionValidationResult,
27
+ validateGameDefinition,
28
+ } from "./validation";