warp12-engine 0.1.0

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 (83) hide show
  1. package/README.md +25 -0
  2. package/dist/index.d.ts +2 -0
  3. package/dist/index.d.ts.map +1 -0
  4. package/dist/index.js +2028 -0
  5. package/dist/lib/ai/actions.d.ts +32 -0
  6. package/dist/lib/ai/actions.d.ts.map +1 -0
  7. package/dist/lib/ai/candidate-generator.d.ts +20 -0
  8. package/dist/lib/ai/candidate-generator.d.ts.map +1 -0
  9. package/dist/lib/ai/context.d.ts +19 -0
  10. package/dist/lib/ai/context.d.ts.map +1 -0
  11. package/dist/lib/ai/create-warp-ai.d.ts +49 -0
  12. package/dist/lib/ai/create-warp-ai.d.ts.map +1 -0
  13. package/dist/lib/ai/explain-action.d.ts +8 -0
  14. package/dist/lib/ai/explain-action.d.ts.map +1 -0
  15. package/dist/lib/ai/heuristics.d.ts +21 -0
  16. package/dist/lib/ai/heuristics.d.ts.map +1 -0
  17. package/dist/lib/ai/index.d.ts +19 -0
  18. package/dist/lib/ai/index.d.ts.map +1 -0
  19. package/dist/lib/ai/observation.d.ts +20 -0
  20. package/dist/lib/ai/observation.d.ts.map +1 -0
  21. package/dist/lib/ai/q-flash.d.ts +11 -0
  22. package/dist/lib/ai/q-flash.d.ts.map +1 -0
  23. package/dist/lib/ai/search-model.d.ts +33 -0
  24. package/dist/lib/ai/search-model.d.ts.map +1 -0
  25. package/dist/lib/ai/self-play.d.ts +50 -0
  26. package/dist/lib/ai/self-play.d.ts.map +1 -0
  27. package/dist/lib/ai/skill.d.ts +8 -0
  28. package/dist/lib/ai/skill.d.ts.map +1 -0
  29. package/dist/lib/ai/test-fixtures.d.ts +18 -0
  30. package/dist/lib/ai/test-fixtures.d.ts.map +1 -0
  31. package/dist/lib/constants/setup.d.ts +15 -0
  32. package/dist/lib/constants/setup.d.ts.map +1 -0
  33. package/dist/lib/domino/coordinates.d.ts +13 -0
  34. package/dist/lib/domino/coordinates.d.ts.map +1 -0
  35. package/dist/lib/engine/apply-action.d.ts +7 -0
  36. package/dist/lib/engine/apply-action.d.ts.map +1 -0
  37. package/dist/lib/engine/beacon.d.ts +30 -0
  38. package/dist/lib/engine/beacon.d.ts.map +1 -0
  39. package/dist/lib/engine/dead-red-alert.d.ts +4 -0
  40. package/dist/lib/engine/dead-red-alert.d.ts.map +1 -0
  41. package/dist/lib/engine/helpers.d.ts +12 -0
  42. package/dist/lib/engine/helpers.d.ts.map +1 -0
  43. package/dist/lib/engine/legal-moves.d.ts +10 -0
  44. package/dist/lib/engine/legal-moves.d.ts.map +1 -0
  45. package/dist/lib/engine/q-continuum.d.ts +25 -0
  46. package/dist/lib/engine/q-continuum.d.ts.map +1 -0
  47. package/dist/lib/engine/round-resolution.d.ts +13 -0
  48. package/dist/lib/engine/round-resolution.d.ts.map +1 -0
  49. package/dist/lib/engine/scoring.d.ts +9 -0
  50. package/dist/lib/engine/scoring.d.ts.map +1 -0
  51. package/dist/lib/engine/test-helpers.d.ts +26 -0
  52. package/dist/lib/engine/test-helpers.d.ts.map +1 -0
  53. package/dist/lib/fixtures/create-demo-game.d.ts +17 -0
  54. package/dist/lib/fixtures/create-demo-game.d.ts.map +1 -0
  55. package/dist/lib/setup/create-game.d.ts +51 -0
  56. package/dist/lib/setup/create-game.d.ts.map +1 -0
  57. package/dist/lib/table/pip-inventory.d.ts +12 -0
  58. package/dist/lib/table/pip-inventory.d.ts.map +1 -0
  59. package/dist/lib/table/table-state.d.ts +14 -0
  60. package/dist/lib/table/table-state.d.ts.map +1 -0
  61. package/dist/lib/types/actions.d.ts +64 -0
  62. package/dist/lib/types/actions.d.ts.map +1 -0
  63. package/dist/lib/types/anomalies.d.ts +29 -0
  64. package/dist/lib/types/anomalies.d.ts.map +1 -0
  65. package/dist/lib/types/coordinate.d.ts +22 -0
  66. package/dist/lib/types/coordinate.d.ts.map +1 -0
  67. package/dist/lib/types/game-state.d.ts +73 -0
  68. package/dist/lib/types/game-state.d.ts.map +1 -0
  69. package/dist/lib/types/modules.d.ts +32 -0
  70. package/dist/lib/types/modules.d.ts.map +1 -0
  71. package/dist/lib/types/objective.d.ts +5 -0
  72. package/dist/lib/types/objective.d.ts.map +1 -0
  73. package/dist/lib/types/player.d.ts +12 -0
  74. package/dist/lib/types/player.d.ts.map +1 -0
  75. package/dist/lib/types/q-continuum.d.ts +50 -0
  76. package/dist/lib/types/q-continuum.d.ts.map +1 -0
  77. package/dist/lib/types/trails.d.ts +20 -0
  78. package/dist/lib/types/trails.d.ts.map +1 -0
  79. package/dist/lib/violations/format-violation.d.ts +3 -0
  80. package/dist/lib/violations/format-violation.d.ts.map +1 -0
  81. package/dist/lib/warp12-lib.d.ts +22 -0
  82. package/dist/lib/warp12-lib.d.ts.map +1 -0
  83. package/package.json +50 -0
