From d0c11aa52736ac38b28525f8e9d2b4bb03c26c13 Mon Sep 17 00:00:00 2001 From: Branden J Brown Date: Thu, 3 Sep 2026 15:30:29 -0400 Subject: [PATCH] zenno: section speed does not use base wit Per analysis by Aya, only final wit is used for section speed. Fixes #40. --- zenno/src/lib/race.ts | 14 +++----- zenno/src/routes/doc/frbm/+page.svelte | 37 +++++++++----------- zenno/src/routes/doc/race/+page.svelte | 26 +++++++------- zenno/src/routes/race/carryover/+page.svelte | 2 +- zenno/src/routes/race/gate/+page.svelte | 2 +- zenno/src/routes/race/stam/+page.svelte | 10 ++---- 6 files changed, 37 insertions(+), 54 deletions(-) diff --git a/zenno/src/lib/race.ts b/zenno/src/lib/race.ts index d2d2bb1..7bbe016 100644 --- a/zenno/src/lib/race.ts +++ b/zenno/src/lib/race.ts @@ -504,14 +504,12 @@ export function baseTargetSpeed( /** * Calculate the range of section speed values for a horse in early or mid race. * @param raceLen Length of the race in meters - * @param baseWit Base wit, after mood but before other modifiers * @param witStat Final wit stat, including strategy proficiency and skills * @param style Horse's running style * @param phase Phase of the current section */ export function sectionSpeed( raceLen: number, - baseWit: number, witStat: number, style: RunningStyle, phase: Exclude, @@ -520,7 +518,6 @@ export function sectionSpeed( * Calculate the range of section speed values for a horse not spurting during late race. * @param raceLen Length of the race in meters * @param speedStat Final speed stat - * @param baseWit Base wit, after mood but before other modifiers * @param witStat Final wit stat, including strategy proficiency and skills * @param style Horse's running style * @param distance Hores's distance proficiency aptitude @@ -529,7 +526,6 @@ export function sectionSpeed( export function sectionSpeed( raceLen: number, speedStat: number, - baseWit: number, witStat: number, style: RunningStyle, distance: AptitudeLevel, @@ -537,16 +533,14 @@ export function sectionSpeed( ): [number, number]; export function sectionSpeed( raceLen: number, - speedStatOrBaseWit: number, - baseWitOrWitStat: number, + speedStatOrWitStat: number, witStatOrStyle: number | RunningStyle, styleOrPhase: RunningStyle | Phase, distance?: AptitudeLevel, lateRace?: Phase.LateRace, ): [number, number] { - const speedStat = lateRace !== undefined ? speedStatOrBaseWit : 0; - const baseWit = lateRace !== undefined ? baseWitOrWitStat : speedStatOrBaseWit; - const witStat = lateRace !== undefined ? witStatOrStyle : baseWitOrWitStat; + const speedStat = lateRace !== undefined ? speedStatOrWitStat : 0; + const witStat = lateRace !== undefined ? witStatOrStyle : speedStatOrWitStat; const style = lateRace !== undefined ? (styleOrPhase as RunningStyle) : (witStatOrStyle as RunningStyle); const phase = lateRace !== undefined ? lateRace : (styleOrPhase as Phase); const base = baseSpeed(raceLen); @@ -555,7 +549,7 @@ export function sectionSpeed( phase === Phase.LateRace ? (math.sqrt(500 * speedStat) as number) * distanceProficiencyMod[distance ?? AptitudeLevel.A] * 0.002 : 0; - const u = (witStat / 550000) * math.log10(baseWit * 0.1); + const u = (witStat / 550000) * math.log10(witStat * 0.1); const l = u - 0.0065; return [baseTarget + late + base * l, baseTarget + late + base * u]; } diff --git a/zenno/src/routes/doc/frbm/+page.svelte b/zenno/src/routes/doc/frbm/+page.svelte index e67e06f..9fb3c66 100644 --- a/zenno/src/routes/doc/frbm/+page.svelte +++ b/zenno/src/routes/doc/frbm/+page.svelte @@ -123,7 +123,7 @@ const uphillSlowPow = 800; const uphillFastPow = 1200; - const uphillBaseSpeed = mean2(sectionSpeed(2500, 1200, 1200, RunningStyle.FrontRunner, Phase.MidRace)); + const uphillBaseSpeed = mean2(sectionSpeed(2500, 1200, RunningStyle.FrontRunner, Phase.MidRace)); const uphillSlopes = [ { per: 2, len: 110 }, { per: 1.5, len: 200 }, @@ -137,13 +137,13 @@ const secSpeedS = 1200; const secSpeedA = 1000; - const secSpeedExample = $derived(sectionSpeed(raceLen, secSpeedS, secSpeedS * 1.1, RunningStyle.FrontRunner, Phase.EarlyRace)); + const secSpeedExample = $derived(sectionSpeed(raceLen, secSpeedS * 1.1, RunningStyle.FrontRunner, Phase.EarlyRace)); const secSpeedInfo = $derived(secSpeedExample.map((x) => x.toFixed(2)).join(' to ')); const secSpeedPassTime = $derived.by(() => { - const sSecSpeed = mean2(sectionSpeed(raceLen, secSpeedS, secSpeedS * 1.1, RunningStyle.FrontRunner, Phase.MidRace)); + const sSecSpeed = mean2(sectionSpeed(raceLen, secSpeedS * 1.1, RunningStyle.FrontRunner, Phase.MidRace)); const sMode = frontModeEnterChance(secSpeedS * 1.1); const sOvertakeSpeed = modemean(sSecSpeed, 1.05, sMode); - const aSecSpeed = mean2(sectionSpeed(raceLen, secSpeedA, secSpeedA, RunningStyle.FrontRunner, Phase.MidRace)); + const aSecSpeed = mean2(sectionSpeed(raceLen, secSpeedA, RunningStyle.FrontRunner, Phase.MidRace)); const aMode = frontModeEnterChance(secSpeedA); const aSpeedupSpeed = modemean(aSecSpeed, 1.04, aMode); const r = 2 / (sOvertakeSpeed - aSpeedupSpeed); @@ -183,35 +183,35 @@ const secSpeedSeries: Array = $derived([ { label: 'Early Race', - y1: (x) => sectionSpeed(raceLen, x, x, secSpeedStyle, Phase.EarlyRace)[0], - y2: (x) => sectionSpeed(raceLen, x, x, secSpeedStyle, Phase.EarlyRace)[1], + y1: (x) => sectionSpeed(raceLen, x, secSpeedStyle, Phase.EarlyRace)[0], + y2: (x) => sectionSpeed(raceLen, x, secSpeedStyle, Phase.EarlyRace)[1], }, { label: 'Mid Race', - y1: (x) => sectionSpeed(raceLen, x, x, secSpeedStyle, Phase.MidRace)[0], - y2: (x) => sectionSpeed(raceLen, x, x, secSpeedStyle, Phase.MidRace)[1], + y1: (x) => sectionSpeed(raceLen, x, secSpeedStyle, Phase.MidRace)[0], + y2: (x) => sectionSpeed(raceLen, x, secSpeedStyle, Phase.MidRace)[1], }, secIsFront ? { label: 'Early Race + Mean Speed-Up Mode', - y1: (x) => modemean(sectionSpeed(raceLen, x, x, secSpeedStyle, Phase.EarlyRace)[0], 1.04, frontModeEnterChance(x)), - y2: (x) => modemean(sectionSpeed(raceLen, x, x, secSpeedStyle, Phase.EarlyRace)[1], 1.04, frontModeEnterChance(x)), + y1: (x) => modemean(sectionSpeed(raceLen, x, secSpeedStyle, Phase.EarlyRace)[0], 1.04, frontModeEnterChance(x)), + y2: (x) => modemean(sectionSpeed(raceLen, x, secSpeedStyle, Phase.EarlyRace)[1], 1.04, frontModeEnterChance(x)), } : { label: 'Early Race + Mean Pace-Up Mode', - y1: (x) => modemean(sectionSpeed(raceLen, x, x, secSpeedStyle, Phase.EarlyRace)[0], 1.04, paceUpEnterChance(x)), - y2: (x) => modemean(sectionSpeed(raceLen, x, x, secSpeedStyle, Phase.EarlyRace)[1], 1.04, paceUpEnterChance(x)), + y1: (x) => modemean(sectionSpeed(raceLen, x, secSpeedStyle, Phase.EarlyRace)[0], 1.04, paceUpEnterChance(x)), + y2: (x) => modemean(sectionSpeed(raceLen, x, secSpeedStyle, Phase.EarlyRace)[1], 1.04, paceUpEnterChance(x)), }, secIsFront ? { label: 'Mid Race + Mean Speed-Up Mode', - y1: (x) => modemean(sectionSpeed(raceLen, x, x, secSpeedStyle, Phase.MidRace)[0], 1.04, frontModeEnterChance(x)), - y2: (x) => modemean(sectionSpeed(raceLen, x, x, secSpeedStyle, Phase.MidRace)[1], 1.04, frontModeEnterChance(x)), + y1: (x) => modemean(sectionSpeed(raceLen, x, secSpeedStyle, Phase.MidRace)[0], 1.04, frontModeEnterChance(x)), + y2: (x) => modemean(sectionSpeed(raceLen, x, secSpeedStyle, Phase.MidRace)[1], 1.04, frontModeEnterChance(x)), } : { label: 'Mid Race + Mean Pace-Up Mode', - y1: (x) => modemean(sectionSpeed(raceLen, x, x, secSpeedStyle, Phase.MidRace)[0], 1.04, paceUpEnterChance(x)), - y2: (x) => modemean(sectionSpeed(raceLen, x, x, secSpeedStyle, Phase.MidRace)[1], 1.04, paceUpEnterChance(x)), + y1: (x) => modemean(sectionSpeed(raceLen, x, secSpeedStyle, Phase.MidRace)[0], 1.04, paceUpEnterChance(x)), + y2: (x) => modemean(sectionSpeed(raceLen, x, secSpeedStyle, Phase.MidRace)[1], 1.04, paceUpEnterChance(x)), }, ]); const fcSeries: ComputedSeries[] = $derived([ @@ -649,10 +649,7 @@ ref="https://docs.google.com/document/d/15VzW9W2tXBBTibBRbZ8IVpW6HaMX8H0RP03kq6Az7Xg/edit?tab=t.0#heading=h.xdha40q2176g" >Section Speed -

