From 04be0b75a99f31965f8b473b4cb5f89ac1b7b9f9 Mon Sep 17 00:00:00 2001 From: Branden J Brown Date: Sat, 31 Oct 2026 13:23:15 -0400 Subject: [PATCH] zenno: lint fixes --- zenno/src/lib/CourseChart.svelte | 3 +- zenno/src/lib/all.ts | 2 +- zenno/src/lib/racelib/condition.spec.ts | 154 ++++++------ zenno/src/lib/racelib/condition.ts | 225 +++++++++++------- zenno/src/routes/doc/fans/fans.ts | 2 +- zenno/src/routes/race/skillzone/+page.svelte | 6 +- .../src/routes/race/skillzone/TopChart.svelte | 2 +- 7 files changed, 228 insertions(+), 166 deletions(-) diff --git a/zenno/src/lib/CourseChart.svelte b/zenno/src/lib/CourseChart.svelte index 46d5aa1..dfbc0c9 100644 --- a/zenno/src/lib/CourseChart.svelte +++ b/zenno/src/lib/CourseChart.svelte @@ -18,11 +18,10 @@ title?: string; class?: ClassValue | null; plotOptions?: Omit; - fontColor?: string; draw: DrawSpan[]; } - let { course, title, class: className, plotOptions, fontColor, draw }: Props = $props(); + let { course, title, class: className, plotOptions, draw }: Props = $props(); let width = $state(0); let height = $state(0); diff --git a/zenno/src/lib/all.ts b/zenno/src/lib/all.ts index f58d43b..75e2599 100644 --- a/zenno/src/lib/all.ts +++ b/zenno/src/lib/all.ts @@ -109,7 +109,7 @@ export const PAGES = { { route: resolve('/doc/fans'), name: 'Career Fan Gain Is Solved', - description: 'The exact formula for fan gain from career races – nothing "random" about it.' + description: 'The exact formula for fan gain from career races – nothing "random" about it.', }, ] satisfies Page[], } as const; diff --git a/zenno/src/lib/racelib/condition.spec.ts b/zenno/src/lib/racelib/condition.spec.ts index 013f536..e682e9b 100644 --- a/zenno/src/lib/racelib/condition.spec.ts +++ b/zenno/src/lib/racelib/condition.spec.ts @@ -2,81 +2,81 @@ import { expect, test } from 'vitest'; import * as condition from './condition'; test.for([ - { - c: '', - want: [], - }, - { - c: 'all_corner_random==1', - want: [[{ type: 'all_corner_random', op: '==', arg: 1 }]], - }, - { - c: 'corner!=0', - want: [[{ type: 'corner', op: '!=', arg: 0 }]], - }, - { - c: 'order_rate<80', - want: [[{ type: 'order_rate', op: '<', arg: 80 }]], - }, - { - c: 'distance_diff_top<=5', - want: [[{ type: 'distance_diff_top', op: '<=', arg: 5 }]], - }, - { - c: 'motivation>=4', - want: [[{ type: 'motivation', op: '>=', arg: 4 }]], - }, - { - c: 'compete_fight_count>0', - want: [[{ type: 'compete_fight_count', op: '>', arg: 0 }]], - }, - { - c: 'phase==1&change_order_onetime<0', - want: [ - [ - { type: 'phase', op: '==', arg: 1 }, - { type: 'change_order_onetime', op: '<', arg: 0 }, - ], - ], - }, - { - c: 'temptation_count==0&remain_distance<=201&remain_distance>=199&order>=4&order_rate<=70', - want: [ - [ - { type: 'temptation_count', op: '==', arg: 0 }, - { type: 'remain_distance', op: '<=', arg: 201 }, - { type: 'remain_distance', op: '>=', arg: 199 }, - { type: 'order', op: '>=', arg: 4 }, - { type: 'order_rate', op: '<=', arg: 70 }, - ], - ], - }, - { - c: 'track_id==10001@track_id==10002@track_id==10004@track_id==10010@track_id==10103@track_id==10104', - want: [ - [{ type: 'track_id', op: '==', arg: 10001 }], - [{ type: 'track_id', op: '==', arg: 10002 }], - [{ type: 'track_id', op: '==', arg: 10004 }], - [{ type: 'track_id', op: '==', arg: 10010 }], - [{ type: 'track_id', op: '==', arg: 10103 }], - [{ type: 'track_id', op: '==', arg: 10104 }], - ], - }, - { - c: 'distance_type==1&phase>=2&order_rate>=50&is_overtake==1@distance_type==2&phase>=2&order_rate>=50&is_overtake==1', - want: [ - [ - { type: 'distance_type', op: '==', arg: 1 }, - { type: 'phase', op: '>=', arg: 2 }, - { type: 'order_rate', op: '>=', arg: 50 }, - { type: 'is_overtake', op: '==', arg: 1 }, - ], - [ - { type: 'distance_type', op: '==', arg: 2 }, - { type: 'phase', op: '>=', arg: 2 }, - { type: 'order_rate', op: '>=', arg: 50 }, - { type: 'is_overtake', op: '==', arg: 1 }, - ], - ], - }, + { + c: '', + want: [], + }, + { + c: 'all_corner_random==1', + want: [[{ type: 'all_corner_random', op: '==', arg: 1 }]], + }, + { + c: 'corner!=0', + want: [[{ type: 'corner', op: '!=', arg: 0 }]], + }, + { + c: 'order_rate<80', + want: [[{ type: 'order_rate', op: '<', arg: 80 }]], + }, + { + c: 'distance_diff_top<=5', + want: [[{ type: 'distance_diff_top', op: '<=', arg: 5 }]], + }, + { + c: 'motivation>=4', + want: [[{ type: 'motivation', op: '>=', arg: 4 }]], + }, + { + c: 'compete_fight_count>0', + want: [[{ type: 'compete_fight_count', op: '>', arg: 0 }]], + }, + { + c: 'phase==1&change_order_onetime<0', + want: [ + [ + { type: 'phase', op: '==', arg: 1 }, + { type: 'change_order_onetime', op: '<', arg: 0 }, + ], + ], + }, + { + c: 'temptation_count==0&remain_distance<=201&remain_distance>=199&order>=4&order_rate<=70', + want: [ + [ + { type: 'temptation_count', op: '==', arg: 0 }, + { type: 'remain_distance', op: '<=', arg: 201 }, + { type: 'remain_distance', op: '>=', arg: 199 }, + { type: 'order', op: '>=', arg: 4 }, + { type: 'order_rate', op: '<=', arg: 70 }, + ], + ], + }, + { + c: 'track_id==10001@track_id==10002@track_id==10004@track_id==10010@track_id==10103@track_id==10104', + want: [ + [{ type: 'track_id', op: '==', arg: 10001 }], + [{ type: 'track_id', op: '==', arg: 10002 }], + [{ type: 'track_id', op: '==', arg: 10004 }], + [{ type: 'track_id', op: '==', arg: 10010 }], + [{ type: 'track_id', op: '==', arg: 10103 }], + [{ type: 'track_id', op: '==', arg: 10104 }], + ], + }, + { + c: 'distance_type==1&phase>=2&order_rate>=50&is_overtake==1@distance_type==2&phase>=2&order_rate>=50&is_overtake==1', + want: [ + [ + { type: 'distance_type', op: '==', arg: 1 }, + { type: 'phase', op: '>=', arg: 2 }, + { type: 'order_rate', op: '>=', arg: 50 }, + { type: 'is_overtake', op: '==', arg: 1 }, + ], + [ + { type: 'distance_type', op: '==', arg: 2 }, + { type: 'phase', op: '>=', arg: 2 }, + { type: 'order_rate', op: '>=', arg: 50 }, + { type: 'is_overtake', op: '==', arg: 1 }, + ], + ], + }, ])('$c', ({ c, want }) => expect(condition.parse(c)).toEqual(want)); diff --git a/zenno/src/lib/racelib/condition.ts b/zenno/src/lib/racelib/condition.ts index 9c742d0..e168a25 100644 --- a/zenno/src/lib/racelib/condition.ts +++ b/zenno/src/lib/racelib/condition.ts @@ -55,17 +55,17 @@ function phase(course: racecourse.Course, op: Op, phase: race.Phase, type: SpanT if (phase === race.Phase.EarlyRace) { return { start: 0, end: p[race.Phase.EarlyRace], type }; } - return { start: p[phase - 1 as race.Phase], end: p[phase], type }; + return { start: p[(phase - 1) as race.Phase], end: p[phase], type }; case Op.Le: return { start: 0, end: p[phase], type }; case Op.Ge: if (phase === race.Phase.EarlyRace) { return { start: 0, end: course.distance, type }; } - return { start: p[phase - 1 as race.Phase], end: course.distance, type }; + return { start: p[(phase - 1) as race.Phase], end: course.distance, type }; default: // Other operators aren't used. - throw new Error("weird operator on phase condition"); + throw new Error('weird operator on phase condition'); } } @@ -90,10 +90,10 @@ function distrate(course: racecourse.Course, cond: Condition, type: SpanType): S switch (cond.op) { case Op.Le: case Op.Lt: - return { start: 0, end: Math.round(course.distance * cond.arg / 100), type }; + return { start: 0, end: Math.round((course.distance * cond.arg) / 100), type }; case Op.Ge: case Op.Gt: - return { start: Math.round(course.distance * cond.arg / 100), end: course.distance, type }; + return { start: Math.round((course.distance * cond.arg) / 100), end: course.distance, type }; } return { start: 0, end: 0, type: SpanType.Never }; } @@ -103,7 +103,7 @@ function remaining(course: racecourse.Course, cond: Condition, type: SpanType): case Op.Eq: return { start: course.distance - cond.arg - 1, end: course.distance - cond.arg + 1, type }; case Op.Ne: - throw new Error("weird operator on remaining distance"); + throw new Error('weird operator on remaining distance'); case Op.Le: case Op.Lt: return { start: course.distance - cond.arg, end: course.distance, type }; @@ -169,33 +169,58 @@ function must(x: boolean): SpanType { } const perCond: Readonly Span[]>>> = { - 'accumulatetime': (course, cond) => [accumulatetime(course, cond.arg, SpanType.Whenever)], - 'activate_count_all_team': (course) => [{ start: 0, end: course.distance, type: SpanType.Skill }], - 'activate_count_end_after': (course) => [endafter(course, SpanType.Skill)], - 'activate_count_heal': (course) => [{ start: 0, end: course.distance, type: SpanType.Skill }], - 'activate_count_later_half': (course) => [{ start: course.distance / 2, end: course.distance, type: SpanType.Skill }], - 'activate_count_middle': (course) => [phase(course, Op.Eq, race.Phase.MidRace, SpanType.Skill)], - 'activate_count_start': (course) => [phase(course, Op.Eq, race.Phase.EarlyRace, SpanType.Skill)], - 'all_corner_random': (course) => coursespans(course.spans.filter((s) => s.corner != null), SpanType.Random), - 'change_order_up_end_after': (course) => [endafter(course, SpanType.Whenever)], - 'change_order_up_finalcorner_after': (course) => [is_finalcorner(course, true, SpanType.Whenever)], - 'change_order_up_middle': (course) => [phase(course, Op.Eq, race.Phase.MidRace, SpanType.Whenever)], - 'compete_fight_count': (course) => [laststraight(course, SpanType.Whenever)], - 'corner': (course, cond) => coursespans(course.spans.filter((s) => opcompare(cond, s.corner ?? 0)), SpanType.Whenever), - 'corner_count': (course, cond) => [{ start: 0, end: course.distance, type: opcompare(cond, course.spans.reduce((n, s) => n + (s.corner != null ? 1 : 0), 0)) ? SpanType.Never : SpanType.Whenever }], - 'corner_random': (course, cond) => { + accumulatetime: (course, cond) => [accumulatetime(course, cond.arg, SpanType.Whenever)], + activate_count_all_team: (course) => [{ start: 0, end: course.distance, type: SpanType.Skill }], + activate_count_end_after: (course) => [endafter(course, SpanType.Skill)], + activate_count_heal: (course) => [{ start: 0, end: course.distance, type: SpanType.Skill }], + activate_count_later_half: (course) => [{ start: course.distance / 2, end: course.distance, type: SpanType.Skill }], + activate_count_middle: (course) => [phase(course, Op.Eq, race.Phase.MidRace, SpanType.Skill)], + activate_count_start: (course) => [phase(course, Op.Eq, race.Phase.EarlyRace, SpanType.Skill)], + all_corner_random: (course) => + coursespans( + course.spans.filter((s) => s.corner != null), + SpanType.Random, + ), + change_order_up_end_after: (course) => [endafter(course, SpanType.Whenever)], + change_order_up_finalcorner_after: (course) => [is_finalcorner(course, true, SpanType.Whenever)], + change_order_up_middle: (course) => [phase(course, Op.Eq, race.Phase.MidRace, SpanType.Whenever)], + compete_fight_count: (course) => [laststraight(course, SpanType.Whenever)], + corner: (course, cond) => + coursespans( + course.spans.filter((s) => opcompare(cond, s.corner ?? 0)), + SpanType.Whenever, + ), + corner_count: (course, cond) => [ + { + start: 0, + end: course.distance, + type: opcompare( + cond, + course.spans.reduce((n, s) => n + (s.corner != null ? 1 : 0), 0), + ) + ? SpanType.Never + : SpanType.Whenever, + }, + ], + corner_random: (course, cond) => { const c = course.spans.findLast((s) => s.corner === cond.arg); if (c == null) { return [{ start: 0, end: 0, type: SpanType.Never }]; } return [{ start: c.start, end: c.end, type: SpanType.Random }]; }, - 'course_distance': (course, cond) => [{ start: 0, end: course.distance, type: must(opcompare(cond, course.distance)) }], - 'distance_rate': (course, cond) => [distrate(course, cond, SpanType.Whenever)], - 'distance_rate_after_random': (course, cond) => [distrate(course, cond, SpanType.Random)], - 'distance_type': (course, cond) => [{ start: 0, end: course.distance, type: must(opcompare(cond, racecourse.distanceType(course))) }], - 'down_slope_random': (course) => coursespans(course.slopes.filter((s) => s.per < 0), SpanType.Random), - 'furlong': (course, cond) => { + course_distance: (course, cond) => [{ start: 0, end: course.distance, type: must(opcompare(cond, course.distance)) }], + distance_rate: (course, cond) => [distrate(course, cond, SpanType.Whenever)], + distance_rate_after_random: (course, cond) => [distrate(course, cond, SpanType.Random)], + distance_type: (course, cond) => [ + { start: 0, end: course.distance, type: must(opcompare(cond, racecourse.distanceType(course))) }, + ], + down_slope_random: (course) => + coursespans( + course.slopes.filter((s) => s.per < 0), + SpanType.Random, + ), + furlong: (course, cond) => { switch (cond.op) { case Op.Eq: return [{ start: cond.arg * 201.168, end: (cond.arg + 1) * 201.168, type: SpanType.Whenever }]; @@ -212,9 +237,9 @@ const perCond: Readonly [{ start: 0, end: course.distance, type: must(opcompare(cond, course.ground)) }], + ground_type: (course, cond) => [{ start: 0, end: course.distance, type: must(opcompare(cond, course.ground)) }], // hp_per would be an interesting challenge... - 'is_finalcorner': (course, cond) => { + is_finalcorner: (course, cond) => { // Either is_finalcorner==1 or is_finalcorner==0. if (cond.arg === 1) { return [is_finalcorner(course, true, SpanType.Whenever)]; @@ -226,84 +251,122 @@ const perCond: Readonly [{ start: 0, end: course.distance, type: SpanType.Skill }], - 'is_activate_heal_skill': (course) => [{ start: 0, end: course.distance, type: SpanType.Skill }], - 'is_basis_distance': (course, cond) => [{ start: 0, end: course.distance, type: must(opcompare(cond, course.distance % 400 === 0 ? 1 : 0)) }], - 'is_dirtgrade': (course, cond) => { + is_activate_any_skill: (course) => [{ start: 0, end: course.distance, type: SpanType.Skill }], + is_activate_heal_skill: (course) => [{ start: 0, end: course.distance, type: SpanType.Skill }], + is_basis_distance: (course, cond) => [ + { start: 0, end: course.distance, type: must(opcompare(cond, course.distance % 400 === 0 ? 1 : 0)) }, + ], + is_dirtgrade: (course, cond) => { // NAR tracks: Ooi, Kawasaki, Funabashi, Morioka const is = [10101, 10103, 10104, 10105].includes(course.race_track_id); return [{ start: 0, end: course.distance, type: must(opcompare(cond, is ? 1 : 0)) }]; }, - 'is_finalcorner_laterhalf': (course) => [later(is_finalcorner(course, false, SpanType.Whenever), 0.5)], - 'is_finalcorner_random': (course) => [is_finalcorner(course, false, SpanType.Random)], - 'is_last_straight': (course) => [laststraight(course, SpanType.Whenever)], - 'is_last_straight_onetime': (course) => [first(laststraight(course, SpanType.Whenever), 0.02)], - 'is_lastspurt': (course) => [endafter(course, SpanType.Whenever)], + is_finalcorner_laterhalf: (course) => [later(is_finalcorner(course, false, SpanType.Whenever), 0.5)], + is_finalcorner_random: (course) => [is_finalcorner(course, false, SpanType.Random)], + is_last_straight: (course) => [laststraight(course, SpanType.Whenever)], + is_last_straight_onetime: (course) => [first(laststraight(course, SpanType.Whenever), 0.02)], + is_lastspurt: (course) => [endafter(course, SpanType.Whenever)], // could implement is_temptation!=0, but that condition isn't used - 'is_tight_track': (course, cond) => { + is_tight_track: (course, cond) => { // Sapporo, Hakodate, Fukushima, Kokura, Kawasaki, Funabashi // TODO(zeph): GameTora says "afaik" for this list... const is = [10001, 10002, 10004, 10010, 10103, 10104]; return [{ start: 0, end: course.distance, type: must(opcompare(cond, is ? 1 : 0)) }]; }, - 'last_straight_random': (course) => [laststraight(course, SpanType.Random)], - 'lastspurt': (course) => [endafter(course, SpanType.Whenever)], - 'order_rate_in20_continue': (course) => [accumulatetime(course, 5, SpanType.Whenever)], - 'order_rate_in40_continue': (course) => [accumulatetime(course, 5, SpanType.Whenever)], - 'order_rate_in50_continue': (course) => [accumulatetime(course, 5, SpanType.Whenever)], - 'order_rate_in80_continue': (course) => [accumulatetime(course, 5, SpanType.Whenever)], - 'order_rate_out20_continue': (course) => [accumulatetime(course, 5, SpanType.Whenever)], - 'order_rate_out40_continue': (course) => [accumulatetime(course, 5, SpanType.Whenever)], - 'order_rate_out50_continue': (course) => [accumulatetime(course, 5, SpanType.Whenever)], - 'order_rate_out70_continue': (course) => [accumulatetime(course, 5, SpanType.Whenever)], - 'phase': (course, cond) => [phase(course, cond.op, cond.arg, SpanType.Whenever)], - 'phase_corner_random': (course, cond) => { + last_straight_random: (course) => [laststraight(course, SpanType.Random)], + lastspurt: (course) => [endafter(course, SpanType.Whenever)], + order_rate_in20_continue: (course) => [accumulatetime(course, 5, SpanType.Whenever)], + order_rate_in40_continue: (course) => [accumulatetime(course, 5, SpanType.Whenever)], + order_rate_in50_continue: (course) => [accumulatetime(course, 5, SpanType.Whenever)], + order_rate_in80_continue: (course) => [accumulatetime(course, 5, SpanType.Whenever)], + order_rate_out20_continue: (course) => [accumulatetime(course, 5, SpanType.Whenever)], + order_rate_out40_continue: (course) => [accumulatetime(course, 5, SpanType.Whenever)], + order_rate_out50_continue: (course) => [accumulatetime(course, 5, SpanType.Whenever)], + order_rate_out70_continue: (course) => [accumulatetime(course, 5, SpanType.Whenever)], + phase: (course, cond) => [phase(course, cond.op, cond.arg, SpanType.Whenever)], + phase_corner_random: (course, cond) => { const p = phase(course, Op.Eq, cond.arg, SpanType.Random); - return coursespans(course.spans.filter((s) => s.corner != null), SpanType.Random).map((s) => intersection(s, p)); + return coursespans( + course.spans.filter((s) => s.corner != null), + SpanType.Random, + ).map((s) => intersection(s, p)); }, - 'phase_first_half_straight_random': (course, cond) => { + phase_first_half_straight_random: (course, cond) => { const p = first(phase(course, Op.Eq, cond.arg, SpanType.Random), 0.5); - return coursespans(course.spans.filter((s) => s.straight != null), SpanType.Random).map((s) => intersection(s, p)); + return coursespans( + course.spans.filter((s) => s.straight != null), + SpanType.Random, + ).map((s) => intersection(s, p)); }, - 'phase_firsthalf': (course, cond) => [first(phase(course, Op.Eq, cond.arg, SpanType.Whenever), 0.5)], - 'phase_firsthalf_random': (course, cond) => [first(phase(course, Op.Eq, cond.arg, SpanType.Random), 0.5)], - 'phase_firstquarter': (course, cond) => [first(phase(course, Op.Eq, cond.arg, SpanType.Whenever), 0.25)], - 'phase_firstquarter_random': (course, cond) => [first(phase(course, Op.Eq, cond.arg, SpanType.Random), 0.25)], - 'phase_laterhalf': (course, cond) => [later(phase(course, Op.Eq, cond.arg, SpanType.Whenever), 0.5)], - 'phase_laterhalf_random': (course, cond) => [later(phase(course, Op.Eq, cond.arg, SpanType.Random), 0.5)], - 'phase_latter_half_straight_random': (course, cond) => { + phase_firsthalf: (course, cond) => [first(phase(course, Op.Eq, cond.arg, SpanType.Whenever), 0.5)], + phase_firsthalf_random: (course, cond) => [first(phase(course, Op.Eq, cond.arg, SpanType.Random), 0.5)], + phase_firstquarter: (course, cond) => [first(phase(course, Op.Eq, cond.arg, SpanType.Whenever), 0.25)], + phase_firstquarter_random: (course, cond) => [first(phase(course, Op.Eq, cond.arg, SpanType.Random), 0.25)], + phase_laterhalf: (course, cond) => [later(phase(course, Op.Eq, cond.arg, SpanType.Whenever), 0.5)], + phase_laterhalf_random: (course, cond) => [later(phase(course, Op.Eq, cond.arg, SpanType.Random), 0.5)], + phase_latter_half_straight_random: (course, cond) => { const p = later(phase(course, Op.Eq, cond.arg, SpanType.Random), 0.5); - return coursespans(course.spans.filter((s) => s.straight != null), SpanType.Random).map((s) => intersection(s, p)); + return coursespans( + course.spans.filter((s) => s.straight != null), + SpanType.Random, + ).map((s) => intersection(s, p)); }, - 'phase_random': (course, cond) => [phase(course, cond.op, cond.arg, SpanType.Random)], - 'phase_straight_random': (course, cond) => { + phase_random: (course, cond) => [phase(course, cond.op, cond.arg, SpanType.Random)], + phase_straight_random: (course, cond) => { const p = phase(course, Op.Eq, cond.arg, SpanType.Random); - return coursespans(course.spans.filter((s) => s.straight != null), SpanType.Random).map((s) => intersection(s, p)); + return coursespans( + course.spans.filter((s) => s.straight != null), + SpanType.Random, + ).map((s) => intersection(s, p)); }, - 'remain_distance': (course, cond) => [remaining(course, cond, SpanType.Whenever)], - 'remain_distance_viewer_id': (course, cond) => [remaining(course, cond, SpanType.Whenever)], - 'rotation': (course, cond) => [{ start: 0, end: course.distance, type: must(opcompare(cond, course.turn)) }], - 'run_at_full_speed_random': (course) => [endafter(course, SpanType.Random)], - 'slope': (course, cond) => { + remain_distance: (course, cond) => [remaining(course, cond, SpanType.Whenever)], + remain_distance_viewer_id: (course, cond) => [remaining(course, cond, SpanType.Whenever)], + rotation: (course, cond) => [{ start: 0, end: course.distance, type: must(opcompare(cond, course.turn)) }], + run_at_full_speed_random: (course) => [endafter(course, SpanType.Random)], + slope: (course, cond) => { switch (cond.arg) { case 0: - return coursespans(racecourse.fillSlopes(course.slopes, course.distance).filter((s) => s.per === 0), SpanType.Whenever); + return coursespans( + racecourse.fillSlopes(course.slopes, course.distance).filter((s) => s.per === 0), + SpanType.Whenever, + ); case 1: - return coursespans(course.slopes.filter((s) => s.per > 0), SpanType.Whenever); + return coursespans( + course.slopes.filter((s) => s.per > 0), + SpanType.Whenever, + ); case 2: - return coursespans(course.slopes.filter((s) => s.per < 0), SpanType.Whenever); + return coursespans( + course.slopes.filter((s) => s.per < 0), + SpanType.Whenever, + ); default: - throw new Error("weird argument to slope condition"); + throw new Error('weird argument to slope condition'); } }, - 'straight_front_type': (course, cond) => coursespans(course.spans.filter((s) => s.straight === cond.arg), SpanType.Whenever), - 'straight_random': (course) => coursespans(course.spans.filter((s) => s.straight != null), SpanType.Random), - 'track_id': (course, cond) => [{ start: 0, end: course.distance, type: must(opcompare(cond, course.race_track_id)) }], - 'up_slope_random': (course) => coursespans(course.slopes.filter((s) => s.per > 0), SpanType.Random), - 'up_slope_random_later_half': (course) => { + straight_front_type: (course, cond) => + coursespans( + course.spans.filter((s) => s.straight === cond.arg), + SpanType.Whenever, + ), + straight_random: (course) => + coursespans( + course.spans.filter((s) => s.straight != null), + SpanType.Random, + ), + track_id: (course, cond) => [{ start: 0, end: course.distance, type: must(opcompare(cond, course.race_track_id)) }], + up_slope_random: (course) => + coursespans( + course.slopes.filter((s) => s.per > 0), + SpanType.Random, + ), + up_slope_random_later_half: (course) => { const p = { start: course.distance / 2, end: course.distance, type: SpanType.Random }; - return coursespans(course.slopes.filter((s) => s.per > 0), SpanType.Random).map((s) => intersection(s, p)); - } + return coursespans( + course.slopes.filter((s) => s.per > 0), + SpanType.Random, + ).map((s) => intersection(s, p)); + }, } as const; export function spans(course: racecourse.Course, cond: Condition): Span[] { diff --git a/zenno/src/routes/doc/fans/fans.ts b/zenno/src/routes/doc/fans/fans.ts index 7772948..3fe5f23 100644 --- a/zenno/src/routes/doc/fans/fans.ts +++ b/zenno/src/routes/doc/fans/fans.ts @@ -7,7 +7,7 @@ const placementPms = [1000, 400, 250, 150, 100, 90, 80, 70, 60, 50, 40, 30, 25, * @returns Base fans for the race and placement together */ export function baseFans(winBase: number, finishOrder: number): number { - return Math.round(winBase * placementPms[finishOrder - 1] / 1000); + return Math.round((winBase * placementPms[finishOrder - 1]) / 1000); } const margins = [ diff --git a/zenno/src/routes/race/skillzone/+page.svelte b/zenno/src/routes/race/skillzone/+page.svelte index 7e5b644..4041d8c 100644 --- a/zenno/src/routes/race/skillzone/+page.svelte +++ b/zenno/src/routes/race/skillzone/+page.svelte @@ -196,7 +196,7 @@ >   - green with an "Immediate" label. + green with an "Immediate" label.
  • Random conditions that are satisfied at a single point within their range, or several points in the case of e.g. phase_random==0 & accumulatetime>=5 skills. These conditions are mapped in
     
    - yellow with a "Random" label. + yellow with a "Random" label.
  • Conditions satisfied by the Uma's other skills triggering. These are mapped in
     
    - red with an "N/A" label. + red with an "N/A" label.
  • diff --git a/zenno/src/routes/race/skillzone/TopChart.svelte b/zenno/src/routes/race/skillzone/TopChart.svelte index 10d2ebc..1383b2b 100644 --- a/zenno/src/routes/race/skillzone/TopChart.svelte +++ b/zenno/src/routes/race/skillzone/TopChart.svelte @@ -50,4 +50,4 @@ ]); - +