claude/probability-config-review-1tdolw #119
2 changed files with 25 additions and 9 deletions
|
|
@ -69,6 +69,25 @@ interface ActionData {
|
||||||
message: string;
|
message: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Engine knobs that a simulator (or the shared input-policy resolver) actually
|
||||||
|
* reads from config. The structured Engine fields are limited to these so the UI
|
||||||
|
* never shows a control that silently does nothing — bespoke per-sim constants
|
||||||
|
* (e.g. homeFieldElo, eloDivisor, srsEloScale, raceNoise) that live in a profile
|
||||||
|
* but are not read from config stay editable only via the raw-JSON escape hatch.
|
||||||
|
*/
|
||||||
|
const HONORED_ENGINE_KNOBS = new Set([
|
||||||
|
"iterations",
|
||||||
|
"parityFactor",
|
||||||
|
"seasonGames",
|
||||||
|
"overtimeRate",
|
||||||
|
"matchParityFactor",
|
||||||
|
"averageOpponentElo",
|
||||||
|
"baseDrawRate",
|
||||||
|
"drawDecay",
|
||||||
|
"ratingScaleFactor",
|
||||||
|
]);
|
||||||
|
|
||||||
function parseOptionalNumber(value: string | undefined): number | null {
|
function parseOptionalNumber(value: string | undefined): number | null {
|
||||||
if (value === undefined || value.trim() === "") return null;
|
if (value === undefined || value.trim() === "") return null;
|
||||||
const parsed = Number(value);
|
const parsed = Number(value);
|
||||||
|
|
@ -335,7 +354,7 @@ export default function AdminSportsSeasonSimulator({ loaderData }: Route.Compone
|
||||||
// nested object edited in its own section). Driving the fields from the merged
|
// nested object edited in its own section). Driving the fields from the merged
|
||||||
// config means each simulator shows exactly the knobs it actually reads.
|
// config means each simulator shows exactly the knobs it actually reads.
|
||||||
const engineEntries = Object.entries(config.config)
|
const engineEntries = Object.entries(config.config)
|
||||||
.filter(([key, value]) => key !== "inputPolicy" && typeof value === "number")
|
.filter(([key, value]) => HONORED_ENGINE_KNOBS.has(key) && typeof value === "number")
|
||||||
.map(([key, value]) => [key, value as unknown as number] as [string, number]);
|
.map(([key, value]) => [key, value as unknown as number] as [string, number]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|
|
||||||
|
|
@ -254,14 +254,11 @@ export function resolveSourceElos(
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
if (oddsInputs.length >= 2) {
|
if (oddsInputs.length >= 2) {
|
||||||
// Map onto the configured Elo band so the policy's floor/ceiling set the
|
// Odds map onto the calibrated band (probability-engine DEFAULT_CALIBRATION);
|
||||||
// odds-derived *spread* (the real flattening dial), not just a post-hoc clamp.
|
// the policy's eloMin/eloMax then clamp the result via clampDerived below, so
|
||||||
const converted = convertFuturesToElo(oddsInputs, "american", {
|
// a season's Elo floor/ceiling still narrows the odds-derived spread without
|
||||||
exponent: 0.33,
|
// widening it (and without clamping a high directly-entered base Elo's blend).
|
||||||
eloMin: policy.eloMin,
|
for (const [participantId, elo] of convertFuturesToElo(oddsInputs)) {
|
||||||
eloMax: policy.eloMax,
|
|
||||||
});
|
|
||||||
for (const [participantId, elo] of converted) {
|
|
||||||
oddsElo.set(participantId, elo);
|
oddsElo.set(participantId, elo);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue