@drmxrcy/tcg-lorcana 0.0.0-202602060544

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 (100) hide show
  1. package/README.md +160 -0
  2. package/package.json +45 -0
  3. package/src/__tests__/integration/move-enumeration.test.ts +256 -0
  4. package/src/__tests__/rules/section-01-concepts.test.ts +426 -0
  5. package/src/__tests__/rules/section-03-gameplay.test.ts +298 -0
  6. package/src/__tests__/rules/section-04-turn-structure.test.ts +708 -0
  7. package/src/__tests__/rules/section-05-cards.test.ts +158 -0
  8. package/src/__tests__/rules/section-06-card-types.test.ts +342 -0
  9. package/src/__tests__/rules/section-07-abilities.test.ts +333 -0
  10. package/src/__tests__/rules/section-08-zones.test.ts +231 -0
  11. package/src/__tests__/rules/section-09-damage.test.ts +148 -0
  12. package/src/__tests__/rules/section-10-keywords.test.ts +469 -0
  13. package/src/__tests__/spec-01-foundation-types.test.ts +534 -0
  14. package/src/__tests__/spec-02-zones-card-states.test.ts +295 -0
  15. package/src/card-utils.ts +302 -0
  16. package/src/cards/README.md +296 -0
  17. package/src/cards/abilities/index.ts +175 -0
  18. package/src/cards/index.ts +10 -0
  19. package/src/deck-validation.ts +175 -0
  20. package/src/engine/lorcana-engine.ts +625 -0
  21. package/src/game-definition/__tests__/core-zone-integration.test.ts +553 -0
  22. package/src/game-definition/__tests__/zone-operations.test.ts +362 -0
  23. package/src/game-definition/__tests__/zones.test.ts +176 -0
  24. package/src/game-definition/definition.ts +45 -0
  25. package/src/game-definition/flow/turn-flow.ts +216 -0
  26. package/src/game-definition/index.ts +31 -0
  27. package/src/game-definition/moves/abilities/activate-ability.ts +51 -0
  28. package/src/game-definition/moves/core/__tests__/move-parameter-enumeration.test.ts +316 -0
  29. package/src/game-definition/moves/core/challenge.test.ts +545 -0
  30. package/src/game-definition/moves/core/challenge.ts +81 -0
  31. package/src/game-definition/moves/core/play-card.ts +83 -0
  32. package/src/game-definition/moves/core/quest.test.ts +448 -0
  33. package/src/game-definition/moves/core/quest.ts +49 -0
  34. package/src/game-definition/moves/debug/manual-exert.ts +36 -0
  35. package/src/game-definition/moves/effects/resolve-bag.ts +35 -0
  36. package/src/game-definition/moves/effects/resolve-effect.ts +34 -0
  37. package/src/game-definition/moves/index.ts +85 -0
  38. package/src/game-definition/moves/locations/move-character-to-location.ts +42 -0
  39. package/src/game-definition/moves/resources/put-card-into-inkwell.test.ts +462 -0
  40. package/src/game-definition/moves/resources/put-card-into-inkwell.ts +51 -0
  41. package/src/game-definition/moves/setup/alter-hand.test.ts +395 -0
  42. package/src/game-definition/moves/setup/alter-hand.ts +210 -0
  43. package/src/game-definition/moves/setup/choose-first-player.test.ts +450 -0
  44. package/src/game-definition/moves/setup/choose-first-player.ts +105 -0
  45. package/src/game-definition/moves/setup/draw-cards.ts +37 -0
  46. package/src/game-definition/moves/songs/sing-together.ts +47 -0
  47. package/src/game-definition/moves/songs/sing.ts +56 -0
  48. package/src/game-definition/moves/standard/concede.test.ts +189 -0
  49. package/src/game-definition/moves/standard/concede.ts +72 -0
  50. package/src/game-definition/moves/standard/pass-turn.ts +49 -0
  51. package/src/game-definition/setup/game-setup.ts +19 -0
  52. package/src/game-definition/trackers/tracker-config.ts +23 -0
  53. package/src/game-definition/win-conditions/lore-victory.ts +26 -0
  54. package/src/game-definition/zone-operations.ts +405 -0
  55. package/src/game-definition/zones/zone-configs.ts +59 -0
  56. package/src/game-definition/zones.ts +283 -0
  57. package/src/index.ts +189 -0
  58. package/src/operations/index.ts +7 -0
  59. package/src/operations/lorcana-operations.ts +288 -0
  60. package/src/queries/README.md +56 -0
  61. package/src/resolvers/__tests__/condition-resolver.test.ts +301 -0
  62. package/src/resolvers/condition-registry.ts +70 -0
  63. package/src/resolvers/condition-resolver.ts +85 -0
  64. package/src/resolvers/conditions/basic.ts +81 -0
  65. package/src/resolvers/conditions/card-state.ts +12 -0
  66. package/src/resolvers/conditions/comparison.ts +102 -0
  67. package/src/resolvers/conditions/existence.ts +219 -0
  68. package/src/resolvers/conditions/history.ts +68 -0
  69. package/src/resolvers/conditions/index.ts +15 -0
  70. package/src/resolvers/conditions/logical.ts +55 -0
  71. package/src/resolvers/conditions/resolution.ts +41 -0
  72. package/src/resolvers/conditions/revealed.ts +42 -0
  73. package/src/resolvers/conditions/zone.ts +84 -0
  74. package/src/setup.test.ts +18 -0
  75. package/src/targeting/__tests__/filter-resolver.test.ts +294 -0
  76. package/src/targeting/__tests__/real-cards-targeting.test.ts +303 -0
  77. package/src/targeting/__tests__/targeting-dsl.test.ts +386 -0
  78. package/src/targeting/enum-expansion.ts +387 -0
  79. package/src/targeting/filter-registry.ts +322 -0
  80. package/src/targeting/filter-resolver.ts +145 -0
  81. package/src/targeting/index.ts +91 -0
  82. package/src/targeting/lorcana-target-dsl.ts +495 -0
  83. package/src/targeting/targeting-ui.ts +407 -0
  84. package/src/testing/index.ts +14 -0
  85. package/src/testing/lorcana-test-engine.ts +813 -0
  86. package/src/types/README.md +303 -0
  87. package/src/types/__tests__/lorcana-state.test.ts +168 -0
  88. package/src/types/__tests__/move-enumeration.test.ts +179 -0
  89. package/src/types/branded-types.ts +106 -0
  90. package/src/types/game-state.ts +184 -0
  91. package/src/types/index.ts +87 -0
  92. package/src/types/keywords.ts +187 -0
  93. package/src/types/lorcana-state.ts +260 -0
  94. package/src/types/move-enumeration.ts +126 -0
  95. package/src/types/move-params.ts +216 -0
  96. package/src/validators/index.ts +7 -0
  97. package/src/validators/move-validators.ts +374 -0
  98. package/src/zones/card-state.ts +234 -0
  99. package/src/zones/index.ts +42 -0
  100. package/src/zones/zone-config.ts +150 -0
