@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.
- package/README.md +160 -0
- package/package.json +45 -0
- package/src/__tests__/integration/move-enumeration.test.ts +256 -0
- package/src/__tests__/rules/section-01-concepts.test.ts +426 -0
- package/src/__tests__/rules/section-03-gameplay.test.ts +298 -0
- package/src/__tests__/rules/section-04-turn-structure.test.ts +708 -0
- package/src/__tests__/rules/section-05-cards.test.ts +158 -0
- package/src/__tests__/rules/section-06-card-types.test.ts +342 -0
- package/src/__tests__/rules/section-07-abilities.test.ts +333 -0
- package/src/__tests__/rules/section-08-zones.test.ts +231 -0
- package/src/__tests__/rules/section-09-damage.test.ts +148 -0
- package/src/__tests__/rules/section-10-keywords.test.ts +469 -0
- package/src/__tests__/spec-01-foundation-types.test.ts +534 -0
- package/src/__tests__/spec-02-zones-card-states.test.ts +295 -0
- package/src/card-utils.ts +302 -0
- package/src/cards/README.md +296 -0
- package/src/cards/abilities/index.ts +175 -0
- package/src/cards/index.ts +10 -0
- package/src/deck-validation.ts +175 -0
- package/src/engine/lorcana-engine.ts +625 -0
- package/src/game-definition/__tests__/core-zone-integration.test.ts +553 -0
- package/src/game-definition/__tests__/zone-operations.test.ts +362 -0
- package/src/game-definition/__tests__/zones.test.ts +176 -0
- package/src/game-definition/definition.ts +45 -0
- package/src/game-definition/flow/turn-flow.ts +216 -0
- package/src/game-definition/index.ts +31 -0
- package/src/game-definition/moves/abilities/activate-ability.ts +51 -0
- package/src/game-definition/moves/core/__tests__/move-parameter-enumeration.test.ts +316 -0
- package/src/game-definition/moves/core/challenge.test.ts +545 -0
- package/src/game-definition/moves/core/challenge.ts +81 -0
- package/src/game-definition/moves/core/play-card.ts +83 -0
- package/src/game-definition/moves/core/quest.test.ts +448 -0
- package/src/game-definition/moves/core/quest.ts +49 -0
- package/src/game-definition/moves/debug/manual-exert.ts +36 -0
- package/src/game-definition/moves/effects/resolve-bag.ts +35 -0
- package/src/game-definition/moves/effects/resolve-effect.ts +34 -0
- package/src/game-definition/moves/index.ts +85 -0
- package/src/game-definition/moves/locations/move-character-to-location.ts +42 -0
- package/src/game-definition/moves/resources/put-card-into-inkwell.test.ts +462 -0
- package/src/game-definition/moves/resources/put-card-into-inkwell.ts +51 -0
- package/src/game-definition/moves/setup/alter-hand.test.ts +395 -0
- package/src/game-definition/moves/setup/alter-hand.ts +210 -0
- package/src/game-definition/moves/setup/choose-first-player.test.ts +450 -0
- package/src/game-definition/moves/setup/choose-first-player.ts +105 -0
- package/src/game-definition/moves/setup/draw-cards.ts +37 -0
- package/src/game-definition/moves/songs/sing-together.ts +47 -0
- package/src/game-definition/moves/songs/sing.ts +56 -0
- package/src/game-definition/moves/standard/concede.test.ts +189 -0
- package/src/game-definition/moves/standard/concede.ts +72 -0
- package/src/game-definition/moves/standard/pass-turn.ts +49 -0
- package/src/game-definition/setup/game-setup.ts +19 -0
- package/src/game-definition/trackers/tracker-config.ts +23 -0
- package/src/game-definition/win-conditions/lore-victory.ts +26 -0
- package/src/game-definition/zone-operations.ts +405 -0
- package/src/game-definition/zones/zone-configs.ts +59 -0
- package/src/game-definition/zones.ts +283 -0
- package/src/index.ts +189 -0
- package/src/operations/index.ts +7 -0
- package/src/operations/lorcana-operations.ts +288 -0
- package/src/queries/README.md +56 -0
- package/src/resolvers/__tests__/condition-resolver.test.ts +301 -0
- package/src/resolvers/condition-registry.ts +70 -0
- package/src/resolvers/condition-resolver.ts +85 -0
- package/src/resolvers/conditions/basic.ts +81 -0
- package/src/resolvers/conditions/card-state.ts +12 -0
- package/src/resolvers/conditions/comparison.ts +102 -0
- package/src/resolvers/conditions/existence.ts +219 -0
- package/src/resolvers/conditions/history.ts +68 -0
- package/src/resolvers/conditions/index.ts +15 -0
- package/src/resolvers/conditions/logical.ts +55 -0
- package/src/resolvers/conditions/resolution.ts +41 -0
- package/src/resolvers/conditions/revealed.ts +42 -0
- package/src/resolvers/conditions/zone.ts +84 -0
- package/src/setup.test.ts +18 -0
- package/src/targeting/__tests__/filter-resolver.test.ts +294 -0
- package/src/targeting/__tests__/real-cards-targeting.test.ts +303 -0
- package/src/targeting/__tests__/targeting-dsl.test.ts +386 -0
- package/src/targeting/enum-expansion.ts +387 -0
- package/src/targeting/filter-registry.ts +322 -0
- package/src/targeting/filter-resolver.ts +145 -0
- package/src/targeting/index.ts +91 -0
- package/src/targeting/lorcana-target-dsl.ts +495 -0
- package/src/targeting/targeting-ui.ts +407 -0
- package/src/testing/index.ts +14 -0
- package/src/testing/lorcana-test-engine.ts +813 -0
- package/src/types/README.md +303 -0
- package/src/types/__tests__/lorcana-state.test.ts +168 -0
- package/src/types/__tests__/move-enumeration.test.ts +179 -0
- package/src/types/branded-types.ts +106 -0
- package/src/types/game-state.ts +184 -0
- package/src/types/index.ts +87 -0
- package/src/types/keywords.ts +187 -0
- package/src/types/lorcana-state.ts +260 -0
- package/src/types/move-enumeration.ts +126 -0
- package/src/types/move-params.ts +216 -0
- package/src/validators/index.ts +7 -0
- package/src/validators/move-validators.ts +374 -0
- package/src/zones/card-state.ts +234 -0
- package/src/zones/index.ts +42 -0
- package/src/zones/zone-config.ts +150 -0
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import type { RevealedMatchesNamedCondition } from "@drmxrcy/tcg-lorcana-types";
|
|
2
|
+
import { conditionRegistry } from "../condition-registry";
|
|
3
|
+
|
|
4
|
+
conditionRegistry.register<RevealedMatchesNamedCondition>(
|
|
5
|
+
"revealed-matches-named",
|
|
6
|
+
{
|
|
7
|
+
complexity: 40,
|
|
8
|
+
evaluate: (_condition, _sourceCard, { state, context, registry }) => {
|
|
9
|
+
const namedCard = state.external.namedCard;
|
|
10
|
+
if (!namedCard) return false;
|
|
11
|
+
|
|
12
|
+
// Check revealed cards in context
|
|
13
|
+
if (!context?.revealedCards || context.revealedCards.length === 0) {
|
|
14
|
+
return false;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
// Usually checks the "top card" or just "the revealed card"
|
|
18
|
+
// We iterate all revealed cards in context. If any matches, we return true?
|
|
19
|
+
// Or stricter: "If IT matches". Usually implies a single card was revealed.
|
|
20
|
+
// We'll check if ANY of the revealed cards matches the name.
|
|
21
|
+
|
|
22
|
+
return context.revealedCards.some((cardId) => {
|
|
23
|
+
// We need lookup definition from registry using the card ID?
|
|
24
|
+
// context.revealedCards are IDs?
|
|
25
|
+
// Wait, context.revealedCards in DSL is string[]. Assuming IDs.
|
|
26
|
+
|
|
27
|
+
// We need to look up the card in state to get definitionId?
|
|
28
|
+
// Or if it's from deck, it might not be in state.cards yet?
|
|
29
|
+
// In Lorcana engine, usually cards in deck are fully instantiated or just definitions?
|
|
30
|
+
// If they are IDs, they should be in state.cards (even if in deck).
|
|
31
|
+
|
|
32
|
+
const card = state.internal.cards[cardId];
|
|
33
|
+
if (!card) return false;
|
|
34
|
+
|
|
35
|
+
const def = registry.getCard(card.definitionId);
|
|
36
|
+
if (!def) return false;
|
|
37
|
+
|
|
38
|
+
return def.name === namedCard || def.fullName === namedCard;
|
|
39
|
+
});
|
|
40
|
+
},
|
|
41
|
+
},
|
|
42
|
+
);
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import type { CardInstance, PlayerId } from "@drmxrcy/tcg-core";
|
|
2
|
+
import type {
|
|
3
|
+
AtLocationCondition,
|
|
4
|
+
HasCharacterHereCondition,
|
|
5
|
+
HasNamedLocationCondition,
|
|
6
|
+
ZoneCondition,
|
|
7
|
+
} from "@drmxrcy/tcg-lorcana-types";
|
|
8
|
+
import type { LorcanaCardMeta } from "../../types/game-state";
|
|
9
|
+
import { conditionRegistry } from "../condition-registry";
|
|
10
|
+
|
|
11
|
+
conditionRegistry.register<ZoneCondition>("zone", {
|
|
12
|
+
complexity: 20,
|
|
13
|
+
evaluate: (condition, sourceCard, { state, registry }) => {
|
|
14
|
+
// Determine target controller
|
|
15
|
+
const targetControllerId =
|
|
16
|
+
condition.controller === "you"
|
|
17
|
+
? sourceCard.controller
|
|
18
|
+
: (Object.keys(state.external.loreScores).find(
|
|
19
|
+
(id) => id !== sourceCard.controller,
|
|
20
|
+
) as PlayerId);
|
|
21
|
+
|
|
22
|
+
if (!targetControllerId) return false;
|
|
23
|
+
|
|
24
|
+
// Filter cards
|
|
25
|
+
const matchingCards = Object.values(state.internal.cards).filter((c) => {
|
|
26
|
+
if (c.controller !== targetControllerId) return false;
|
|
27
|
+
if (c.zone !== condition.zone) return false;
|
|
28
|
+
|
|
29
|
+
// Registry check
|
|
30
|
+
if (condition.cardType || condition.cardName) {
|
|
31
|
+
const def = registry.getCard(c.definitionId);
|
|
32
|
+
if (!def) return false;
|
|
33
|
+
if (condition.cardType && def.cardType !== condition.cardType)
|
|
34
|
+
return false;
|
|
35
|
+
if (condition.cardName && def.name !== condition.cardName) return false;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
return true;
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
if (condition.hasCards !== undefined) {
|
|
42
|
+
return matchingCards.length > 0 === condition.hasCards;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
return matchingCards.length > 0;
|
|
46
|
+
},
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
conditionRegistry.register<AtLocationCondition>("at-location", {
|
|
50
|
+
complexity: 20,
|
|
51
|
+
evaluate: (_condition, sourceCard) => {
|
|
52
|
+
// Check if sourceCard has attached location
|
|
53
|
+
return !!sourceCard.atLocationId;
|
|
54
|
+
},
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
conditionRegistry.register<HasCharacterHereCondition>("has-character-here", {
|
|
58
|
+
complexity: 30,
|
|
59
|
+
evaluate: (_condition, sourceCard, { state }) => {
|
|
60
|
+
// Check all cards in play to see if their 'atLocationId' matches sourceCard.id
|
|
61
|
+
return Object.values(state.internal.cards).some((c) => {
|
|
62
|
+
const card = c as CardInstance<LorcanaCardMeta>;
|
|
63
|
+
return card.zone === "play" && card.atLocationId === sourceCard.id;
|
|
64
|
+
});
|
|
65
|
+
},
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
conditionRegistry.register<HasNamedLocationCondition>("has-named-location", {
|
|
69
|
+
complexity: 40,
|
|
70
|
+
evaluate: (condition, sourceCard, { state, registry }) => {
|
|
71
|
+
return Object.values(state.internal.cards).some((c) => {
|
|
72
|
+
if (c.zone !== "play") return false;
|
|
73
|
+
const def = registry.getCard(c.definitionId);
|
|
74
|
+
if (def?.cardType !== "location") return false;
|
|
75
|
+
if (condition.name && def.name !== condition.name) return false;
|
|
76
|
+
|
|
77
|
+
if (condition.controller === "you")
|
|
78
|
+
return c.controller === sourceCard.controller;
|
|
79
|
+
if (condition.controller === "opponent")
|
|
80
|
+
return c.controller !== sourceCard.controller;
|
|
81
|
+
return true;
|
|
82
|
+
});
|
|
83
|
+
},
|
|
84
|
+
});
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { describe, expect, it } from "bun:test";
|
|
2
|
+
|
|
3
|
+
describe("@drmxrcy/tcg-lorcana Package Setup", () => {
|
|
4
|
+
it("package is properly configured", () => {
|
|
5
|
+
// Basic smoke test to verify package structure
|
|
6
|
+
expect(true).toBe(true);
|
|
7
|
+
});
|
|
8
|
+
|
|
9
|
+
it(
|
|
10
|
+
"can import from @drmxrcy/tcg-core",
|
|
11
|
+
async () => {
|
|
12
|
+
const core = await import("@drmxrcy/tcg-core");
|
|
13
|
+
expect(core).toBeDefined();
|
|
14
|
+
expect(core.RuleEngine).toBeDefined();
|
|
15
|
+
},
|
|
16
|
+
{ timeout: 30000 },
|
|
17
|
+
);
|
|
18
|
+
});
|
|
@@ -0,0 +1,294 @@
|
|
|
1
|
+
import { describe, expect, it } from "bun:test";
|
|
2
|
+
import type { CardRegistry } from "@drmxrcy/tcg-core";
|
|
3
|
+
import type { LorcanaCardDefinition } from "@drmxrcy/tcg-lorcana-types";
|
|
4
|
+
import { createDefaultCardMeta } from "../../types/game-state";
|
|
5
|
+
import {
|
|
6
|
+
createTargetFiltersPredicate,
|
|
7
|
+
matchesLorcanaFilter,
|
|
8
|
+
sortFilters,
|
|
9
|
+
} from "../filter-resolver";
|
|
10
|
+
import type { LorcanaFilter } from "../lorcana-target-dsl";
|
|
11
|
+
|
|
12
|
+
// Mock Registry
|
|
13
|
+
const mockRegistry: CardRegistry<LorcanaCardDefinition> = {
|
|
14
|
+
getCard: (id: string) => {
|
|
15
|
+
return MOCK_DEFINITIONS[id];
|
|
16
|
+
},
|
|
17
|
+
getAllCards: () => Object.values(MOCK_DEFINITIONS),
|
|
18
|
+
hasCard: (id: string) => !!MOCK_DEFINITIONS[id],
|
|
19
|
+
queryCards: () => [],
|
|
20
|
+
getCardCount: () => 0,
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
const MOCK_DEFINITIONS: Record<string, LorcanaCardDefinition> = {
|
|
24
|
+
"char-1": {
|
|
25
|
+
id: "char-1",
|
|
26
|
+
name: "Mickey Mouse",
|
|
27
|
+
set: "set1",
|
|
28
|
+
cardType: "character",
|
|
29
|
+
cost: 3,
|
|
30
|
+
inkable: true,
|
|
31
|
+
inkType: ["ruby"],
|
|
32
|
+
strength: 3,
|
|
33
|
+
willpower: 3,
|
|
34
|
+
lore: 1,
|
|
35
|
+
abilities: [
|
|
36
|
+
{ type: "keyword", keyword: "Evasive", id: "k1", text: "Evasive" },
|
|
37
|
+
],
|
|
38
|
+
},
|
|
39
|
+
"char-2": {
|
|
40
|
+
id: "char-2",
|
|
41
|
+
name: "Donald Duck",
|
|
42
|
+
set: "set1",
|
|
43
|
+
cardType: "character",
|
|
44
|
+
cost: 5,
|
|
45
|
+
inkable: false,
|
|
46
|
+
inkType: ["sapphire"],
|
|
47
|
+
strength: 5,
|
|
48
|
+
willpower: 6,
|
|
49
|
+
lore: 2,
|
|
50
|
+
classifications: ["Hero"],
|
|
51
|
+
},
|
|
52
|
+
"loc-1": {
|
|
53
|
+
id: "loc-1",
|
|
54
|
+
name: "The Library",
|
|
55
|
+
set: "set1",
|
|
56
|
+
cardType: "location",
|
|
57
|
+
cost: 2,
|
|
58
|
+
inkable: true,
|
|
59
|
+
inkType: ["amethyst"],
|
|
60
|
+
moveCost: 1,
|
|
61
|
+
lore: 1,
|
|
62
|
+
},
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
const mockState: any = {}; // Simple mock state
|
|
66
|
+
|
|
67
|
+
describe("Filter Resolver", () => {
|
|
68
|
+
describe("Ranking", () => {
|
|
69
|
+
it("should sort filters by rank", () => {
|
|
70
|
+
const filters: LorcanaFilter[] = [
|
|
71
|
+
{ type: "cost", comparison: "eq", value: 3 },
|
|
72
|
+
{ type: "ready" },
|
|
73
|
+
{ type: "has-keyword", keyword: "Evasive" },
|
|
74
|
+
];
|
|
75
|
+
|
|
76
|
+
const sorted = sortFilters(filters);
|
|
77
|
+
expect(sorted[0].type).toBe("ready"); // Cheap
|
|
78
|
+
expect(sorted[1].type).toBe("has-keyword"); // Property
|
|
79
|
+
expect(sorted[2].type).toBe("cost"); // Numeric
|
|
80
|
+
});
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
describe("Matching", () => {
|
|
84
|
+
const char1Instance = {
|
|
85
|
+
definitionId: "char-1",
|
|
86
|
+
...createDefaultCardMeta(),
|
|
87
|
+
state: "ready",
|
|
88
|
+
damage: 0,
|
|
89
|
+
} as any;
|
|
90
|
+
|
|
91
|
+
const char1Exerted = {
|
|
92
|
+
definitionId: "char-1",
|
|
93
|
+
...createDefaultCardMeta(),
|
|
94
|
+
state: "exerted",
|
|
95
|
+
damage: 2,
|
|
96
|
+
} as any;
|
|
97
|
+
|
|
98
|
+
it("should match state filters", () => {
|
|
99
|
+
expect(
|
|
100
|
+
matchesLorcanaFilter(
|
|
101
|
+
char1Instance,
|
|
102
|
+
{ type: "ready" },
|
|
103
|
+
mockState,
|
|
104
|
+
mockRegistry,
|
|
105
|
+
),
|
|
106
|
+
).toBe(true);
|
|
107
|
+
expect(
|
|
108
|
+
matchesLorcanaFilter(
|
|
109
|
+
char1Exerted,
|
|
110
|
+
{ type: "ready" },
|
|
111
|
+
mockState,
|
|
112
|
+
mockRegistry,
|
|
113
|
+
),
|
|
114
|
+
).toBe(false);
|
|
115
|
+
|
|
116
|
+
expect(
|
|
117
|
+
matchesLorcanaFilter(
|
|
118
|
+
char1Exerted,
|
|
119
|
+
{ type: "exerted" },
|
|
120
|
+
mockState,
|
|
121
|
+
mockRegistry,
|
|
122
|
+
),
|
|
123
|
+
).toBe(true);
|
|
124
|
+
|
|
125
|
+
expect(
|
|
126
|
+
matchesLorcanaFilter(
|
|
127
|
+
char1Exerted,
|
|
128
|
+
{ type: "damaged" },
|
|
129
|
+
mockState,
|
|
130
|
+
mockRegistry,
|
|
131
|
+
),
|
|
132
|
+
).toBe(true);
|
|
133
|
+
expect(
|
|
134
|
+
matchesLorcanaFilter(
|
|
135
|
+
char1Instance,
|
|
136
|
+
{ type: "damaged" },
|
|
137
|
+
mockState,
|
|
138
|
+
mockRegistry,
|
|
139
|
+
),
|
|
140
|
+
).toBe(false);
|
|
141
|
+
|
|
142
|
+
expect(
|
|
143
|
+
matchesLorcanaFilter(
|
|
144
|
+
char1Instance,
|
|
145
|
+
{ type: "undamaged" },
|
|
146
|
+
mockState,
|
|
147
|
+
mockRegistry,
|
|
148
|
+
),
|
|
149
|
+
).toBe(true);
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
it("should match property filters", () => {
|
|
153
|
+
expect(
|
|
154
|
+
matchesLorcanaFilter(
|
|
155
|
+
char1Instance,
|
|
156
|
+
{ type: "has-keyword", keyword: "Evasive" },
|
|
157
|
+
mockState,
|
|
158
|
+
mockRegistry,
|
|
159
|
+
),
|
|
160
|
+
).toBe(true);
|
|
161
|
+
expect(
|
|
162
|
+
matchesLorcanaFilter(
|
|
163
|
+
char1Instance,
|
|
164
|
+
{ type: "has-keyword", keyword: "Rush" },
|
|
165
|
+
mockState,
|
|
166
|
+
mockRegistry,
|
|
167
|
+
),
|
|
168
|
+
).toBe(false);
|
|
169
|
+
|
|
170
|
+
const char2Instance = {
|
|
171
|
+
definitionId: "char-2",
|
|
172
|
+
...createDefaultCardMeta(),
|
|
173
|
+
} as any;
|
|
174
|
+
expect(
|
|
175
|
+
matchesLorcanaFilter(
|
|
176
|
+
char2Instance,
|
|
177
|
+
{ type: "has-classification", classification: "Hero" },
|
|
178
|
+
mockState,
|
|
179
|
+
mockRegistry,
|
|
180
|
+
),
|
|
181
|
+
).toBe(true);
|
|
182
|
+
expect(
|
|
183
|
+
matchesLorcanaFilter(
|
|
184
|
+
char1Instance,
|
|
185
|
+
{ type: "has-classification", classification: "Hero" },
|
|
186
|
+
mockState,
|
|
187
|
+
mockRegistry,
|
|
188
|
+
),
|
|
189
|
+
).toBe(false);
|
|
190
|
+
|
|
191
|
+
expect(
|
|
192
|
+
matchesLorcanaFilter(
|
|
193
|
+
char1Instance,
|
|
194
|
+
{ type: "name", equals: "Mickey Mouse" },
|
|
195
|
+
mockState,
|
|
196
|
+
mockRegistry,
|
|
197
|
+
),
|
|
198
|
+
).toBe(true);
|
|
199
|
+
expect(
|
|
200
|
+
matchesLorcanaFilter(
|
|
201
|
+
char1Instance,
|
|
202
|
+
{ type: "name", contains: "Mickey" },
|
|
203
|
+
mockState,
|
|
204
|
+
mockRegistry,
|
|
205
|
+
),
|
|
206
|
+
).toBe(true);
|
|
207
|
+
});
|
|
208
|
+
|
|
209
|
+
it("should match numeric filters", () => {
|
|
210
|
+
expect(
|
|
211
|
+
matchesLorcanaFilter(
|
|
212
|
+
char1Instance,
|
|
213
|
+
{ type: "strength", comparison: "eq", value: 3 },
|
|
214
|
+
mockState,
|
|
215
|
+
mockRegistry,
|
|
216
|
+
),
|
|
217
|
+
).toBe(true);
|
|
218
|
+
expect(
|
|
219
|
+
matchesLorcanaFilter(
|
|
220
|
+
char1Instance,
|
|
221
|
+
{ type: "strength", comparison: "gt", value: 2 },
|
|
222
|
+
mockState,
|
|
223
|
+
mockRegistry,
|
|
224
|
+
),
|
|
225
|
+
).toBe(true);
|
|
226
|
+
expect(
|
|
227
|
+
matchesLorcanaFilter(
|
|
228
|
+
char1Instance,
|
|
229
|
+
{ type: "strength", comparison: "lt", value: 4 },
|
|
230
|
+
mockState,
|
|
231
|
+
mockRegistry,
|
|
232
|
+
),
|
|
233
|
+
).toBe(true);
|
|
234
|
+
|
|
235
|
+
expect(
|
|
236
|
+
matchesLorcanaFilter(
|
|
237
|
+
char1Instance,
|
|
238
|
+
{ type: "cost", comparison: "lte", value: 3 },
|
|
239
|
+
mockState,
|
|
240
|
+
mockRegistry,
|
|
241
|
+
),
|
|
242
|
+
).toBe(true);
|
|
243
|
+
});
|
|
244
|
+
|
|
245
|
+
it("should match composite filters", () => {
|
|
246
|
+
const filter: LorcanaFilter = {
|
|
247
|
+
type: "and",
|
|
248
|
+
filters: [
|
|
249
|
+
{ type: "ready" },
|
|
250
|
+
{ type: "strength", comparison: "eq", value: 3 },
|
|
251
|
+
],
|
|
252
|
+
};
|
|
253
|
+
expect(
|
|
254
|
+
matchesLorcanaFilter(char1Instance, filter, mockState, mockRegistry),
|
|
255
|
+
).toBe(true);
|
|
256
|
+
|
|
257
|
+
const orFilter: LorcanaFilter = {
|
|
258
|
+
type: "or",
|
|
259
|
+
filters: [
|
|
260
|
+
{ type: "exerted" }, // false
|
|
261
|
+
{ type: "strength", comparison: "eq", value: 3 }, // true
|
|
262
|
+
],
|
|
263
|
+
};
|
|
264
|
+
expect(
|
|
265
|
+
matchesLorcanaFilter(char1Instance, orFilter, mockState, mockRegistry),
|
|
266
|
+
).toBe(true);
|
|
267
|
+
});
|
|
268
|
+
});
|
|
269
|
+
|
|
270
|
+
describe("Predicate Creation", () => {
|
|
271
|
+
it("should create working predicate", () => {
|
|
272
|
+
const predicate = createTargetFiltersPredicate(
|
|
273
|
+
[{ type: "ready" }, { type: "cost", comparison: "eq", value: 3 }],
|
|
274
|
+
mockState,
|
|
275
|
+
mockRegistry,
|
|
276
|
+
);
|
|
277
|
+
|
|
278
|
+
const char1Instance = {
|
|
279
|
+
definitionId: "char-1",
|
|
280
|
+
...createDefaultCardMeta(),
|
|
281
|
+
state: "ready",
|
|
282
|
+
} as any;
|
|
283
|
+
|
|
284
|
+
const char1Exerted = {
|
|
285
|
+
definitionId: "char-1",
|
|
286
|
+
...createDefaultCardMeta(),
|
|
287
|
+
state: "exerted",
|
|
288
|
+
} as any;
|
|
289
|
+
|
|
290
|
+
expect(predicate(char1Instance)).toBe(true);
|
|
291
|
+
expect(predicate(char1Exerted)).toBe(false);
|
|
292
|
+
});
|
|
293
|
+
});
|
|
294
|
+
});
|