- Each section, each horse gets a random modifier to target speed. The modifier's range is determined by the wit stat. - (Curiously, the calculation uses both wit as modified by style proficiency and green skills as well as base wit.) -

+

Each section, each horse gets a random modifier to target speed. The modifier's range is determined by the wit stat.

= $derived([ { label: 'Early Race', - y1: (x) => race.sectionSpeed(raceLen, x, x, style, race.Phase.EarlyRace)[0], - y2: (x) => race.sectionSpeed(raceLen, x, x, style, race.Phase.EarlyRace)[1], + y1: (x) => race.sectionSpeed(raceLen, x, style, race.Phase.EarlyRace)[0], + y2: (x) => race.sectionSpeed(raceLen, x, style, race.Phase.EarlyRace)[1], }, { label: 'Mid Race', - y1: (x) => race.sectionSpeed(raceLen, x, x, style, race.Phase.MidRace)[0], - y2: (x) => race.sectionSpeed(raceLen, x, x, style, race.Phase.MidRace)[1], + y1: (x) => race.sectionSpeed(raceLen, x, style, race.Phase.MidRace)[0], + y2: (x) => race.sectionSpeed(raceLen, x, style, race.Phase.MidRace)[1], }, styleIsFront ? { label: 'Early Race + Mean Speed-Up Mode', - y1: (x) => - modemean(race.sectionSpeed(raceLen, x, x, style, race.Phase.EarlyRace)[0], 1.04, race.frontModeEnterChance(x)), - y2: (x) => - modemean(race.sectionSpeed(raceLen, x, x, style, race.Phase.EarlyRace)[1], 1.04, race.frontModeEnterChance(x)), + y1: (x) => modemean(race.sectionSpeed(raceLen, x, style, race.Phase.EarlyRace)[0], 1.04, race.frontModeEnterChance(x)), + y2: (x) => modemean(race.sectionSpeed(raceLen, x, style, race.Phase.EarlyRace)[1], 1.04, race.frontModeEnterChance(x)), } : { label: 'Early Race + Mean Pace-Up Mode', - y1: (x) => modemean(race.sectionSpeed(raceLen, x, x, style, race.Phase.EarlyRace)[0], 1.04, race.paceUpEnterChance(x)), - y2: (x) => modemean(race.sectionSpeed(raceLen, x, x, style, race.Phase.EarlyRace)[1], 1.04, race.paceUpEnterChance(x)), + y1: (x) => modemean(race.sectionSpeed(raceLen, x, style, race.Phase.EarlyRace)[0], 1.04, race.paceUpEnterChance(x)), + y2: (x) => modemean(race.sectionSpeed(raceLen, x, style, race.Phase.EarlyRace)[1], 1.04, race.paceUpEnterChance(x)), }, styleIsFront ? { label: 'Mid Race + Mean Speed-Up Mode', - y1: (x) => modemean(race.sectionSpeed(raceLen, x, x, style, race.Phase.MidRace)[0], 1.04, race.frontModeEnterChance(x)), - y2: (x) => modemean(race.sectionSpeed(raceLen, x, x, style, race.Phase.MidRace)[1], 1.04, race.frontModeEnterChance(x)), + y1: (x) => modemean(race.sectionSpeed(raceLen, x, style, race.Phase.MidRace)[0], 1.04, race.frontModeEnterChance(x)), + y2: (x) => modemean(race.sectionSpeed(raceLen, x, style, race.Phase.MidRace)[1], 1.04, race.frontModeEnterChance(x)), } : { label: 'Mid Race + Mean Pace-Up Mode', - y1: (x) => modemean(race.sectionSpeed(raceLen, x, x, style, race.Phase.MidRace)[0], 1.04, race.paceUpEnterChance(x)), - y2: (x) => modemean(race.sectionSpeed(raceLen, x, x, style, race.Phase.MidRace)[1], 1.04, race.paceUpEnterChance(x)), + y1: (x) => modemean(race.sectionSpeed(raceLen, x, style, race.Phase.MidRace)[0], 1.04, race.paceUpEnterChance(x)), + y2: (x) => modemean(race.sectionSpeed(raceLen, x, style, race.Phase.MidRace)[1], 1.04, race.paceUpEnterChance(x)), }, ]); const downhill: ComputedSeries[] = [ diff --git a/zenno/src/routes/race/carryover/+page.svelte b/zenno/src/routes/race/carryover/+page.svelte index 1b3b512..4d97a87 100644 --- a/zenno/src/routes/race/carryover/+page.svelte +++ b/zenno/src/routes/race/carryover/+page.svelte @@ -74,7 +74,7 @@ const damCarry = $derived(allowDAM && slope < 0 ? race.downhillAccelMod(slope) : 0); const carry = $derived(skillCarry + damCarry); const midSpeed = $derived.by(() => { - const s = race.sectionSpeed(raceLen, baseWit, wit, style, race.Phase.MidRace) satisfies [number, number]; + const s = race.sectionSpeed(raceLen, wit, style, race.Phase.MidRace) satisfies [number, number]; const u = slope > 0 ? race.uphillMod(power, slope) : 0; return [s[0] + u, s[1] + u] as const; }); diff --git a/zenno/src/routes/race/gate/+page.svelte b/zenno/src/routes/race/gate/+page.svelte index 74ef6af..a4935a5 100644 --- a/zenno/src/routes/race/gate/+page.svelte +++ b/zenno/src/routes/race/gate/+page.svelte @@ -83,7 +83,7 @@ } return [0, 0]; }); - const earlyTarget = $derived(race.sectionSpeed(raceLen, baseWit, wit, style, race.Phase.EarlyRace)); + const earlyTarget = $derived(race.sectionSpeed(raceLen, wit, style, race.Phase.EarlyRace)); const speeds: [number, number] = $derived([earlyTarget[0] + hillMod[0], earlyTarget[1] + hillMod[1]]); const startInstant: accel.Instant = { t: 0, x: 0, v: 3 }; diff --git a/zenno/src/routes/race/stam/+page.svelte b/zenno/src/routes/race/stam/+page.svelte index e17ba03..8639dc9 100644 --- a/zenno/src/routes/race/stam/+page.svelte +++ b/zenno/src/routes/race/stam/+page.svelte @@ -96,14 +96,8 @@ const distType = $derived(race.distance(raceLen)); const phaseSpeed = $derived({ - [race.Phase.EarlyRace]: race.sectionSpeed( - raceLen, - baseStats[race.Stat.Wit], - stats[race.Stat.Wit], - style, - race.Phase.EarlyRace, - ), - [race.Phase.MidRace]: race.sectionSpeed(raceLen, baseStats[race.Stat.Wit], stats[race.Stat.Wit], style, race.Phase.MidRace), + [race.Phase.EarlyRace]: race.sectionSpeed(raceLen, stats[race.Stat.Wit], style, race.Phase.EarlyRace), + [race.Phase.MidRace]: race.sectionSpeed(raceLen, stats[race.Stat.Wit], style, race.Phase.MidRace), [race.Phase.LateRace]: [ race.spurtSpeed(stats[race.Stat.Speed], stats[race.Stat.Guts], style, distanceApt, raceLen), race.spurtSpeed(stats[race.Stat.Speed], stats[race.Stat.Guts], style, distanceApt, raceLen),