@@ -0,0 +1,260 @@
1
+ /**
2
+ * Lorcana Game State Types
3
+ *
4
+ * Task 1.2: Define LorcanaState type extending base game state
5
+ *
6
+ * Complete type definition for Disney Lorcana game state following official rules:
7
+ * - Lore tracking for win condition (Rule 1.9.1.1 - win at 20 lore)
8
+ * - Ink management (total capacity and available per turn)
9
+ * - Character states (drying status, damage, exerted)
10
+ * - Turn metadata (cards played, characters questing, ink used)
11
+ * - Challenge state for combat resolution
12
+ * - Location and item states
13
+ *
14
+ * References:
15
+ * - Rule 3.1.4 (Lore starts at 0)
16
+ * - Rule 4.2.2.1 (Drying characters)
17
+ * - Rule 4.3.3 (Inkwell - once per turn)
18
+ * - Rule 4.3.6 (Challenge mechanics)
19
+ * - Rule 9 (Damage counters)
20
+ */
21
+
22
+ import type { CardId, PlayerId } from "./branded-types";
23
+
24
+ /**
25
+ * Lorcana Phase
26
+ *
27
+ * Three-phase turn structure (Rule 4.1.1):
28
+ * - beginning: Ready, Set, Draw steps
29
+ * - main: Player can take turn actions
30
+ * - end: End of turn cleanup
31
+ */
32
+ export type LorcanaPhase = "beginning" | "main" | "end";
33
+
34
+ /**
35
+ * Character State
36
+ *
37
+ * Runtime state for a character card in play.
38
+ * Tracks damage, exerted status, and "drying" state.
39
+ *
40
+ * Rule 4.2.2.1: Characters are "drying" the turn they're played
41
+ * Rule 6.1.4: Must be dry to quest, challenge, or exert
42
+ * Rule 9: Damage represented by counters
43
+ */
44
+ export type CharacterState = {
45
+ /**
46
+ * "Drying" status - true if played this turn
47
+ *
48
+ * Characters that are "drying" cannot:
49
+ * - Quest (Rule 4.3.5)
50
+ * - Challenge (Rule 4.3.6.6)
51
+ * - Be exerted to pay costs (Rule 6.1.4)
52
+ *
53
+ * Becomes false at Set step of next turn (Rule 4.2.2.1)
54
+ */
55
+ playedThisTurn: boolean;
56
+
57
+ /**
58
+ * Damage counters on this character
59
+ *
60
+ * Rule 9.1: Each counter represents 1 damage
61
+ * Rule 1.9.1.3: Banished when damage >= Willpower
62
+ */
63
+ damage: number;
64
+
65
+ /**
66
+ * Exerted status - true if turned sideways
67
+ *
68
+ * Rule 5.1.2: Exerted cards turned sideways
69
+ * Rule 4.2.1.1: Readied at start of turn
70
+ */
71
+ exerted: boolean;
72
+ };
73
+
74
+ /**
75
+ * Permanent State
76
+ *
77
+ * Runtime state for locations and items in play.
78
+ * Currently only tracks damage (for locations).
79
+ */
80
+ export type PermanentState = {
81
+ /**
82
+ * Damage counters (for locations)
83
+ *
84
+ * Rule 4.3.6.19-22: Locations can be challenged
85
+ * Rule 6.5: Locations have Willpower
86
+ */
87
+ damage: number;
88
+ };
89
+
90
+ /**
91
+ * Challenge State
92
+ *
93
+ * Temporary state during challenge resolution.
94
+ *
95
+ * Rule 4.3.6: Challenge mechanics
96
+ * - Attacker declared and exerted
97
+ * - Defender chosen
98
+ * - Damage calculated and dealt
99
+ * - Challenge ends
100
+ */
101
+ export type ChallengeState = {
102
+ /**
103
+ * Attacking character
104
+ */
105
+ attacker: CardId;
106
+
107
+ /**
108
+ * Defending character or location
109
+ */
110
+ defender: CardId;
111
+
112
+ /**
113
+ * Calculated damage attacker will deal
114
+ *
115
+ * Rule 4.3.6.14: Based on Strength with modifiers
116
+ * Rule 10.3: Challenger +N applies
117
+ */
118
+ attackerDamage: number;
119
+
120
+ /**
121
+ * Calculated damage defender will deal
122
+ *
123
+ * Rule 4.3.6.14: Based on Strength with modifiers
124
+ * Rule 4.3.6.22: Locations deal no damage
125
+ */
126
+ defenderDamage: number;
127
+ };
128
+
129
+ /**
130
+ * Turn Metadata
131
+ *
132
+ * Tracks actions taken this turn for validation and cleanup.
133
+ * Reset at start of each turn.
134
+ */
135
+ export type TurnMetadata = {
136
+ /**
137
+ * Cards played this turn
138
+ *
139
+ * Tracked for effects that reference "cards played this turn"
140
+ */
141
+ cardsPlayedThisTurn: CardId[];
142
+
143
+ /**
144
+ * Characters that quested this turn
145
+ *
146
+ * Rule 4.3.5: Each character can quest once per turn
147
+ * Tracked for effects that reference "whenever quests"
148
+ */
149
+ charactersQuesting: CardId[];
150
+
151
+ /**
152
+ * Whether player has put a card into inkwell this turn
153
+ *
154
+ * Rule 4.3.3: Limited to once per turn
155
+ */
156
+ inkedThisTurn: boolean;
157
+ };
158
+
159
+ /**
160
+ * Lorcana-specific Game State
161
+ *
162
+ * Complete game state for Disney Lorcana extending base game state structure.
163
+ * All Lorcana-specific data nested under `lorcana` property.
164
+ */
165
+ export type LorcanaState = {
166
+ /**
167
+ * Players in the game
168
+ *
169
+ * Rule 2.1: Standard format is 2 players
170
+ */
171
+ players: PlayerId[];
172
+
173
+ /**
174
+ * Current player index (into players array)
175
+ *
176
+ * Rule 1.3: Active player takes their turn
177
+ */
178
+ currentPlayerIndex: number;
179
+
180
+ /**
181
+ * Turn number (starts at 1)
182
+ *
183
+ * Rule 3.1: First turn determined randomly
184
+ * Rule 4.2.3.2: Starting player skips draw on turn 1
185
+ */
186
+ turnNumber: number;
187
+
188
+ /**
189
+ * Current phase
190
+ *
191
+ * Rule 4.1: Three phases - Beginning, Main, End
192
+ */
193
+ phase: LorcanaPhase;
194
+
195
+ /**
196
+ * Lorcana-specific game state
197
+ */
198
+ lorcana: {
199
+ /**
200
+ * Lore totals for each player
201
+ *
202
+ * Rule 1.9.1.1: Win condition - first to 20 lore
203
+ * Rule 3.1.4: Starts at 0
204
+ * Rule 4.2.2.2: Gained from locations during Set step
205
+ * Rule 4.3.5.8: Gained from questing
206
+ */
207
+ lore: Record<PlayerId, number>;
208
+
209
+ /**
210
+ * Ink management
211
+ *
212
+ * Rule 4.3.3: Put card into inkwell once per turn
213
+ * Rule 8.5.1: Each ink card represents 1 ink
214
+ */
215
+ ink: {
216
+ /**
217
+ * Available ink this turn (can be spent)
218
+ *
219
+ * Replenished when cards readied at start of turn
220
+ */
221
+ available: Record<PlayerId, number>;
222
+
223
+ /**
224
+ * Total ink capacity (total cards in inkwell)
225
+ *
226
+ * Never decreases, only increases when inking
227
+ */
228
+ total: Record<PlayerId, number>;
229
+ };
230
+
231
+ /**
232
+ * Turn metadata - reset each turn
233
+ *
234
+ * Tracks what actions player has taken this turn
235
+ */
236
+ turnMetadata: TurnMetadata;
237
+
238
+ /**
239
+ * Character states
240
+ *
241
+ * Keyed by CardId, tracks runtime state of each character in play
242
+ */
243
+ characterStates: Record<CardId, CharacterState>;
244
+
245
+ /**
246
+ * Permanent states (locations and items)
247
+ *
248
+ * Keyed by CardId, tracks runtime state of non-character permanents
249
+ */
250
+ permanentStates: Record<CardId, PermanentState>;
251
+
252
+ /**
253
+ * Challenge state (only present during challenge)
254
+ *
255
+ * Rule 4.3.6: Challenge mechanics
256
+ * Optional - only set during challenge resolution
257
+ */
258
+ challengeState?: ChallengeState;
259
+ };
260
+ };
@@ -0,0 +1,126 @@
1
+ /**
2
+ * Move Enumeration Types
3
+ *
4
+ * Type definitions for the move enumeration system that allows AI agents
5
+ * and UI components to discover available moves and their parameters.
6
+ */
7
+
8
+ /**
9
+ * Parameter field schema
10
+ *
11
+ * Describes a single parameter field for a move, including its type,
12
+ * description, and valid values.
13
+ */
14
+ export type ParamFieldSchema = {
15
+ /** Parameter name */
16
+ name: string;
17
+
18
+ /** Parameter type */
19
+ type: "cardId" | "playerId" | "number" | "boolean" | "object" | "string";
20
+
21
+ /** Human-readable description */
22
+ description: string;
23
+
24
+ /** For cardId/playerId type: valid IDs that can be used */
25
+ validValues?: string[];
26
+
27
+ /** For enum types: valid enum values */
28
+ enumValues?: string[];
29
+ };
30
+
31
+ /**
32
+ * Move parameter schema
33
+ *
34
+ * Defines the structure of parameters a move accepts, including
35
+ * required and optional fields.
36
+ */
37
+ export type MoveParamSchema = {
38
+ /** Required parameters */
39
+ required: ParamFieldSchema[];
40
+
41
+ /** Optional parameters */
42
+ optional?: ParamFieldSchema[];
43
+ };
44
+
45
+ /**
46
+ * Available move information
47
+ *
48
+ * Metadata about a move that is available to execute, including
49
+ * display information and parameter schema.
50
+ */
51
+ export type AvailableMoveInfo = {
52
+ /** Unique move identifier */
53
+ moveId: string;
54
+
55
+ /** Human-readable display name */
56
+ displayName: string;
57
+
58
+ /** Brief description of what the move does */
59
+ description: string;
60
+
61
+ /** Icon hint for UI (optional) */
62
+ icon?: string;
63
+
64
+ /** Parameter schema (undefined if move has no parameters) */
65
+ paramSchema?: MoveParamSchema;
66
+ };
67
+
68
+ /**
69
+ * Parameter information
70
+ *
71
+ * Detailed information about a specific parameter, including
72
+ * type, description, and constraints.
73
+ */
74
+ export type ParameterInfo = {
75
+ /** Parameter type */
76
+ type: "cardId" | "playerId" | "number" | "boolean" | "object" | "string";
77
+
78
+ /** Human-readable description */
79
+ description: string;
80
+
81
+ /** Valid values for this parameter */
82
+ validValues?: any[];
83
+
84
+ /** Minimum value (for number types) */
85
+ min?: number;
86
+
87
+ /** Maximum value (for number types) */
88
+ max?: number;
89
+ };
90
+
91
+ /**
92
+ * Move parameter options
93
+ *
94
+ * All valid parameter combinations for a specific move, along with
95
+ * metadata about each parameter field.
96
+ */
97
+ export type MoveParameterOptions = {
98
+ /** All valid parameter combinations for this move */
99
+ validCombinations: Record<string, any>[];
100
+
101
+ /** Information about each parameter */
102
+ parameterInfo: Record<string, ParameterInfo>;
103
+ };
104
+
105
+ /**
106
+ * Move validation error
107
+ *
108
+ * Detailed information about why a move cannot be executed,
109
+ * including error code, reason, context, and suggestions.
110
+ */
111
+ export type MoveValidationError = {
112
+ /** Move that was attempted */
113
+ moveId: string;
114
+
115
+ /** Error code for programmatic handling */
116
+ errorCode: string;
117
+
118
+ /** Human-readable reason */
119
+ reason: string;
120
+
121
+ /** Additional error context */
122
+ context?: Record<string, any>;
123
+
124
+ /** Suggested actions to make the move valid */
125
+ suggestions?: string[];
126
+ };
@@ -0,0 +1,216 @@
1
+ /**
2
+ * Lorcana Move Parameter Types
3
+ *
4
+ * Type-safe move parameters using discriminated unions for better type safety.
5
+ * Each move has explicit parameter types that leverage TypeScript's type narrowing.
6
+ */
7
+
8
+ import type { CardId, PlayerId } from "./branded-types";
9
+ import type { LorcanaCardMeta } from "./game-state";
10
+
11
+ /**
12
+ * Play Card Cost Types - Discriminated Union
13
+ *
14
+ * Uses discriminated unions for type-safe alternative costs.
15
+ * TypeScript can narrow types based on the `cost` discriminator.
16
+ */
17
+ export type PlayCardCost =
18
+ | {
19
+ /** Standard ink cost payment */
20
+ cost: "standard";
21
+ }
22
+ | {
23
+ /** Shift cost - play over another character */
24
+ cost: "shift";
25
+ /** Character to shift onto (must share name) */
26
+ shiftTarget: CardId;
27
+ }
28
+ | {
29
+ /** Sing cost - exert a character to play a song */
30
+ cost: "sing";
31
+ /** Character exerting to sing the song */
32
+ singer: CardId;
33
+ }
34
+ | {
35
+ /** Sing Together cost - exert multiple characters */
36
+ cost: "singTogether";
37
+ /** Characters exerting to sing together */
38
+ singers: CardId[];
39
+ }
40
+ | {
41
+ /** Free play (for effects that play cards) */
42
+ cost: "free";
43
+ };
44
+
45
+ /**
46
+ * Lorcana Move Parameters
47
+ *
48
+ * Exhaustive type definition for all Lorcana moves.
49
+ * Each move has explicit, well-typed parameters.
50
+ */
51
+ export type LorcanaMoveParams = {
52
+ // ===== Setup Moves =====
53
+ /**
54
+ * Choose who goes first
55
+ *
56
+ * Rule 3.1.1: First player determined randomly
57
+ */
58
+ chooseWhoGoesFirstMove: { playerId: PlayerId };
59
+
60
+ /**
61
+ * Alter hand (mulligan)
62
+ *
63
+ * Rule 3.1.6: Players may mulligan by putting cards on bottom of deck
64
+ */
65
+ alterHand: {
66
+ playerId: PlayerId;
67
+ /** Cards to put on bottom of deck (empty array = keep all) */
68
+ cardsToMulligan: CardId[];
69
+ };
70
+
71
+ /**
72
+ * Draw cards (utility move for testing/effects)
73
+ */
74
+ drawCards: {
75
+ playerId: PlayerId;
76
+ count: number;
77
+ };
78
+
79
+ // ===== Resource Moves =====
80
+ /**
81
+ * Put a card into the inkwell
82
+ *
83
+ * Rule 4.3.3: Once per turn, put an inkable card into inkwell
84
+ */
85
+ putACardIntoTheInkwell: {
86
+ cardId: CardId;
87
+ };
88
+
89
+ // ===== Core Game Moves =====
90
+ /**
91
+ * Play a card from hand
92
+ *
93
+ * Rule 4.3.4: Pay cost and put card into play (or discard for actions)
94
+ * Rule 6.3.3: Songs can be sung as alternative cost
95
+ * Rule 10.18: Shift allows playing over same-named characters
96
+ */
97
+ playCard: {
98
+ cardId: CardId;
99
+ } & PlayCardCost;
100
+
101
+ /**
102
+ * Quest with a character
103
+ *
104
+ * Rule 4.3.5: Exert character to gain lore equal to its Lore value
105
+ */
106
+ quest: {
107
+ cardId: CardId;
108
+ };
109
+
110
+ /**
111
+ * Challenge with a character
112
+ *
113
+ * Rule 4.3.6: Attack another character or location
114
+ */
115
+ challenge: {
116
+ /** Attacking character */
117
+ attackerId: CardId;
118
+ /** Defending character or location */
119
+ defenderId: CardId;
120
+ };
121
+
122
+ /**
123
+ * Sing a song (legacy - prefer playCard with "sing" cost)
124
+ *
125
+ * Rule 6.3.3: Exert a character to play a song for free
126
+ */
127
+ sing: {
128
+ /** Character doing the singing */
129
+ singerId: CardId;
130
+ /** Song being sung */
131
+ songId: CardId;
132
+ };
133
+
134
+ /**
135
+ * Sing Together (legacy - prefer playCard with "singTogether" cost)
136
+ *
137
+ * Rule 10.10: Multiple characters exert to sing a song together
138
+ */
139
+ singTogether: {
140
+ /** Characters singing together */
141
+ singersIds: CardId[];
142
+ /** Song being sung */
143
+ songId: CardId;
144
+ };
145
+
146
+ /**
147
+ * Move a character to a location
148
+ *
149
+ * Rule 6.5: Characters can move to locations
150
+ */
151
+ moveCharacterToLocation: {
152
+ characterId: CardId;
153
+ locationId: CardId;
154
+ };
155
+
156
+ /**
157
+ * Activate an ability
158
+ *
159
+ * Rule 7: Abilities with costs can be activated
160
+ */
161
+ activateAbility: {
162
+ cardId: CardId;
163
+ /** Optional ability selection (if card has multiple) */
164
+ abilityIndex?: number;
165
+ /** Alternative identification by ability text */
166
+ abilityText?: string;
167
+ /** Alternative cost for ability (e.g., sing to activate) */
168
+ alternativeCost?: PlayCardCost;
169
+ };
170
+
171
+ // ===== Effect Resolution =====
172
+ /**
173
+ * Resolve an effect from the bag
174
+ *
175
+ * Rule 8.7: Bag system for triggered effects
176
+ */
177
+ resolveBag: {
178
+ bagId: string;
179
+ /** Parameters for the effect */
180
+ params: unknown;
181
+ };
182
+
183
+ /**
184
+ * Resolve an effect
185
+ */
186
+ resolveEffect: {
187
+ effectId: string;
188
+ /** Parameters for the effect */
189
+ params: unknown;
190
+ };
191
+
192
+ // ===== Manual Actions (Testing/Debug) =====
193
+ /**
194
+ * Manually exert a card (for testing)
195
+ */
196
+ manualExert: {
197
+ cardId: CardId;
198
+ };
199
+
200
+ // ===== Standard Moves =====
201
+ /**
202
+ * Pass turn to next player
203
+ *
204
+ * Rule 4.1.2: Player completes their turn
205
+ */
206
+ passTurn: Record<string, never>;
207
+
208
+ /**
209
+ * Concede the game
210
+ *
211
+ * Rule 1.9.1.2: Player can concede at any time
212
+ */
213
+ concede: Record<string, never>;
214
+ };
215
+
216
+ // LorcanaGameState is exported from game-state.ts - import from there
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Lorcana Move Validators
3
+ *
4
+ * Public exports for composable validation functions
5
+ */
6
+
7
+ export * from "./move-validators";