poke-crucible 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 (99) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +192 -0
  3. package/dist/battle/pairing.d.ts +25 -0
  4. package/dist/battle/pairing.js +59 -0
  5. package/dist/battle/pairing.js.map +1 -0
  6. package/dist/battle/pool.d.ts +25 -0
  7. package/dist/battle/pool.js +97 -0
  8. package/dist/battle/pool.js.map +1 -0
  9. package/dist/battle/run-battle.d.ts +13 -0
  10. package/dist/battle/run-battle.js +49 -0
  11. package/dist/battle/run-battle.js.map +1 -0
  12. package/dist/battle/worker.d.ts +1 -0
  13. package/dist/battle/worker.js +21 -0
  14. package/dist/battle/worker.js.map +1 -0
  15. package/dist/cli.d.ts +2 -0
  16. package/dist/cli.js +326 -0
  17. package/dist/cli.js.map +1 -0
  18. package/dist/critic/agent-sdk-model.d.ts +27 -0
  19. package/dist/critic/agent-sdk-model.js +21 -0
  20. package/dist/critic/agent-sdk-model.js.map +1 -0
  21. package/dist/critic/critique.d.ts +21 -0
  22. package/dist/critic/critique.js +51 -0
  23. package/dist/critic/critique.js.map +1 -0
  24. package/dist/critic/evidence.d.ts +24 -0
  25. package/dist/critic/evidence.js +17 -0
  26. package/dist/critic/evidence.js.map +1 -0
  27. package/dist/critic/llm.d.ts +34 -0
  28. package/dist/critic/llm.js +23 -0
  29. package/dist/critic/llm.js.map +1 -0
  30. package/dist/critic/parse.d.ts +5 -0
  31. package/dist/critic/parse.js +84 -0
  32. package/dist/critic/parse.js.map +1 -0
  33. package/dist/critic/prompt.d.ts +14 -0
  34. package/dist/critic/prompt.js +56 -0
  35. package/dist/critic/prompt.js.map +1 -0
  36. package/dist/critic/types.d.ts +52 -0
  37. package/dist/critic/types.js +2 -0
  38. package/dist/critic/types.js.map +1 -0
  39. package/dist/designer/agent-sdk-model.d.ts +30 -0
  40. package/dist/designer/agent-sdk-model.js +62 -0
  41. package/dist/designer/agent-sdk-model.js.map +1 -0
  42. package/dist/designer/design.d.ts +12 -0
  43. package/dist/designer/design.js +218 -0
  44. package/dist/designer/design.js.map +1 -0
  45. package/dist/designer/fitness.d.ts +25 -0
  46. package/dist/designer/fitness.js +24 -0
  47. package/dist/designer/fitness.js.map +1 -0
  48. package/dist/designer/model.d.ts +14 -0
  49. package/dist/designer/model.js +27 -0
  50. package/dist/designer/model.js.map +1 -0
  51. package/dist/designer/mutate.d.ts +31 -0
  52. package/dist/designer/mutate.js +209 -0
  53. package/dist/designer/mutate.js.map +1 -0
  54. package/dist/designer/rng.d.ts +17 -0
  55. package/dist/designer/rng.js +40 -0
  56. package/dist/designer/rng.js.map +1 -0
  57. package/dist/designer/types.d.ts +103 -0
  58. package/dist/designer/types.js +2 -0
  59. package/dist/designer/types.js.map +1 -0
  60. package/dist/evaluate.d.ts +11 -0
  61. package/dist/evaluate.js +99 -0
  62. package/dist/evaluate.js.map +1 -0
  63. package/dist/gauntlet/gauntlet.d.ts +18 -0
  64. package/dist/gauntlet/gauntlet.js +41 -0
  65. package/dist/gauntlet/gauntlet.js.map +1 -0
  66. package/dist/gauntlet/teams-g1.d.ts +23 -0
  67. package/dist/gauntlet/teams-g1.js +476 -0
  68. package/dist/gauntlet/teams-g1.js.map +1 -0
  69. package/dist/gauntlet/teams-g2.d.ts +14 -0
  70. package/dist/gauntlet/teams-g2.js +725 -0
  71. package/dist/gauntlet/teams-g2.js.map +1 -0
  72. package/dist/index.d.ts +25 -0
  73. package/dist/index.js +24 -0
  74. package/dist/index.js.map +1 -0
  75. package/dist/llm/agent-query.d.ts +21 -0
  76. package/dist/llm/agent-query.js +70 -0
  77. package/dist/llm/agent-query.js.map +1 -0
  78. package/dist/pilot/damage.d.ts +34 -0
  79. package/dist/pilot/damage.js +75 -0
  80. package/dist/pilot/damage.js.map +1 -0
  81. package/dist/pilot/eval-pilot.d.ts +39 -0
  82. package/dist/pilot/eval-pilot.js +161 -0
  83. package/dist/pilot/eval-pilot.js.map +1 -0
  84. package/dist/pilot/pilot.d.ts +71 -0
  85. package/dist/pilot/pilot.js +290 -0
  86. package/dist/pilot/pilot.js.map +1 -0
  87. package/dist/ranking.d.ts +25 -0
  88. package/dist/ranking.js +40 -0
  89. package/dist/ranking.js.map +1 -0
  90. package/dist/seed.d.ts +13 -0
  91. package/dist/seed.js +42 -0
  92. package/dist/seed.js.map +1 -0
  93. package/dist/team.d.ts +29 -0
  94. package/dist/team.js +100 -0
  95. package/dist/team.js.map +1 -0
  96. package/dist/types.d.ts +77 -0
  97. package/dist/types.js +8 -0
  98. package/dist/types.js.map +1 -0
  99. package/package.json +57 -0
