@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.
- package/README.md +882 -0
- package/package.json +58 -0
- package/src/__tests__/alpha-clash-engine-definition.test.ts +319 -0
- package/src/__tests__/createMockAlphaClashGame.ts +462 -0
- package/src/__tests__/createMockGrandArchiveGame.ts +373 -0
- package/src/__tests__/createMockGundamGame.ts +379 -0
- package/src/__tests__/createMockLorcanaGame.ts +328 -0
- package/src/__tests__/createMockOnePieceGame.ts +429 -0
- package/src/__tests__/createMockRiftboundGame.ts +462 -0
- package/src/__tests__/grand-archive-engine-definition.test.ts +118 -0
- package/src/__tests__/gundam-engine-definition.test.ts +110 -0
- package/src/__tests__/integration-complete-game.test.ts +508 -0
- package/src/__tests__/integration-network-sync.test.ts +469 -0
- package/src/__tests__/lorcana-engine-definition.test.ts +100 -0
- package/src/__tests__/move-enumeration.test.ts +725 -0
- package/src/__tests__/multiplayer-engine.test.ts +555 -0
- package/src/__tests__/one-piece-engine-definition.test.ts +114 -0
- package/src/__tests__/riftbound-engine-definition.test.ts +124 -0
- package/src/actions/action-definition.test.ts +201 -0
- package/src/actions/action-definition.ts +122 -0
- package/src/actions/action-timing.test.ts +490 -0
- package/src/actions/action-timing.ts +257 -0
- package/src/cards/card-definition.test.ts +268 -0
- package/src/cards/card-definition.ts +27 -0
- package/src/cards/card-instance.test.ts +422 -0
- package/src/cards/card-instance.ts +49 -0
- package/src/cards/computed-properties.test.ts +530 -0
- package/src/cards/computed-properties.ts +84 -0
- package/src/cards/conditional-modifiers.test.ts +390 -0
- package/src/cards/modifiers.test.ts +286 -0
- package/src/cards/modifiers.ts +51 -0
- package/src/engine/MULTIPLAYER.md +425 -0
- package/src/engine/__tests__/rule-engine-flow.test.ts +348 -0
- package/src/engine/__tests__/rule-engine-history.test.ts +535 -0
- package/src/engine/__tests__/rule-engine-moves.test.ts +488 -0
- package/src/engine/__tests__/rule-engine.test.ts +366 -0
- package/src/engine/index.ts +14 -0
- package/src/engine/multiplayer-engine.example.ts +571 -0
- package/src/engine/multiplayer-engine.ts +409 -0
- package/src/engine/rule-engine.test.ts +286 -0
- package/src/engine/rule-engine.ts +1539 -0
- package/src/engine/tracker-system.ts +172 -0
- package/src/examples/__tests__/coin-flip-game.test.ts +641 -0
- package/src/filtering/card-filter.test.ts +230 -0
- package/src/filtering/card-filter.ts +91 -0
- package/src/filtering/card-query.test.ts +901 -0
- package/src/filtering/card-query.ts +273 -0
- package/src/filtering/filter-matching.test.ts +944 -0
- package/src/filtering/filter-matching.ts +315 -0
- package/src/flow/SERIALIZATION.md +428 -0
- package/src/flow/__tests__/flow-definition.test.ts +427 -0
- package/src/flow/__tests__/flow-manager.test.ts +756 -0
- package/src/flow/__tests__/flow-serialization.test.ts +565 -0
- package/src/flow/flow-definition.ts +453 -0
- package/src/flow/flow-manager.ts +1044 -0
- package/src/flow/index.ts +35 -0
- package/src/game-definition/__tests__/game-definition-validation.test.ts +359 -0
- package/src/game-definition/__tests__/game-definition.test.ts +291 -0
- package/src/game-definition/__tests__/move-definitions.test.ts +328 -0
- package/src/game-definition/game-definition.ts +261 -0
- package/src/game-definition/index.ts +28 -0
- package/src/game-definition/move-definitions.ts +188 -0
- package/src/game-definition/validation.ts +183 -0
- package/src/history/history-manager.test.ts +497 -0
- package/src/history/history-manager.ts +312 -0
- package/src/history/history-operations.ts +122 -0
- package/src/history/index.ts +9 -0
- package/src/history/types.ts +255 -0
- package/src/index.ts +32 -0
- package/src/logging/index.ts +27 -0
- package/src/logging/log-formatter.ts +187 -0
- package/src/logging/logger.ts +276 -0
- package/src/logging/types.ts +148 -0
- package/src/moves/create-move.test.ts +331 -0
- package/src/moves/create-move.ts +64 -0
- package/src/moves/move-enumeration.ts +228 -0
- package/src/moves/move-executor.test.ts +431 -0
- package/src/moves/move-executor.ts +195 -0
- package/src/moves/move-system.test.ts +380 -0
- package/src/moves/move-system.ts +463 -0
- package/src/moves/standard-moves.ts +231 -0
- package/src/operations/card-operations.test.ts +236 -0
- package/src/operations/card-operations.ts +116 -0
- package/src/operations/card-registry-impl.test.ts +251 -0
- package/src/operations/card-registry-impl.ts +70 -0
- package/src/operations/card-registry.test.ts +234 -0
- package/src/operations/card-registry.ts +106 -0
- package/src/operations/counter-operations.ts +152 -0
- package/src/operations/game-operations.test.ts +280 -0
- package/src/operations/game-operations.ts +140 -0
- package/src/operations/index.ts +24 -0
- package/src/operations/operations-impl.test.ts +354 -0
- package/src/operations/operations-impl.ts +468 -0
- package/src/operations/zone-operations.test.ts +295 -0
- package/src/operations/zone-operations.ts +223 -0
- package/src/rng/seeded-rng.test.ts +339 -0
- package/src/rng/seeded-rng.ts +123 -0
- package/src/targeting/index.ts +48 -0
- package/src/targeting/target-definition.test.ts +273 -0
- package/src/targeting/target-definition.ts +37 -0
- package/src/targeting/target-dsl.ts +279 -0
- package/src/targeting/target-resolver.ts +486 -0
- package/src/targeting/target-validation.test.ts +994 -0
- package/src/targeting/target-validation.ts +286 -0
- package/src/telemetry/events.ts +202 -0
- package/src/telemetry/index.ts +21 -0
- package/src/telemetry/telemetry-manager.ts +127 -0
- package/src/telemetry/types.ts +68 -0
- package/src/testing/__tests__/testing-utilities-integration.test.ts +161 -0
- package/src/testing/index.ts +88 -0
- package/src/testing/test-assertions.test.ts +341 -0
- package/src/testing/test-assertions.ts +256 -0
- package/src/testing/test-card-factory.test.ts +228 -0
- package/src/testing/test-card-factory.ts +111 -0
- package/src/testing/test-context-factory.ts +187 -0
- package/src/testing/test-end-assertions.test.ts +262 -0
- package/src/testing/test-end-assertions.ts +95 -0
- package/src/testing/test-engine-builder.test.ts +389 -0
- package/src/testing/test-engine-builder.ts +46 -0
- package/src/testing/test-flow-assertions.test.ts +284 -0
- package/src/testing/test-flow-assertions.ts +115 -0
- package/src/testing/test-player-builder.test.ts +132 -0
- package/src/testing/test-player-builder.ts +46 -0
- package/src/testing/test-replay-assertions.test.ts +356 -0
- package/src/testing/test-replay-assertions.ts +164 -0
- package/src/testing/test-rng-helpers.test.ts +260 -0
- package/src/testing/test-rng-helpers.ts +190 -0
- package/src/testing/test-state-builder.test.ts +373 -0
- package/src/testing/test-state-builder.ts +99 -0
- package/src/testing/test-zone-factory.test.ts +295 -0
- package/src/testing/test-zone-factory.ts +224 -0
- package/src/types/branded-utils.ts +54 -0
- package/src/types/branded.test.ts +175 -0
- package/src/types/branded.ts +33 -0
- package/src/types/index.ts +8 -0
- package/src/types/state.test.ts +198 -0
- package/src/types/state.ts +154 -0
- package/src/validation/card-type-guards.test.ts +242 -0
- package/src/validation/card-type-guards.ts +179 -0
- package/src/validation/index.ts +40 -0
- package/src/validation/schema-builders.test.ts +403 -0
- package/src/validation/schema-builders.ts +345 -0
- package/src/validation/type-guard-builder.test.ts +216 -0
- package/src/validation/type-guard-builder.ts +109 -0
- package/src/validation/validator-builder.test.ts +375 -0
- package/src/validation/validator-builder.ts +273 -0
- package/src/zones/index.ts +28 -0
- package/src/zones/zone-factory.test.ts +183 -0
- package/src/zones/zone-factory.ts +44 -0
- package/src/zones/zone-operations.test.ts +800 -0
- package/src/zones/zone-operations.ts +306 -0
- package/src/zones/zone-state-helpers.test.ts +337 -0
- package/src/zones/zone-state-helpers.ts +128 -0
- package/src/zones/zone-visibility.test.ts +156 -0
- package/src/zones/zone-visibility.ts +36 -0
- package/src/zones/zone.test.ts +186 -0
- package/src/zones/zone.ts +66 -0
|
@@ -0,0 +1,348 @@
|
|
|
1
|
+
import { describe, expect, it } from "bun:test";
|
|
2
|
+
import type { FlowDefinition } from "../../flow/flow-definition";
|
|
3
|
+
import type { GameDefinition } from "../../game-definition/game-definition";
|
|
4
|
+
import type { GameMoveDefinitions } from "../../game-definition/move-definitions";
|
|
5
|
+
import { createPlayerId } from "../../types";
|
|
6
|
+
import { RuleEngine } from "../rule-engine";
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Task 11.27, 11.28: Flow Integration Tests
|
|
10
|
+
*
|
|
11
|
+
* Tests verify RuleEngine integrates with FlowManager for:
|
|
12
|
+
* - Turn/phase orchestration
|
|
13
|
+
* - Flow lifecycle hooks
|
|
14
|
+
* - Flow state access
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
type TestGameState = {
|
|
18
|
+
players: Array<{ id: string; name: string; score: number }>;
|
|
19
|
+
currentPlayerIndex: number;
|
|
20
|
+
turnNumber: number;
|
|
21
|
+
phase: "ready" | "draw" | "main" | "end";
|
|
22
|
+
log: string[];
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
type TestMoves = {
|
|
26
|
+
incrementScore: { amount: number };
|
|
27
|
+
nextPhase: Record<string, never>;
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
describe("RuleEngine - Flow Integration", () => {
|
|
31
|
+
describe("Task 11.27, 11.28: Flow Manager Integration", () => {
|
|
32
|
+
it("should initialize flow manager when flow definition provided", () => {
|
|
33
|
+
const moves: GameMoveDefinitions<TestGameState, TestMoves> = {
|
|
34
|
+
incrementScore: { reducer: () => {} },
|
|
35
|
+
nextPhase: { reducer: () => {} },
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
const flow: FlowDefinition<TestGameState> = {
|
|
39
|
+
turn: {
|
|
40
|
+
phases: {
|
|
41
|
+
ready: { order: 0, next: "draw" },
|
|
42
|
+
draw: { order: 1, next: "main" },
|
|
43
|
+
main: { order: 2, next: "end" },
|
|
44
|
+
end: { order: 3, next: undefined },
|
|
45
|
+
},
|
|
46
|
+
},
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
const gameDef: GameDefinition<TestGameState, TestMoves> = {
|
|
50
|
+
name: "Test Game",
|
|
51
|
+
setup: (players) => ({
|
|
52
|
+
players: players.map((p) => ({
|
|
53
|
+
id: p.id,
|
|
54
|
+
name: p.name || "Player",
|
|
55
|
+
score: 0,
|
|
56
|
+
})),
|
|
57
|
+
currentPlayerIndex: 0,
|
|
58
|
+
turnNumber: 1,
|
|
59
|
+
phase: "ready",
|
|
60
|
+
log: [],
|
|
61
|
+
}),
|
|
62
|
+
moves,
|
|
63
|
+
flow,
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
const players = [
|
|
67
|
+
{ id: createPlayerId("p1"), name: "Alice" },
|
|
68
|
+
{ id: createPlayerId("p2"), name: "Bob" },
|
|
69
|
+
];
|
|
70
|
+
|
|
71
|
+
const engine = new RuleEngine(gameDef, players);
|
|
72
|
+
const flowManager = engine.getFlowManager();
|
|
73
|
+
|
|
74
|
+
expect(flowManager).toBeDefined();
|
|
75
|
+
expect(flowManager?.getCurrentPhase()).toBe("ready");
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
it("should return undefined flow manager when no flow definition", () => {
|
|
79
|
+
const moves: GameMoveDefinitions<TestGameState, TestMoves> = {
|
|
80
|
+
incrementScore: { reducer: () => {} },
|
|
81
|
+
nextPhase: { reducer: () => {} },
|
|
82
|
+
};
|
|
83
|
+
|
|
84
|
+
const gameDef: GameDefinition<TestGameState, TestMoves> = {
|
|
85
|
+
name: "Test Game",
|
|
86
|
+
setup: (players) => ({
|
|
87
|
+
players: players.map((p) => ({
|
|
88
|
+
id: p.id,
|
|
89
|
+
name: p.name || "Player",
|
|
90
|
+
score: 0,
|
|
91
|
+
})),
|
|
92
|
+
currentPlayerIndex: 0,
|
|
93
|
+
turnNumber: 1,
|
|
94
|
+
phase: "ready",
|
|
95
|
+
log: [],
|
|
96
|
+
}),
|
|
97
|
+
moves,
|
|
98
|
+
// No flow definition
|
|
99
|
+
};
|
|
100
|
+
|
|
101
|
+
const players = [
|
|
102
|
+
{ id: createPlayerId("p1"), name: "Alice" },
|
|
103
|
+
{ id: createPlayerId("p2"), name: "Bob" },
|
|
104
|
+
];
|
|
105
|
+
|
|
106
|
+
const engine = new RuleEngine(gameDef, players);
|
|
107
|
+
const flowManager = engine.getFlowManager();
|
|
108
|
+
|
|
109
|
+
expect(flowManager).toBeUndefined();
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
it("should execute flow lifecycle hooks on initialization", () => {
|
|
113
|
+
const moves: GameMoveDefinitions<TestGameState, TestMoves> = {
|
|
114
|
+
incrementScore: { reducer: () => {} },
|
|
115
|
+
nextPhase: { reducer: () => {} },
|
|
116
|
+
};
|
|
117
|
+
|
|
118
|
+
const flow: FlowDefinition<TestGameState> = {
|
|
119
|
+
turn: {
|
|
120
|
+
onBegin: (context) => {
|
|
121
|
+
context.state.log.push("turn-begin");
|
|
122
|
+
},
|
|
123
|
+
phases: {
|
|
124
|
+
ready: {
|
|
125
|
+
order: 0,
|
|
126
|
+
next: undefined,
|
|
127
|
+
onBegin: (context) => {
|
|
128
|
+
context.state.log.push("ready-begin");
|
|
129
|
+
},
|
|
130
|
+
},
|
|
131
|
+
},
|
|
132
|
+
},
|
|
133
|
+
};
|
|
134
|
+
|
|
135
|
+
const gameDef: GameDefinition<TestGameState, TestMoves> = {
|
|
136
|
+
name: "Test Game",
|
|
137
|
+
setup: (players) => ({
|
|
138
|
+
players: players.map((p) => ({
|
|
139
|
+
id: p.id,
|
|
140
|
+
name: p.name || "Player",
|
|
141
|
+
score: 0,
|
|
142
|
+
})),
|
|
143
|
+
currentPlayerIndex: 0,
|
|
144
|
+
turnNumber: 1,
|
|
145
|
+
phase: "ready",
|
|
146
|
+
log: [],
|
|
147
|
+
}),
|
|
148
|
+
moves,
|
|
149
|
+
flow,
|
|
150
|
+
};
|
|
151
|
+
|
|
152
|
+
const players = [
|
|
153
|
+
{ id: createPlayerId("p1"), name: "Alice" },
|
|
154
|
+
{ id: createPlayerId("p2"), name: "Bob" },
|
|
155
|
+
];
|
|
156
|
+
|
|
157
|
+
const engine = new RuleEngine(gameDef, players);
|
|
158
|
+
const flowManager = engine.getFlowManager();
|
|
159
|
+
|
|
160
|
+
// Flow hooks should have executed
|
|
161
|
+
const gameState = flowManager?.getGameState();
|
|
162
|
+
expect(gameState?.log).toContain("turn-begin");
|
|
163
|
+
expect(gameState?.log).toContain("ready-begin");
|
|
164
|
+
});
|
|
165
|
+
|
|
166
|
+
it("should allow manual flow progression through flow manager", () => {
|
|
167
|
+
const moves: GameMoveDefinitions<TestGameState, TestMoves> = {
|
|
168
|
+
incrementScore: { reducer: () => {} },
|
|
169
|
+
nextPhase: { reducer: () => {} },
|
|
170
|
+
};
|
|
171
|
+
|
|
172
|
+
const flow: FlowDefinition<TestGameState> = {
|
|
173
|
+
turn: {
|
|
174
|
+
phases: {
|
|
175
|
+
ready: { order: 0, next: "draw" },
|
|
176
|
+
draw: { order: 1, next: "main" },
|
|
177
|
+
main: { order: 2, next: undefined },
|
|
178
|
+
},
|
|
179
|
+
},
|
|
180
|
+
};
|
|
181
|
+
|
|
182
|
+
const gameDef: GameDefinition<TestGameState, TestMoves> = {
|
|
183
|
+
name: "Test Game",
|
|
184
|
+
setup: (players) => ({
|
|
185
|
+
players: players.map((p) => ({
|
|
186
|
+
id: p.id,
|
|
187
|
+
name: p.name || "Player",
|
|
188
|
+
score: 0,
|
|
189
|
+
})),
|
|
190
|
+
currentPlayerIndex: 0,
|
|
191
|
+
turnNumber: 1,
|
|
192
|
+
phase: "ready",
|
|
193
|
+
log: [],
|
|
194
|
+
}),
|
|
195
|
+
moves,
|
|
196
|
+
flow,
|
|
197
|
+
};
|
|
198
|
+
|
|
199
|
+
const players = [
|
|
200
|
+
{ id: createPlayerId("p1"), name: "Alice" },
|
|
201
|
+
{ id: createPlayerId("p2"), name: "Bob" },
|
|
202
|
+
];
|
|
203
|
+
|
|
204
|
+
const engine = new RuleEngine(gameDef, players);
|
|
205
|
+
const flowManager = engine.getFlowManager();
|
|
206
|
+
|
|
207
|
+
expect(flowManager?.getCurrentPhase()).toBe("ready");
|
|
208
|
+
|
|
209
|
+
// Progress through flow
|
|
210
|
+
flowManager?.nextPhase();
|
|
211
|
+
expect(flowManager?.getCurrentPhase()).toBe("draw");
|
|
212
|
+
|
|
213
|
+
flowManager?.nextPhase();
|
|
214
|
+
expect(flowManager?.getCurrentPhase()).toBe("main");
|
|
215
|
+
});
|
|
216
|
+
|
|
217
|
+
it("should support automatic flow transitions via endIf", () => {
|
|
218
|
+
const moves: GameMoveDefinitions<TestGameState, TestMoves> = {
|
|
219
|
+
incrementScore: {
|
|
220
|
+
reducer: (draft, context) => {
|
|
221
|
+
const player = draft.players[draft.currentPlayerIndex];
|
|
222
|
+
if (player && context.params?.amount) {
|
|
223
|
+
player.score += context.params.amount as number;
|
|
224
|
+
}
|
|
225
|
+
},
|
|
226
|
+
},
|
|
227
|
+
nextPhase: { reducer: () => {} },
|
|
228
|
+
};
|
|
229
|
+
|
|
230
|
+
const flow: FlowDefinition<TestGameState> = {
|
|
231
|
+
turn: {
|
|
232
|
+
phases: {
|
|
233
|
+
ready: {
|
|
234
|
+
order: 0,
|
|
235
|
+
next: "main",
|
|
236
|
+
endIf: (context) => {
|
|
237
|
+
// Auto-end when any player has score >= 5
|
|
238
|
+
return context.state.players.some((p) => p.score >= 5);
|
|
239
|
+
},
|
|
240
|
+
},
|
|
241
|
+
main: {
|
|
242
|
+
order: 1,
|
|
243
|
+
next: undefined,
|
|
244
|
+
},
|
|
245
|
+
},
|
|
246
|
+
},
|
|
247
|
+
};
|
|
248
|
+
|
|
249
|
+
const gameDef: GameDefinition<TestGameState, TestMoves> = {
|
|
250
|
+
name: "Test Game",
|
|
251
|
+
setup: (players) => ({
|
|
252
|
+
players: players.map((p) => ({
|
|
253
|
+
id: p.id,
|
|
254
|
+
name: p.name || "Player",
|
|
255
|
+
score: 0,
|
|
256
|
+
})),
|
|
257
|
+
currentPlayerIndex: 0,
|
|
258
|
+
turnNumber: 1,
|
|
259
|
+
phase: "ready",
|
|
260
|
+
log: [],
|
|
261
|
+
}),
|
|
262
|
+
moves,
|
|
263
|
+
flow,
|
|
264
|
+
};
|
|
265
|
+
|
|
266
|
+
const players = [
|
|
267
|
+
{ id: createPlayerId("p1"), name: "Alice" },
|
|
268
|
+
{ id: createPlayerId("p2"), name: "Bob" },
|
|
269
|
+
];
|
|
270
|
+
|
|
271
|
+
const engine = new RuleEngine(gameDef, players);
|
|
272
|
+
const flowManager = engine.getFlowManager();
|
|
273
|
+
|
|
274
|
+
expect(flowManager?.getCurrentPhase()).toBe("ready");
|
|
275
|
+
|
|
276
|
+
// Execute move that triggers endIf condition
|
|
277
|
+
// Note: Flow manager would need to sync state from engine
|
|
278
|
+
// For now, test that flow manager exists and can be accessed
|
|
279
|
+
expect(flowManager).toBeDefined();
|
|
280
|
+
});
|
|
281
|
+
});
|
|
282
|
+
|
|
283
|
+
describe("Game End Condition", () => {
|
|
284
|
+
it("should check game end condition via endIf", () => {
|
|
285
|
+
const moves: GameMoveDefinitions<TestGameState, TestMoves> = {
|
|
286
|
+
incrementScore: {
|
|
287
|
+
reducer: (draft, context) => {
|
|
288
|
+
const player = draft.players[draft.currentPlayerIndex];
|
|
289
|
+
if (player && context.params?.amount) {
|
|
290
|
+
player.score += context.params.amount as number;
|
|
291
|
+
}
|
|
292
|
+
},
|
|
293
|
+
},
|
|
294
|
+
nextPhase: { reducer: () => {} },
|
|
295
|
+
};
|
|
296
|
+
|
|
297
|
+
const gameDef: GameDefinition<TestGameState, TestMoves> = {
|
|
298
|
+
name: "Test Game",
|
|
299
|
+
setup: (players) => ({
|
|
300
|
+
players: players.map((p) => ({
|
|
301
|
+
id: p.id,
|
|
302
|
+
name: p.name || "Player",
|
|
303
|
+
score: 0,
|
|
304
|
+
})),
|
|
305
|
+
currentPlayerIndex: 0,
|
|
306
|
+
turnNumber: 1,
|
|
307
|
+
phase: "ready",
|
|
308
|
+
log: [],
|
|
309
|
+
}),
|
|
310
|
+
moves,
|
|
311
|
+
endIf: (state) => {
|
|
312
|
+
// Game ends when any player reaches 10 points
|
|
313
|
+
const winner = state.players.find((p) => p.score >= 10);
|
|
314
|
+
if (winner) {
|
|
315
|
+
return {
|
|
316
|
+
winner: winner.id,
|
|
317
|
+
reason: "Score limit reached",
|
|
318
|
+
};
|
|
319
|
+
}
|
|
320
|
+
return undefined;
|
|
321
|
+
},
|
|
322
|
+
};
|
|
323
|
+
|
|
324
|
+
const players = [
|
|
325
|
+
{ id: createPlayerId("p1"), name: "Alice" },
|
|
326
|
+
{ id: createPlayerId("p2"), name: "Bob" },
|
|
327
|
+
];
|
|
328
|
+
|
|
329
|
+
const engine = new RuleEngine(gameDef, players);
|
|
330
|
+
|
|
331
|
+
// Game should not be ended yet
|
|
332
|
+
let gameEnd = engine.checkGameEnd();
|
|
333
|
+
expect(gameEnd).toBeUndefined();
|
|
334
|
+
|
|
335
|
+
// Execute move to reach winning score
|
|
336
|
+
engine.executeMove("incrementScore", {
|
|
337
|
+
playerId: createPlayerId("p1"),
|
|
338
|
+
params: { amount: 10 },
|
|
339
|
+
});
|
|
340
|
+
|
|
341
|
+
// Game should be ended now
|
|
342
|
+
gameEnd = engine.checkGameEnd();
|
|
343
|
+
expect(gameEnd).toBeDefined();
|
|
344
|
+
expect(gameEnd?.winner).toBe(createPlayerId("p1"));
|
|
345
|
+
expect(gameEnd?.reason).toBe("Score limit reached");
|
|
346
|
+
});
|
|
347
|
+
});
|
|
348
|
+
});
|