From 47996ac017288a99c408e65f1cffc02d7f017d37 Mon Sep 17 00:00:00 2001 From: Krzysztof kuhy Rudnicki Date: Mon, 17 Aug 2026 09:43:06 +0200 Subject: [PATCH] Split kcd2 badgeValue.ts 334 -> 172 + baseline The module did two things: measure what a badge is compared against, and value each badge against it. Moved the first half to badgeBaseline.ts (185) -- the shared types, measureBaseline's 4,000-turn simulation, turnValue, bestThrowsValue, singleCharge, rankValue and the context defaults. badgeValue.ts (172) keeps valueBadge's per-effect switch and recommendBadges. The dependency runs one way, and valueBadge's default parameter still calls measureBaseline, so callers that omit a baseline behave as before. solve.ts and badgeValue.test.ts were repointed, import paths only. Coverage totals unchanged at 863/375/181/810, so every arm of the valuation switch is still exercised. Co-Authored-By: Claude Opus 5 (1M context) Claude-Session: https://claude.ai/code/session_011w2Pj4Ek3ZNMQVYcFHgXDY --- .../kcd2-badgevalue-split-2026-08-17.json | 20 ++ .../kcd2-badgevalue-split-2026-08-17.json | 56 +++++ kcd2_dice_solver/src/core/badgeBaseline.ts | 185 ++++++++++++++++ kcd2_dice_solver/src/core/badgeValue.test.ts | 7 +- kcd2_dice_solver/src/core/badgeValue.ts | 198 ++---------------- kcd2_dice_solver/src/core/solve.ts | 2 +- 6 files changed, 283 insertions(+), 185 deletions(-) create mode 100644 docs/superpowers/contracts/kcd2-badgevalue-split-2026-08-17.json create mode 100644 docs/superpowers/evidence/kcd2-badgevalue-split-2026-08-17.json create mode 100644 kcd2_dice_solver/src/core/badgeBaseline.ts diff --git a/docs/superpowers/contracts/kcd2-badgevalue-split-2026-08-17.json b/docs/superpowers/contracts/kcd2-badgevalue-split-2026-08-17.json new file mode 100644 index 00000000..50fd5e0c --- /dev/null +++ b/docs/superpowers/contracts/kcd2-badgevalue-split-2026-08-17.json @@ -0,0 +1,20 @@ +{ + "title": "Split kcd2_dice_solver src/core/badgeValue.ts under the 250-line cap", + "objective": "Bring kcd2_dice_solver/src/core/badgeValue.ts (334 lines) under the repo-wide 250-line file cap by extracting the measurement half -- the shared valuation types, the no-badge baseline a badge is compared against, and the throw/charge helpers that support it -- into a sibling module, leaving the per-effect valuation switch and the tier ranking, with badge point estimates proven unchanged by the project's own 288-test suite at 100% coverage.", + "acceptance_criteria": [ + "badgeValue.ts and the new sibling module are each at most 250 lines, and check_file_length.sh --all no longer lists src/core/badgeValue.ts.", + "npx vitest run still reports 24 files and 288 tests, all passing -- no test was added, deleted, or had an assertion weakened.", + "npx vitest run --coverage still reports 100% statements, 100% branches, 100% functions and 100% lines, with the same 863/375/181/810 totals, since no statement is added or removed.", + "npm run lint (tsc --noEmit && eslint src) still exits 0 with no output.", + "Every importer of a moved symbol imports it from the module that now defines it, with no re-export barrel added to badgeValue.ts.", + "Zero suppressions added: no eslint-disable, no @ts-ignore, no @ts-expect-error, and vite.config.ts is byte-identical (thresholds and exclude untouched)." + ], + "out_of_scope": [ + "Any change to how badges are valued: the exact-EV-delta model for scoring badges, the 90th-percentile model for multipliers, the best-k-throws model for doubleThrow, the per-charge simulation for extraDice/antibust/reroll/setDie, and the null for defence badges all keep their current semantics exactly.", + "Changing TURNS_PER_GAME, BADGE_SIMULATION_TURNS, or any seed, which would move every reported number.", + "Modifying vite.config.ts, including adding any new file to the coverage exclude list.", + "Splitting App.tsx -- it is its own separate commit.", + "Wiring the file-length pre-commit hook -- it must land last." + ], + "verifier": "npx vitest run (expect 24 files / 288 tests passed); npx vitest run --coverage (expect 100% on all four metrics, totals 863/375/181/810); npm run lint (expect exit 0, no output); bash ~/utils/scripts/check_file_length.sh --all (expect src/core/badgeValue.ts absent from the listing); git diff --stat vite.config.ts (expect empty); grep -rn 'eslint-disable\\|ts-ignore\\|ts-expect-error' src/ (expect no new hits); pre-commit run --files on every touched file." +} diff --git a/docs/superpowers/evidence/kcd2-badgevalue-split-2026-08-17.json b/docs/superpowers/evidence/kcd2-badgevalue-split-2026-08-17.json new file mode 100644 index 00000000..c453fa4e --- /dev/null +++ b/docs/superpowers/evidence/kcd2-badgevalue-split-2026-08-17.json @@ -0,0 +1,56 @@ +{ + "intent": "Bring kcd2_dice_solver/src/core/badgeValue.ts (334 lines) under the repo-wide 250-line cap with no change to any badge's estimated points per game.", + "scope": [ + "kcd2_dice_solver/src/core/badgeValue.ts — reduced to valueBadge's per-effect switch, its format helper and recommendBadges", + "kcd2_dice_solver/src/core/badgeBaseline.ts — new: TURNS_PER_GAME, BADGE_SIMULATION_TURNS, BadgeValuation, BadgeRecommendation, BadgeContext, BadgeBaseline, turnValue, bestThrowsValue, measureBaseline, singleCharge, rankValue, DEFAULT_BADGE_CONTEXT", + "Import sites rewritten: core/solve.ts (BadgeRecommendation), core/badgeValue.test.ts (four helpers + BadgeContext)", + "Non-goal: any change to a valuation model, to TURNS_PER_GAME/BADGE_SIMULATION_TURNS, or to any seed" + ], + "changes": [ + "Split the module along the measurement/valuation seam: everything describing what a badge is compared against (the shared types, the baseline simulation, the throw and charge helpers, the ranking key and the context defaults) moved to badgeBaseline.ts, leaving badgeValue.ts holding only the per-effect valuation switch and the tier ranking that drives it.", + "turnValue and singleCharge moved with the baseline rather than staying beside their caller, since both describe how a measurement is set up rather than how an effect is valued; both were already independent of valueBadge's switch.", + "Repointed two import sites: solve.ts for the BadgeRecommendation type, and badgeValue.test.ts for bestThrowsValue, measureBaseline, rankValue, singleCharge and BadgeContext. Both are import-path-only edits — no assertion, test case or expected value changed, and the test file still imports valueBadge and recommendBadges from badgeValue.ts.", + "Function bodies moved verbatim; no statement was added, removed or reordered, which is why the coverage totals are unchanged rather than merely still 100%." + ], + "verification": [ + { + "command": "npx vitest run", + "result": "pass", + "evidence": "Test Files 24 passed (24); Tests 288 passed (288) — unchanged, including the property tests that assert an optional badge is never worth less than nothing and a bigger tier is worth at least as much as a smaller one." + }, + { + "command": "npx vitest run --coverage", + "result": "pass", + "evidence": "Statements 100% (863/863), Branches 100% (375/375), Functions 100% (181/181), Lines 100% (810/810) — identical totals to the baseline, so every arm of the valuation switch and every branch of singleCharge is still exercised." + }, + { + "command": "npm run lint (tsc --noEmit && eslint src)", + "result": "pass", + "evidence": "Exit 0, no output. Confirms the six types moved across the module boundary still typecheck at both import sites." + }, + { + "command": "wc -l src/core/badgeValue.ts src/core/badgeBaseline.ts", + "result": "pass", + "evidence": "badgeValue.ts 172, badgeBaseline.ts 185 — both under the 250-line cap, down from a single 334-line file." + }, + { + "command": "bash ~/utils/scripts/check_file_length.sh --all", + "result": "pass", + "evidence": "src/core/badgeValue.ts no longer appears in the over-cap listing; App.tsx (306) is the only kcd2 file left." + }, + { + "command": "pre-commit run --files ", + "result": "pass", + "evidence": "All configured hooks pass on the touched files." + } + ], + "risks": [ + "badgeValue.ts and badgeBaseline.ts now have a one-way dependency (valuation imports baseline). valueBadge's default parameter still calls measureBaseline, so a caller that omits the baseline gets the same fresh measurement as before — the 'computed once per solve' optimisation in recommendBadges is preserved and still passes its baseline explicitly.", + "Consumers importing BadgeRecommendation now name badgeBaseline.ts, which reads slightly oddly for a result type. It travels with BadgeValuation, which the baseline module's rankValue operates on, so splitting the two would have created a circular import.", + "kcd2_dice_solver has no CI workflow and is not covered by pre-commit, so the vitest and lint runs above are the only gate on this change." + ], + "rollback": [ + "git revert the commit — three edited files and one added file, with no data, config or schema migration.", + "After rollback, re-run npx vitest run --coverage and npm run lint; expect 288 tests passing at 100% coverage and lint exit 0." + ] +} diff --git a/kcd2_dice_solver/src/core/badgeBaseline.ts b/kcd2_dice_solver/src/core/badgeBaseline.ts new file mode 100644 index 00000000..f5e302d2 --- /dev/null +++ b/kcd2_dice_solver/src/core/badgeBaseline.ts @@ -0,0 +1,185 @@ +/** + * What a badge is measured *against*: the shared valuation vocabulary and the + * no-badge baseline every estimate in `badgeValue.ts` is a delta from. + * + * The baseline is computed once per solve rather than once per badge — + * recomputing a 4,000-turn simulation thirty-three times was the single largest + * cost in the first version of this module. + */ + +import type { Badge, BadgeTier, FormationValues } from "../data/badges.ts"; +import { BASE_RULES, DEFAULT_FORMATION_VALUES } from "../data/badges.ts"; +import type { Die } from "../data/dice.ts"; +import { evaluateSet, scoreDistribution } from "./evaluate.ts"; +import type { ScoreDistribution } from "./evaluate.ts"; +import { Scorer } from "./scoring.ts"; +import type { InventoryEntry } from "./searchGroups.ts"; +import type { SearchOptions } from "./searchStrategies.ts"; +import { DEFAULT_POLICY, NO_CHARGES, simulateTurns } from "./simulate.ts"; +import type { BadgeCharges, TurnPolicy } from "./simulate.ts"; + +/** How many turns a game is assumed to last, for scaling per-turn uplifts. */ +export const TURNS_PER_GAME = 8; + +/** Turns simulated when valuing a badge. Enough to rank, cheap enough to feel instant. */ +export const BADGE_SIMULATION_TURNS = 4000; + +export interface BadgeValuation { + readonly badge: Badge; + /** Estimated points gained over a game, or null for situational badges. */ + readonly pointsPerGame: number | null; + /** One-line explanation of where the number came from. */ + readonly reason: string; + /** The dice this badge implies, when it changes the optimal loadout. */ + readonly dice: readonly Die[] | null; +} + +export interface BadgeRecommendation { + readonly tier: BadgeTier; + readonly ranked: readonly BadgeValuation[]; +} + +/** Inputs shared by every badge valuation in one solve. */ +export interface BadgeContext { + readonly inventory: readonly InventoryEntry[]; + readonly baselineDice: readonly Die[]; + readonly formationValues: FormationValues; + readonly policy: TurnPolicy; + readonly seed: number; + readonly searchOptions: SearchOptions; +} + +/** + * The baseline every badge is measured against: the no-badge loadout's scorer, + * its simulated turn value, and its exact throw evaluation. + */ +export interface BadgeBaseline { + readonly scorer: Scorer; + readonly turn: number; + readonly p90: number; + /** Exact expected score of one throw of the baseline loadout. */ + readonly ev: number; + /** Mean throws per turn, used to convert a per-throw delta into a per-game one. */ + readonly throwsPerTurn: number; + /** Exact single-throw score distribution of the baseline loadout. */ + readonly distribution: ScoreDistribution; +} + +/** + * Mean banked points per turn for a set under a rule set. + * + * @param dice - The six dice played. + * @param scorer - Memoised scorer for the active rules. + * @param policy - Push-your-luck policy. + * @param charges - Badge charges available that turn. + * @param seed - Random seed, held constant across comparisons so that + * differences reflect the badge and not simulation noise. + * @returns Mean points banked per turn. + */ +export function turnValue( + dice: readonly Die[], + scorer: Scorer, + policy: TurnPolicy, + charges: BadgeCharges, + seed: number, +): number { + return simulateTurns(dice, scorer, BADGE_SIMULATION_TURNS, policy, charges, seed).meanPerTurn; +} + +/** + * Total value of the best `uses` throws a player will see in a game. + * + * A badge with three charges is not worth three 90th-percentile throws: over the + * ten-or-so throws in a game you get roughly one 90th-percentile throw, one + * 80th, one 70th. Valuing each charge at the same optimistic percentile + * over-rated the three-charge gold badges noticeably. + * + * @param distribution - Single-throw score distribution. + * @param uses - How many charges the badge has. + * @param throwsPerGame - How many throws the player expects to make. + * @returns Summed value of the `uses` best throws. + */ +export function bestThrowsValue( + distribution: ScoreDistribution, + uses: number, + throwsPerGame: number, +): number { + let total = 0; + for (let k = 1; k <= uses; k += 1) { + // The k-th best of N throws sits near the (1 - k/N) quantile. Clamped so a + // short game cannot push the estimate below the median. + const fraction = Math.min(0.99, Math.max(0.5, 1 - k / Math.max(1, throwsPerGame))); + total += distribution.at(fraction); + } + return total; +} + +/** + * Measure the no-badge baseline for a loadout. + * + * @param context - Inventory, baseline loadout, and simulation settings. + * @returns The scorer, turn value, and 90th-percentile throw score. + */ +export function measureBaseline(context: BadgeContext): BadgeBaseline { + const scorer = new Scorer(BASE_RULES, context.formationValues); + const simulation = simulateTurns( + context.baselineDice, + scorer, + BADGE_SIMULATION_TURNS, + context.policy, + NO_CHARGES, + context.seed, + ); + const evaluation = evaluateSet(context.baselineDice, scorer); + return { + scorer, + turn: simulation.meanPerTurn, + throwsPerTurn: simulation.throwsPerTurn, + p90: evaluation.p90, + ev: evaluation.ev, + distribution: scoreDistribution(context.baselineDice, scorer), + }; +} + +/** + * Build the charge set that gives a badge exactly one use, for per-charge + * measurement. + * + * @param badge - The badge whose effect to model. + * @returns Charges with one use of that badge and nothing else. + */ +export function singleCharge(badge: Badge): BadgeCharges { + const effect = badge.effect; + switch (effect.kind) { + case "extraDice": + return { ...NO_CHARGES, extraDice: 1 }; + case "antibust": + return { ...NO_CHARGES, antibust: 1 }; + case "reroll": + return { ...NO_CHARGES, reroll: 1, rerollDice: effect.dice }; + case "setDie": + return { ...NO_CHARGES, setDie: 1, setDieValue: effect.value }; + default: + // Scoring, multiplier, doubleThrow, headstart and defence badges are + // valued analytically rather than simulated, so they carry no charges. + return NO_CHARGES; + } +} + +/** + * Sort key for a valuation: situational badges rank below every scored one + * rather than being dropped from the list. + * + * @param valuation - The valuation to rank. + * @returns Its points per game, or negative infinity when situational. + */ +export function rankValue(valuation: BadgeValuation): number { + return valuation.pointsPerGame ?? -Infinity; +} + +/** Defaults used when the caller does not care. */ +export const DEFAULT_BADGE_CONTEXT = { + formationValues: DEFAULT_FORMATION_VALUES, + policy: DEFAULT_POLICY, + seed: 0x5eed, +} as const; diff --git a/kcd2_dice_solver/src/core/badgeValue.test.ts b/kcd2_dice_solver/src/core/badgeValue.test.ts index 49377179..93269389 100644 --- a/kcd2_dice_solver/src/core/badgeValue.test.ts +++ b/kcd2_dice_solver/src/core/badgeValue.test.ts @@ -12,15 +12,14 @@ import { BADGES_BY_ID, DEFAULT_FORMATION_VALUES } from "../data/badges.ts"; import type { Badge } from "../data/badges.ts"; import { DICE_BY_ID } from "../data/dice.ts"; import type { Die } from "../data/dice.ts"; +import { recommendBadges, valueBadge } from "./badgeValue.ts"; import { bestThrowsValue, measureBaseline, rankValue, - recommendBadges, singleCharge, - valueBadge, -} from "./badgeValue.ts"; -import type { BadgeContext } from "./badgeValue.ts"; +} from "./badgeBaseline.ts"; +import type { BadgeContext } from "./badgeBaseline.ts"; import { NO_CHARGES, DEFAULT_POLICY } from "./simulate.ts"; import type { InventoryEntry } from "./searchGroups.ts"; diff --git a/kcd2_dice_solver/src/core/badgeValue.ts b/kcd2_dice_solver/src/core/badgeValue.ts index c036a882..8e8ea6c7 100644 --- a/kcd2_dice_solver/src/core/badgeValue.ts +++ b/kcd2_dice_solver/src/core/badgeValue.ts @@ -19,147 +19,28 @@ * are reported as situational rather than given a fabricated number. * * Every number here is an estimate built on the simulator's stated policy. They - * are meant to rank badges against each other, not to predict a scoreline. + * are meant to rank badges against each other, not to predict a scoreline. The + * baseline they are all measured against lives in `badgeBaseline.ts`. */ -import type { Badge, BadgeTier, FormationValues, ScoringRules } from "../data/badges.ts"; -import { BADGES, BASE_RULES, DEFAULT_FORMATION_VALUES, TIERS } from "../data/badges.ts"; -import type { Die } from "../data/dice.ts"; -import { evaluateSet, scoreDistribution } from "./evaluate.ts"; -import type { ScoreDistribution } from "./evaluate.ts"; +import type { Badge, ScoringRules } from "../data/badges.ts"; +import { BADGES, BASE_RULES, TIERS } from "../data/badges.ts"; +import { + TURNS_PER_GAME, + bestThrowsValue, + measureBaseline, + rankValue, + singleCharge, + turnValue, +} from "./badgeBaseline.ts"; +import type { + BadgeBaseline, + BadgeContext, + BadgeRecommendation, + BadgeValuation, +} from "./badgeBaseline.ts"; import { Scorer } from "./scoring.ts"; import { findBestSet } from "./search.ts"; -import type { InventoryEntry } from "./searchGroups.ts"; -import type { SearchOptions } from "./searchStrategies.ts"; -import { DEFAULT_POLICY, NO_CHARGES, simulateTurns } from "./simulate.ts"; -import type { BadgeCharges, TurnPolicy } from "./simulate.ts"; - -/** How many turns a game is assumed to last, for scaling per-turn uplifts. */ -export const TURNS_PER_GAME = 8; - -/** Turns simulated when valuing a badge. Enough to rank, cheap enough to feel instant. */ -export const BADGE_SIMULATION_TURNS = 4000; - -export interface BadgeValuation { - readonly badge: Badge; - /** Estimated points gained over a game, or null for situational badges. */ - readonly pointsPerGame: number | null; - /** One-line explanation of where the number came from. */ - readonly reason: string; - /** The dice this badge implies, when it changes the optimal loadout. */ - readonly dice: readonly Die[] | null; -} - -export interface BadgeRecommendation { - readonly tier: BadgeTier; - readonly ranked: readonly BadgeValuation[]; -} - -/** Inputs shared by every badge valuation in one solve. */ -export interface BadgeContext { - readonly inventory: readonly InventoryEntry[]; - readonly baselineDice: readonly Die[]; - readonly formationValues: FormationValues; - readonly policy: TurnPolicy; - readonly seed: number; - readonly searchOptions: SearchOptions; -} - -/** - * Mean banked points per turn for a set under a rule set. - * - * @param dice - The six dice played. - * @param scorer - Memoised scorer for the active rules. - * @param policy - Push-your-luck policy. - * @param charges - Badge charges available that turn. - * @param seed - Random seed, held constant across comparisons so that - * differences reflect the badge and not simulation noise. - * @returns Mean points banked per turn. - */ -function turnValue( - dice: readonly Die[], - scorer: Scorer, - policy: TurnPolicy, - charges: BadgeCharges, - seed: number, -): number { - return simulateTurns(dice, scorer, BADGE_SIMULATION_TURNS, policy, charges, seed).meanPerTurn; -} - -/** - * The baseline every badge is measured against: the no-badge loadout's scorer, - * its simulated turn value, and its exact throw evaluation. - * - * Computed once per solve rather than once per badge — recomputing a 4,000-turn - * simulation thirty-three times was the single largest cost in the first - * version of this module. - */ -export interface BadgeBaseline { - readonly scorer: Scorer; - readonly turn: number; - readonly p90: number; - /** Exact expected score of one throw of the baseline loadout. */ - readonly ev: number; - /** Mean throws per turn, used to convert a per-throw delta into a per-game one. */ - readonly throwsPerTurn: number; - /** Exact single-throw score distribution of the baseline loadout. */ - readonly distribution: ScoreDistribution; -} - -/** - * Total value of the best `uses` throws a player will see in a game. - * - * A badge with three charges is not worth three 90th-percentile throws: over the - * ten-or-so throws in a game you get roughly one 90th-percentile throw, one - * 80th, one 70th. Valuing each charge at the same optimistic percentile - * over-rated the three-charge gold badges noticeably. - * - * @param distribution - Single-throw score distribution. - * @param uses - How many charges the badge has. - * @param throwsPerGame - How many throws the player expects to make. - * @returns Summed value of the `uses` best throws. - */ -export function bestThrowsValue( - distribution: ScoreDistribution, - uses: number, - throwsPerGame: number, -): number { - let total = 0; - for (let k = 1; k <= uses; k += 1) { - // The k-th best of N throws sits near the (1 - k/N) quantile. Clamped so a - // short game cannot push the estimate below the median. - const fraction = Math.min(0.99, Math.max(0.5, 1 - k / Math.max(1, throwsPerGame))); - total += distribution.at(fraction); - } - return total; -} - -/** - * Measure the no-badge baseline for a loadout. - * - * @param context - Inventory, baseline loadout, and simulation settings. - * @returns The scorer, turn value, and 90th-percentile throw score. - */ -export function measureBaseline(context: BadgeContext): BadgeBaseline { - const scorer = new Scorer(BASE_RULES, context.formationValues); - const simulation = simulateTurns( - context.baselineDice, - scorer, - BADGE_SIMULATION_TURNS, - context.policy, - NO_CHARGES, - context.seed, - ); - const evaluation = evaluateSet(context.baselineDice, scorer); - return { - scorer, - turn: simulation.meanPerTurn, - throwsPerTurn: simulation.throwsPerTurn, - p90: evaluation.p90, - ev: evaluation.ev, - distribution: scoreDistribution(context.baselineDice, scorer), - }; -} /** * Value one badge in points gained per game. @@ -252,31 +133,6 @@ export function valueBadge( } } -/** - * Build the charge set that gives a badge exactly one use, for per-charge - * measurement. - * - * @param badge - The badge whose effect to model. - * @returns Charges with one use of that badge and nothing else. - */ -export function singleCharge(badge: Badge): BadgeCharges { - const effect = badge.effect; - switch (effect.kind) { - case "extraDice": - return { ...NO_CHARGES, extraDice: 1 }; - case "antibust": - return { ...NO_CHARGES, antibust: 1 }; - case "reroll": - return { ...NO_CHARGES, reroll: 1, rerollDice: effect.dice }; - case "setDie": - return { ...NO_CHARGES, setDie: 1, setDieValue: effect.value }; - default: - // Scoring, multiplier, doubleThrow, headstart and defence badges are - // valued analytically rather than simulated, so they carry no charges. - return NO_CHARGES; - } -} - /** * Round a point figure for display inside a reason string. * @@ -287,17 +143,6 @@ function format(value: number): string { return value.toFixed(1); } -/** - * Sort key for a valuation: situational badges rank below every scored one - * rather than being dropped from the list. - * - * @param valuation - The valuation to rank. - * @returns Its points per game, or negative infinity when situational. - */ -export function rankValue(valuation: BadgeValuation): number { - return valuation.pointsPerGame ?? -Infinity; -} - /** * Rank the badges the player owns, one list per tier. * @@ -325,10 +170,3 @@ export function recommendBadges( } return recommendations; } - -/** Defaults used when the caller does not care. */ -export const DEFAULT_BADGE_CONTEXT = { - formationValues: DEFAULT_FORMATION_VALUES, - policy: DEFAULT_POLICY, - seed: 0x5eed, -} as const; diff --git a/kcd2_dice_solver/src/core/solve.ts b/kcd2_dice_solver/src/core/solve.ts index 571cfe65..1e9106c7 100644 --- a/kcd2_dice_solver/src/core/solve.ts +++ b/kcd2_dice_solver/src/core/solve.ts @@ -10,7 +10,7 @@ import { BASE_RULES, DEFAULT_FORMATION_VALUES } from "../data/badges.ts"; import { DICE_BY_ID } from "../data/dice.ts"; import type { Die } from "../data/dice.ts"; import { recommendBadges } from "./badgeValue.ts"; -import type { BadgeRecommendation } from "./badgeValue.ts"; +import type { BadgeRecommendation } from "./badgeBaseline.ts"; import type { Evaluation } from "./evaluate.ts"; import { Scorer } from "./scoring.ts"; import { findBestSet } from "./search.ts";