@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,283 @@
1
+ /**
2
+ * Lorcana Zone Configurations
3
+ *
4
+ * Task 1.4: Define all 5 zones for Disney Lorcana
5
+ *
6
+ * Zone properties follow Comprehensive Rules Section 8:
7
+ * - visibility: "owner" (private) or "all" (public)
8
+ * - ordered: true if card order matters, false if can rearrange
9
+ * - facedown: true if cards not visible even to owner
10
+ *
11
+ * References:
12
+ * - Rule 8.1.2 (Public zones - all can see/count)
13
+ * - Rule 8.1.3 (Private zones - only owner can see)
14
+ * - Rule 8.2 (Deck - private, ordered, facedown)
15
+ * - Rule 8.3 (Hand - private, can rearrange)
16
+ * - Rule 8.4 (Play - public, all can see)
17
+ * - Rule 8.5 (Inkwell - private, facedown, can arrange)
18
+ * - Rule 8.6 (Discard - public, ordered, faceup)
19
+ */
20
+
21
+ /**
22
+ * Lorcana Zone visibility types
23
+ *
24
+ * - "owner": Private zone - only owner can see cards (Rule 8.1.3)
25
+ * - "all": Public zone - all players can see cards (Rule 8.1.2)
26
+ *
27
+ * Note: This is different from core's ZoneVisibility which uses "private"/"public"/"secret"
28
+ */
29
+ export type LorcanaZoneVisibility = "owner" | "all";
30
+
31
+ /**
32
+ * Lorcana Zone configuration
33
+ *
34
+ * Defines properties for each zone type in Lorcana.
35
+ * This is a simpler version of core's ZoneConfig, tailored to Lorcana's rule structure.
36
+ */
37
+ export type LorcanaZoneConfig = {
38
+ /**
39
+ * Who can see cards in this zone
40
+ *
41
+ * - "owner": Only the owner can see (private)
42
+ * - "all": All players can see (public)
43
+ */
44
+ visibility: LorcanaZoneVisibility;
45
+
46
+ /**
47
+ * Whether card order matters
48
+ *
49
+ * - true: Order is significant (e.g., deck, discard)
50
+ * - false: Can rearrange freely (e.g., hand, play)
51
+ */
52
+ ordered: boolean;
53
+
54
+ /**
55
+ * Whether cards are facedown
56
+ *
57
+ * - true: Cards not visible even to owner (e.g., deck, inkwell)
58
+ * - false: Cards are visible to appropriate players
59
+ */
60
+ facedown: boolean;
61
+ };
62
+
63
+ /**
64
+ * Lorcana zone identifiers
65
+ *
66
+ * The 5 zones in Disney Lorcana (Rule 8)
67
+ */
68
+ export type LorcanaZoneId =
69
+ | "deck"
70
+ | "hand"
71
+ | "play"
72
+ | "discard"
73
+ | "inkwell"
74
+ | "limbo";
75
+
76
+ /**
77
+ * Lorcana Zone Configurations
78
+ *
79
+ * Complete zone definitions for Disney Lorcana following Comprehensive Rules Section 8.
80
+ *
81
+ * @example
82
+ * ```typescript
83
+ * // Check if deck is private
84
+ * if (lorcanaZones.deck.visibility === "owner") {
85
+ * // Only owner can see deck cards
86
+ * }
87
+ *
88
+ * // Check if discard maintains order
89
+ * if (lorcanaZones.discard.ordered) {
90
+ * // Card order matters in discard
91
+ * }
92
+ * ```
93
+ */
94
+ export const lorcanaZones: Record<LorcanaZoneId, LorcanaZoneConfig> = {
95
+ /**
96
+ * Deck Zone
97
+ *
98
+ * Rule 8.2: Player's deck of cards to draw from
99
+ * - Private zone (Rule 8.2.2)
100
+ * - Ordered (cards remain in sequence)
101
+ * - Facedown (cannot look at)
102
+ *
103
+ * Key Rules:
104
+ * - 8.2.2: Cards remain facedown at all times
105
+ * - 8.2.2: Players can count cards but not see them
106
+ * - 8.2.3: Draw from top of deck
107
+ * - 3.2.1.2: Attempting to draw from empty deck = loss
108
+ */
109
+ deck: {
110
+ visibility: "owner",
111
+ ordered: true,
112
+ facedown: true,
113
+ },
114
+
115
+ /**
116
+ * Hand Zone
117
+ *
118
+ * Rule 8.3: Where drawn cards are held
119
+ * - Private zone (Rule 8.3.2)
120
+ * - Unordered (can rearrange freely)
121
+ * - Not facedown (owner can see)
122
+ *
123
+ * Key Rules:
124
+ * - 8.3.2: Can't look at opponent's hand
125
+ * - 8.3.2: Can rearrange your own hand
126
+ * - 8.3.3: No maximum hand size
127
+ * - 8.3.4: Discard means choose from hand
128
+ */
129
+ hand: {
130
+ visibility: "owner",
131
+ ordered: false,
132
+ facedown: false,
133
+ },
134
+
135
+ /**
136
+ * Play Zone
137
+ *
138
+ * Rule 8.4: Where characters, items, and locations are played
139
+ * - Public zone (Rule 8.4.3)
140
+ * - Unordered (no specific arrangement)
141
+ * - Not facedown (all can see)
142
+ *
143
+ * Key Rules:
144
+ * - 8.4.1: Only characters, items, locations can be here
145
+ * - 8.4.2: Only cards here are "in play"
146
+ * - 8.4.3: All players can look at and count cards
147
+ * - 8.4.4: Leaving play may trigger abilities
148
+ */
149
+ play: {
150
+ visibility: "all",
151
+ ordered: false,
152
+ facedown: false,
153
+ },
154
+
155
+ /**
156
+ * Discard Zone
157
+ *
158
+ * Rule 8.6: Where banished cards and resolved actions go
159
+ * - Public zone (Rule 8.6.3)
160
+ * - Ordered (maintain sequence)
161
+ * - Not facedown (all can see)
162
+ *
163
+ * Key Rules:
164
+ * - 8.6.2: Banished cards and actions go here
165
+ * - 8.6.3: Cards remain faceup
166
+ * - 8.6.3: Can look at and count anytime
167
+ * - 8.6.3: Owner can rearrange their own discard
168
+ * - 8.6.4: Multiple cards enter in owner's chosen order
169
+ */
170
+ discard: {
171
+ visibility: "all",
172
+ ordered: true,
173
+ facedown: false,
174
+ },
175
+
176
+ /**
177
+ * Inkwell Zone
178
+ *
179
+ * Rule 8.5: Where ink cards are placed to generate resources
180
+ * - Private zone (Rule 8.5.3)
181
+ * - Unordered (can arrange as convenient)
182
+ * - Facedown (cannot look at)
183
+ *
184
+ * Key Rules:
185
+ * - 8.5.1: Each card represents 1 ink
186
+ * - 8.5.2: Cards enter facedown and ready
187
+ * - 8.5.3: Can't look at cards in inkwell (even your own)
188
+ * - 8.5.4: Can arrange physically but stay facedown
189
+ * - 8.5.5: Can put additional cards via effects
190
+ * - 8.5.6: Cards put in don't need inkwell symbol
191
+ * - 4.3.3: Limited to once per turn normally
192
+ */
193
+ inkwell: {
194
+ visibility: "owner",
195
+ ordered: false,
196
+ facedown: true,
197
+ },
198
+
199
+ /**
200
+ * Limbo Zone (Phased Out)
201
+ *
202
+ * For cards temporarily out of play:
203
+ * - Shifted cards (the underlying card when another card shifts onto it)
204
+ * - Action cards while resolving their effects
205
+ * - Cards being processed by game mechanics
206
+ *
207
+ * - Private zone (not directly interactable)
208
+ * - Ordered (maintain sequence for stacking)
209
+ * - Not facedown (tracking purposes)
210
+ */
211
+ limbo: {
212
+ visibility: "owner",
213
+ ordered: true,
214
+ facedown: false,
215
+ },
216
+ };
217
+
218
+ /**
219
+ * Type guard to check if a string is a valid Lorcana zone ID
220
+ *
221
+ * @param value - Value to check
222
+ * @returns True if value is a valid LorcanaZoneId
223
+ */
224
+ export const isLorcanaZoneId = (value: unknown): value is LorcanaZoneId => {
225
+ return (
226
+ typeof value === "string" &&
227
+ ["deck", "hand", "play", "discard", "inkwell", "limbo"].includes(value)
228
+ );
229
+ };
230
+
231
+ /**
232
+ * Get zone configuration by ID
233
+ *
234
+ * @param zoneId - The zone identifier
235
+ * @returns Zone configuration
236
+ * @throws Error if zoneId is invalid
237
+ */
238
+ export const getZoneConfig = (zoneId: string): LorcanaZoneConfig => {
239
+ if (!isLorcanaZoneId(zoneId)) {
240
+ throw new Error(`Invalid zone ID: ${zoneId}`);
241
+ }
242
+ return lorcanaZones[zoneId];
243
+ };
244
+
245
+ /**
246
+ * Check if zone is public (all players can see)
247
+ *
248
+ * @param zoneId - The zone identifier
249
+ * @returns True if zone is public
250
+ */
251
+ export const isPublicZone = (zoneId: LorcanaZoneId): boolean => {
252
+ return lorcanaZones[zoneId].visibility === "all";
253
+ };
254
+
255
+ /**
256
+ * Check if zone is private (only owner can see)
257
+ *
258
+ * @param zoneId - The zone identifier
259
+ * @returns True if zone is private
260
+ */
261
+ export const isPrivateZone = (zoneId: LorcanaZoneId): boolean => {
262
+ return lorcanaZones[zoneId].visibility === "owner";
263
+ };
264
+
265
+ /**
266
+ * Check if zone maintains card order
267
+ *
268
+ * @param zoneId - The zone identifier
269
+ * @returns True if zone is ordered
270
+ */
271
+ export const isOrderedZone = (zoneId: LorcanaZoneId): boolean => {
272
+ return lorcanaZones[zoneId].ordered;
273
+ };
274
+
275
+ /**
276
+ * Check if zone cards are facedown
277
+ *
278
+ * @param zoneId - The zone identifier
279
+ * @returns True if zone is facedown
280
+ */
281
+ export const isFacedownZone = (zoneId: LorcanaZoneId): boolean => {
282
+ return lorcanaZones[zoneId].facedown;
283
+ };
package/src/index.ts ADDED
@@ -0,0 +1,189 @@
1
+ /**
2
+ * @drmxrcy/tcg-lorcana - Disney Lorcana TCG Engine
3
+ *
4
+ * A complete implementation of Disney Lorcana using the @drmxrcy/tcg-core framework.
5
+ * This package serves as both a production-ready Lorcana engine and a reference
6
+ * implementation demonstrating best practices for building TCG engines.
7
+ *
8
+ * Key Concepts:
9
+ * - NO defineMove(), defineZone(), definePhase(), defineCard() helpers
10
+ * - Use GameDefinition<TState, TMoves> type directly
11
+ * - Zones are simple state arrays: Record<PlayerId, CardId[]>
12
+ * - Cards are plain objects in lookup tables
13
+ * - Moves use GameMoveDefinitions with condition and reducer
14
+ * - Flow is optional - use FlowDefinition or simple state tracking
15
+ */
16
+
17
+ // Re-export core framework types for convenience
18
+ export type {
19
+ GameDefinition,
20
+ MoveContext,
21
+ MoveExecutionResult,
22
+ RuleEngine,
23
+ RuleEngineOptions,
24
+ } from "@drmxrcy/tcg-core";
25
+ // Export card-utils
26
+ export {
27
+ canInk,
28
+ canQuest,
29
+ getAllKeywords,
30
+ getAmpersandNames,
31
+ getLoreValue,
32
+ getMoveCost,
33
+ getShiftCost,
34
+ getShiftTargetName,
35
+ getStrength,
36
+ getTotalKeyword,
37
+ getWillpower,
38
+ hasAmpersandName,
39
+ hasBodyguard,
40
+ hasEvasive,
41
+ hasKeyword,
42
+ hasReckless,
43
+ hasRush,
44
+ hasSameName,
45
+ hasShift,
46
+ hasVanish,
47
+ hasWard,
48
+ isAction,
49
+ isCharacter,
50
+ isItem,
51
+ isLocation,
52
+ isSong,
53
+ } from "./card-utils";
54
+ // Card/Ability type exports - explicit to avoid conflicts with types/keywords.ts
55
+ export {
56
+ // Version and examples
57
+ ABILITY_TYPES_VERSION,
58
+ // Ability types (main)
59
+ type Ability,
60
+ // Cost types
61
+ type AbilityCost,
62
+ type AbilityWithText,
63
+ type ActivatedAbility,
64
+ type ActivatedRestriction, // @deprecated - use Restriction
65
+ type Amount,
66
+ activated,
67
+ and,
68
+ banishSelfCost,
69
+ boost,
70
+ type CardTarget,
71
+ type CharacterTarget,
72
+ // Effect types for composite effects
73
+ type ChoiceEffect,
74
+ COMMON_TRIGGERS,
75
+ // Condition types
76
+ type Condition,
77
+ type CostComponent,
78
+ challenger,
79
+ discardCost,
80
+ // Effect types
81
+ type Effect,
82
+ type EffectDuration,
83
+ EXAMPLE_ABILITIES,
84
+ exertAndBanishItemCost,
85
+ exertAndInkCost,
86
+ exertCost,
87
+ type ForEachCounter,
88
+ getInkCost,
89
+ hasCharacterCount,
90
+ hasCharacterNamed,
91
+ hasRestriction,
92
+ type InChallengeCondition,
93
+ type ItemTarget,
94
+ ifUsedShift,
95
+ inChallenge,
96
+ isActivatedAbility,
97
+ isCharacterTargetQuery,
98
+ isControlFlowEffect,
99
+ isCountCondition,
100
+ isFreeCost,
101
+ isItemTargetQuery,
102
+ // Note: KeywordType, SimpleKeywordType, ParameterizedKeywordType, ValueKeywordType, ComplexKeywordType
103
+ // and isSimpleKeyword, isParameterizedKeyword, isValueKeyword, isShiftKeyword, isComplexKeyword
104
+ // are exported from ./types/keywords.ts
105
+ isKeywordAbility,
106
+ isLocationTargetQuery,
107
+ isLogicalCondition,
108
+ isNamedAbility,
109
+ isParameterizedKeywordAbility,
110
+ isPhaseTrigger,
111
+ isPlayerChoice,
112
+ isReplacementAbility,
113
+ isSelfTrigger,
114
+ isShiftKeywordAbility,
115
+ // Keyword ability type guards
116
+ isSimpleKeywordAbility,
117
+ isStaticAbility,
118
+ isTriggeredAbility,
119
+ isTriggerSubjectQuery,
120
+ isValueKeywordAbility,
121
+ isVariableAmount,
122
+ type KeywordAbility,
123
+ // Builders
124
+ keyword,
125
+ type LocationTarget,
126
+ // Composite effect types
127
+ type OptionalEffect,
128
+ or,
129
+ type ParameterizedKeywordAbility,
130
+ // Target types
131
+ type PlayerTarget,
132
+ type ReplacementAbility,
133
+ type Restriction,
134
+ requiresBanish,
135
+ requiresDiscard,
136
+ requiresExert,
137
+ requiresInk,
138
+ resist,
139
+ // Composite effect types
140
+ type SequenceEffect,
141
+ type ShiftKeywordAbility,
142
+ // Strict keyword ability variants
143
+ type SimpleKeywordAbility,
144
+ type SimpleKeywordType,
145
+ type StaticAbility,
146
+ type StaticAffects,
147
+ type StaticEffect,
148
+ shift,
149
+ shiftInk,
150
+ singer,
151
+ singTogether,
152
+ staticAbility,
153
+ type TargetController,
154
+ type TargetZone,
155
+ // Trigger types
156
+ type Trigger,
157
+ type TriggerCardType,
158
+ type TriggerEvent,
159
+ type TriggeredAbility,
160
+ type TriggerRestriction,
161
+ type TriggerSubject,
162
+ type TriggerSubjectEnum,
163
+ type TriggerSubjectQuery,
164
+ type TriggerTiming,
165
+ targetsCharacters,
166
+ triggered,
167
+ type ValueKeywordAbility,
168
+ type VariableAmount,
169
+ whileHasDamage,
170
+ whileNoDamage,
171
+ youMay,
172
+ } from "./cards";
173
+ // Spec 1: Foundation & Types
174
+ export * from "./deck-validation";
175
+ // Engine exports
176
+ export { LorcanaEngine } from "./engine/lorcana-engine";
177
+ // Targeting DSL
178
+ export * from "./targeting";
179
+ // Type exports
180
+ export * from "./types";
181
+ // Move enumeration type exports
182
+ export type {
183
+ AvailableMoveInfo,
184
+ MoveParameterOptions,
185
+ MoveParamSchema,
186
+ MoveValidationError,
187
+ ParameterInfo,
188
+ ParamFieldSchema,
189
+ } from "./types/move-enumeration";
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Lorcana Operations
3
+ *
4
+ * Public exports for Lorcana-specific operations
5
+ */
6
+
7
+ export * from "./lorcana-operations";