@@ -0,0 +1,290 @@
1
+ /**
2
+ * The Pilot — one competent Gen 3 heuristic policy, identical on both sides
3
+ * (ADR 0002). Given the omniscient battle state and the current request, it
4
+ * returns a Showdown choice string.
5
+ *
6
+ * Competence is layered as an ordered decision tree; each layer only fires when
7
+ * the earlier, higher-priority ones decline:
8
+ * 1. secure a KO we can safely land (max-damage core)
9
+ * 2. pivot out of a losing matchup (matchup switching)
10
+ * 3. heal when worn down and safe (recovery / stall)
11
+ * 4. lay Spikes / spin them away (hazards)
12
+ * 5. spread status on a fresh target (Toxic / paralysis / burn)
13
+ * 6. set up when the foe can't pressure us (Dragon Dance, Calm Mind, ...)
14
+ * 7. otherwise hit as hard as possible (max-damage fallback)
15
+ *
16
+ * Per ADR 0002 / the PRD, the Pilot is never asserted move-by-move: its
17
+ * competence is judged only through outcome thresholds at the `evaluate` seam.
18
+ */
19
+ import { BattleStreams } from '@pkmn/sim';
20
+ import { bestDamageFraction, estimateDamageFraction, resolveMove, } from './damage.js';
21
+ // --- Move classifications -------------------------------------------------
22
+ const RECOVERY = new Set([
23
+ 'recover', 'softboiled', 'milkdrink', 'slackoff', 'moonlight', 'morningsun',
24
+ 'synthesis', 'rest', 'wish',
25
+ ]);
26
+ const SPIKES = 'spikes';
27
+ const RAPID_SPIN = 'rapidspin';
28
+ const TOXIC = 'toxic';
29
+ const PARALYZE = new Set(['thunderwave', 'stunspore', 'glare']);
30
+ const BURN = new Set(['willowisp']);
31
+ const PHAZE = new Set(['roar', 'whirlwind']);
32
+ /** Setup moves → the offensive stat they raise and the boost level to stop at. */
33
+ const SETUP = {
34
+ swordsdance: { stat: 'atk', cap: 2 },
35
+ dragondance: { stat: 'atk', cap: 2 },
36
+ bulkup: { stat: 'atk', cap: 2 },
37
+ curse: { stat: 'atk', cap: 2 },
38
+ calmmind: { stat: 'spa', cap: 3 },
39
+ agility: { stat: 'spe', cap: 2 },
40
+ };
41
+ // --- Small state helpers --------------------------------------------------
42
+ function hpFraction(p) {
43
+ return p.maxhp > 0 ? p.hp / p.maxhp : 0;
44
+ }
45
+ function effectiveSpeed(p) {
46
+ let spe = p.getStat('spe');
47
+ if (p.status === 'par')
48
+ spe = Math.floor(spe * 0.25);
49
+ return spe;
50
+ }
51
+ function spikesLayers(side) {
52
+ const cond = side.sideConditions?.['spikes'];
53
+ if (!cond)
54
+ return 0;
55
+ return typeof cond.layers === 'number' ? cond.layers : 1;
56
+ }
57
+ /** Find the live battle Pokemon matching a request slot (Species Clause ⇒ unique). */
58
+ function battleMon(team, rp) {
59
+ const species = (rp.details.split(',')[0] ?? '').trim();
60
+ return team.find((m) => m && m.species && m.species.name === species) ?? null;
61
+ }
62
+ function isFainted(rp) {
63
+ return rp.condition.includes('fnt');
64
+ }
65
+ /** Moves that are actually choosable this turn (present, not disabled, PP left). */
66
+ function usableMoves(battle, me, active) {
67
+ const out = [];
68
+ active.moves.forEach((rm, i) => {
69
+ if (rm.disabled)
70
+ return;
71
+ if (typeof rm.pp === 'number' && rm.pp <= 0)
72
+ return;
73
+ const name = me.set?.moves?.[i] ?? rm.move ?? rm.id;
74
+ out.push({ index: i, move: resolveMove(battle, name) });
75
+ });
76
+ return out;
77
+ }
78
+ // --- Status legality (heuristic, Gen 3) -----------------------------------
79
+ function canToxic(foe) {
80
+ return (!foe.types.includes('Steel') &&
81
+ !foe.types.includes('Poison') &&
82
+ foe.ability !== 'immunity');
83
+ }
84
+ function canParalyze(battle, moveId, foe) {
85
+ if (foe.ability === 'limber')
86
+ return false;
87
+ // Thunder Wave respects Electric-type immunity (Ground is immune); powders don't hit Grass? (Grass isn't immune in gen 3).
88
+ if (moveId === 'thunderwave' && !battle.dex.getImmunity('Electric', foe.types))
89
+ return false;
90
+ return true;
91
+ }
92
+ function canBurn(foe) {
93
+ return !foe.types.includes('Fire') && foe.ability !== 'waterveil';
94
+ }
95
+ /**
96
+ * Best Pokemon to bring in against `foe`: the one that takes the least from the
97
+ * foe's hardest hit, lightly favouring candidates that threaten back.
98
+ * `requireOffense` filters to candidates that can actually dent the foe.
99
+ */
100
+ function bestSwitch(battle, sideID, request, foe, requireOffense = false) {
101
+ const myIdx = sideID === 'p1' ? 0 : 1;
102
+ const team = battle.sides[myIdx]?.pokemon ?? [];
103
+ let best = null;
104
+ let bestValue = -Infinity;
105
+ request.side.pokemon.forEach((rp, i) => {
106
+ if (rp.active || isFainted(rp))
107
+ return;
108
+ const c = battleMon(team, rp);
109
+ if (!c)
110
+ return;
111
+ const foeDamage = bestDamageFraction(battle, foe, c);
112
+ const myDamage = bestDamageFraction(battle, c, foe);
113
+ if (requireOffense && myDamage < 0.12)
114
+ return;
115
+ // Favour candidates that take the least from the foe's hardest hit, with a
116
+ // small bonus for those that threaten back.
117
+ const value = -foeDamage + 0.3 * Math.min(myDamage, 1);
118
+ if (value > bestValue) {
119
+ bestValue = value;
120
+ best = { choiceIndex: i + 1, foeDamage };
121
+ }
122
+ });
123
+ return best;
124
+ }
125
+ // --- The decision ---------------------------------------------------------
126
+ function defaultMove(active) {
127
+ const i = active.moves.findIndex((m) => !m.disabled && (m.pp === undefined || m.pp > 0));
128
+ return i >= 0 ? `move ${i + 1}` : 'move 1';
129
+ }
130
+ /** The first alive benched Pokemon, as a `switch N` choice (or `pass`). */
131
+ function firstAliveSwitch(request) {
132
+ const i = request.side.pokemon.findIndex((p) => !p.active && !isFainted(p));
133
+ return i >= 0 ? `switch ${i + 1}` : 'pass';
134
+ }
135
+ export function chooseAction(battle, sideID, request) {
136
+ const myIdx = sideID === 'p1' ? 0 : 1;
137
+ const mySide = battle.sides[myIdx];
138
+ const foeSide = battle.sides[1 - myIdx];
139
+ const me = mySide?.active?.[0];
140
+ const foe = foeSide?.active?.[0];
141
+ // Forced to send in a new Pokemon (ours fainted / was dragged out).
142
+ if (request.forceSwitch?.[0]) {
143
+ if (foe) {
144
+ const sw = bestSwitch(battle, sideID, request, foe);
145
+ if (sw)
146
+ return `switch ${sw.choiceIndex}`;
147
+ }
148
+ return firstAliveSwitch(request);
149
+ }
150
+ const active = request.active?.[0];
151
+ if (!active)
152
+ return 'default'; // team preview (none in gen 3) or nothing to do
153
+ if (!me || !foe || !mySide || !foeSide)
154
+ return defaultMove(active);
155
+ const usable = usableMoves(battle, me, active);
156
+ const damaging = usable
157
+ .map((u) => ({ ...u, frac: estimateDamageFraction(battle, me, foe, u.move) }))
158
+ .filter((u) => u.frac > 0)
159
+ .sort((a, b) => b.frac * b.move.accuracy - a.frac * a.move.accuracy);
160
+ const best = damaging[0];
161
+ const myHp = hpFraction(me);
162
+ const foeHp = hpFraction(foe);
163
+ const foeThreat = bestDamageFraction(battle, foe, me);
164
+ const iAmFaster = effectiveSpeed(me) > effectiveSpeed(foe);
165
+ const myKO = best ? best.frac >= foeHp : false;
166
+ const foeKO = foeThreat >= myHp;
167
+ const safe = !foeKO || (iAmFaster && myKO);
168
+ const trapped = active.trapped || active.maybeTrapped;
169
+ const canSwitch = !trapped && request.side.pokemon.some((p) => !p.active && !isFainted(p));
170
+ // 1) Secure a KO we can safely land.
171
+ if (best && myKO && (iAmFaster || !foeKO))
172
+ return `move ${best.index + 1}`;
173
+ // 2) Losing matchup: the foe threatens to KO us and we can't KO first — pivot
174
+ // to a teammate that walls it better.
175
+ if (canSwitch && foeKO && !(myKO && iAmFaster)) {
176
+ const sw = bestSwitch(battle, sideID, request, foe);
177
+ if (sw && sw.foeDamage < foeThreat * 0.85 && sw.foeDamage < 0.6) {
178
+ return `switch ${sw.choiceIndex}`;
179
+ }
180
+ }
181
+ const foeBoosted = foe.boosts.atk > 0 || foe.boosts.spa > 0 || foe.boosts.spe > 0;
182
+ const foeHasSub = !!foe.volatiles && 'substitute' in foe.volatiles;
183
+ const strongHit = !!best && best.frac >= 0.45;
184
+ // 3) Phaze a Pokemon setting up on us — undo its boosts and rack Spikes chip.
185
+ if (foeBoosted && (iAmFaster || !foeKO)) {
186
+ const phaze = usable.find((u) => PHAZE.has(u.move.id));
187
+ if (phaze)
188
+ return `move ${phaze.index + 1}`;
189
+ }
190
+ // 4) Defensive utility — the heart of competent stall. A wall stays in on a
191
+ // foe it can't hurt (as long as it's safe) and does something useful:
192
+ // heal, lay Spikes, cripple with burn/Toxic/paralysis, spin hazards away.
193
+ if (safe) {
194
+ if (myHp < 0.55) {
195
+ const rec = usable.find((u) => RECOVERY.has(u.move.id));
196
+ if (rec)
197
+ return `move ${rec.index + 1}`;
198
+ }
199
+ const spikes = usable.find((u) => u.move.id === SPIKES);
200
+ if (spikes && spikesLayers(foeSide) < 3)
201
+ return `move ${spikes.index + 1}`;
202
+ // Status is worth a turn only when we can't just beat the foe down.
203
+ if (!strongHit && foe.status === '' && !foeHasSub) {
204
+ const foePhysical = foe.getStat('atk') >= foe.getStat('spa');
205
+ if (foePhysical) {
206
+ const wisp = usable.find((u) => BURN.has(u.move.id) && canBurn(foe));
207
+ if (wisp)
208
+ return `move ${wisp.index + 1}`;
209
+ }
210
+ if (foeHp > 0.5) {
211
+ const toxic = usable.find((u) => u.move.id === TOXIC);
212
+ if (toxic && canToxic(foe))
213
+ return `move ${toxic.index + 1}`;
214
+ }
215
+ if (!iAmFaster) {
216
+ const para = usable.find((u) => PARALYZE.has(u.move.id) && canParalyze(battle, u.move.id, foe));
217
+ if (para)
218
+ return `move ${para.index + 1}`;
219
+ }
220
+ }
221
+ const spin = usable.find((u) => u.move.id === RAPID_SPIN);
222
+ if (spin && spikesLayers(mySide) > 0)
223
+ return `move ${spin.index + 1}`;
224
+ // 5) Set up while the foe can't pressure us.
225
+ if (foeThreat < 0.35 && foeHp > 0.6) {
226
+ const setup = usable.find((u) => {
227
+ const s = SETUP[u.move.id];
228
+ return s && me.boosts[s.stat] < s.cap;
229
+ });
230
+ if (setup)
231
+ return `move ${setup.index + 1}`;
232
+ }
233
+ }
234
+ // 6) Otherwise, hit as hard as possible.
235
+ if (best && best.frac >= 0.06)
236
+ return `move ${best.index + 1}`;
237
+ // 7) We can't meaningfully damage the foe. If safe with nothing useful left,
238
+ // pivot to a teammate that threatens it; else make the best move we have.
239
+ if (canSwitch) {
240
+ const sw = bestSwitch(battle, sideID, request, foe, /*requireOffense*/ true);
241
+ if (sw && sw.foeDamage < 0.85)
242
+ return `switch ${sw.choiceIndex}`;
243
+ }
244
+ if (best)
245
+ return `move ${best.index + 1}`;
246
+ if (usable[0])
247
+ return `move ${usable[0].index + 1}`;
248
+ return defaultMove(active);
249
+ }
250
+ /**
251
+ * A player that drives one side of a BattleStream with the heuristic Pilot.
252
+ * It reads the shared omniscient {@link Battle} through `getBattle` (both sides
253
+ * see the same information, so the contest stays fair).
254
+ */
255
+ export class PilotPlayer extends BattleStreams.BattlePlayer {
256
+ getBattle;
257
+ sideID;
258
+ /** Guards against re-entrant recovery within a single stuck decision. */
259
+ recovering = false;
260
+ constructor(playerStream, getBattle, sideID) {
261
+ super(playerStream);
262
+ this.getBattle = getBattle;
263
+ this.sideID = sideID;
264
+ }
265
+ receiveRequest(request) {
266
+ this.recovering = false; // a fresh request clears any prior recovery
267
+ if (request?.wait)
268
+ return; // opponent must act; nothing for us to choose
269
+ const battle = this.getBattle();
270
+ if (!battle) {
271
+ this.choose('default');
272
+ return;
273
+ }
274
+ this.choose(chooseAction(battle, this.sideID, request));
275
+ }
276
+ /**
277
+ * Safety net: if the heuristic ever produces an illegal choice (e.g. an
278
+ * uncertain trap it mis-read), fall back to the safest legal action instead
279
+ * of aborting the whole evaluation. Any other error still propagates.
280
+ */
281
+ receiveError(error) {
282
+ if (!this.recovering && /\[(Invalid|Unavailable) choice\]/.test(error.message)) {
283
+ this.recovering = true;
284
+ this.choose('default');
285
+ return;
286
+ }
287
+ throw error;
288
+ }
289
+ }
290
+ //# sourceMappingURL=pilot.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pilot.js","sourceRoot":"","sources":["../../src/pilot/pilot.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,25 @@
1
+ /**
2
+ * Ranking / aggregation. Strength is a win-rate under the Pilot; each row also
3
+ * carries a 95% Wilson confidence interval so callers don't over-read a
4
+ * difference that's within noise. Elo/Glicko is deferred (out of scope).
5
+ */
6
+ import type { MatchupResult, StrengthRow } from './types.js';
7
+ /**
8
+ * Wilson score interval for a binomial proportion. `successes` may be
9
+ * fractional (ties count as half a win); `n` is the battle count.
10
+ */
11
+ export declare function wilsonInterval(successes: number, n: number, z?: number): [number, number];
12
+ /** Fractional successes for a matchup: full for wins, half for ties. */
13
+ export declare function successes(m: {
14
+ wins: number;
15
+ battles: number;
16
+ winRate: number;
17
+ }): number;
18
+ /**
19
+ * Build a Strength row for one Team from its per-opponent matchups. Overall
20
+ * Strength is total successes over total battles (a raw win-rate), which for
21
+ * equal battle counts per matchup equals the mean of the per-matchup win-rates.
22
+ */
23
+ export declare function buildStrengthRow(team: string, matchups: MatchupResult[]): StrengthRow;
24
+ /** Sort strongest first; ties broken by name so the order is deterministic. */
25
+ export declare function rankRows(rows: StrengthRow[]): StrengthRow[];
@@ -0,0 +1,40 @@
1
+ /**
2
+ * Wilson score interval for a binomial proportion. `successes` may be
3
+ * fractional (ties count as half a win); `n` is the battle count.
4
+ */
5
+ export function wilsonInterval(successes, n, z = 1.96) {
6
+ if (n <= 0)
7
+ return [0, 0];
8
+ const p = successes / n;
9
+ const z2 = z * z;
10
+ const denom = 1 + z2 / n;
11
+ const center = (p + z2 / (2 * n)) / denom;
12
+ const margin = (z * Math.sqrt((p * (1 - p) + z2 / (4 * n)) / n)) / denom;
13
+ return [Math.max(0, center - margin), Math.min(1, center + margin)];
14
+ }
15
+ /** Fractional successes for a matchup: full for wins, half for ties. */
16
+ export function successes(m) {
17
+ return m.winRate * m.battles;
18
+ }
19
+ /**
20
+ * Build a Strength row for one Team from its per-opponent matchups. Overall
21
+ * Strength is total successes over total battles (a raw win-rate), which for
22
+ * equal battle counts per matchup equals the mean of the per-matchup win-rates.
23
+ */
24
+ export function buildStrengthRow(team, matchups) {
25
+ const battles = matchups.reduce((s, m) => s + m.battles, 0);
26
+ const succ = matchups.reduce((s, m) => s + successes(m), 0);
27
+ const strength = battles > 0 ? succ / battles : 0;
28
+ return {
29
+ team,
30
+ strength,
31
+ battles,
32
+ ci95: wilsonInterval(succ, battles),
33
+ matchups,
34
+ };
35
+ }
36
+ /** Sort strongest first; ties broken by name so the order is deterministic. */
37
+ export function rankRows(rows) {
38
+ return [...rows].sort((a, b) => b.strength - a.strength || a.team.localeCompare(b.team));
39
+ }
40
+ //# sourceMappingURL=ranking.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ranking.js","sourceRoot":"","sources":["../src/ranking.ts"],"names":[],"mappings":"AAOA;;;GAGG;AACH,MAAM,UAAU,cAAc,CAAC,SAAiB,EAAE,CAAS,EAAE,CAAC,GAAG,IAAI;IACnE,IAAI,CAAC,IAAI,CAAC;QAAE,OAAO,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;IAC1B,MAAM,CAAC,GAAG,SAAS,GAAG,CAAC,CAAC;IACxB,MAAM,EAAE,GAAG,CAAC,GAAG,CAAC,CAAC;IACjB,MAAM,KAAK,GAAG,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC;IACzB,MAAM,MAAM,GAAG,CAAC,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,KAAK,CAAC;IAC1C,MAAM,MAAM,GAAG,CAAC,CAAC,GAAG,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,KAAK,CAAC;IACzE,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC,CAAC,CAAC;AACtE,CAAC;AAED,wEAAwE;AACxE,MAAM,UAAU,SAAS,CAAC,CAAqD;IAC7E,OAAO,CAAC,CAAC,OAAO,GAAG,CAAC,CAAC,OAAO,CAAC;AAC/B,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,gBAAgB,CAAC,IAAY,EAAE,QAAyB;IACtE,MAAM,OAAO,GAAG,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC;IAC5D,MAAM,IAAI,GAAG,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,GAAG,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;IAC5D,MAAM,QAAQ,GAAG,OAAO,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC;IAClD,OAAO;QACL,IAAI;QACJ,QAAQ;QACR,OAAO;QACP,IAAI,EAAE,cAAc,CAAC,IAAI,EAAE,OAAO,CAAC;QACnC,QAAQ;KACT,CAAC;AACJ,CAAC;AAED,+EAA+E;AAC/E,MAAM,UAAU,QAAQ,CAAC,IAAmB;IAC1C,OAAO,CAAC,GAAG,IAAI,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,GAAG,CAAC,CAAC,QAAQ,IAAI,CAAC,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC;AAC3F,CAAC"}
package/dist/seed.d.ts ADDED
@@ -0,0 +1,13 @@
1
+ /**
2
+ * Deterministic seed derivation. A single master seed (a string) threads through
3
+ * the whole run; every Battle gets a sub-seed derived from the master plus the
4
+ * pairing and battle indices, so a fixed master reproduces identical results
5
+ * regardless of iteration order.
6
+ */
7
+ /** `@pkmn/sim`'s PRNG seed: four 16-bit words. */
8
+ export type PRNGSeed = [number, number, number, number];
9
+ /**
10
+ * Derive a reproducible 4-word PRNG seed from the master seed and any number of
11
+ * integer indices (e.g. team index, opponent index, battle index).
12
+ */
13
+ export declare function deriveSeed(master: string, ...ints: number[]): PRNGSeed;
package/dist/seed.js ADDED
@@ -0,0 +1,42 @@
1
+ /**
2
+ * Deterministic seed derivation. A single master seed (a string) threads through
3
+ * the whole run; every Battle gets a sub-seed derived from the master plus the
4
+ * pairing and battle indices, so a fixed master reproduces identical results
5
+ * regardless of iteration order.
6
+ */
7
+ function fnv1a(master, ints) {
8
+ let h = 2166136261 >>> 0;
9
+ const mix = (x) => {
10
+ h ^= x & 0xff;
11
+ h = Math.imul(h, 16777619) >>> 0;
12
+ h ^= (x >>> 8) & 0xff;
13
+ h = Math.imul(h, 16777619) >>> 0;
14
+ h ^= (x >>> 16) & 0xff;
15
+ h = Math.imul(h, 16777619) >>> 0;
16
+ h ^= (x >>> 24) & 0xff;
17
+ h = Math.imul(h, 16777619) >>> 0;
18
+ };
19
+ for (let i = 0; i < master.length; i++)
20
+ mix(master.charCodeAt(i));
21
+ mix(0x9e3779b1);
22
+ for (const n of ints)
23
+ mix(n >>> 0);
24
+ return h >>> 0;
25
+ }
26
+ /**
27
+ * Derive a reproducible 4-word PRNG seed from the master seed and any number of
28
+ * integer indices (e.g. team index, opponent index, battle index).
29
+ */
30
+ export function deriveSeed(master, ...ints) {
31
+ let s = fnv1a(master, ints);
32
+ // mulberry32 stepped four times → four independent 16-bit words.
33
+ const next = () => {
34
+ s = (s + 0x6d2b79f5) >>> 0;
35
+ let t = s;
36
+ t = Math.imul(t ^ (t >>> 15), t | 1);
37
+ t ^= t + Math.imul(t ^ (t >>> 7), t | 61);
38
+ return (t ^ (t >>> 14)) >>> 0;
39
+ };
40
+ return [next() % 65536, next() % 65536, next() % 65536, next() % 65536];
41
+ }
42
+ //# sourceMappingURL=seed.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"seed.js","sourceRoot":"","sources":["../src/seed.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAKH,SAAS,KAAK,CAAC,MAAc,EAAE,IAAc;IAC3C,IAAI,CAAC,GAAG,UAAU,KAAK,CAAC,CAAC;IACzB,MAAM,GAAG,GAAG,CAAC,CAAS,EAAE,EAAE;QACxB,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;QACd,CAAC,GAAG,IAAI,CAAC,IAAI,CAAC,CAAC,EAAE,QAAQ,CAAC,KAAK,CAAC,CAAC;QACjC,CAAC,IAAI,CAAC,CAAC,KAAK,CAAC,CAAC,GAAG,IAAI,CAAC;QACtB,CAAC,GAAG,IAAI,CAAC,IAAI,CAAC,CAAC,EAAE,QAAQ,CAAC,KAAK,CAAC,CAAC;QACjC,CAAC,IAAI,CAAC,CAAC,KAAK,EAAE,CAAC,GAAG,IAAI,CAAC;QACvB,CAAC,GAAG,IAAI,CAAC,IAAI,CAAC,CAAC,EAAE,QAAQ,CAAC,KAAK,CAAC,CAAC;QACjC,CAAC,IAAI,CAAC,CAAC,KAAK,EAAE,CAAC,GAAG,IAAI,CAAC;QACvB,CAAC,GAAG,IAAI,CAAC,IAAI,CAAC,CAAC,EAAE,QAAQ,CAAC,KAAK,CAAC,CAAC;IACnC,CAAC,CAAC;IACF,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,CAAC,EAAE;QAAE,GAAG,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,CAAC;IAClE,GAAG,CAAC,UAAU,CAAC,CAAC;IAChB,KAAK,MAAM,CAAC,IAAI,IAAI;QAAE,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC;IACnC,OAAO,CAAC,KAAK,CAAC,CAAC;AACjB,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,UAAU,CAAC,MAAc,EAAE,GAAG,IAAc;IAC1D,IAAI,CAAC,GAAG,KAAK,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;IAC5B,iEAAiE;IACjE,MAAM,IAAI,GAAG,GAAW,EAAE;QACxB,CAAC,GAAG,CAAC,CAAC,GAAG,UAAU,CAAC,KAAK,CAAC,CAAC;QAC3B,IAAI,CAAC,GAAG,CAAC,CAAC;QACV,CAAC,GAAG,IAAI,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,CAAC,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC;QACrC,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,GAAG,EAAE,CAAC,CAAC;QAC1C,OAAO,CAAC,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC;IAChC,CAAC,CAAC;IACF,OAAO,CAAC,IAAI,EAAE,GAAG,KAAK,EAAE,IAAI,EAAE,GAAG,KAAK,EAAE,IAAI,EAAE,GAAG,KAAK,EAAE,IAAI,EAAE,GAAG,KAAK,CAAC,CAAC;AAC1E,CAAC"}
package/dist/team.d.ts ADDED
@@ -0,0 +1,29 @@
1
+ import type { PokemonSet } from '@pkmn/sim';
2
+ import { type Submission, type ValidationError } from './types.js';
3
+ export type Team = PokemonSet[];
4
+ /** A submission that parsed and passed Gen 3 OU legality. */
5
+ export interface LegalTeam {
6
+ name: string;
7
+ team: Team;
8
+ /** The packed form fed to the sim's `>player` command. */
9
+ packed: string;
10
+ }
11
+ /**
12
+ * Split a raw paste that may hold several Teams (Showdown's multi-team export
13
+ * uses `=== [format] name ===` header lines) into one {@link Submission} each.
14
+ * A paste with no header markers is treated as a single unnamed Team.
15
+ */
16
+ export declare function splitSubmissions(raw: string): Submission[];
17
+ /** Normalize the flexible `teams` input of {@link EvaluateInput} into Submissions. */
18
+ export declare function toSubmissions(input: string | Array<string | Submission>): Submission[];
19
+ export interface ParseOutcome {
20
+ legal: LegalTeam[];
21
+ errors: ValidationError[];
22
+ }
23
+ /**
24
+ * Parse and legality-check a batch of submissions. Never throws for team
25
+ * content problems: unparseable or illegal Teams become {@link ValidationError}s.
26
+ */
27
+ export declare function parseAndValidate(submissions: Submission[]): ParseOutcome;
28
+ /** Round-trip a single paste through parse + serialize (used by tests/tools). */
29
+ export declare function reserialize(paste: string): string;
package/dist/team.js ADDED
@@ -0,0 +1,100 @@
1
+ /**
2
+ * Team I/O — parse and serialize Showdown paste text and validate legality as
3
+ * Gen 3 OU. Illegal Teams surface as {@link ValidationError} values rather than
4
+ * throwing, so the `evaluate` seam can report them instead of crashing.
5
+ */
6
+ import { Teams, TeamValidator } from '@pkmn/sim';
7
+ import { FORMAT } from './types.js';
8
+ // One shared validator; constructing it parses the whole gen-3 ruleset.
9
+ const validator = new TeamValidator(FORMAT);
10
+ /**
11
+ * Split a raw paste that may hold several Teams (Showdown's multi-team export
12
+ * uses `=== [format] name ===` header lines) into one {@link Submission} each.
13
+ * A paste with no header markers is treated as a single unnamed Team.
14
+ */
15
+ export function splitSubmissions(raw) {
16
+ const headerRe = /^===\s*(?:\[[^\]]*\]\s*)?(.*?)\s*===\s*$/;
17
+ const lines = raw.replace(/\r\n/g, '\n').split('\n');
18
+ const hasHeaders = lines.some((l) => headerRe.test(l.trim()));
19
+ if (!hasHeaders) {
20
+ const paste = raw.trim();
21
+ return paste ? [{ name: '', paste }] : [];
22
+ }
23
+ const out = [];
24
+ let name = '';
25
+ let buf = [];
26
+ const flush = () => {
27
+ const paste = buf.join('\n').trim();
28
+ if (paste)
29
+ out.push({ name, paste });
30
+ buf = [];
31
+ };
32
+ for (const line of lines) {
33
+ const m = headerRe.exec(line.trim());
34
+ if (m) {
35
+ flush();
36
+ name = (m[1] ?? '').trim();
37
+ }
38
+ else {
39
+ buf.push(line);
40
+ }
41
+ }
42
+ flush();
43
+ return out;
44
+ }
45
+ /** Normalize the flexible `teams` input of {@link EvaluateInput} into Submissions. */
46
+ export function toSubmissions(input) {
47
+ if (typeof input === 'string')
48
+ return splitSubmissions(input);
49
+ const out = [];
50
+ for (const entry of input) {
51
+ if (typeof entry === 'string')
52
+ out.push(...splitSubmissions(entry));
53
+ else
54
+ out.push(entry);
55
+ }
56
+ return out;
57
+ }
58
+ /** A fallback display name derived from a parsed Team's lead species. */
59
+ function defaultName(team, index) {
60
+ const lead = team[0];
61
+ const species = lead?.species || lead?.name;
62
+ return species ? `${species} team` : `Team ${index + 1}`;
63
+ }
64
+ /**
65
+ * Parse and legality-check a batch of submissions. Never throws for team
66
+ * content problems: unparseable or illegal Teams become {@link ValidationError}s.
67
+ */
68
+ export function parseAndValidate(submissions) {
69
+ const legal = [];
70
+ const errors = [];
71
+ submissions.forEach((sub, i) => {
72
+ let team;
73
+ try {
74
+ team = Teams.import(sub.paste);
75
+ }
76
+ catch (e) {
77
+ team = null;
78
+ }
79
+ const name = sub.name || (team ? defaultName(team, i) : `Team ${i + 1}`);
80
+ if (!team || team.length === 0) {
81
+ errors.push({ team: name, problems: ['Could not parse any Pokémon from the paste.'] });
82
+ return;
83
+ }
84
+ const problems = validator.validateTeam(team);
85
+ if (problems && problems.length > 0) {
86
+ errors.push({ team: name, problems });
87
+ return;
88
+ }
89
+ legal.push({ name, team, packed: Teams.pack(team) });
90
+ });
91
+ return { legal, errors };
92
+ }
93
+ /** Round-trip a single paste through parse + serialize (used by tests/tools). */
94
+ export function reserialize(paste) {
95
+ const team = Teams.import(paste);
96
+ if (!team)
97
+ throw new Error('unparseable team');
98
+ return Teams.export(team);
99
+ }
100
+ //# sourceMappingURL=team.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"team.js","sourceRoot":"","sources":["../src/team.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,77 @@
1
+ /**
2
+ * Public result contract for the evaluation harness.
3
+ *
4
+ * This is the structure the Slice-2 Critic will read, so it is the stable
5
+ * shape callers depend on. See the PRD "Results contract".
6
+ */
7
+ export declare const FORMAT: "gen3ou";
8
+ export type Mode = 'gauntlet' | 'round-robin';
9
+ /** A team the user submitted for evaluation, as Showdown paste text. */
10
+ export interface Submission {
11
+ /** Display name for reports (defaults to the lead species when omitted). */
12
+ name: string;
13
+ /** Standard Showdown export/paste text for one Team. */
14
+ paste: string;
15
+ }
16
+ /** Opposition configuration: what the submitted Teams are measured against. */
17
+ export type Opposition = {
18
+ mode: 'gauntlet';
19
+ gauntletVersion?: string;
20
+ } | {
21
+ mode: 'round-robin';
22
+ };
23
+ export interface EvaluateInput {
24
+ /**
25
+ * Submitted Teams. Either an array of pastes / {name,paste} entries, or a
26
+ * single string containing one or more Teams (multi-team `=== name ===`
27
+ * markers are split apart).
28
+ */
29
+ teams: string | Array<string | Submission>;
30
+ opposition: Opposition;
31
+ /** Battles run per Team-vs-opponent pairing (sides swapped 50/50). */
32
+ battlesPerPairing?: number;
33
+ /** Master seed; identical seed + teams + config reproduces identical results. */
34
+ seed?: string;
35
+ }
36
+ /** A submitted Team that failed Gen 3 OU legality, named so the user can fix it. */
37
+ export interface ValidationError {
38
+ team: string;
39
+ problems: string[];
40
+ }
41
+ /** Win-rate of a submitted Team against one specific opponent. */
42
+ export interface MatchupResult {
43
+ /** Opponent name — a Gauntlet member or a peer Team. */
44
+ opponent: string;
45
+ /** Archetype label of the opponent (Gauntlet members only). */
46
+ archetype?: string;
47
+ /** Battles won outright. */
48
+ wins: number;
49
+ /** Battles that ended in a tie (counted as half a win in `winRate`). */
50
+ ties: number;
51
+ battles: number;
52
+ /** (wins + ties/2) / battles, in [0, 1]. */
53
+ winRate: number;
54
+ }
55
+ /** One row of the Strength ranking. */
56
+ export interface StrengthRow {
57
+ team: string;
58
+ /** Overall Strength: win-rate under the Pilot, in [0, 1]. */
59
+ strength: number;
60
+ battles: number;
61
+ /** 95% Wilson confidence interval on the overall win-rate. */
62
+ ci95: [number, number];
63
+ /** Per-opponent breakdown. */
64
+ matchups: MatchupResult[];
65
+ }
66
+ /** The full structured result returned from the single `evaluate` seam. */
67
+ export interface EvaluationResult {
68
+ mode: Mode;
69
+ seed: string;
70
+ /** Present in gauntlet mode; records which Gauntlet version produced the result. */
71
+ gauntletVersion?: string;
72
+ battlesPerPairing: number;
73
+ /** Strength rows, sorted strongest first. */
74
+ ranking: StrengthRow[];
75
+ /** Submitted Teams that were illegal and therefore not run. */
76
+ errors: ValidationError[];
77
+ }