Calculator updates
This commit is contained in:
+63
-33
@@ -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" }
|
import rawStandards from "./standards.json" assert { type: "json" }
|
||||||
|
|
||||||
const standards = rawStandards as StandardsItem[];
|
const standards = rawStandards as StandardsItem[];
|
||||||
@@ -35,10 +35,24 @@ export const calcAttributeLevel = (
|
|||||||
attribute: Attribute,
|
attribute: Attribute,
|
||||||
benchmarkPerformances: BenchmarkPerformance[],
|
benchmarkPerformances: BenchmarkPerformance[],
|
||||||
): number => {
|
): number => {
|
||||||
|
// must be activities of the given attribute
|
||||||
if (benchmarkPerformances.some((bp) => getActivityAttribute(bp.activity) !== attribute)) {
|
if (benchmarkPerformances.some((bp) => getActivityAttribute(bp.activity) !== attribute)) {
|
||||||
throw new Error("Wrong benchmark performance 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) =>
|
const benchmarkLevels = benchmarkPerformances.map((abp) =>
|
||||||
calcBenchmarkLevel(abp.performance, abp.levels),
|
calcBenchmarkLevel(abp.performance, abp.levels),
|
||||||
);
|
);
|
||||||
@@ -51,16 +65,22 @@ export const calcAttributeLevel = (
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const calcAttributeLevels = (
|
export const calcAttributeLevels = (
|
||||||
|
player: Player,
|
||||||
benchmarkPerformances: BenchmarkPerformance[],
|
benchmarkPerformances: BenchmarkPerformance[],
|
||||||
): Record<Attribute, number> => {
|
): Record<Attribute, number> => {
|
||||||
const attrLevels: Record<Attribute, number> = {
|
const attrLevels: Record<Attribute, number> = {
|
||||||
[attributes.STRENGTH]: 1,
|
[attributes.STRENGTH]: 0,
|
||||||
[attributes.POWER]: 1,
|
[attributes.POWER]: 0,
|
||||||
[attributes.ENDURANCE]: 1,
|
[attributes.ENDURANCE]: 0,
|
||||||
[attributes.SPEED]: 1,
|
// [attributes.SPEED]: 0,
|
||||||
[attributes.AGILITY]: 1,
|
[attributes.AGILITY]: 0,
|
||||||
} as const;
|
} as const;
|
||||||
|
|
||||||
|
for (const value of Object.values(player)) {
|
||||||
|
if (!value)
|
||||||
|
return attrLevels;
|
||||||
|
}
|
||||||
|
|
||||||
(Object.values(attributes) as Attribute[]).forEach((attribute) => {
|
(Object.values(attributes) as Attribute[]).forEach((attribute) => {
|
||||||
const attrBenchmarkPerformances = benchmarkPerformances.filter(
|
const attrBenchmarkPerformances = benchmarkPerformances.filter(
|
||||||
(bp) => getActivityAttribute(bp.activity) === attribute,
|
(bp) => getActivityAttribute(bp.activity) === attribute,
|
||||||
@@ -71,8 +91,18 @@ export const calcAttributeLevels = (
|
|||||||
return attrLevels;
|
return attrLevels;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const calcPlayerLevel = (benchmarkPerformances: BenchmarkPerformance[]): number => {
|
export const calcPlayerLevel = (player: Player, benchmarkPerformances: BenchmarkPerformance[]): number => {
|
||||||
const attrLevels = calcAttributeLevels(benchmarkPerformances);
|
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);
|
const attrLevelsSum = Object.values(attrLevels).reduce((sum, lvl) => sum + lvl, 0);
|
||||||
return Math.round(attrLevelsSum / Object.keys(attributes).length);
|
return Math.round(attrLevelsSum / Object.keys(attributes).length);
|
||||||
};
|
};
|
||||||
@@ -80,31 +110,31 @@ export const calcPlayerLevel = (benchmarkPerformances: BenchmarkPerformance[]):
|
|||||||
// -------------------------------------------------------------------------------------------------
|
// -------------------------------------------------------------------------------------------------
|
||||||
// Presentation helpers
|
// Presentation helpers
|
||||||
// -------------------------------------------------------------------------------------------------
|
// -------------------------------------------------------------------------------------------------
|
||||||
export const formatLevelValues = (
|
// export const formatLevelValues = (
|
||||||
levels: Record<number, number>,
|
// levels: Record<number, number>,
|
||||||
activity: Activity,
|
// activity: Activity,
|
||||||
): Record<number, string> => {
|
// ): Record<number, string> => {
|
||||||
const timedActivities: Activity[] = [activities.RUN, activities.DASH, activities.CONE_DRILL];
|
// const timedActivities: Activity[] = [activities.RUN, activities.DASH, activities.CONE_DRILL];
|
||||||
|
//
|
||||||
const formattedLevels: Record<number, string> = {};
|
// const formattedLevels: Record<number, string> = {};
|
||||||
for (const level in levels) {
|
// for (const level in levels) {
|
||||||
const lvl = +level as number;
|
// const lvl = +level as number;
|
||||||
let displayValue = "";
|
// let displayValue = "";
|
||||||
if (timedActivities.includes(activity)) {
|
// if (timedActivities.includes(activity)) {
|
||||||
displayValue = msToTime(levels[lvl]);
|
// displayValue = msToTime(levels[lvl]);
|
||||||
} else if (activity === activities.TREADMILL_DASH) {
|
// } else if (activity === activities.TREADMILL_DASH) {
|
||||||
displayValue = `${(levels[lvl] * 1000).toFixed(2)} m/s`;
|
// displayValue = `${(levels[lvl] * 1000).toFixed(2)} m/s`;
|
||||||
} else if (activity === activities.BROAD_JUMP) {
|
// } else if (activity === activities.BROAD_JUMP) {
|
||||||
displayValue = `${(levels[lvl] * 0.0328084).toFixed(2)} ft.`;
|
// displayValue = `${(levels[lvl] * 0.0328084).toFixed(2)} ft.`;
|
||||||
} else {
|
// } else {
|
||||||
displayValue = kgToLb(levels[lvl]).toFixed(0);
|
// displayValue = kgToLb(levels[lvl]).toFixed(0);
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
formattedLevels[lvl] = displayValue;
|
// formattedLevels[lvl] = displayValue;
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
return formattedLevels;
|
// return formattedLevels;
|
||||||
};
|
// };
|
||||||
|
|
||||||
// -------------------------------------------------------------------------------------------------
|
// -------------------------------------------------------------------------------------------------
|
||||||
// Level utilities
|
// Level utilities
|
||||||
|
|||||||
+41
-24
@@ -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
|
// Enumerations & basic value objects
|
||||||
// -------------------------------------------------------------------------------------------------
|
// -------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
export interface Player {
|
||||||
|
age: number,
|
||||||
|
weightKG: number,
|
||||||
|
gender: Gender
|
||||||
|
}
|
||||||
|
|
||||||
export const activities = Object.freeze({
|
export const activities = Object.freeze({
|
||||||
BACK_SQUAT: "Back Squat",
|
BACK_SQUAT: "Back Squat",
|
||||||
DEADLIFT: "Deadlift",
|
DEADLIFT: "Deadlift",
|
||||||
@@ -14,42 +34,39 @@ export const activities = Object.freeze({
|
|||||||
|
|
||||||
export type Activity = typeof activities[keyof typeof activities];
|
export type Activity = typeof activities[keyof typeof activities];
|
||||||
|
|
||||||
export const genders = Object.freeze({
|
const activityAttrMap: Record<Activity, Attribute> = {
|
||||||
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<Activity, Attribute> = {
|
|
||||||
[activities.RUN]: attributes.ENDURANCE,
|
[activities.RUN]: attributes.ENDURANCE,
|
||||||
[activities.TREADMILL_DASH]: attributes.SPEED,
|
// [activities.TREADMILL_DASH]: attributes.SPEED,
|
||||||
[activities.DASH]: attributes.SPEED,
|
// [activities.DASH]: attributes.SPEED,
|
||||||
[activities.DEADLIFT]: attributes.STRENGTH,
|
[activities.DEADLIFT]: attributes.STRENGTH,
|
||||||
[activities.BACK_SQUAT]: attributes.STRENGTH,
|
[activities.BACK_SQUAT]: attributes.STRENGTH,
|
||||||
[activities.BENCH_PRESS]: attributes.STRENGTH,
|
[activities.BENCH_PRESS]: attributes.STRENGTH,
|
||||||
[activities.BROAD_JUMP]: attributes.POWER,
|
[activities.BROAD_JUMP]: attributes.POWER,
|
||||||
[activities.CONE_DRILL]: attributes.AGILITY,
|
[activities.CONE_DRILL]: attributes.AGILITY,
|
||||||
} as const;
|
} as const;
|
||||||
|
|
||||||
|
export const getActivityAttribute = (activity: Activity): Attribute => {
|
||||||
const attribute = activityAttrMap[activity];
|
const attribute = activityAttrMap[activity];
|
||||||
if (!attribute) throw new Error(`${activity} does not have an associated attribute`);
|
if (!attribute) throw new Error(`${activity} does not have an associated attribute`);
|
||||||
return 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];
|
||||||
|
|
||||||
// -------------------------------------------------------------------------------------------------
|
// -------------------------------------------------------------------------------------------------
|
||||||
// Data model interfaces
|
// Data model interfaces
|
||||||
// -------------------------------------------------------------------------------------------------
|
// -------------------------------------------------------------------------------------------------
|
||||||
|
|||||||
@@ -1,15 +1,11 @@
|
|||||||
import { calcAttributeLevels, calcPlayerLevel, computeLevels } from "./calculator/calc.ts";
|
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 { productRecords } from "./commerce/product-records.ts";
|
||||||
import { Printful } from "./commerce/printful.ts"
|
import { Printful } from "./commerce/printful.ts"
|
||||||
import { Webflow } from "./commerce/webflow.ts";
|
import { Webflow } from "./commerce/webflow.ts";
|
||||||
|
|
||||||
interface CalcRequest {
|
interface CalcRequest {
|
||||||
player: {
|
player: Player,
|
||||||
age: number,
|
|
||||||
weightKG: number,
|
|
||||||
gender: Gender
|
|
||||||
}
|
|
||||||
performances: {
|
performances: {
|
||||||
activity: Activity,
|
activity: Activity,
|
||||||
performance: number,
|
performance: number,
|
||||||
@@ -59,8 +55,8 @@ const server = Bun.serve({
|
|||||||
|
|
||||||
return ClientResponse.json({
|
return ClientResponse.json({
|
||||||
levels: {
|
levels: {
|
||||||
attributes: calcAttributeLevels(computedPerformances),
|
attributes: calcAttributeLevels(player, computedPerformances),
|
||||||
player: calcPlayerLevel(computedPerformances)
|
player: calcPlayerLevel(player, computedPerformances)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user