diff --git a/zenno/src/lib/alpha123Umalator.ts b/zenno/src/lib/alpha123Umalator.ts
new file mode 100644
index 0000000..0cf4439
--- /dev/null
+++ b/zenno/src/lib/alpha123Umalator.ts
@@ -0,0 +1,78 @@
+import { AptitudeLevel, Mood, RunningStyle } from './race';
+import type { Runner } from './runner';
+
+const aptitude_map = {
+ G: AptitudeLevel.G,
+ F: AptitudeLevel.F,
+ E: AptitudeLevel.E,
+ D: AptitudeLevel.D,
+ C: AptitudeLevel.C,
+ B: AptitudeLevel.B,
+ A: AptitudeLevel.A,
+ S: AptitudeLevel.S,
+} as const;
+type AptitudeString = keyof typeof aptitude_map;
+
+const style_map = {
+ Nige: RunningStyle.FrontRunner,
+ Sentou: RunningStyle.PaceChaser,
+ Sasi: RunningStyle.LateSurger,
+ Oikomi: RunningStyle.EndCloser,
+ Oonige: RunningStyle.GreatEscape,
+} as const;
+
+export interface ImportUma {
+ outfitId: string;
+ starCount: number;
+ speed: number;
+ stamina: number;
+ power: number;
+ guts: number;
+ wisdom: number;
+ strategy: keyof typeof style_map;
+ distanceAptitude: AptitudeString;
+ surfaceAptitude: AptitudeString;
+ strategyAptitude: AptitudeString;
+ aptitudes: [
+ AptitudeString,
+ AptitudeString,
+ AptitudeString,
+ AptitudeString,
+ AptitudeString,
+ AptitudeString,
+ AptitudeString,
+ AptitudeString,
+ AptitudeString,
+ AptitudeString,
+ ];
+ skills: string[];
+ uniqueLv: number;
+ mood: Mood;
+ popularity: number;
+}
+
+export function load(obj: ImportUma, name?: string): Runner {
+ return {
+ name: name ?? '',
+ chara_card_id: obj.outfitId !== '' ? parseInt(obj.outfitId) : 0,
+ style: style_map[obj.strategy],
+ mood: obj.mood,
+ speed: obj.speed,
+ stamina: obj.stamina,
+ power: obj.power,
+ guts: obj.guts,
+ wit: obj.wisdom,
+ sprint: aptitude_map[obj.aptitudes[0]],
+ mile: aptitude_map[obj.aptitudes[1]],
+ medium: aptitude_map[obj.aptitudes[2]],
+ long: aptitude_map[obj.aptitudes[3]],
+ front: aptitude_map[obj.aptitudes[4]],
+ pace: aptitude_map[obj.aptitudes[5]],
+ late: aptitude_map[obj.aptitudes[6]],
+ end: aptitude_map[obj.aptitudes[7]],
+ turf: aptitude_map[obj.aptitudes[8]],
+ dirt: aptitude_map[obj.aptitudes[9]],
+ skills: obj.skills.map((s) => parseInt(s)),
+ unique_level: obj.uniqueLv,
+ };
+}
diff --git a/zenno/src/lib/race.ts b/zenno/src/lib/race.ts
index f6d2659..8d72bdf 100644
--- a/zenno/src/lib/race.ts
+++ b/zenno/src/lib/race.ts
@@ -1,8 +1,6 @@
// Umamusume race mechanics adapted from KuromiAK's doc:
// https://docs.google.com/document/d/15VzW9W2tXBBTibBRbZ8IVpW6HaMX8H0RP03kq6Az7Xg/edit?usp=sharing
-import type { Uma } from './data/uma';
-
/**
* Fundamental stats of umas.
*/
@@ -19,70 +17,6 @@ export enum Stat {
*/
export const StatList = [Stat.Speed, Stat.Stamina, Stat.Power, Stat.Guts, Stat.Wit] as const;
-/**
- * Race runner, i.e. a trained horse.
- */
-export interface Runner {
- name: string;
-
- chara_card_id: number;
- style: RunningStyle;
- mood: Mood;
-
- speed: number;
- stamina: number;
- power: number;
- guts: number;
- wit: number;
-
- sprint: AptitudeLevel;
- mile: AptitudeLevel;
- medium: AptitudeLevel;
- long: AptitudeLevel;
- front: AptitudeLevel;
- pace: AptitudeLevel;
- late: AptitudeLevel;
- end: AptitudeLevel;
- turf: AptitudeLevel;
- dirt: AptitudeLevel;
-
- skills: number[];
- unique_level: number;
-}
-
-/**
- * Create a new runner with baseline stats.
- * @param name Name to apply to the runner
- * @param base_uma Character card (trainee or uma) to use for aptitudes; otherwise all aptitudes are A
- * @returns Baseline runner
- */
-export function new_runner(name?: string, base_uma?: Uma): Runner {
- return {
- name: name ?? '',
- chara_card_id: base_uma?.chara_card_id ?? 0,
- // TODO(zeph): default running style
- style: RunningStyle.FrontRunner,
- mood: Mood.Normal,
- speed: 1200,
- stamina: 1200,
- power: 1200,
- guts: 1200,
- wit: 1200,
- sprint: base_uma?.sprint ?? AptitudeLevel.A,
- mile: base_uma?.mile ?? AptitudeLevel.A,
- medium: base_uma?.medium ?? AptitudeLevel.A,
- long: base_uma?.long ?? AptitudeLevel.A,
- front: base_uma?.front ?? AptitudeLevel.A,
- pace: base_uma?.pace ?? AptitudeLevel.A,
- late: base_uma?.late ?? AptitudeLevel.A,
- end: base_uma?.end ?? AptitudeLevel.A,
- turf: base_uma?.turf ?? AptitudeLevel.A,
- dirt: base_uma?.dirt ?? AptitudeLevel.A,
- skills: base_uma != null ? [base_uma.unique] : [],
- unique_level: 4,
- };
-}
-
/**
* Mood levels.
*/
@@ -145,106 +79,6 @@ export enum Phase {
LateRace,
}
-namespace Alpha123Umalator {
- const aptitude_map = {
- G: AptitudeLevel.G,
- F: AptitudeLevel.F,
- E: AptitudeLevel.E,
- D: AptitudeLevel.D,
- C: AptitudeLevel.C,
- B: AptitudeLevel.B,
- A: AptitudeLevel.A,
- S: AptitudeLevel.S,
- } as const;
- type AptitudeString = keyof typeof aptitude_map;
-
- const style_map = {
- Nige: RunningStyle.FrontRunner,
- Sentou: RunningStyle.PaceChaser,
- Sasi: RunningStyle.LateSurger,
- Oikomi: RunningStyle.EndCloser,
- Oonige: RunningStyle.GreatEscape,
- } as const;
-
- export interface ImportUma {
- outfitId: string;
- starCount: number;
- speed: number;
- stamina: number;
- power: number;
- guts: number;
- wisdom: number;
- strategy: keyof typeof style_map;
- distanceAptitude: AptitudeString;
- surfaceAptitude: AptitudeString;
- strategyAptitude: AptitudeString;
- aptitudes: [
- AptitudeString,
- AptitudeString,
- AptitudeString,
- AptitudeString,
- AptitudeString,
- AptitudeString,
- AptitudeString,
- AptitudeString,
- AptitudeString,
- AptitudeString,
- ];
- skills: string[];
- uniqueLv: number;
- mood: Mood;
- popularity: number;
- }
-
- export function load(obj: ImportUma, name?: string): Runner {
- return {
- name: name ?? '',
- chara_card_id: obj.outfitId !== '' ? parseInt(obj.outfitId) : 0,
- style: style_map[obj.strategy],
- mood: obj.mood,
- speed: obj.speed,
- stamina: obj.stamina,
- power: obj.power,
- guts: obj.guts,
- wit: obj.wisdom,
- sprint: aptitude_map[obj.aptitudes[0]],
- mile: aptitude_map[obj.aptitudes[1]],
- medium: aptitude_map[obj.aptitudes[2]],
- long: aptitude_map[obj.aptitudes[3]],
- front: aptitude_map[obj.aptitudes[4]],
- pace: aptitude_map[obj.aptitudes[5]],
- late: aptitude_map[obj.aptitudes[6]],
- end: aptitude_map[obj.aptitudes[7]],
- turf: aptitude_map[obj.aptitudes[8]],
- dirt: aptitude_map[obj.aptitudes[9]],
- skills: obj.skills.map((s) => parseInt(s)),
- unique_level: obj.uniqueLv,
- };
- }
-}
-
-/**
- * Import a runner from an external source.
- * @param obj Decoded object to import
- * @param name Name or memo to apply to the runner
- * @returns Imported runner, or null if import was not possible
- */
-export function import_runner(obj: any, name?: string): Runner | null {
- // TODO(zeph): check for keys that identify the uma source
- if (typeof obj === 'object') {
- try {
- const r = Alpha123Umalator.load(obj as Alpha123Umalator.ImportUma, name);
- // TODO(zeph): validate?
- return r;
- } catch (exc) {
- console.warn('failed to import', obj, 'as alpha123 umalator:', exc);
- return null;
- }
- }
- console.warn('no guess on how to import', obj);
- return null;
-}
-
function baseSpeed(raceLen: number): number {
return 20 - (raceLen - 2000) / 1000;
}
diff --git a/zenno/src/lib/runner.ts b/zenno/src/lib/runner.ts
new file mode 100644
index 0000000..8555974
--- /dev/null
+++ b/zenno/src/lib/runner.ts
@@ -0,0 +1,89 @@
+import type { Uma } from './data/uma';
+import { AptitudeLevel, Mood, RunningStyle } from './race';
+import * as alpha123Umalator from './alpha123Umalator';
+
+/**
+ * Race runner, i.e. a trained horse.
+ */
+export interface Runner {
+ name: string;
+
+ chara_card_id: number;
+ style: RunningStyle;
+ mood: Mood;
+
+ speed: number;
+ stamina: number;
+ power: number;
+ guts: number;
+ wit: number;
+
+ sprint: AptitudeLevel;
+ mile: AptitudeLevel;
+ medium: AptitudeLevel;
+ long: AptitudeLevel;
+ front: AptitudeLevel;
+ pace: AptitudeLevel;
+ late: AptitudeLevel;
+ end: AptitudeLevel;
+ turf: AptitudeLevel;
+ dirt: AptitudeLevel;
+
+ skills: number[];
+ unique_level: number;
+}
+
+/**
+ * Create a new runner with baseline stats.
+ * @param name Name to apply to the runner
+ * @param base_uma Character card (trainee or uma) to use for aptitudes; otherwise all aptitudes are A
+ * @returns Baseline runner
+ */
+export function new_runner(name?: string, base_uma?: Uma): Runner {
+ return {
+ name: name ?? '',
+ chara_card_id: base_uma?.chara_card_id ?? 0,
+ // TODO(zeph): default running style
+ style: RunningStyle.FrontRunner,
+ mood: Mood.Normal,
+ speed: 1200,
+ stamina: 1200,
+ power: 1200,
+ guts: 1200,
+ wit: 1200,
+ sprint: base_uma?.sprint ?? AptitudeLevel.A,
+ mile: base_uma?.mile ?? AptitudeLevel.A,
+ medium: base_uma?.medium ?? AptitudeLevel.A,
+ long: base_uma?.long ?? AptitudeLevel.A,
+ front: base_uma?.front ?? AptitudeLevel.A,
+ pace: base_uma?.pace ?? AptitudeLevel.A,
+ late: base_uma?.late ?? AptitudeLevel.A,
+ end: base_uma?.end ?? AptitudeLevel.A,
+ turf: base_uma?.turf ?? AptitudeLevel.A,
+ dirt: base_uma?.dirt ?? AptitudeLevel.A,
+ skills: base_uma != null ? [base_uma.unique] : [],
+ unique_level: 4,
+ };
+}
+
+/**
+ * Import a runner from an external source.
+ * @param obj Decoded object to import
+ * @param name Name or memo to apply to the runner
+ * @returns Imported runner, or null if import was not possible
+ */
+export function import_runner(obj: unknown, name?: string): Runner | null {
+ // TODO(zeph): check for keys that identify the uma source
+ if (typeof obj === 'object') {
+ try {
+ const r = alpha123Umalator.load(obj as alpha123Umalator.ImportUma, name);
+ // TODO(zeph): validate?
+ return r;
+ } catch (exc) {
+ console.warn('failed to import', obj, 'as alpha123 umalator:', exc);
+ return null;
+ }
+ }
+ console.warn('no guess on how to import', obj);
+ return null;
+}
diff --git a/zenno/src/routes/mspeed/+page.svelte b/zenno/src/routes/mspeed/+page.svelte
index 7088b49..f936dee 100644
--- a/zenno/src/routes/mspeed/+page.svelte
+++ b/zenno/src/routes/mspeed/+page.svelte
@@ -131,14 +131,14 @@