From f134c4cf97efc07395e147f254912d75c726bbad Mon Sep 17 00:00:00 2001 From: Rene Saarsoo Date: Fri, 20 Nov 2020 13:21:54 +0200 Subject: [PATCH] Initial XP calculations --- src/stats/xp.test.ts | 80 ++++++++++++++++++++++++++++++++++++++++++++ src/stats/xp.ts | 20 +++++++++++ 2 files changed, 100 insertions(+) create mode 100644 src/stats/xp.test.ts create mode 100644 src/stats/xp.ts diff --git a/src/stats/xp.test.ts b/src/stats/xp.test.ts new file mode 100644 index 0000000..3c4819b --- /dev/null +++ b/src/stats/xp.test.ts @@ -0,0 +1,80 @@ +import { xp } from "./xp"; +import { Interval } from "../ast"; +import { Duration } from "../Duration"; +import { ConstantIntensity, RangeIntensity } from "../Intensity"; + +describe("xp()", () => { + describe("ConstantIntensity interval", () => { + const createTestInterval = (seconds: number): Interval => ({ + type: "Interval", + duration: new Duration(seconds), + intensity: new ConstantIntensity(100), + comments: [], + }); + + [ + [1, 0], + [2, 0], + [5, 0], + [10, 1], + [15, 2], + [30, 5], + [45, 8], + [50, 8], + [55, 9], + [56, 10], + [57, 10], + [58, 10], + [59, 10], + [60, 10], + [61, 10], + [62, 11], + [63, 11], + [64, 11], + [65, 11], + [1, 0], + [1, 0], + ].forEach(([seconds, expectedXp]) => { + it(`${seconds}s produces ${expectedXp} XP`, () => { + expect(xp([createTestInterval(seconds)])).toEqual(expectedXp); + }); + }); + }); + + describe("RangeIntensity interval", () => { + const createTestInterval = (seconds: number): Interval => ({ + type: "Warmup", + duration: new Duration(seconds), + intensity: new RangeIntensity(50, 75), + comments: [], + }); + + [ + [55, 5], + [56, 5], + [57, 5], + [58, 5], + [59, 5], + [60, 6], + [61, 6], + [5 * 60, 30], + ].forEach(([seconds, expectedXp]) => { + it(`${seconds}s produces ${expectedXp} XP`, () => { + expect(xp([createTestInterval(seconds)])).toEqual(expectedXp); + }); + }); + }); + + // Intervals + // + // 4 x 15s = 1min --> 11 XP + // 4 x 30s = 2min --> 23 XP + // 4 x 60s = 4min --> 47 XP + + // Other XP + // + // 20 XP per km + + // 221 km + // 6:42 of intervals +}); diff --git a/src/stats/xp.ts b/src/stats/xp.ts new file mode 100644 index 0000000..0cff315 --- /dev/null +++ b/src/stats/xp.ts @@ -0,0 +1,20 @@ +import { Interval } from "../ast"; +import { sum } from "ramda"; +import { RangeIntensity, ConstantIntensity, FreeIntensity } from "../Intensity"; + +const intervalXp = ({ intensity, duration }: Interval): number => { + if (intensity instanceof RangeIntensity) { + // 6 XP per minute (1XP for every 10 seconds) + return Math.floor(duration.seconds / 10); + } else if (intensity instanceof ConstantIntensity) { + // 10 XP per minute (1XP for every 5.56 seconds) + return Math.floor(duration.seconds / 5.56); + } else if (intensity instanceof FreeIntensity) { + // 5 XP per minute + return Math.round(duration.seconds / 60) * 5; + } else { + throw new Error("Unknown type of intensity"); + } +}; + +export const xp = (intervals: Interval[]): number => sum(intervals.map(intervalXp));