diff --git a/calculator/calc.ts b/calculator/calc.ts index e80aea8..9556193 100644 --- a/calculator/calc.ts +++ b/calculator/calc.ts @@ -1,4 +1,4 @@ -import { activities, Activity, Attribute, attributes, BenchmarkPerformance, findNearestPoints, Gender, getActivityAttribute, kgToLb, Levels, msToTime, StandardsItem } from "./util"; +import { Activity, Attribute, attributes, BenchmarkPerformance, findNearestPoints, Gender, getActivityAttribute, getAttributeActivities, kgToLb, Levels, msToTime, Player, StandardsItem } from "./util"; import rawStandards from "./standards.json" assert { type: "json" } const standards = rawStandards as StandardsItem[]; @@ -35,10 +35,24 @@ export const calcAttributeLevel = ( attribute: Attribute, benchmarkPerformances: BenchmarkPerformance[], ): number => { + // must be activities of the given attribute if (benchmarkPerformances.some((bp) => getActivityAttribute(bp.activity) !== attribute)) { throw new Error("Wrong benchmark performance attribute"); } + // must perform all of an attributes activities + for (const activity of getAttributeActivities(attribute)) { + const benchmarkActivites = benchmarkPerformances.map((bp) => bp.activity); + if (!benchmarkActivites.includes(activity)) + return 0; + } + + // must have all benchmarks using a valid performance value (> 0) + for (const benchmarkPerformance of benchmarkPerformances) { + if (benchmarkPerformance.performance <= 0) + return 0 + } + const benchmarkLevels = benchmarkPerformances.map((abp) => calcBenchmarkLevel(abp.performance, abp.levels), ); @@ -51,16 +65,22 @@ export const calcAttributeLevel = ( }; export const calcAttributeLevels = ( + player: Player, benchmarkPerformances: BenchmarkPerformance[], ): Record => { const attrLevels: Record = { - [attributes.STRENGTH]: 1, - [attributes.POWER]: 1, - [attributes.ENDURANCE]: 1, - [attributes.SPEED]: 1, - [attributes.AGILITY]: 1, + [attributes.STRENGTH]: 0, + [attributes.POWER]: 0, + [attributes.ENDURANCE]: 0, + // [attributes.SPEED]: 0, + [attributes.AGILITY]: 0, } as const; + for (const value of Object.values(player)) { + if (!value) + return attrLevels; + } + (Object.values(attributes) as Attribute[]).forEach((attribute) => { const attrBenchmarkPerformances = benchmarkPerformances.filter( (bp) => getActivityAttribute(bp.activity) === attribute, @@ -71,8 +91,18 @@ export const calcAttributeLevels = ( return attrLevels; }; -export const calcPlayerLevel = (benchmarkPerformances: BenchmarkPerformance[]): number => { - const attrLevels = calcAttributeLevels(benchmarkPerformances); +export const calcPlayerLevel = (player: Player, benchmarkPerformances: BenchmarkPerformance[]): number => { + for (const value of Object.values(player)) { + if (!value) + return 0; + } + + const attrLevels = calcAttributeLevels(player, benchmarkPerformances); + for (const level of Object.values(attrLevels)) { + if (!level) + return 0; + } + const attrLevelsSum = Object.values(attrLevels).reduce((sum, lvl) => sum + lvl, 0); return Math.round(attrLevelsSum / Object.keys(attributes).length); }; @@ -80,31 +110,31 @@ export const calcPlayerLevel = (benchmarkPerformances: BenchmarkPerformance[]): // ------------------------------------------------------------------------------------------------- // Presentation helpers // ------------------------------------------------------------------------------------------------- -export const formatLevelValues = ( - levels: Record, - activity: Activity, -): Record => { - const timedActivities: Activity[] = [activities.RUN, activities.DASH, activities.CONE_DRILL]; - - const formattedLevels: Record = {}; - for (const level in levels) { - const lvl = +level as number; - let displayValue = ""; - if (timedActivities.includes(activity)) { - displayValue = msToTime(levels[lvl]); - } else if (activity === activities.TREADMILL_DASH) { - displayValue = `${(levels[lvl] * 1000).toFixed(2)} m/s`; - } else if (activity === activities.BROAD_JUMP) { - displayValue = `${(levels[lvl] * 0.0328084).toFixed(2)} ft.`; - } else { - displayValue = kgToLb(levels[lvl]).toFixed(0); - } - - formattedLevels[lvl] = displayValue; - } - - return formattedLevels; -}; +// export const formatLevelValues = ( +// levels: Record, +// activity: Activity, +// ): Record => { +// const timedActivities: Activity[] = [activities.RUN, activities.DASH, activities.CONE_DRILL]; +// +// const formattedLevels: Record = {}; +// for (const level in levels) { +// const lvl = +level as number; +// let displayValue = ""; +// if (timedActivities.includes(activity)) { +// displayValue = msToTime(levels[lvl]); +// } else if (activity === activities.TREADMILL_DASH) { +// displayValue = `${(levels[lvl] * 1000).toFixed(2)} m/s`; +// } else if (activity === activities.BROAD_JUMP) { +// displayValue = `${(levels[lvl] * 0.0328084).toFixed(2)} ft.`; +// } else { +// displayValue = kgToLb(levels[lvl]).toFixed(0); +// } +// +// formattedLevels[lvl] = displayValue; +// } +// +// return formattedLevels; +// }; // ------------------------------------------------------------------------------------------------- // Level utilities diff --git a/calculator/util.ts b/calculator/util.ts index fdbb598..f90a38a 100644 --- a/calculator/util.ts +++ b/calculator/util.ts @@ -1,6 +1,26 @@ +// ------------------------------------------------------------------------------------------------- +// Attributes +// ------------------------------------------------------------------------------------------------- + +export const attributes = Object.freeze({ + STRENGTH: "Strength", + POWER: "Power", + ENDURANCE: "Endurance", + // SPEED: "Speed", + AGILITY: "Agility", +} as const); +export type Attribute = typeof attributes[keyof typeof attributes]; + // ------------------------------------------------------------------------------------------------- // Enumerations & basic value objects // ------------------------------------------------------------------------------------------------- + +export interface Player { + age: number, + weightKG: number, + gender: Gender +} + export const activities = Object.freeze({ BACK_SQUAT: "Back Squat", DEADLIFT: "Deadlift", @@ -14,42 +34,39 @@ export const activities = Object.freeze({ export type Activity = typeof activities[keyof typeof activities]; +const activityAttrMap: Record = { + [activities.RUN]: attributes.ENDURANCE, + // [activities.TREADMILL_DASH]: attributes.SPEED, + // [activities.DASH]: attributes.SPEED, + [activities.DEADLIFT]: attributes.STRENGTH, + [activities.BACK_SQUAT]: attributes.STRENGTH, + [activities.BENCH_PRESS]: attributes.STRENGTH, + [activities.BROAD_JUMP]: attributes.POWER, + [activities.CONE_DRILL]: attributes.AGILITY, +} as const; + +export const getActivityAttribute = (activity: Activity): Attribute => { + const attribute = activityAttrMap[activity]; + if (!attribute) throw new Error(`${activity} does not have an associated attribute`); + return attribute; +}; + +export const getAttributeActivities = (attribute: Attribute): Activity[] => { + const activities: Activity[] = []; + for (const activity in activityAttrMap) { + if (activityAttrMap[activity] === attribute) { + activities.push(activity as Activity); + } + } + return activities; +} + export const genders = Object.freeze({ MALE: "male", FEMALE: "female", } as const); export type Gender = typeof genders[keyof typeof genders]; -// ------------------------------------------------------------------------------------------------- -// Attributes -// ------------------------------------------------------------------------------------------------- - -export const attributes = Object.freeze({ - STRENGTH: "Strength", - POWER: "Power", - ENDURANCE: "Endurance", - SPEED: "Speed", - AGILITY: "Agility", -} as const); -export type Attribute = typeof attributes[keyof typeof attributes]; - -export const getActivityAttribute = (activity: Activity): Attribute => { - const activityAttrMap: Record = { - [activities.RUN]: attributes.ENDURANCE, - [activities.TREADMILL_DASH]: attributes.SPEED, - [activities.DASH]: attributes.SPEED, - [activities.DEADLIFT]: attributes.STRENGTH, - [activities.BACK_SQUAT]: attributes.STRENGTH, - [activities.BENCH_PRESS]: attributes.STRENGTH, - [activities.BROAD_JUMP]: attributes.POWER, - [activities.CONE_DRILL]: attributes.AGILITY, - } as const; - - const attribute = activityAttrMap[activity]; - if (!attribute) throw new Error(`${activity} does not have an associated attribute`); - return attribute; -}; - // ------------------------------------------------------------------------------------------------- // Data model interfaces // ------------------------------------------------------------------------------------------------- diff --git a/main.ts b/main.ts index f670b2d..e897274 100644 --- a/main.ts +++ b/main.ts @@ -1,15 +1,11 @@ import { calcAttributeLevels, calcPlayerLevel, computeLevels } from "./calculator/calc.ts"; -import { Activity, BenchmarkPerformance, Gender } from "./calculator/util.ts"; +import { Activity, BenchmarkPerformance, Player } from "./calculator/util.ts"; import { productRecords } from "./commerce/product-records.ts"; import { Printful } from "./commerce/printful.ts" import { Webflow } from "./commerce/webflow.ts"; interface CalcRequest { - player: { - age: number, - weightKG: number, - gender: Gender - } + player: Player, performances: { activity: Activity, performance: number, @@ -59,8 +55,8 @@ const server = Bun.serve({ return ClientResponse.json({ levels: { - attributes: calcAttributeLevels(computedPerformances), - player: calcPlayerLevel(computedPerformances) + attributes: calcAttributeLevels(player, computedPerformances), + player: calcPlayerLevel(player, computedPerformances) } }) }