zenno/race/stam: beginning of stamina calculator
This commit is contained in:
@@ -0,0 +1,136 @@
|
||||
import * as math from 'mathjs';
|
||||
import { Phase, type Stat, type Surface } from "./race";
|
||||
|
||||
export interface Race {
|
||||
location: RaceLocation;
|
||||
length: number;
|
||||
surface: Surface;
|
||||
direction: Direction;
|
||||
spans: Span[];
|
||||
slopes: Slope[];
|
||||
thresholds: [] | [Stat] | [Stat, Stat];
|
||||
}
|
||||
|
||||
export enum RaceLocation {
|
||||
Sapporo = 'Sapporo',
|
||||
Hakodate = 'Hakodate',
|
||||
Niigata = 'Niigata',
|
||||
Fukushima = 'Fukushima',
|
||||
Nakayama = 'Nakayama',
|
||||
Tokyo = 'Tokyo',
|
||||
Chukyo = 'Chukyo',
|
||||
Kyoto = 'Kyoto',
|
||||
Hanshin = 'Hanshin',
|
||||
Kokura = 'Kokura',
|
||||
Ooi = 'Ooi',
|
||||
Kawasaki = 'Kawasaki',
|
||||
Funabashi = 'Funabashi',
|
||||
Morioka = 'Morioka',
|
||||
Longchamp = 'Longchamp',
|
||||
SantaAnitaPark = 'Santa Anita Park',
|
||||
DelMar = 'Del Mar',
|
||||
}
|
||||
|
||||
export enum Direction {
|
||||
Right,
|
||||
Left,
|
||||
Stretch,
|
||||
}
|
||||
|
||||
export enum SpanType {
|
||||
FrontStraight,
|
||||
AcrossStraight,
|
||||
FalseStraight,
|
||||
Corner1,
|
||||
Corner2,
|
||||
Corner3,
|
||||
Corner4,
|
||||
}
|
||||
|
||||
export interface Span {
|
||||
type: SpanType;
|
||||
span: [number, number];
|
||||
}
|
||||
|
||||
export interface Slope {
|
||||
per: -2 | -1.5 | -1 | 1 | 1.5 | 2;
|
||||
span: [number, number];
|
||||
}
|
||||
|
||||
function sortSlopes(slopes: Slope[]): Slope[] {
|
||||
return slopes.toSorted((a, b) => a.span[0] - b.span[0]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the distances into a race where each phase ends.
|
||||
* @param raceLen Length of the race in meters
|
||||
* @returns Quadruple of phase end points, including separate late and last spurt phases
|
||||
*/
|
||||
export function phases(raceLen: number): [number, number, number, number] {
|
||||
const sixth = raceLen / 6;
|
||||
const half = raceLen / 2;
|
||||
return [sixth, sixth + half, 2*sixth + half, raceLen];
|
||||
}
|
||||
|
||||
export interface SectionInfo {
|
||||
section: number;
|
||||
phase: Phase;
|
||||
endRate: number;
|
||||
posKeep: boolean;
|
||||
rush: boolean;
|
||||
spotStruggleEnter: boolean;
|
||||
}
|
||||
|
||||
export const SECTIONS: ReadonlyArray<SectionInfo> = [
|
||||
{section: 1, phase: Phase.EarlyRace, endRate: 1/24, posKeep: true, rush: false, spotStruggleEnter: false},
|
||||
{section: 2, phase: Phase.EarlyRace, endRate: 2/24, posKeep: true, rush: true, spotStruggleEnter: true},
|
||||
{section: 3, phase: Phase.EarlyRace, endRate: 3/24, posKeep: true, rush: true, spotStruggleEnter: true},
|
||||
{section: 4, phase: Phase.EarlyRace, endRate: 4/24, posKeep: true, rush: true, spotStruggleEnter: true},
|
||||
{section: 5, phase: Phase.MidRace, endRate: 5/24, posKeep: true, rush: true, spotStruggleEnter: true},
|
||||
{section: 6, phase: Phase.MidRace, endRate: 6/24, posKeep: true, rush: true, spotStruggleEnter: false},
|
||||
{section: 7, phase: Phase.MidRace, endRate: 7/24, posKeep: true, rush: true, spotStruggleEnter: false},
|
||||
{section: 8, phase: Phase.MidRace, endRate: 8/24, posKeep: true, rush: true, spotStruggleEnter: false},
|
||||
{section: 9, phase: Phase.MidRace, endRate: 9/24, posKeep: true, rush: true, spotStruggleEnter: false},
|
||||
{section: 10, phase: Phase.MidRace, endRate: 10/24, posKeep: true, rush: false, spotStruggleEnter: false},
|
||||
{section: 11, phase: Phase.MidRace, endRate: 11/24, posKeep: false, rush: false, spotStruggleEnter: false},
|
||||
{section: 12, phase: Phase.MidRace, endRate: 12/24, posKeep: false, rush: false, spotStruggleEnter: false},
|
||||
{section: 13, phase: Phase.MidRace, endRate: 13/24, posKeep: false, rush: false, spotStruggleEnter: false},
|
||||
{section: 14, phase: Phase.MidRace, endRate: 14/24, posKeep: false, rush: false, spotStruggleEnter: false},
|
||||
{section: 15, phase: Phase.MidRace, endRate: 15/24, posKeep: false, rush: false, spotStruggleEnter: false},
|
||||
{section: 16, phase: Phase.MidRace, endRate: 16/24, posKeep: false, rush: false, spotStruggleEnter: false},
|
||||
{section: 17, phase: Phase.LateRace, endRate: 17/24, posKeep: false, rush: false, spotStruggleEnter: false},
|
||||
{section: 18, phase: Phase.LateRace, endRate: 18/24, posKeep: false, rush: false, spotStruggleEnter: false},
|
||||
{section: 19, phase: Phase.LateRace, endRate: 19/24, posKeep: false, rush: false, spotStruggleEnter: false},
|
||||
{section: 20, phase: Phase.LateRace, endRate: 20/24, posKeep: false, rush: false, spotStruggleEnter: false},
|
||||
{section: 21, phase: Phase.LateRace, endRate: 21/24, posKeep: false, rush: false, spotStruggleEnter: false},
|
||||
{section: 22, phase: Phase.LateRace, endRate: 22/24, posKeep: false, rush: false, spotStruggleEnter: false},
|
||||
{section: 23, phase: Phase.LateRace, endRate: 23/24, posKeep: false, rush: false, spotStruggleEnter: false},
|
||||
{section: 24, phase: Phase.LateRace, endRate: 24/24, posKeep: false, rush: false, spotStruggleEnter: false},
|
||||
];
|
||||
|
||||
export interface SectionSlopeSegment extends SectionInfo {
|
||||
startDist: number;
|
||||
endDist: number;
|
||||
slope: -2 | -1.5 | -1 | 0 | 1 | 1.5 | 2;
|
||||
}
|
||||
|
||||
export function splitSectionSlopes(raceLen: number, slopes: Slope[]): SectionSlopeSegment[] {
|
||||
const ss = sortSlopes(slopes);
|
||||
return SECTIONS.flatMap<SectionSlopeSegment>((sec) => {
|
||||
const startDist = (sec.section - 1) * raceLen / 24;
|
||||
const endDist = sec.section * raceLen / 24;
|
||||
const s = ss.filter((sl) => sl.span[0] < endDist && sl.span[1] > startDist);
|
||||
if (s.length === 0) {
|
||||
return [{...sec, startDist, endDist, slope: 0}];
|
||||
}
|
||||
const r: Array<SectionSlopeSegment> = [{...sec, startDist, endDist: math.min(s[0].span[0], endDist), slope: 0}];
|
||||
for (const [i, sl] of s.entries()) {
|
||||
const slopeEnd = math.min(endDist, sl.span[1]);
|
||||
r.push(
|
||||
{...sec, startDist: math.max(startDist, sl.span[0]), endDist: slopeEnd, slope: sl.per},
|
||||
{...sec, startDist: slopeEnd, endDist: math.min(s[i+1]?.span[0] ?? endDist, endDist), slope: 0},
|
||||
);
|
||||
}
|
||||
return r.filter((sl) => sl.startDist < sl.endDist);
|
||||
})
|
||||
}
|
||||
Reference in New Issue
Block a user