package/dist/index.js ADDED
@@ -0,0 +1,2028 @@
1
+ import { SKILL_PRESETS as e, chooseActionIndex as t, createPolicyPlayer as n, scoreWithHeuristics as r, searchActionValues as i } from "doubletwelve";
2
+ //#region libs/engine/src/lib/types/coordinate.ts
3
+ function a({ low: e, high: t }) {
4
+ return `${e}-${t}`;
5
+ }
6
+ function o({ low: e, high: t }) {
7
+ return e === t;
8
+ }
9
+ function s({ low: e, high: t }) {
10
+ return e + t;
11
+ }
12
+ function c(e, t) {
13
+ return e <= t ? {
14
+ low: e,
15
+ high: t
16
+ } : {
17
+ low: t,
18
+ high: e
19
+ };
20
+ }
21
+ function l(e, t) {
22
+ return e.low === t.low && e.high === t.high;
23
+ }
24
+ function u({ low: e, high: t }, n) {
25
+ return e === n || t === n;
26
+ }
27
+ function d(e, t) {
28
+ return e.low === t ? e.high : e.high === t ? e.low : null;
29
+ }
30
+ //#endregion
31
+ //#region libs/engine/src/lib/types/trails.ts
32
+ function f(e) {
33
+ return e.distressBeacon.active;
34
+ }
35
+ //#endregion
36
+ //#region libs/engine/src/lib/constants/setup.ts
37
+ var p = {
38
+ 2: 15,
39
+ 3: 15,
40
+ 4: 15,
41
+ 5: 12,
42
+ 6: 12,
43
+ 7: 10,
44
+ 8: 10
45
+ }, ee = 91, te = 12, ne = 12, re = 24;
46
+ function ie(e) {
47
+ let t = p[e];
48
+ if (t === void 0) throw RangeError(`Warp 12 supports 2–8 captains; received ${e}.`);
49
+ return t;
50
+ }
51
+ function ae(e) {
52
+ if (e < 1 || e > 13) throw RangeError(`Round number must be 1–13 for a double-twelve set; received ${e}.`);
53
+ return 12 - (e - 1);
54
+ }
55
+ function m(e) {
56
+ return e > 1;
57
+ }
58
+ //#endregion
59
+ //#region libs/engine/src/lib/table/pip-inventory.ts
60
+ function oe(e, t = 12) {
61
+ return e === t ? 1 : t + 1;
62
+ }
63
+ function* se(e) {
64
+ let t = e.spacedockValue;
65
+ yield {
66
+ low: t,
67
+ high: t
68
+ };
69
+ for (let t of Object.values(e.table.warpTrails)) for (let e of t.tiles) yield e.coordinate;
70
+ for (let t of e.table.neutralZone.tiles) yield t.coordinate;
71
+ let n = e.table.subspaceFracture;
72
+ if (n) for (let e of n.stabilizers) yield e.coordinate;
73
+ }
74
+ function ce(e, t) {
75
+ return e.low === t || e.high === t;
76
+ }
77
+ function le(e, t) {
78
+ let n = 0;
79
+ for (let r of se(e)) ce(r, t) && n++;
80
+ return n;
81
+ }
82
+ function ue(e, t, n = 12) {
83
+ return le(e, t) >= oe(t, n);
84
+ }
85
+ function de(e) {
86
+ let t = e.table.redAlert;
87
+ if (!t?.active) return !1;
88
+ let n = t.anchor.coordinate;
89
+ return o(n) ? ue(e, n.low) : !1;
90
+ }
91
+ //#endregion
92
+ //#region libs/engine/src/lib/types/anomalies.ts
93
+ function fe(e) {
94
+ return Math.min(e.stabilizers.length, 3);
95
+ }
96
+ function pe(e) {
97
+ return e === null || e.stabilizers.length >= 3;
98
+ }
99
+ function h(e) {
100
+ return e?.active === !0 && e.stabilizers.length < 3;
101
+ }
102
+ function g(e, t) {
103
+ return e?.active === !0 && e.responsiblePlayerId === t;
104
+ }
105
+ function me(e) {
106
+ return e.table.redAlert?.active ? !de(e) : !1;
107
+ }
108
+ //#endregion
109
+ //#region libs/engine/src/lib/types/modules.ts
110
+ var he = {
111
+ qContinuum: {
112
+ enabled: !1,
113
+ activeFlash: null
114
+ },
115
+ salamanderPenalty: { enabled: !1 },
116
+ subspaceFracture: { enabled: !1 }
117
+ };
118
+ function ge(e = {}) {
119
+ return {
120
+ qContinuum: {
121
+ enabled: e.qContinuum ?? !1,
122
+ activeFlash: null
123
+ },
124
+ salamanderPenalty: { enabled: e.salamanderPenalty ?? !1 },
125
+ subspaceFracture: { enabled: e.subspaceFracture ?? !1 }
126
+ };
127
+ }
128
+ //#endregion
129
+ //#region libs/engine/src/lib/types/objective.ts
130
+ var _e = "penalty", ve = {
131
+ "go-out": "Go out — first empty hand wins the sector",
132
+ penalty: "Points — lowest penalty total wins the campaign (13 rounds)"
133
+ };
134
+ //#endregion
135
+ //#region libs/engine/src/lib/domino/coordinates.ts
136
+ function _(e = 12) {
137
+ let t = [];
138
+ for (let n = 0; n <= e; n++) for (let r = n; r <= e; r++) t.push({
139
+ low: n,
140
+ high: r
141
+ });
142
+ return t;
143
+ }
144
+ function ye(e, t = 91) {
145
+ if (e.length !== t) throw Error(`Expected ${t} coordinates in the set; received ${e.length}.`);
146
+ if (new Set(e.map(a)).size !== t) throw Error("Coordinate set contains duplicate tiles.");
147
+ }
148
+ function v(e, t = Math.random) {
149
+ let n = e.map((e) => ({ ...e }));
150
+ for (let e = n.length - 1; e > 0; e--) {
151
+ let r = Math.floor(t() * (e + 1));
152
+ [n[e], n[r]] = [n[r], n[e]];
153
+ }
154
+ return n;
155
+ }
156
+ function be(e, t) {
157
+ let n = a(c(t.low, t.high));
158
+ return e.findIndex((e) => a(e) === n);
159
+ }
160
+ function xe(e, t) {
161
+ let n = be(e, t);
162
+ if (n === -1) return {
163
+ hand: [...e],
164
+ removed: null
165
+ };
166
+ let r = [...e], [i] = r.splice(n, 1);
167
+ return {
168
+ hand: r,
169
+ removed: i
170
+ };
171
+ }
172
+ function Se(e, t) {
173
+ return be(e, t) !== -1;
174
+ }
175
+ //#endregion
176
+ //#region libs/engine/src/lib/table/table-state.ts
177
+ function Ce(e, t, n) {
178
+ let r = {};
179
+ for (let t of e) r[t] = {
180
+ playerId: t,
181
+ tiles: [],
182
+ distressBeacon: { active: !1 }
183
+ };
184
+ return {
185
+ spacedock: {
186
+ value: t,
187
+ placedBy: n
188
+ },
189
+ warpTrails: r,
190
+ neutralZone: { tiles: [] },
191
+ subspaceFracture: null,
192
+ redAlert: null
193
+ };
194
+ }
195
+ function y(e, t) {
196
+ return e.tiles.length === 0 ? t : e.tiles[e.tiles.length - 1].openValue;
197
+ }
198
+ function b(e, t) {
199
+ return e.tiles.length === 0 ? t : e.tiles[e.tiles.length - 1].openValue;
200
+ }
201
+ function x(e) {
202
+ if (e.tiles.length === 0) return !1;
203
+ let { coordinate: t, openValue: n } = e.tiles[e.tiles.length - 1];
204
+ return t.low === t.high && n === t.low;
205
+ }
206
+ function S(e) {
207
+ if (e.tiles.length === 0) return !1;
208
+ let { coordinate: t, openValue: n } = e.tiles[e.tiles.length - 1];
209
+ return t.low === t.high && n === t.low;
210
+ }
211
+ function we(e) {
212
+ if (S(e.neutralZone)) return !0;
213
+ for (let t of Object.values(e.warpTrails)) if (x(t)) return !0;
214
+ return !1;
215
+ }
216
+ function Te(e) {
217
+ let t = 0;
218
+ for (let n of Object.values(e.warpTrails)) for (let e of n.tiles) o(e.coordinate) && (t += 1);
219
+ for (let n of e.neutralZone.tiles) o(n.coordinate) && (t += 1);
220
+ return t;
221
+ }
222
+ function Ee(e) {
223
+ let t = 0;
224
+ for (let n of Object.values(e.warpTrails)) n.distressBeacon.active && (t += 1);
225
+ return t;
226
+ }
227
+ //#endregion
228
+ //#region libs/engine/src/lib/setup/create-game.ts
229
+ function De(e, t) {
230
+ return {
231
+ id: e,
232
+ displayName: t,
233
+ penaltyScore: 0
234
+ };
235
+ }
236
+ function Oe(e) {
237
+ return {
238
+ id: e.id,
239
+ phase: "lobby",
240
+ captains: e.captains.map((e) => De(e.id, e.displayName)),
241
+ round: null,
242
+ completedRounds: 0,
243
+ modules: ge(e.modules),
244
+ objective: e.objective ?? "penalty"
245
+ };
246
+ }
247
+ function ke(e, t) {
248
+ if (e.length === 0) throw RangeError("Turn order must include at least one captain.");
249
+ return e[(t - 1) % e.length];
250
+ }
251
+ function C(e) {
252
+ let t = ae(e.roundNumber), n = a(c(t, t)), r = [...e.shuffledCoordinates], i = r.findIndex((e) => a(e) === n);
253
+ if (i === -1) throw Error(`Spacedock coordinate ${n} is missing from the shuffled set.`);
254
+ r.splice(i, 1);
255
+ let o = ie(e.captains.length), s = {};
256
+ for (let t of e.captains) s[t.id] = r.splice(0, o);
257
+ let l = e.roundStarterId ?? ke(e.turnOrder, e.roundNumber);
258
+ return {
259
+ roundNumber: e.roundNumber,
260
+ captains: e.captains,
261
+ turnOrder: e.turnOrder,
262
+ spacedockValue: t,
263
+ spacedockPlacedBy: l,
264
+ hands: s,
265
+ unchartedSectors: r
266
+ };
267
+ }
268
+ function w(e) {
269
+ return {
270
+ roundNumber: e.roundNumber,
271
+ spacedockValue: e.spacedockValue,
272
+ phase: "playing",
273
+ activePlayerId: e.spacedockPlacedBy,
274
+ turnOrder: e.turnOrder,
275
+ table: Ce(e.turnOrder, e.spacedockValue, e.spacedockPlacedBy),
276
+ unchartedSectors: [...e.unchartedSectors],
277
+ hands: { ...e.hands },
278
+ treatyDeclarationRequired: !1,
279
+ treatyDeclared: !1,
280
+ roundWinnerId: null,
281
+ qPendingInvoker: null,
282
+ qEffects: null,
283
+ qGamblePending: null,
284
+ mandatoryPlay: null,
285
+ pendingRoundWin: null,
286
+ roundBlocked: !1
287
+ };
288
+ }
289
+ function Ae(e) {
290
+ return w(C({
291
+ shuffledCoordinates: e.shuffledCoordinates,
292
+ roundNumber: e.roundNumber,
293
+ captains: e.captains,
294
+ turnOrder: e.turnOrder,
295
+ roundStarterId: e.spacedockPlacedBy
296
+ }));
297
+ }
298
+ function T(e, t) {
299
+ let n = Oe(e), r = n.captains.map((e) => e.id), i = C({
300
+ shuffledCoordinates: t.shuffledCoordinates,
301
+ roundNumber: 1,
302
+ captains: n.captains,
303
+ turnOrder: r,
304
+ roundStarterId: t.roundStarterId
305
+ });
306
+ return {
307
+ ...n,
308
+ phase: "active",
309
+ round: w(i)
310
+ };
311
+ }
312
+ function je(e) {
313
+ let t = [...e.unchartedSectors];
314
+ for (let n of e.turnOrder) t.push(...e.hands[n] ?? []);
315
+ for (let n of Object.values(e.table.warpTrails)) for (let e of n.tiles) t.push(e.coordinate);
316
+ for (let n of e.table.neutralZone.tiles) t.push(n.coordinate);
317
+ let n = e.table.subspaceFracture;
318
+ if (n) for (let e of n.stabilizers) t.push(e.coordinate);
319
+ return t.push(c(e.spacedockValue, e.spacedockValue)), t;
320
+ }
321
+ function Me(e, t) {
322
+ let n = a(c(t, t));
323
+ for (let [t, r] of Object.entries(e)) if (r.some((e) => a(e) === n)) return t;
324
+ return null;
325
+ }
326
+ //#endregion
327
+ //#region libs/engine/src/lib/engine/helpers.ts
328
+ function E(e) {
329
+ return {
330
+ ok: !1,
331
+ violation: e
332
+ };
333
+ }
334
+ function Ne(e) {
335
+ return e.phase !== "active" || !e.round ? "GAME_NOT_ACTIVE" : e.round.phase === "playing" ? e.round : "ROUND_NOT_PLAYING";
336
+ }
337
+ function D(e, t) {
338
+ return e.activePlayerId === t ? !0 : "NOT_YOUR_TURN";
339
+ }
340
+ function O(e, t) {
341
+ return {
342
+ ...e,
343
+ round: t
344
+ };
345
+ }
346
+ function Pe(e, t, n) {
347
+ return {
348
+ ...e,
349
+ round: t,
350
+ captains: n
351
+ };
352
+ }
353
+ //#endregion
354
+ //#region libs/engine/src/lib/types/q-continuum.ts
355
+ var k = [
356
+ {
357
+ kind: "reverse-turn-order",
358
+ label: "Reverse turn order",
359
+ description: "Helm passes counter-clockwise for the rest of the round."
360
+ },
361
+ {
362
+ kind: "skip-lowest-penalty",
363
+ label: "Skip lowest penalty",
364
+ description: "The captain with the lowest campaign penalty score skips their next turn."
365
+ },
366
+ {
367
+ kind: "peek-uncharted",
368
+ label: "Peek Uncharted Sector",
369
+ description: "Reveal the top tile in the Uncharted Sectors (invoker only)."
370
+ },
371
+ {
372
+ kind: "temporal-inversion",
373
+ label: "Temporal inversion",
374
+ description: "Turn order reverses until the next double is charted on the table."
375
+ },
376
+ {
377
+ kind: "distress-amplification",
378
+ label: "Distress amplification",
379
+ description: "All warp trails stay open to every captain for the rest of the round."
380
+ },
381
+ {
382
+ kind: "fracture-immunity",
383
+ label: "Fracture immunity",
384
+ description: "The next double charted on an own trail will not open Subspace Fracture."
385
+ },
386
+ {
387
+ kind: "salamander-swap",
388
+ label: "Salamander swap",
389
+ description: "If anyone holds 12-12 at round end, that penalty applies to the highest-penalty captain instead.",
390
+ requiresSalamander: !0
391
+ },
392
+ {
393
+ kind: "treaty-echo",
394
+ label: "Impulse echo",
395
+ description: "Any captain going out this round must drop to impulse before the sector closes."
396
+ },
397
+ {
398
+ kind: "q-gamble",
399
+ label: "Q's gamble",
400
+ description: "Draw two tiles from Uncharted Sectors — keep one, return the other face-down."
401
+ }
402
+ ], Fe = {
403
+ reverseTurnOrder: !1,
404
+ temporalInversion: !1,
405
+ openAllTrails: !1,
406
+ suppressNextFracture: !1,
407
+ skipNextTurnFor: [],
408
+ peekedSector: null,
409
+ salamanderSwap: !1,
410
+ treatyEcho: !1
411
+ };
412
+ function Ie(e, t) {
413
+ let n = k.find((t) => t.kind === e.kind)?.label ?? e.kind;
414
+ if (e.kind === "skip-lowest-penalty" && e.targetPlayerId) return `${n}: ${t[e.targetPlayerId] ?? e.targetPlayerId}`;
415
+ if (e.kind === "peek-uncharted" && e.peek) {
416
+ let { low: t, high: r } = e.peek.coordinate;
417
+ return `${n}: ${t}-${r}`;
418
+ }
419
+ return n;
420
+ }
421
+ function A(e, t, n) {
422
+ return k.filter((r) => {
423
+ if (r.requiresSalamander && !t.salamanderPenalty.enabled) return !1;
424
+ switch (r.kind) {
425
+ case "peek-uncharted": return e.unchartedSectors.length > 0;
426
+ case "q-gamble": return e.unchartedSectors.length >= 2;
427
+ case "skip-lowest-penalty": return n.length > 1;
428
+ default: return !0;
429
+ }
430
+ }).map((e) => e.kind);
431
+ }
432
+ function Le(e) {
433
+ return e.qPendingInvoker !== null || e.qGamblePending !== null;
434
+ }
435
+ //#endregion
436
+ //#region libs/engine/src/lib/engine/q-continuum.ts
437
+ function j(e, t) {
438
+ let n = e ?? {
439
+ reverseTurnOrder: !1,
440
+ temporalInversion: !1,
441
+ openAllTrails: !1,
442
+ suppressNextFracture: !1,
443
+ skipNextTurnFor: [],
444
+ peekedSector: null,
445
+ salamanderSwap: !1,
446
+ treatyEcho: !1
447
+ };
448
+ return {
449
+ reverseTurnOrder: t.reverseTurnOrder ?? n.reverseTurnOrder,
450
+ temporalInversion: t.temporalInversion ?? n.temporalInversion,
451
+ openAllTrails: t.openAllTrails ?? n.openAllTrails,
452
+ suppressNextFracture: t.suppressNextFracture ?? n.suppressNextFracture,
453
+ skipNextTurnFor: t.skipNextTurnFor ?? n.skipNextTurnFor,
454
+ peekedSector: t.peekedSector === void 0 ? n.peekedSector : t.peekedSector,
455
+ salamanderSwap: t.salamanderSwap ?? n.salamanderSwap,
456
+ treatyEcho: t.treatyEcho ?? n.treatyEcho
457
+ };
458
+ }
459
+ function Re(e) {
460
+ let t = e.qEffects;
461
+ return t ? t.reverseTurnOrder || t.temporalInversion : !1;
462
+ }
463
+ function ze(e, t) {
464
+ return !e || !e.skipNextTurnFor.includes(t) ? e : j(e, { skipNextTurnFor: e.skipNextTurnFor.filter((e) => e !== t) });
465
+ }
466
+ function M(e, t) {
467
+ let n = Re(e) ? [...e.turnOrder].reverse() : [...e.turnOrder], r = n.indexOf(t), i = new Set(e.qEffects?.skipNextTurnFor ?? []);
468
+ for (let e = 1; e <= n.length; e += 1) {
469
+ let t = n[(r + e) % n.length];
470
+ if (!i.has(t)) return t;
471
+ }
472
+ return n[(r + 1) % n.length];
473
+ }
474
+ function Be(e) {
475
+ let t = M(e, e.activePlayerId);
476
+ return {
477
+ ...e,
478
+ activePlayerId: t,
479
+ qEffects: ze(e.qEffects, t)
480
+ };
481
+ }
482
+ function Ve(e) {
483
+ return e.length === 0 ? null : e.reduce((e, t) => t.penaltyScore < e.penaltyScore ? t : e).id;
484
+ }
485
+ function He(e, t) {
486
+ let n = t ? e.filter((e) => e.id !== t) : e;
487
+ return n.length === 0 ? null : n.reduce((e, t) => t.penaltyScore > e.penaltyScore ? t : e).id;
488
+ }
489
+ function Ue(e, t, n, r) {
490
+ if (!A(n, t.modules, t.captains).includes(e)) return null;
491
+ switch (e) {
492
+ case "reverse-turn-order": return { kind: e };
493
+ case "skip-lowest-penalty": {
494
+ let n = Ve(t.captains);
495
+ return n ? {
496
+ kind: e,
497
+ targetPlayerId: n
498
+ } : null;
499
+ }
500
+ case "peek-uncharted": {
501
+ let t = n.unchartedSectors[0];
502
+ return t ? {
503
+ kind: e,
504
+ peek: {
505
+ index: 0,
506
+ coordinate: t
507
+ }
508
+ } : null;
509
+ }
510
+ case "temporal-inversion": return { kind: e };
511
+ case "distress-amplification": return { kind: e };
512
+ case "fracture-immunity": return { kind: e };
513
+ case "salamander-swap": return { kind: e };
514
+ case "treaty-echo": return { kind: e };
515
+ case "q-gamble": return { kind: e };
516
+ }
517
+ }
518
+ function We(e, t, n) {
519
+ let r = {
520
+ ...e,
521
+ qPendingInvoker: null
522
+ }, i = null;
523
+ switch (t.kind) {
524
+ case "reverse-turn-order":
525
+ r = {
526
+ ...r,
527
+ qEffects: j(r.qEffects, { reverseTurnOrder: !0 })
528
+ };
529
+ break;
530
+ case "skip-lowest-penalty":
531
+ if (t.targetPlayerId) {
532
+ let e = r.qEffects?.skipNextTurnFor ?? [];
533
+ r = {
534
+ ...r,
535
+ qEffects: j(r.qEffects, { skipNextTurnFor: e.includes(t.targetPlayerId) ? e : [...e, t.targetPlayerId] })
536
+ };
537
+ }
538
+ break;
539
+ case "peek-uncharted":
540
+ t.peek && (r = {
541
+ ...r,
542
+ qEffects: j(r.qEffects, { peekedSector: {
543
+ index: t.peek.index,
544
+ coordinate: t.peek.coordinate,
545
+ visibleTo: n
546
+ } })
547
+ });
548
+ break;
549
+ case "temporal-inversion":
550
+ r = {
551
+ ...r,
552
+ qEffects: j(r.qEffects, { temporalInversion: !0 })
553
+ };
554
+ break;
555
+ case "distress-amplification":
556
+ r = {
557
+ ...r,
558
+ qEffects: j(r.qEffects, { openAllTrails: !0 })
559
+ };
560
+ break;
561
+ case "fracture-immunity":
562
+ r = {
563
+ ...r,
564
+ qEffects: j(r.qEffects, { suppressNextFracture: !0 })
565
+ };
566
+ break;
567
+ case "salamander-swap":
568
+ r = {
569
+ ...r,
570
+ qEffects: j(r.qEffects, { salamanderSwap: !0 })
571
+ };
572
+ break;
573
+ case "treaty-echo":
574
+ r = {
575
+ ...r,
576
+ qEffects: j(r.qEffects, { treatyEcho: !0 })
577
+ };
578
+ break;
579
+ case "q-gamble": {
580
+ let [e, t, ...a] = r.unchartedSectors;
581
+ if (!e || !t) break;
582
+ i = {
583
+ playerId: n,
584
+ options: [e, t]
585
+ }, r = {
586
+ ...r,
587
+ unchartedSectors: a,
588
+ qGamblePending: i
589
+ };
590
+ break;
591
+ }
592
+ }
593
+ return {
594
+ round: r,
595
+ gamble: i
596
+ };
597
+ }
598
+ function Ge(e) {
599
+ return e.qEffects?.temporalInversion ? {
600
+ ...e,
601
+ qEffects: j(e.qEffects, { temporalInversion: !1 })
602
+ } : e;
603
+ }
604
+ function Ke(e) {
605
+ return e.qEffects?.suppressNextFracture ? {
606
+ round: {
607
+ ...e,
608
+ qEffects: j(e.qEffects, { suppressNextFracture: !1 })
609
+ },
610
+ consumed: !0
611
+ } : {
612
+ round: e,
613
+ consumed: !1
614
+ };
615
+ }
616
+ function qe(e, t, n) {
617
+ let r = e.qGamblePending;
618
+ if (!r || r.playerId !== t) return e;
619
+ let i = r.options[n], a = r.options[+(n === 0)];
620
+ return {
621
+ ...e,
622
+ qGamblePending: null,
623
+ unchartedSectors: [a, ...e.unchartedSectors],
624
+ hands: {
625
+ ...e.hands,
626
+ [t]: [...e.hands[t] ?? [], i]
627
+ }
628
+ };
629
+ }
630
+ function Je(e, t) {
631
+ return t === "neutral-zone" ? !0 : e.qEffects?.treatyEcho === !0;
632
+ }
633
+ function N(e, t) {
634
+ return e.qEffects?.openAllTrails ? !0 : e.table.warpTrails[t]?.distressBeacon.active === !0;
635
+ }
636
+ //#endregion
637
+ //#region libs/engine/src/lib/engine/dead-red-alert.ts
638
+ function P(e) {
639
+ return de(e) ? {
640
+ ...e,
641
+ table: {
642
+ ...e.table,
643
+ redAlert: null
644
+ }
645
+ } : e;
646
+ }
647
+ //#endregion
648
+ //#region libs/engine/src/lib/engine/legal-moves.ts
649
+ function F(e, t, n) {
650
+ let r = d(e, n);
651
+ return r === null ? null : {
652
+ coordinate: e,
653
+ index: t,
654
+ openValue: r
655
+ };
656
+ }
657
+ function I(e, t, n, r, i, a) {
658
+ let o = e.table.warpTrails[i];
659
+ if (!o || i !== n && !N(e, i)) return !1;
660
+ let s = y(o, e.spacedockValue);
661
+ return a ? x(o) && u(r, s) : x(o) && i === n && g(e.table.redAlert, n) ? !1 : u(r, s);
662
+ }
663
+ function Ye(e, t) {
664
+ return u(t, b(e.table.neutralZone, e.spacedockValue));
665
+ }
666
+ function Xe(e, t) {
667
+ let n = e.table.subspaceFracture;
668
+ return !n?.active || n.stabilizers.length >= 3 ? !1 : u(t, n.requiredValue);
669
+ }
670
+ function L(e, t) {
671
+ if (e = P(e), e.qPendingInvoker === t || e.qGamblePending?.playerId === t) return [];
672
+ let n = e.hands[t] ?? [], r = e.mandatoryPlay, i = r?.playerId === t ? n.filter((e) => a(e) === a(r.coordinate)) : n, o = [], s = h(e.table.subspaceFracture), c = e.table.redAlert;
673
+ if (c?.active && c.responsiblePlayerId !== t) return [];
674
+ let l = g(c, t);
675
+ for (let n of i) {
676
+ if (s) {
677
+ Xe(e, n) && o.push({
678
+ coordinate: n,
679
+ route: { kind: "fracture-stabilizer" }
680
+ });
681
+ continue;
682
+ }
683
+ if (l) {
684
+ if (c.neutralZone) {
685
+ let t = b(e.table.neutralZone, e.spacedockValue);
686
+ S(e.table.neutralZone) && u(n, t) && o.push({
687
+ coordinate: n,
688
+ route: {
689
+ kind: "red-alert-cover",
690
+ neutralZone: !0
691
+ }
692
+ });
693
+ } else {
694
+ let r = c.trailPlayerId;
695
+ I(e, t, t, n, r, !0) && o.push({
696
+ coordinate: n,
697
+ route: {
698
+ kind: "red-alert-cover",
699
+ trailPlayerId: r
700
+ }
701
+ });
702
+ }
703
+ continue;
704
+ }
705
+ I(e, t, t, n, t, !1) && o.push({
706
+ coordinate: n,
707
+ route: {
708
+ kind: "warp-trail",
709
+ playerId: t
710
+ }
711
+ }), Ye(e, n) && o.push({
712
+ coordinate: n,
713
+ route: { kind: "neutral-zone" }
714
+ });
715
+ for (let r of e.turnOrder) r !== t && I(e, t, t, n, r, !1) && o.push({
716
+ coordinate: n,
717
+ route: {
718
+ kind: "warp-trail",
719
+ playerId: r
720
+ }
721
+ });
722
+ }
723
+ return o;
724
+ }
725
+ function Ze(e, t, n, r) {
726
+ return L(e, t).some((e) => Qe(e.route, r) && e.coordinate.low === n.low && e.coordinate.high === n.high);
727
+ }
728
+ function Qe(e, t) {
729
+ if (e.kind !== t.kind) return !1;
730
+ switch (e.kind) {
731
+ case "warp-trail": return t.kind === "warp-trail" && e.playerId === t.playerId;
732
+ case "neutral-zone": return t.kind === "neutral-zone";
733
+ case "fracture-stabilizer": return t.kind === "fracture-stabilizer";
734
+ case "red-alert-cover": return t.kind === "red-alert-cover" && !!e.neutralZone == !!t.neutralZone && (e.neutralZone === !0 || e.trailPlayerId !== void 0 && e.trailPlayerId === t.trailPlayerId);
735
+ }
736
+ }
737
+ //#endregion
738
+ //#region libs/engine/src/lib/engine/beacon.ts
739
+ function $e(e, t) {
740
+ return (e.table.warpTrails[t]?.tiles.length ?? 0) > 0;
741
+ }
742
+ function et(e, t) {
743
+ return e.qPendingInvoker === t || e.qGamblePending?.playerId === t;
744
+ }
745
+ function tt(e, t) {
746
+ return h(e.table.subspaceFracture) ? L(e, t).some((e) => e.route.kind === "fracture-stabilizer") : !1;
747
+ }
748
+ function R(e, t) {
749
+ return L(e, t).length === 0 && e.unchartedSectors.length > 0;
750
+ }
751
+ function z(e, t, n) {
752
+ return et(e, t) || e.table.warpTrails[t]?.distressBeacon.active === !0 || g(e.table.redAlert, t) || tt(e, t) || L(e, t).length > 0 ? !1 : n?.afterDraw ? !0 : !R(e, t);
753
+ }
754
+ function B(e, t, n) {
755
+ return et(e, t) || tt(e, t) || L(e, t).length > 0 || g(e.table.redAlert, t) || e.table.warpTrails[t]?.distressBeacon.active !== !0 ? !1 : n?.afterDraw ? !0 : !R(e, t);
756
+ }
757
+ function V(e, t, n) {
758
+ return !g(e.table.redAlert, t) || et(e, t) || tt(e, t) || L(e, t).length > 0 ? !1 : n?.afterDraw ? !0 : !R(e, t);
759
+ }
760
+ function nt(e, t) {
761
+ return e.table.warpTrails[t]?.distressBeacon.active === !0 ? L(e, t).some((e) => e.route.kind === "warp-trail" && e.route.playerId === t) : !1;
762
+ }
763
+ //#endregion
764
+ //#region libs/engine/src/lib/engine/scoring.ts
765
+ function rt(e, t, n, r) {
766
+ let i = t && m(n), a = 0;
767
+ for (let t of e) i && !r?.ignoreSalamanderDoubling && t.low === 12 && t.high === 12 ? a += 24 : a += s(t);
768
+ return a;
769
+ }
770
+ function it(e, t, n) {
771
+ return rt(e, t, n);
772
+ }
773
+ function at(e) {
774
+ return {
775
+ ...e,
776
+ modules: {
777
+ ...e.modules,
778
+ qContinuum: {
779
+ ...e.modules.qContinuum,
780
+ activeFlash: null
781
+ }
782
+ }
783
+ };
784
+ }
785
+ function ot(e, t) {
786
+ let n = e.modules.salamanderPenalty.enabled, r = n && t.qEffects?.salamanderSwap === !0, i = null, a = null;
787
+ if (r) {
788
+ for (let n of e.captains) if (n.id !== t.roundWinnerId && (t.hands[n.id] ?? []).some((e) => e.low === 12 && e.high === 12)) {
789
+ i = n.id;
790
+ break;
791
+ }
792
+ i && (a = He(e.captains, t.roundWinnerId ?? void 0));
793
+ }
794
+ return e.captains.map((e) => {
795
+ if (!t.roundBlocked && e.id === t.roundWinnerId) return e;
796
+ let r = rt(t.hands[e.id] ?? [], n, t.roundNumber, { ignoreSalamanderDoubling: e.id === i });
797
+ return a && i && a !== i && e.id === a && (r += 24), {
798
+ ...e,
799
+ penaltyScore: e.penaltyScore + r
800
+ };
801
+ });
802
+ }
803
+ function H(e, t, n = Math.random) {
804
+ if (t.phase !== "ended" || !t.roundBlocked && !t.roundWinnerId) return {
805
+ ok: !1,
806
+ violation: "ROUND_NOT_PLAYING"
807
+ };
808
+ let r = e.objective === "go-out" ? e.captains : ot(e, t);
809
+ if (e.objective === "go-out") return {
810
+ ok: !0,
811
+ state: at({
812
+ ...e,
813
+ phase: "complete",
814
+ captains: r,
815
+ round: {
816
+ ...t,
817
+ phase: "ended"
818
+ },
819
+ completedRounds: 1
820
+ })
821
+ };
822
+ let i = t.roundNumber + 1;
823
+ if (i > 13) return {
824
+ ok: !0,
825
+ state: at({
826
+ ...e,
827
+ phase: "complete",
828
+ captains: r,
829
+ round: {
830
+ ...t,
831
+ phase: "ended"
832
+ },
833
+ completedRounds: e.completedRounds + 1
834
+ })
835
+ };
836
+ let a = w(C({
837
+ roundNumber: i,
838
+ captains: r,
839
+ shuffledCoordinates: v(je(t), n),
840
+ turnOrder: t.turnOrder
841
+ }));
842
+ return {
843
+ ok: !0,
844
+ state: at(Pe({
845
+ ...e,
846
+ phase: "active",
847
+ completedRounds: e.completedRounds + 1
848
+ }, a, r))
849
+ };
850
+ }
851
+ //#endregion
852
+ //#region libs/engine/src/lib/engine/round-resolution.ts
853
+ function st(e) {
854
+ if (e.unchartedSectors.length > 0 || e.treatyDeclarationRequired && !e.treatyDeclared || e.qPendingInvoker || e.qGamblePending || h(e.table.subspaceFracture) || e.table.redAlert?.active) return !1;
855
+ let t = P(e);
856
+ if (t.table.redAlert?.active) return !1;
857
+ for (let e of t.turnOrder) if (L(t, e).length > 0) return !1;
858
+ return !0;
859
+ }
860
+ function ct(e) {
861
+ return {
862
+ ...e,
863
+ phase: "ended",
864
+ roundWinnerId: null,
865
+ roundBlocked: !0,
866
+ treatyDeclarationRequired: !1,
867
+ treatyDeclared: !0,
868
+ mandatoryPlay: null,
869
+ pendingRoundWin: null
870
+ };
871
+ }
872
+ function U(e) {
873
+ let t = P(e);
874
+ return t.phase === "ended" || !st(t) ? t : ct(t);
875
+ }
876
+ function lt(e) {
877
+ if (!e.pendingRoundWin) return e;
878
+ let { playerId: t, routeKind: n } = e.pendingRoundWin, r = n === "neutral-zone" || e.qEffects?.treatyEcho === !0;
879
+ return {
880
+ ...e,
881
+ pendingRoundWin: null,
882
+ roundWinnerId: t,
883
+ treatyDeclarationRequired: r,
884
+ treatyDeclared: !r,
885
+ phase: r ? "playing" : "ended"
886
+ };
887
+ }
888
+ function ut(e) {
889
+ let t = lt(e);
890
+ return t.phase === "ended" ? U(t) : t;
891
+ }
892
+ //#endregion
893
+ //#region libs/engine/src/lib/engine/apply-action.ts
894
+ function dt(e, t) {
895
+ return {
896
+ ...e,
897
+ tiles: [...e.tiles, t]
898
+ };
899
+ }
900
+ function ft(e) {
901
+ return e.distressBeacon.active ? {
902
+ ...e,
903
+ distressBeacon: { active: !1 }
904
+ } : e;
905
+ }
906
+ function pt(e, t, n) {
907
+ return {
908
+ ...e,
909
+ hands: {
910
+ ...e.hands,
911
+ [t]: n
912
+ }
913
+ };
914
+ }
915
+ function W(e, t) {
916
+ return {
917
+ ...e,
918
+ table: t
919
+ };
920
+ }
921
+ function mt(e, t) {
922
+ return {
923
+ active: !0,
924
+ anchor: e,
925
+ stabilizers: t?.stabilizers ?? [],
926
+ requiredValue: e.coordinate.low
927
+ };
928
+ }
929
+ function ht(e, t, n, r = !1) {
930
+ return {
931
+ active: !0,
932
+ anchor: e,
933
+ responsiblePlayerId: t,
934
+ trailPlayerId: n,
935
+ ...r ? { neutralZone: !0 } : {}
936
+ };
937
+ }
938
+ function gt(e, t, n, r, i, a) {
939
+ let s = e.table, c = s.redAlert, l = e;
940
+ i && c?.active && (c = null);
941
+ let u = o(n.coordinate), d = r.kind === "warp-trail" && r.playerId === t;
942
+ if (u && (l = Ge(l)), u && !i) if (r.kind === "warp-trail" || r.kind === "neutral-zone") {
943
+ c = ht(n, t, r.kind === "warp-trail" ? r.playerId : "", r.kind === "neutral-zone");
944
+ let { round: e, consumed: i } = Ke(l);
945
+ l = e, s = {
946
+ ...s,
947
+ redAlert: c,
948
+ subspaceFracture: d && a && !i ? mt(n, s.subspaceFracture) : s.subspaceFracture
949
+ };
950
+ } else s = {
951
+ ...s,
952
+ redAlert: c
953
+ };
954
+ else s = {
955
+ ...s,
956
+ redAlert: c
957
+ };
958
+ return W(l, s);
959
+ }
960
+ function _t(e, t, n, r, i) {
961
+ let { hand: a, removed: s } = xe(e.hands[t] ?? [], n);
962
+ if (!s) throw Error("Coordinate not in hand");
963
+ let c = pt(e, t, a), l = { ...c.table }, u, d = !1;
964
+ switch (r.kind) {
965
+ case "fracture-stabilizer": {
966
+ let e = l.subspaceFracture;
967
+ u = F(s, e.stabilizers.length, e.requiredValue);
968
+ let t = [...e.stabilizers, u];
969
+ l = {
970
+ ...l,
971
+ subspaceFracture: {
972
+ ...e,
973
+ stabilizers: t,
974
+ active: t.length < 3
975
+ }
976
+ };
977
+ break;
978
+ }
979
+ case "red-alert-cover": {
980
+ if (r.neutralZone) {
981
+ let t = b(l.neutralZone, e.spacedockValue), n = F(s, l.neutralZone.tiles.length, t);
982
+ u = n, d = !0, l = {
983
+ ...l,
984
+ neutralZone: { tiles: [...l.neutralZone.tiles, n] }
985
+ };
986
+ break;
987
+ }
988
+ let t = r.trailPlayerId, n = l.warpTrails[t], i = y(n, e.spacedockValue), a = F(s, n.tiles.length, i);
989
+ u = a, d = !0, l = {
990
+ ...l,
991
+ warpTrails: {
992
+ ...l.warpTrails,
993
+ [t]: dt(n, a)
994
+ }
995
+ };
996
+ break;
997
+ }
998
+ case "neutral-zone": {
999
+ let t = b(l.neutralZone, e.spacedockValue);
1000
+ u = F(s, l.neutralZone.tiles.length, t), l = {
1001
+ ...l,
1002
+ neutralZone: { tiles: [...l.neutralZone.tiles, u] }
1003
+ };
1004
+ break;
1005
+ }
1006
+ case "warp-trail": {
1007
+ let n = l.warpTrails[r.playerId], i = y(n, e.spacedockValue);
1008
+ u = F(s, n.tiles.length, i);
1009
+ let a = r.playerId === t ? dt(ft(n), u) : dt(n, u);
1010
+ l = {
1011
+ ...l,
1012
+ warpTrails: {
1013
+ ...l.warpTrails,
1014
+ [r.playerId]: a
1015
+ }
1016
+ };
1017
+ break;
1018
+ }
1019
+ }
1020
+ c = gt(W(c, l), t, u, r, d, i.subspaceFractureEnabled), c = P(c), i.qContinuumEnabled && o(u.coordinate) && u.coordinate.low === 0 && r.kind === "warp-trail" && r.playerId === t && (c = {
1021
+ ...c,
1022
+ qPendingInvoker: t
1023
+ });
1024
+ let f = (c.hands[t] ?? []).length === 0;
1025
+ if (c.qPendingInvoker || c.qGamblePending) return f && (c = {
1026
+ ...c,
1027
+ pendingRoundWin: {
1028
+ playerId: t,
1029
+ routeKind: r.kind
1030
+ }
1031
+ }), {
1032
+ ...c,
1033
+ mandatoryPlay: null
1034
+ };
1035
+ if (f) {
1036
+ let e = Je(c, r.kind);
1037
+ return c = {
1038
+ ...c,
1039
+ treatyDeclarationRequired: e,
1040
+ treatyDeclared: !e,
1041
+ roundWinnerId: t,
1042
+ phase: e ? "playing" : "ended",
1043
+ mandatoryPlay: null
1044
+ }, U(c);
1045
+ }
1046
+ return c = {
1047
+ ...c,
1048
+ mandatoryPlay: null
1049
+ }, g(c.table.redAlert, t) || h(c.table.subspaceFracture) ? c : U(G(c));
1050
+ }
1051
+ function G(e) {
1052
+ return e.treatyDeclarationRequired && !e.treatyDeclared || e.qPendingInvoker || e.qGamblePending ? e : Be(e);
1053
+ }
1054
+ function vt(e, t, n) {
1055
+ if (t.unchartedSectors.length === 0) return E("EMPTY_UNCHARTED");
1056
+ let [r, ...i] = t.unchartedSectors, a = pt(t, n, [...t.hands[n] ?? [], r]);
1057
+ return a = {
1058
+ ...a,
1059
+ unchartedSectors: i
1060
+ }, L(a, n).filter((e) => e.coordinate.low === r.low && e.coordinate.high === r.high).length > 0 ? (a = {
1061
+ ...a,
1062
+ mandatoryPlay: {
1063
+ playerId: n,
1064
+ coordinate: r
1065
+ }
1066
+ }, {
1067
+ ok: !0,
1068
+ state: O(e, a)
1069
+ }) : g(a.table.redAlert, n) && V(a, n, { afterDraw: !0 }) ? yt(e, a, n, { afterDraw: !0 }) : z(a, n, { afterDraw: !0 }) ? St(e, a, n, { afterDraw: !0 }) : B(a, n, { afterDraw: !0 }) ? bt(e, a, n, { afterDraw: !0 }) : {
1070
+ ok: !0,
1071
+ state: O(e, U(G(a)))
1072
+ };
1073
+ }
1074
+ function yt(e, t, n, r) {
1075
+ if (!V(t, n, r)) {
1076
+ let e = t.table.redAlert;
1077
+ return !e?.active || e.responsiblePlayerId !== n ? E("RED_ALERT_NOT_ACTIVE") : L(t, n).length > 0 ? E("RED_ALERT_COVER_AVAILABLE") : t.unchartedSectors.length > 0 ? E("MUST_DRAW_FIRST") : E("RED_ALERT_NOT_ACTIVE");
1078
+ }
1079
+ let i = t.table.redAlert, a = M(t, n), o = W(t, {
1080
+ ...t.table,
1081
+ redAlert: {
1082
+ ...i,
1083
+ responsiblePlayerId: a
1084
+ }
1085
+ });
1086
+ return o = {
1087
+ ...o,
1088
+ table: {
1089
+ ...o.table,
1090
+ warpTrails: {
1091
+ ...o.table.warpTrails,
1092
+ [n]: {
1093
+ ...o.table.warpTrails[n],
1094
+ distressBeacon: { active: !0 }
1095
+ }
1096
+ }
1097
+ }
1098
+ }, o = G(o), o = U(o), {
1099
+ ok: !0,
1100
+ state: O(e, o)
1101
+ };
1102
+ }
1103
+ function bt(e, t, n, r) {
1104
+ return B(t, n, r) ? {
1105
+ ok: !0,
1106
+ state: O(e, U(G(t)))
1107
+ } : E("PASS_NOT_ALLOWED");
1108
+ }
1109
+ function xt(e, t, n) {
1110
+ if (!t.treatyDeclarationRequired || t.treatyDeclared || t.roundWinnerId !== n) return E("IMPULSE_FORFEIT_NOT_ALLOWED");
1111
+ if (t.unchartedSectors.length === 0) return E("EMPTY_UNCHARTED");
1112
+ let [r, ...i] = t.unchartedSectors, a = {
1113
+ ...t,
1114
+ unchartedSectors: i,
1115
+ roundWinnerId: null,
1116
+ treatyDeclarationRequired: !1,
1117
+ treatyDeclared: !1,
1118
+ phase: "playing",
1119
+ pendingRoundWin: null,
1120
+ mandatoryPlay: null
1121
+ };
1122
+ return a = pt(a, n, [...a.hands[n] ?? [], r]), a = G(a), a = U(a), {
1123
+ ok: !0,
1124
+ state: O(e, a)
1125
+ };
1126
+ }
1127
+ function St(e, t, n, r) {
1128
+ if (t.table.warpTrails[n]?.distressBeacon.active) return E("BEACON_ALREADY_ACTIVE");
1129
+ if (!z(t, n, r)) return !r?.afterDraw && L(t, n).length === 0 && t.unchartedSectors.length > 0 ? E("MUST_DRAW_FIRST") : E("BEACON_NOT_ALLOWED");
1130
+ let i = t.table.warpTrails[n], a = W(t, {
1131
+ ...t.table,
1132
+ warpTrails: {
1133
+ ...t.table.warpTrails,
1134
+ [n]: {
1135
+ ...i,
1136
+ distressBeacon: { active: !0 }
1137
+ }
1138
+ },
1139
+ redAlert: t.table.redAlert?.responsiblePlayerId === n ? {
1140
+ ...t.table.redAlert,
1141
+ responsiblePlayerId: M(t, n)
1142
+ } : t.table.redAlert
1143
+ });
1144
+ return a = G(a), a = U(a), {
1145
+ ok: !0,
1146
+ state: O(e, a)
1147
+ };
1148
+ }
1149
+ function Ct(e, t, n) {
1150
+ if (!t.treatyDeclarationRequired || t.treatyDeclared) return E("TREATY_NOT_REQUIRED");
1151
+ let r = t.roundWinnerId;
1152
+ if (r != null && r !== n) return E("TREATY_NOT_REQUIRED");
1153
+ let i = t.hands[n] ?? [];
1154
+ return r == null && i.length > 0 ? E("TREATY_NOT_REQUIRED") : {
1155
+ ok: !0,
1156
+ state: O(e, {
1157
+ ...t,
1158
+ roundWinnerId: r ?? n,
1159
+ treatyDeclared: !0,
1160
+ phase: "ended"
1161
+ })
1162
+ };
1163
+ }
1164
+ function K(e, t) {
1165
+ return e.qPendingInvoker === t || e.qGamblePending?.playerId === t;
1166
+ }
1167
+ function wt(e, t, n, r) {
1168
+ if (!e.modules.qContinuum.enabled) return E("INVALID_ROUTE");
1169
+ if (t.qPendingInvoker !== n) return E("Q_FLASH_NOT_PENDING");
1170
+ let i = Ue(r, e, t, n);
1171
+ if (!i) return E("Q_FLASH_UNAVAILABLE");
1172
+ let { round: a } = We(t, i, n), o = ut(a), s = {
1173
+ invokedBy: n,
1174
+ effect: i
1175
+ }, c = {
1176
+ ...e,
1177
+ modules: {
1178
+ ...e.modules,
1179
+ qContinuum: {
1180
+ ...e.modules.qContinuum,
1181
+ activeFlash: s
1182
+ }
1183
+ },
1184
+ round: o
1185
+ };
1186
+ return o.phase === "ended" || !o.qGamblePending && !g(o.table.redAlert, n) && !h(o.table.subspaceFracture) && (o = U(G(o)), c = O(c, o)), {
1187
+ ok: !0,
1188
+ state: c
1189
+ };
1190
+ }
1191
+ function Tt(e, t, n, r) {
1192
+ if (!e.modules.qContinuum.enabled) return E("INVALID_ROUTE");
1193
+ if (t.qGamblePending?.playerId !== n) return E("Q_GAMBLE_NOT_PENDING");
1194
+ let i = ut(qe(t, n, r));
1195
+ return i.phase === "ended" || !g(i.table.redAlert, n) && !h(i.table.subspaceFracture) && (i = U(G(i))), {
1196
+ ok: !0,
1197
+ state: O(e, i)
1198
+ };
1199
+ }
1200
+ function Et(e, t) {
1201
+ if (t.type === "END_ROUND") {
1202
+ if (e.phase !== "active" || !e.round) return E("GAME_NOT_ACTIVE");
1203
+ let n = e.round;
1204
+ return n.phase === "ended" ? n.roundBlocked ? t.winnerId === null ? H(e, n) : E("ROUND_NOT_PLAYING") : n.roundWinnerId === t.winnerId ? H(e, n) : E("ROUND_NOT_PLAYING") : E("ROUND_NOT_PLAYING");
1205
+ }
1206
+ let n = Ne(e);
1207
+ if (typeof n == "string") return E(n);
1208
+ let r = n;
1209
+ switch (t.type) {
1210
+ case "CHART_COORDINATE": {
1211
+ let n = D(r, t.playerId);
1212
+ if (n !== !0) return E(n);
1213
+ if (!Se(r.hands[t.playerId] ?? [], t.coordinate)) return E("COORDINATE_NOT_IN_HAND");
1214
+ if (K(r, t.playerId)) return E("Q_FLASH_NOT_PENDING");
1215
+ if (h(r.table.subspaceFracture)) {
1216
+ if (t.route.kind !== "fracture-stabilizer") return E("FRACTURE_REQUIRES_STABILIZER");
1217
+ } else if (g(r.table.redAlert, t.playerId)) {
1218
+ if (t.route.kind !== "red-alert-cover") return E("RED_ALERT_REQUIRED");
1219
+ } else if (t.route.kind === "fracture-stabilizer" || t.route.kind === "red-alert-cover") return E("INVALID_ROUTE");
1220
+ if (t.route.kind === "warp-trail" && t.route.playerId !== t.playerId && !N(r, t.route.playerId)) return E("SHIELDS_UP");
1221
+ if (!Ze(r, t.playerId, t.coordinate, t.route)) return E("INVALID_ROUTE");
1222
+ try {
1223
+ r = _t(r, t.playerId, t.coordinate, t.route, {
1224
+ subspaceFractureEnabled: e.modules.subspaceFracture.enabled,
1225
+ qContinuumEnabled: e.modules.qContinuum.enabled
1226
+ });
1227
+ } catch {
1228
+ return E("INVALID_ROUTE");
1229
+ }
1230
+ return {
1231
+ ok: !0,
1232
+ state: O(e, r)
1233
+ };
1234
+ }
1235
+ case "DRAW_FROM_UNCHARTED": {
1236
+ let n = D(r, t.playerId);
1237
+ return n === !0 ? K(r, t.playerId) ? E("Q_FLASH_NOT_PENDING") : L(r, t.playerId).length > 0 ? E("INVALID_ROUTE") : vt(e, r, t.playerId) : E(n);
1238
+ }
1239
+ case "PASS_RED_ALERT": {
1240
+ let n = D(r, t.playerId);
1241
+ return n === !0 ? K(r, t.playerId) ? E("Q_FLASH_NOT_PENDING") : yt(e, r, t.playerId) : E(n);
1242
+ }
1243
+ case "PASS_TURN": {
1244
+ let n = D(r, t.playerId);
1245
+ return n === !0 ? K(r, t.playerId) ? E("Q_FLASH_NOT_PENDING") : bt(e, r, t.playerId) : E(n);
1246
+ }
1247
+ case "DEPLOY_DISTRESS_BEACON": {
1248
+ let n = D(r, t.playerId);
1249
+ return n === !0 ? K(r, t.playerId) ? E("Q_FLASH_NOT_PENDING") : St(e, r, t.playerId) : E(n);
1250
+ }
1251
+ case "DECLARE_TREATY": return Ct(e, r, t.playerId);
1252
+ case "FORFEIT_IMPULSE": {
1253
+ let n = D(r, t.playerId);
1254
+ return n === !0 ? xt(e, r, t.playerId) : E(n);
1255
+ }
1256
+ case "INVOKE_Q_FLASH": {
1257
+ let n = D(r, t.playerId);
1258
+ return n === !0 ? wt(e, r, t.playerId, t.effect) : E(n);
1259
+ }
1260
+ case "RESOLVE_Q_GAMBLE": {
1261
+ let n = D(r, t.playerId);
1262
+ return n === !0 ? Tt(e, r, t.playerId, t.keepIndex) : E(n);
1263
+ }
1264
+ default: return E("INVALID_ROUTE");
1265
+ }
1266
+ }
1267
+ //#endregion
1268
+ //#region libs/engine/src/lib/ai/actions.ts
1269
+ function Dt(e, t) {
1270
+ switch (e.kind) {
1271
+ case "chart": return {
1272
+ type: "CHART_COORDINATE",
1273
+ playerId: t,
1274
+ coordinate: e.move.coordinate,
1275
+ route: e.move.route
1276
+ };
1277
+ case "draw": return {
1278
+ type: "DRAW_FROM_UNCHARTED",
1279
+ playerId: t
1280
+ };
1281
+ case "deploy-beacon": return {
1282
+ type: "DEPLOY_DISTRESS_BEACON",
1283
+ playerId: t
1284
+ };
1285
+ case "pass-red-alert": return {
1286
+ type: "PASS_RED_ALERT",
1287
+ playerId: t
1288
+ };
1289
+ case "pass-turn": return {
1290
+ type: "PASS_TURN",
1291
+ playerId: t
1292
+ };
1293
+ case "declare-treaty": return {
1294
+ type: "DECLARE_TREATY",
1295
+ playerId: t
1296
+ };
1297
+ case "invoke-q-flash": return {
1298
+ type: "INVOKE_Q_FLASH",
1299
+ playerId: t,
1300
+ effect: e.effect
1301
+ };
1302
+ case "resolve-q-gamble": return {
1303
+ type: "RESOLVE_Q_GAMBLE",
1304
+ playerId: t,
1305
+ keepIndex: e.keepIndex
1306
+ };
1307
+ }
1308
+ }
1309
+ //#endregion
1310
+ //#region libs/engine/src/lib/ai/observation.ts
1311
+ function Ot(e, t) {
1312
+ return e.round ? {
1313
+ round: e.round,
1314
+ playerId: t,
1315
+ modules: e.modules,
1316
+ objective: e.objective,
1317
+ captains: e.captains
1318
+ } : null;
1319
+ }
1320
+ //#endregion
1321
+ //#region libs/engine/src/lib/ai/context.ts
1322
+ function kt(e) {
1323
+ let t = [{
1324
+ low: e.spacedock.value,
1325
+ high: e.spacedock.value
1326
+ }];
1327
+ for (let n of Object.values(e.warpTrails)) for (let e of n.tiles) t.push(e.coordinate);
1328
+ for (let n of e.neutralZone.tiles) t.push(n.coordinate);
1329
+ if (e.subspaceFracture) {
1330
+ t.push(e.subspaceFracture.anchor.coordinate);
1331
+ for (let n of e.subspaceFracture.stabilizers) t.push(n.coordinate);
1332
+ }
1333
+ return t;
1334
+ }
1335
+ function At(e, t) {
1336
+ switch (t.kind) {
1337
+ case "warp-trail": {
1338
+ let n = e.table.warpTrails[t.playerId];
1339
+ return n ? y(n, e.spacedockValue) : null;
1340
+ }
1341
+ case "red-alert-cover": {
1342
+ if (t.neutralZone) return b(e.table.neutralZone, e.spacedockValue);
1343
+ let n = e.table.warpTrails[t.trailPlayerId];
1344
+ return n ? y(n, e.spacedockValue) : null;
1345
+ }
1346
+ case "neutral-zone": return b(e.table.neutralZone, e.spacedockValue);
1347
+ case "fracture-stabilizer": return e.table.subspaceFracture ? e.table.subspaceFracture.requiredValue : null;
1348
+ }
1349
+ }
1350
+ function jt(e, t) {
1351
+ let n = e.round.hands[e.playerId] ?? [], r = /* @__PURE__ */ new Set();
1352
+ for (let t of kt(e.round.table)) r.add(a(t));
1353
+ for (let e of n) r.add(a(e));
1354
+ return {
1355
+ obs: e,
1356
+ hand: n,
1357
+ unseen: _(12).filter((e) => !r.has(a(e))),
1358
+ rng: t
1359
+ };
1360
+ }
1361
+ //#endregion
1362
+ //#region libs/engine/src/lib/ai/q-flash.ts
1363
+ function Mt(e, t, n) {
1364
+ let { round: r, playerId: i, objective: a } = t, o = n.find((e) => e.id === i), c = n.reduce((e, t) => t.penaltyScore < e.penaltyScore ? t : e), l = n.reduce((e, t) => t.penaltyScore > e.penaltyScore ? t : e), u = r.hands[i] ?? [], d = u.reduce((e, t) => e + s(t), 0);
1365
+ switch (e) {
1366
+ case "reverse-turn-order": return o && o.penaltyScore > l.penaltyScore * .5 ? 8 : 4;
1367
+ case "skip-lowest-penalty": return c.id === i ? -5 : 12;
1368
+ case "peek-uncharted": return r.unchartedSectors.length > 0 ? 6 : 0;
1369
+ case "temporal-inversion": return d > 30 ? 7 : 3;
1370
+ case "distress-amplification": return u.length <= 4 ? 9 : 5;
1371
+ case "fracture-immunity": return u.some((e) => e.low === e.high) ? 10 : 2;
1372
+ case "salamander-swap": return u.some((e) => e.low === 12 && e.high === 12) ? 20 : l.id === i ? 0 : 6;
1373
+ case "treaty-echo": return a === "penalty" ? 5 : 1;
1374
+ case "q-gamble": return r.unchartedSectors.length >= 2 ? 8 : 0;
1375
+ }
1376
+ }
1377
+ function Nt(e, t, n = {}) {
1378
+ let r = n.rng ?? Math.random, i = A(e.round, e.modules, t);
1379
+ return i.length === 0 ? "reverse-turn-order" : i.map((n) => ({
1380
+ kind: n,
1381
+ score: Mt(n, e, t) + r() * 2
1382
+ })).sort((e, t) => t.score - e.score)[0]?.kind ?? i[0];
1383
+ }
1384
+ function Pt(e, t = {}) {
1385
+ let n = e.round.qGamblePending;
1386
+ if (!n) return 0;
1387
+ let [r, i] = n.options, a = s(r), o = s(i);
1388
+ return a === o ? (t.rng ?? Math.random)() < .5 ? 0 : 1 : a > o ? 0 : 1;
1389
+ }
1390
+ //#endregion
1391
+ //#region libs/engine/src/lib/ai/candidate-generator.ts
1392
+ function Ft(e, t) {
1393
+ let { round: n, playerId: r } = e;
1394
+ if (n.qPendingInvoker === r) return [{
1395
+ kind: "invoke-q-flash",
1396
+ effect: Nt(e, e.captains, { rng: t?.rng })
1397
+ }];
1398
+ if (n.qGamblePending?.playerId === r) return [{
1399
+ kind: "resolve-q-gamble",
1400
+ keepIndex: Pt(e, { rng: t?.rng })
1401
+ }];
1402
+ if (n.treatyDeclarationRequired && !n.treatyDeclared && n.roundWinnerId === r) return [{ kind: "declare-treaty" }];
1403
+ let i = L(n, r);
1404
+ return i.length > 0 ? i.map((e) => ({
1405
+ kind: "chart",
1406
+ move: e
1407
+ })) : n.unchartedSectors.length > 0 ? [{ kind: "draw" }] : V(n, r) ? [{ kind: "pass-red-alert" }] : z(n, r) ? [{ kind: "deploy-beacon" }] : B(n, r) ? [{ kind: "pass-turn" }] : [];
1408
+ }
1409
+ //#endregion
1410
+ //#region libs/engine/src/lib/ai/heuristics.ts
1411
+ var q = {
1412
+ preferChart: "prefer-chart",
1413
+ dumpPips: "dump-pips",
1414
+ doublesEarly: "play-doubles-early",
1415
+ ownTrail: "own-trail",
1416
+ coverRelief: "cover-relief",
1417
+ redAlertSafe: "red-alert-safe",
1418
+ handFlexibility: "hand-flexibility",
1419
+ defensiveShared: "defensive-shared",
1420
+ salamanderDump: "salamander-dump",
1421
+ qContinuum: "q-continuum",
1422
+ goOutWin: "go-out-win"
1423
+ }, J = q;
1424
+ function It(e, t) {
1425
+ let n = At(t.obs.round, e.move.route);
1426
+ return n === null ? null : d(e.move.coordinate, n);
1427
+ }
1428
+ var Lt = [
1429
+ {
1430
+ id: J.preferChart,
1431
+ score(e) {
1432
+ switch (e.kind) {
1433
+ case "declare-treaty": return 200;
1434
+ case "chart": return 100;
1435
+ case "draw": return 0;
1436
+ case "pass-red-alert": return -20;
1437
+ case "pass-turn": return -30;
1438
+ case "deploy-beacon": return -40;
1439
+ case "invoke-q-flash":
1440
+ case "resolve-q-gamble": return 150;
1441
+ }
1442
+ }
1443
+ },
1444
+ {
1445
+ id: J.dumpPips,
1446
+ score(e, t) {
1447
+ return e.kind !== "chart" || t.obs.objective === "go-out" ? 0 : s(e.move.coordinate);
1448
+ }
1449
+ },
1450
+ {
1451
+ id: J.goOutWin,
1452
+ score(e, t) {
1453
+ return t.obs.objective !== "go-out" || e.kind !== "chart" ? 0 : t.hand.length === 1 ? 200 : 0;
1454
+ }
1455
+ },
1456
+ {
1457
+ id: J.doublesEarly,
1458
+ score(e, t) {
1459
+ return e.kind !== "chart" || !o(e.move.coordinate) ? 0 : Math.min(t.hand.length, 12);
1460
+ }
1461
+ },
1462
+ {
1463
+ id: J.ownTrail,
1464
+ score(e, t) {
1465
+ if (e.kind !== "chart") return 0;
1466
+ let n = e.move.route;
1467
+ return n.kind === "warp-trail" && n.playerId === t.obs.playerId ? 8 : 0;
1468
+ }
1469
+ },
1470
+ {
1471
+ id: J.coverRelief,
1472
+ score(e) {
1473
+ if (e.kind !== "chart") return 0;
1474
+ let t = e.move.route.kind;
1475
+ return t === "red-alert-cover" || t === "fracture-stabilizer" ? 12 : 0;
1476
+ }
1477
+ },
1478
+ {
1479
+ id: J.redAlertSafe,
1480
+ score(e, t) {
1481
+ if (e.kind !== "chart") return 0;
1482
+ let { coordinate: n, route: r } = e.move;
1483
+ if (!(r.kind === "warp-trail" || r.kind === "neutral-zone") || !o(n)) return 0;
1484
+ let i = n.low, s = a(n);
1485
+ return t.hand.some((e) => a(e) !== s && u(e, i)) ? 0 : -60;
1486
+ }
1487
+ },
1488
+ {
1489
+ id: J.handFlexibility,
1490
+ score(e, t) {
1491
+ if (e.kind !== "chart") return 0;
1492
+ let n = It(e, t);
1493
+ if (n === null) return 0;
1494
+ let r = a(e.move.coordinate), i = !1, o = 0;
1495
+ for (let e of t.hand) {
1496
+ if (!i && a(e) === r) {
1497
+ i = !0;
1498
+ continue;
1499
+ }
1500
+ u(e, n) && o++;
1501
+ }
1502
+ return o * 2;
1503
+ }
1504
+ },
1505
+ {
1506
+ id: J.defensiveShared,
1507
+ score(e, t) {
1508
+ if (e.kind !== "chart") return 0;
1509
+ let n = e.move.route;
1510
+ if (!(n.kind === "neutral-zone" || n.kind === "warp-trail" && n.playerId !== t.obs.playerId)) return 0;
1511
+ let r = It(e, t);
1512
+ if (r === null) return 0;
1513
+ let i = 0;
1514
+ for (let e of t.unseen) u(e, r) && i++;
1515
+ return -i;
1516
+ }
1517
+ },
1518
+ {
1519
+ id: J.salamanderDump,
1520
+ score(e, t) {
1521
+ if (e.kind !== "chart" || t.obs.objective === "go-out" || !t.obs.modules.salamanderPenalty.enabled || !m(t.obs.round.roundNumber)) return 0;
1522
+ let { low: n, high: r } = e.move.coordinate;
1523
+ return n === 12 && r === 12 ? 50 : 0;
1524
+ }
1525
+ },
1526
+ {
1527
+ id: J.qContinuum,
1528
+ score(e, t) {
1529
+ if (e.kind !== "chart" || !t.obs.modules.qContinuum.enabled) return 0;
1530
+ let { low: n, high: r } = e.move.coordinate;
1531
+ return n === 0 && r === 0 ? 15 : 0;
1532
+ }
1533
+ }
1534
+ ], Y = q, Rt = {
1535
+ beginner: {
1536
+ ...e.beginner,
1537
+ enabled: /* @__PURE__ */ new Set([Y.preferChart, Y.dumpPips]),
1538
+ weights: {
1539
+ [Y.preferChart]: 1,
1540
+ [Y.dumpPips]: .2
1541
+ }
1542
+ },
1543
+ intermediate: {
1544
+ ...e.intermediate,
1545
+ enabled: /* @__PURE__ */ new Set([
1546
+ Y.preferChart,
1547
+ Y.dumpPips,
1548
+ Y.doublesEarly,
1549
+ Y.ownTrail,
1550
+ Y.coverRelief
1551
+ ]),
1552
+ weights: {
1553
+ [Y.preferChart]: 1,
1554
+ [Y.dumpPips]: 1,
1555
+ [Y.doublesEarly]: 1,
1556
+ [Y.ownTrail]: 1,
1557
+ [Y.coverRelief]: 1
1558
+ }
1559
+ },
1560
+ advanced: {
1561
+ ...e.advanced,
1562
+ enabled: /* @__PURE__ */ new Set([
1563
+ Y.preferChart,
1564
+ Y.dumpPips,
1565
+ Y.doublesEarly,
1566
+ Y.ownTrail,
1567
+ Y.coverRelief,
1568
+ Y.redAlertSafe,
1569
+ Y.handFlexibility,
1570
+ Y.defensiveShared,
1571
+ Y.salamanderDump,
1572
+ Y.qContinuum
1573
+ ]),
1574
+ weights: {
1575
+ [Y.preferChart]: 1,
1576
+ [Y.dumpPips]: 1.2,
1577
+ [Y.doublesEarly]: 1.5,
1578
+ [Y.ownTrail]: 1,
1579
+ [Y.coverRelief]: 1.5,
1580
+ [Y.redAlertSafe]: 1.5,
1581
+ [Y.handFlexibility]: 1,
1582
+ [Y.defensiveShared]: 1.5,
1583
+ [Y.salamanderDump]: 2,
1584
+ [Y.qContinuum]: 1.5
1585
+ }
1586
+ }
1587
+ }, zt = {
1588
+ beginner: {
1589
+ ...e.beginner,
1590
+ enabled: /* @__PURE__ */ new Set([
1591
+ Y.preferChart,
1592
+ Y.goOutWin,
1593
+ Y.handFlexibility
1594
+ ]),
1595
+ weights: {
1596
+ [Y.preferChart]: 1,
1597
+ [Y.goOutWin]: 1,
1598
+ [Y.handFlexibility]: .5
1599
+ }
1600
+ },
1601
+ intermediate: {
1602
+ ...e.intermediate,
1603
+ enabled: /* @__PURE__ */ new Set([
1604
+ Y.preferChart,
1605
+ Y.goOutWin,
1606
+ Y.doublesEarly,
1607
+ Y.ownTrail,
1608
+ Y.coverRelief,
1609
+ Y.handFlexibility
1610
+ ]),
1611
+ weights: {
1612
+ [Y.preferChart]: 1,
1613
+ [Y.goOutWin]: 1.5,
1614
+ [Y.doublesEarly]: 1,
1615
+ [Y.ownTrail]: 1.2,
1616
+ [Y.coverRelief]: 1,
1617
+ [Y.handFlexibility]: 1.2
1618
+ }
1619
+ },
1620
+ advanced: {
1621
+ ...e.advanced,
1622
+ enabled: /* @__PURE__ */ new Set([
1623
+ Y.preferChart,
1624
+ Y.goOutWin,
1625
+ Y.doublesEarly,
1626
+ Y.ownTrail,
1627
+ Y.coverRelief,
1628
+ Y.redAlertSafe,
1629
+ Y.handFlexibility,
1630
+ Y.defensiveShared,
1631
+ Y.qContinuum
1632
+ ]),
1633
+ weights: {
1634
+ [Y.preferChart]: 1,
1635
+ [Y.goOutWin]: 2,
1636
+ [Y.doublesEarly]: 1.2,
1637
+ [Y.ownTrail]: 1.5,
1638
+ [Y.coverRelief]: 1.5,
1639
+ [Y.redAlertSafe]: 1.5,
1640
+ [Y.handFlexibility]: 1.5,
1641
+ [Y.defensiveShared]: 1.5,
1642
+ [Y.qContinuum]: 1
1643
+ }
1644
+ }
1645
+ }, Bt = Rt;
1646
+ function Vt(e, t = "penalty") {
1647
+ return (t === "go-out" ? zt : Rt)[e];
1648
+ }
1649
+ //#endregion
1650
+ //#region libs/engine/src/lib/ai/search-model.ts
1651
+ function X(e, t, n) {
1652
+ let r = t.salamanderPenalty.enabled && m(n), i = 0;
1653
+ for (let t of e) r && t.low === 12 && t.high === 12 ? i += 24 : i += s(t);
1654
+ return i;
1655
+ }
1656
+ function Z(e) {
1657
+ return e.length;
1658
+ }
1659
+ function Ht(e, t) {
1660
+ let n = e.round;
1661
+ if (!n) return 0;
1662
+ let r = e.modules, i = X(n.hands[t] ?? [], r, n.roundNumber), a = 0, o = 0;
1663
+ for (let e of n.turnOrder) e !== t && (a += X(n.hands[e] ?? [], r, n.roundNumber), o++);
1664
+ let s = o > 0 ? a / o : 0;
1665
+ return n.phase === "ended" ? n.roundWinnerId === t ? 1e4 + s : -1e3 - i + s : s - i;
1666
+ }
1667
+ function Ut(e, t) {
1668
+ let n = e.round;
1669
+ if (!n) return 0;
1670
+ let r = Z(n.hands[t] ?? []), i = 0, a = 0;
1671
+ for (let e of n.turnOrder) e !== t && (i += Z(n.hands[e] ?? []), a++);
1672
+ let o = a > 0 ? i / a : 0;
1673
+ return n.phase === "ended" ? n.roundWinnerId === t ? 1e4 + o : -1e3 - r + o : o - r;
1674
+ }
1675
+ function Wt(e, t) {
1676
+ return e.objective === "go-out" ? Ut(e, t) : Ht(e, t);
1677
+ }
1678
+ function Gt(e) {
1679
+ return {
1680
+ id: "search",
1681
+ phase: "active",
1682
+ captains: e.captains.length > 0 ? e.captains : e.round.turnOrder.map((e) => ({
1683
+ id: e,
1684
+ displayName: e,
1685
+ penaltyScore: 0
1686
+ })),
1687
+ round: e.round,
1688
+ completedRounds: 0,
1689
+ modules: e.modules,
1690
+ objective: e.objective
1691
+ };
1692
+ }
1693
+ function Kt(e, t, n) {
1694
+ switch (e.kind) {
1695
+ case "declare-treaty": return 1e4;
1696
+ case "chart": return t === "go-out" && n === 1 ? 2e4 : t === "go-out" ? 100 + (10 - n) : 100 + s(e.move.coordinate);
1697
+ case "draw": return -1;
1698
+ case "invoke-q-flash":
1699
+ case "resolve-q-gamble": return 5e3;
1700
+ default: return -2;
1701
+ }
1702
+ }
1703
+ function qt(e = "penalty") {
1704
+ return {
1705
+ legalActions(e) {
1706
+ let t = e.round;
1707
+ return !t || t.phase !== "playing" ? [] : Ft({
1708
+ round: t,
1709
+ playerId: t.activePlayerId,
1710
+ modules: e.modules,
1711
+ objective: e.objective,
1712
+ captains: e.captains
1713
+ });
1714
+ },
1715
+ applyAction(e, t) {
1716
+ let n = e.round;
1717
+ if (!n) return e;
1718
+ let r = Et(e, Dt(t, n.activePlayerId));
1719
+ return r.ok ? r.state : e;
1720
+ },
1721
+ isTerminal(e) {
1722
+ return e.phase === "complete" || !e.round || e.round.phase === "ended";
1723
+ },
1724
+ currentPlayer(e) {
1725
+ return e.round?.activePlayerId ?? "";
1726
+ },
1727
+ evaluate: e === "go-out" ? Ut : Ht,
1728
+ orderActions(t, n) {
1729
+ let r = t.round?.hands[t.round.activePlayerId]?.length ?? 0;
1730
+ return [...n].sort((t, n) => Kt(n, e, r) - Kt(t, e, r));
1731
+ },
1732
+ determinize(e, t, n) {
1733
+ let r = e.round;
1734
+ if (!r) return e;
1735
+ let i = /* @__PURE__ */ new Set();
1736
+ for (let e of kt(r.table)) i.add(a(e));
1737
+ let o = r.hands[t] ?? [];
1738
+ for (let e of o) i.add(a(e));
1739
+ let s = v(_(12).filter((e) => !i.has(a(e))), n), c = {}, l = 0;
1740
+ for (let e of r.turnOrder) {
1741
+ if (e === t) {
1742
+ c[e] = [...o];
1743
+ continue;
1744
+ }
1745
+ let n = (r.hands[e] ?? []).length;
1746
+ c[e] = s.slice(l, l + n), l += n;
1747
+ }
1748
+ return {
1749
+ ...e,
1750
+ round: {
1751
+ ...r,
1752
+ hands: c,
1753
+ unchartedSectors: s.slice(l)
1754
+ }
1755
+ };
1756
+ }
1757
+ };
1758
+ }
1759
+ //#endregion
1760
+ //#region libs/engine/src/lib/ai/create-warp-ai.ts
1761
+ function Jt(e) {
1762
+ let r = e.rng ?? Math.random, a = e.objective ?? "penalty", o = e.skill, s = n({
1763
+ skill: o,
1764
+ heuristics: e.heuristics ?? Lt,
1765
+ generateCandidates: e.generateCandidates ?? Ft,
1766
+ buildContext: (e) => jt(e, r),
1767
+ fallback: (e) => e.round.unchartedSectors.length > 0 ? { kind: "draw" } : V(e.round, e.playerId) ? { kind: "pass-red-alert" } : z(e.round, e.playerId) ? { kind: "deploy-beacon" } : B(e.round, e.playerId) ? { kind: "pass-turn" } : { kind: "deploy-beacon" },
1768
+ rng: r
1769
+ }), c = e.lookahead, l = c ? qt(a) : null, u = (e) => {
1770
+ if (!c || !l) return s.decide(e);
1771
+ let n = i(Gt(e), l, {
1772
+ depth: c.depth,
1773
+ perspective: e.playerId,
1774
+ rng: r,
1775
+ determinizations: c.determinizations ?? 6,
1776
+ maxBranch: c.maxBranch ?? 6
1777
+ });
1778
+ return n.length === 0 ? s.decide(e) : n.length === 1 ? n[0].action : o.blunderRate > 0 && r() < o.blunderRate ? n[Math.floor(r() * n.length)].action : n[t(n.map((e) => e.value), o, r)].action;
1779
+ };
1780
+ return {
1781
+ decide: u,
1782
+ decideGameAction(e, t) {
1783
+ let n = Ot(e, t);
1784
+ return n ? Dt(u(n), t) : null;
1785
+ }
1786
+ };
1787
+ }
1788
+ //#endregion
1789
+ //#region libs/engine/src/lib/ai/explain-action.ts
1790
+ var Q = q;
1791
+ function Yt(e, t) {
1792
+ let n = At(t.obs.round, e.move.route);
1793
+ if (n === null) return null;
1794
+ let r = d(e.move.coordinate, n);
1795
+ if (r === null) return null;
1796
+ let i = a(e.move.coordinate), o = !1, s = 0;
1797
+ for (let e of t.hand) {
1798
+ if (!o && a(e) === i) {
1799
+ o = !0;
1800
+ continue;
1801
+ }
1802
+ u(e, r) && s++;
1803
+ }
1804
+ return {
1805
+ endValue: r,
1806
+ matches: s
1807
+ };
1808
+ }
1809
+ function Xt(e, t) {
1810
+ let n = 0;
1811
+ for (let r of t) u(r, e) && n++;
1812
+ return n;
1813
+ }
1814
+ function Zt(e, t, n, r) {
1815
+ if (r === 0) return null;
1816
+ switch (e) {
1817
+ case Q.dumpPips: {
1818
+ if (t.kind !== "chart" || n.obs.objective === "go-out") return null;
1819
+ let e = s(t.move.coordinate);
1820
+ return e >= 10 ? `Sheds ${e} pip points — a heavy tile off your hand.` : e >= 5 ? `Removes ${e} pip points from your hand.` : null;
1821
+ }
1822
+ case Q.goOutWin: return t.kind === "chart" && n.hand.length === 1 ? "Empties your hand — you win the round." : null;
1823
+ case Q.doublesEarly: return t.kind === "chart" && o(t.move.coordinate) ? "Clears a double while your hand is still large." : null;
1824
+ case Q.ownTrail: {
1825
+ if (t.kind !== "chart") return null;
1826
+ let e = t.move.route;
1827
+ return e.kind === "warp-trail" && e.playerId === n.obs.playerId ? "Charts on your own warp trail — shields stay up." : null;
1828
+ }
1829
+ case Q.coverRelief: return t.kind === "chart" ? t.move.route.kind === "red-alert-cover" ? "Covers a Red Alert so the fleet can keep charting." : t.move.route.kind === "fracture-stabilizer" ? "Stabilizes the subspace fracture." : null : null;
1830
+ case Q.redAlertSafe: {
1831
+ if (t.kind !== "chart" || !o(t.move.coordinate)) return null;
1832
+ let e = t.move.route;
1833
+ return e.kind !== "warp-trail" && e.kind !== "neutral-zone" ? null : "Plays a double you can cover — avoids an stuck Red Alert.";
1834
+ }
1835
+ case Q.handFlexibility: {
1836
+ if (t.kind !== "chart") return null;
1837
+ let e = Yt(t, n);
1838
+ return !e || e.matches < 2 ? null : `Leaves ${e.matches} follow-up tiles for open end ${e.endValue}.`;
1839
+ }
1840
+ case Q.defensiveShared: {
1841
+ if (t.kind !== "chart") return null;
1842
+ let e = t.move.route;
1843
+ if (!(e.kind === "neutral-zone" || e.kind === "warp-trail" && e.playerId !== n.obs.playerId)) return null;
1844
+ let r = Yt(t, n);
1845
+ if (!r) return null;
1846
+ let i = Xt(r.endValue, n.unseen);
1847
+ return i <= 3 ? `Shared route ends on ${r.endValue} — only ${i} unseen tiles extend it.` : null;
1848
+ }
1849
+ case Q.salamanderDump: return t.kind === "chart" && t.move.coordinate.low === 12 && t.move.coordinate.high === 12 ? "Offloads 12-12 before the Salamander penalty can apply." : null;
1850
+ case Q.qContinuum: return t.kind === "chart" && t.move.coordinate.low === 0 && t.move.coordinate.high === 0 ? "Triggers a Q-Flash with 0-0." : null;
1851
+ default: return null;
1852
+ }
1853
+ }
1854
+ function Qt(e) {
1855
+ switch (e.kind) {
1856
+ case "draw": return "No legal chart — draw from Uncharted Sectors.";
1857
+ case "deploy-beacon": return "Stuck with no chart — Distress Beacon opens your trail.";
1858
+ case "pass-turn": return "Voluntary pass — shields down on your trail.";
1859
+ case "pass-red-alert": return "Cannot cover the Red Alert — pass to the next captain.";
1860
+ case "declare-treaty": return "Neutral Zone win available — drop to impulse.";
1861
+ case "invoke-q-flash": return `Reality shift: ${e.effect.replaceAll("-", " ")}.`;
1862
+ case "resolve-q-gamble": return `Keep gamble tile ${e.keepIndex + 1} for the best hand shape.`;
1863
+ case "chart": return null;
1864
+ }
1865
+ }
1866
+ function $t(e, t, n, i) {
1867
+ let a = i?.maxReasons ?? 3, o = Ot(e, t);
1868
+ if (!o) return [];
1869
+ let s = Qt(n);
1870
+ if (s) return [s];
1871
+ let c = Vt("advanced", e.objective), l = new Map(Lt.map((e) => [e.id, e])), u = jt(o, Math.random), d = [];
1872
+ for (let e of c.enabled) {
1873
+ let t = l.get(e);
1874
+ if (!t) continue;
1875
+ let r = t.score(n, u), i = c.weights[e] ?? 1, a = Zt(e, n, u, r);
1876
+ a && d.push({
1877
+ impact: Math.abs(i * r),
1878
+ text: a
1879
+ });
1880
+ }
1881
+ d.sort((e, t) => t.impact - e.impact);
1882
+ let f = /* @__PURE__ */ new Set(), p = [];
1883
+ for (let e of d) if (!f.has(e.text) && (f.add(e.text), p.push(e.text), p.length >= a)) break;
1884
+ return p.length === 0 && n.kind === "chart" && r(n, u, l, c) > 0 && p.push("Best balance of pip shed, trail control, and follow-up options."), p;
1885
+ }
1886
+ //#endregion
1887
+ //#region libs/engine/src/lib/ai/self-play.ts
1888
+ function en(e) {
1889
+ let t = e >>> 0;
1890
+ return () => {
1891
+ t |= 0, t = t + 1831565813 | 0;
1892
+ let e = Math.imul(t ^ t >>> 15, 1 | t);
1893
+ return e = e + Math.imul(e ^ e >>> 7, 61 | e) ^ e, ((e ^ e >>> 14) >>> 0) / 4294967296;
1894
+ };
1895
+ }
1896
+ function $(e) {
1897
+ let t = 0;
1898
+ for (let n of e.turnOrder) t += (e.hands[n] ?? []).length;
1899
+ return t;
1900
+ }
1901
+ function tn(e, t) {
1902
+ let n = e.turnOrder[0], r = Infinity;
1903
+ for (let i of e.turnOrder) {
1904
+ let a = e.hands[i] ?? [], o = t.objective === "go-out" ? Z(a) : X(a, t.modules, e.roundNumber);
1905
+ o < r && (r = o, n = i);
1906
+ }
1907
+ return n;
1908
+ }
1909
+ function nn(e) {
1910
+ let t = e.seats, n = e.seed ?? 1, r = t.map((e) => ({
1911
+ id: e.id,
1912
+ displayName: e.displayName ?? e.id
1913
+ })), i = v(_(12), en(n)), a = T({
1914
+ id: "self-play",
1915
+ captains: r,
1916
+ modules: e.modules
1917
+ }, { shuffledCoordinates: i }), o = new Map(t.map((e) => [e.id, e.player])), s = e.maxSteps ?? 2e4, c = en((n ^ 2654435769) >>> 0), l = 0, u = 0, d = a.round ? $(a.round) : -1;
1918
+ for (; a.phase === "active" && l < s;) {
1919
+ l++;
1920
+ let e = a.round;
1921
+ if (!e) break;
1922
+ if (e.phase === "ended") {
1923
+ let t = H(a, e, c);
1924
+ if (!t.ok) break;
1925
+ a = t.state, u = 0, d = a.round ? $(a.round) : -1;
1926
+ continue;
1927
+ }
1928
+ if (e.unchartedSectors.length === 0) {
1929
+ let n = $(e);
1930
+ if (u = n === d ? u + 1 : 0, d = n, u >= t.length * 2) {
1931
+ a = {
1932
+ ...a,
1933
+ round: {
1934
+ ...e,
1935
+ phase: "ended",
1936
+ roundWinnerId: tn(e, a),
1937
+ treatyDeclared: !0,
1938
+ treatyDeclarationRequired: !1
1939
+ }
1940
+ }, u = 0;
1941
+ continue;
1942
+ }
1943
+ } else u = 0, d = $(e);
1944
+ let n = e.activePlayerId, r = o.get(n)?.decideGameAction(a, n);
1945
+ if (!r) break;
1946
+ let i = Et(a, r);
1947
+ if (!i.ok) break;
1948
+ a = i.state;
1949
+ }
1950
+ let f = {};
1951
+ for (let e of a.captains) f[e.id] = e.penaltyScore;
1952
+ let p = null;
1953
+ if (a.phase === "complete") {
1954
+ let e = Infinity;
1955
+ for (let t of a.captains) t.penaltyScore < e && (e = t.penaltyScore, p = t.id);
1956
+ }
1957
+ return {
1958
+ winnerId: p,
1959
+ completed: a.phase === "complete",
1960
+ completedRounds: a.completedRounds,
1961
+ steps: l,
1962
+ penalties: f,
1963
+ finalState: a
1964
+ };
1965
+ }
1966
+ function rn(e, t) {
1967
+ let n = {}, r = {}, i = 0, a = t.seed ?? 1e3;
1968
+ for (let o = 0; o < t.games; o++) {
1969
+ let s = nn({
1970
+ seats: e(o),
1971
+ seed: a + o * 7919,
1972
+ modules: t.modules
1973
+ });
1974
+ s.completed && (i++, s.winnerId !== null && (n[s.winnerId] = (n[s.winnerId] ?? 0) + 1));
1975
+ for (let [e, t] of Object.entries(s.penalties)) r[e] = (r[e] ?? 0) + t;
1976
+ }
1977
+ return {
1978
+ games: t.games,
1979
+ completed: i,
1980
+ wins: n,
1981
+ penalties: r
1982
+ };
1983
+ }
1984
+ //#endregion
1985
+ //#region libs/engine/src/lib/fixtures/create-demo-game.ts
1986
+ var an = [
1987
+ {
1988
+ id: "you",
1989
+ displayName: "You"
1990
+ },
1991
+ {
1992
+ id: "riker",
1993
+ displayName: "Riker"
1994
+ },
1995
+ {
1996
+ id: "troi",
1997
+ displayName: "Troi"
1998
+ },
1999
+ {
2000
+ id: "worf",
2001
+ displayName: "Worf"
2002
+ }
2003
+ ];
2004
+ function on(e) {
2005
+ let t = e;
2006
+ return () => (t = (t * 1664525 + 1013904223) % 4294967296, t / 4294967296);
2007
+ }
2008
+ function sn(e = Date.now()) {
2009
+ let t = v(_(12), on(e));
2010
+ return T({
2011
+ id: `demo-${e}`,
2012
+ captains: [...an],
2013
+ modules: { salamanderPenalty: !0 }
2014
+ }, { shuffledCoordinates: t });
2015
+ }
2016
+ function cn(e) {
2017
+ return Object.fromEntries(e.captains.map((e) => [e.id, e.displayName]));
2018
+ }
2019
+ //#endregion
2020
+ //#region libs/engine/src/lib/violations/format-violation.ts
2021
+ function ln(e) {
2022
+ switch (e) {
2023
+ case "TREATY_NOT_REQUIRED": return "dropping to impulse not required";
2024
+ default: return e.replaceAll("_", " ").toLowerCase();
2025
+ }
2026
+ }
2027
+ //#endregion
2028
+ export { _e as DEFAULT_GAME_OBJECTIVE, he as DEFAULT_MODULES, Lt as DEFAULT_WARP_HEURISTICS, an as DEMO_CAPTAINS, te as DOUBLE_TWELVE_MAX_PIPS, ee as DOUBLE_TWELVE_SET_SIZE, Fe as EMPTY_Q_ROUND_EFFECTS, ve as GAME_OBJECTIVE_LABELS, p as HAND_SIZE_BY_PLAYER_COUNT, ne as INITIAL_SPACEDOCK_VALUE, k as Q_FLASH_CATALOG, re as SALAMANDER_PENALTY_TILE_VALUE, q as WARP_HEURISTIC_IDS, Bt as WARP_SKILL_PRESETS, Be as advanceActivePlayer, G as advanceTurn, Et as applyAction, We as applyQFlashEffect, ye as assertCoordinateSetSize, Ue as buildQFlashEffect, jt as buildWarpContext, z as canDeployDistressBeacon, V as canPassRedAlert, B as canPassTurn, nt as canRaiseShieldsByCharting, Xe as canStabilizeFracture, cn as captainNameMap, Nt as chooseQFlashEffect, Pt as chooseQGambleKeepIndex, Ge as clearTemporalInversionOnDouble, kt as collectPlacedCoordinates, je as collectRoundCoordinatesForRecycle, At as connectingValueForRoute, Ke as consumeFractureImmunity, ze as consumeSkipForPlayer, a as coordinateKey, u as coordinateMatchesValue, s as coordinatePipValue, l as coordinatesEqual, Ee as countActiveDistressBeacons, Te as countDoublesOnTable, De as createCaptain, sn as createDemoGame, Ce as createInitialTable, Oe as createLobbyState, Ae as createRoundState, w as createRoundStateFromDeal, Jt as createWarpAiPlayer, qt as createWarpSearchModel, C as dealRoundFromShuffled, Ie as describeQFlashEffect, $t as explainWarpAiAction, be as findCoordinateInHand, Me as findSpacedockHolder, ln as formatViolation, _ as generateCoordinateSet, A as getAvailableQFlashEffects, L as getLegalMoves, Vt as getWarpSkillProfile, Se as handContains, it as handPenaltyPoints, X as handPips, ie as handSizeForPlayerCount, $e as hasEstablishedWarpTrail, we as hasUncoveredDoubleOnTable, He as highestPenaltyCaptainId, o as isDouble, pe as isFractureResolved, Ze as isLegalMove, h as isNavigationHaltedByFracture, Le as isQResolutionPending, g as isRedAlertBlocking, f as isTrailOpenToOthers, me as isTrueRedAlert, S as isUncoveredDoubleAtNeutralZoneEnd, x as isUncoveredDoubleAtTrailEnd, Ve as lowestPenaltyCaptainId, j as mergeQEffects, R as mustDrawBeforePassing, b as neutralZoneOpenValue, M as nextActivePlayerId, c as normalizeCoordinate, Gt as observationToState, Ot as observe, d as openValueAfterConnection, F as placedTile, nn as playSelfPlayGame, xe as removeCoordinateFromHand, ge as resolveModules, qe as resolveQGamble, ke as roundStarterForRound, rn as runSelfPlayMatch, m as salamanderPenaltyApplies, H as scoreRound, v as shuffleCoordinates, ae as spacedockValueForRound, fe as stabilizersPlaced, T as startGame, Dt as toGameAction, y as trailOpenValue, N as trailsOpenToOthers, Je as treatyRequiredForWin, Re as turnOrderReversed, Ft as warpCandidateGenerator, Wt as warpLeafEval };