More
This commit is contained in:
+55
-20
@@ -1,4 +1,4 @@
|
|||||||
import { Activity, Attribute, attributes, ActivityPerformance, getActivityAttribute, getAttributeActivities, Player, StandardsMap, Levels, Standard, Metrics, Gender } from "./util";
|
import { Activity, Attribute, Player, Gender, ActivityPerformance } from "./util";
|
||||||
|
|
||||||
// SOURCES
|
// SOURCES
|
||||||
// Squat, Bench, Dead Lift:
|
// Squat, Bench, Dead Lift:
|
||||||
@@ -20,6 +20,26 @@ type LevelCalculatorOutput = {
|
|||||||
attributes: Record<Attribute, number>
|
attributes: Record<Attribute, number>
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type Levels = Record<string, number>
|
||||||
|
|
||||||
|
export interface Metrics {
|
||||||
|
age: number;
|
||||||
|
weight: number;
|
||||||
|
gender: Gender;
|
||||||
|
};
|
||||||
|
|
||||||
|
export interface Standard {
|
||||||
|
metrics: Metrics;
|
||||||
|
levels: Levels;
|
||||||
|
}
|
||||||
|
|
||||||
|
export type ActivityStandards = Record<Activity, {
|
||||||
|
metadata: {
|
||||||
|
attribute: Attribute
|
||||||
|
},
|
||||||
|
standards: Standard[]
|
||||||
|
}>;
|
||||||
|
|
||||||
export class LevelCalculator {
|
export class LevelCalculator {
|
||||||
cfg: Required<LevelCalculatorConfig>;
|
cfg: Required<LevelCalculatorConfig>;
|
||||||
standards: Standards;
|
standards: Standards;
|
||||||
@@ -40,10 +60,10 @@ export class LevelCalculator {
|
|||||||
const levels: LevelCalculatorOutput = {
|
const levels: LevelCalculatorOutput = {
|
||||||
player: 0,
|
player: 0,
|
||||||
attributes: {
|
attributes: {
|
||||||
[attributes.STRENGTH]: 0,
|
[Attribute.Strength]: 0,
|
||||||
[attributes.POWER]: 0,
|
[Attribute.Power]: 0,
|
||||||
[attributes.ENDURANCE]: 0,
|
[Attribute.Endurance]: 0,
|
||||||
[attributes.AGILITY]: 0,
|
[Attribute.Agility]: 0,
|
||||||
} as const
|
} as const
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -59,7 +79,7 @@ export class LevelCalculator {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const attrLevelsSum = Object.values(levels.attributes).reduce((sum, lvl) => sum + lvl, 0);
|
const attrLevelsSum = Object.values(levels.attributes).reduce((sum, lvl) => sum + lvl, 0);
|
||||||
levels.player = Math.round(attrLevelsSum / Object.keys(attributes).length);
|
levels.player = Math.round(attrLevelsSum / Object.keys(Attribute).length);
|
||||||
|
|
||||||
return levels;
|
return levels;
|
||||||
}
|
}
|
||||||
@@ -69,10 +89,10 @@ export class LevelCalculator {
|
|||||||
activityPerformances: ActivityPerformance[],
|
activityPerformances: ActivityPerformance[],
|
||||||
): Record<Attribute, number> {
|
): Record<Attribute, number> {
|
||||||
const attrLevels: Record<Attribute, number> = {
|
const attrLevels: Record<Attribute, number> = {
|
||||||
[attributes.STRENGTH]: 0,
|
[Attribute.Strength]: 0,
|
||||||
[attributes.POWER]: 0,
|
[Attribute.Power]: 0,
|
||||||
[attributes.ENDURANCE]: 0,
|
[Attribute.Endurance]: 0,
|
||||||
[attributes.AGILITY]: 0,
|
[Attribute.Agility]: 0,
|
||||||
} as const;
|
} as const;
|
||||||
|
|
||||||
for (const value of Object.values(player)) {
|
for (const value of Object.values(player)) {
|
||||||
@@ -80,9 +100,9 @@ export class LevelCalculator {
|
|||||||
return attrLevels;
|
return attrLevels;
|
||||||
}
|
}
|
||||||
|
|
||||||
(Object.values(attributes) as Attribute[]).forEach((attribute) => {
|
(Object.values(Attribute) as Attribute[]).forEach((attribute) => {
|
||||||
const attrActivityPerformances = activityPerformances.filter(
|
const attrActivityPerformances = activityPerformances.filter(
|
||||||
(p) => getActivityAttribute(p.activity) === attribute,
|
(p) => this.standards.getActivityAttribute(p.activity) === attribute,
|
||||||
);
|
);
|
||||||
attrLevels[attribute] = this.calculateAttributeLevel(attribute, player, attrActivityPerformances);
|
attrLevels[attribute] = this.calculateAttributeLevel(attribute, player, attrActivityPerformances);
|
||||||
});
|
});
|
||||||
@@ -97,12 +117,12 @@ export class LevelCalculator {
|
|||||||
activityPerformances: ActivityPerformance[],
|
activityPerformances: ActivityPerformance[],
|
||||||
): number {
|
): number {
|
||||||
// must be activities of the given attribute
|
// must be activities of the given attribute
|
||||||
if (activityPerformances.some((p) => getActivityAttribute(p.activity) !== attribute)) {
|
if (activityPerformances.some((p) => this.standards.getActivityAttribute(p.activity) !== attribute)) {
|
||||||
throw new Error("Wrong activity performance attribute");
|
throw new Error("Wrong activity performance attribute");
|
||||||
}
|
}
|
||||||
|
|
||||||
// must perform all of an attributes activities
|
// must perform all of an attributes activities
|
||||||
for (const activity of getAttributeActivities(attribute)) {
|
for (const activity of this.standards.getAttributeActivities(attribute)) {
|
||||||
const filteredActivites = activityPerformances.map((p) => p.activity);
|
const filteredActivites = activityPerformances.map((p) => p.activity);
|
||||||
if (!filteredActivites.includes(activity))
|
if (!filteredActivites.includes(activity))
|
||||||
return 0;
|
return 0;
|
||||||
@@ -213,12 +233,12 @@ export class LevelCalculator {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export class Standards {
|
export class Standards {
|
||||||
private standardsMap: StandardsMap;
|
private activityStandards: ActivityStandards;
|
||||||
|
|
||||||
constructor(standardsMap: StandardsMap) {
|
constructor(activityStandards: ActivityStandards) {
|
||||||
this.standardsMap = standardsMap;
|
this.activityStandards = activityStandards;
|
||||||
for (const activity of Object.keys(this.standardsMap) as Activity[]) {
|
for (const activity of Object.keys(this.activityStandards) as Activity[]) {
|
||||||
for (const standard of this.standardsMap[activity]) {
|
for (const standard of this.activityStandards[activity].standards) {
|
||||||
const expandedLevels = this.expandLevels(standard.levels, 5);
|
const expandedLevels = this.expandLevels(standard.levels, 5);
|
||||||
const compressedLevels = this.compressLevels(expandedLevels, 100);
|
const compressedLevels = this.compressLevels(expandedLevels, 100);
|
||||||
standard.levels = compressedLevels;
|
standard.levels = compressedLevels;
|
||||||
@@ -237,7 +257,7 @@ export class Standards {
|
|||||||
|
|
||||||
const execMethods = {
|
const execMethods = {
|
||||||
getAll: function() {
|
getAll: function() {
|
||||||
let filtered = [...self.standardsMap[activity]];
|
let filtered = [...self.activityStandards[activity].standards];
|
||||||
if (state.gender)
|
if (state.gender)
|
||||||
filtered = filtered.filter((s) => s.metrics.gender === state.gender);
|
filtered = filtered.filter((s) => s.metrics.gender === state.gender);
|
||||||
if (state.age)
|
if (state.age)
|
||||||
@@ -280,6 +300,21 @@ export class Standards {
|
|||||||
return { byGender, ...execMethods }
|
return { byGender, ...execMethods }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getActivityAttribute(activity: Activity): Attribute {
|
||||||
|
return this.activityStandards[activity].metadata.attribute;
|
||||||
|
};
|
||||||
|
|
||||||
|
getAttributeActivities(attribute: Attribute): Activity[] {
|
||||||
|
const activities: Activity[] = [];
|
||||||
|
for (const activity in this.activityStandards) {
|
||||||
|
const activityStandard = this.activityStandards[activity] as ActivityStandards[Activity];
|
||||||
|
if (activityStandard.metadata.attribute === attribute) {
|
||||||
|
activities.push(activity as Activity);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return activities;
|
||||||
|
}
|
||||||
|
|
||||||
private expandLevels(
|
private expandLevels(
|
||||||
levels: Levels,
|
levels: Levels,
|
||||||
i: number
|
i: number
|
||||||
|
|||||||
+10
-10
@@ -1,47 +1,47 @@
|
|||||||
import { activities, ActivityPerformance, feetToCm, genders, inchesToCm, lbToKg, minToMs, Player, secToMs, StandardsMap } from "./util";
|
import { Activity, ActivityPerformance, feetToCm, Gender, inchesToCm, lbToKg, minToMs, Player, secToMs } from "./util";
|
||||||
import { LevelCalculator, Standards } from "./calc.ts";
|
import { ActivityStandards, LevelCalculator, Standards } from "./calc.ts";
|
||||||
import rawStandards from "../data/standards.json" assert { type: "json" }
|
import rawStandards from "../data/standards.json" assert { type: "json" }
|
||||||
|
|
||||||
const player: Player = {
|
const player: Player = {
|
||||||
metrics: {
|
metrics: {
|
||||||
age: 25,
|
age: 25,
|
||||||
weight: lbToKg(190),
|
weight: lbToKg(190),
|
||||||
gender: genders.MALE
|
gender: Gender.Male
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const computedPerformances: ActivityPerformance[] = [
|
const computedPerformances: ActivityPerformance[] = [
|
||||||
// STRENGTH
|
// STRENGTH
|
||||||
{
|
{
|
||||||
activity: activities.BENCH_PRESS,
|
activity: Activity.BenchPress,
|
||||||
performance: lbToKg(225)
|
performance: lbToKg(225)
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
activity: activities.DEADLIFT,
|
activity: Activity.Deadlift,
|
||||||
performance: lbToKg(270),
|
performance: lbToKg(270),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
activity: activities.BACK_SQUAT,
|
activity: Activity.BackSquat,
|
||||||
performance: lbToKg(190),
|
performance: lbToKg(190),
|
||||||
},
|
},
|
||||||
// POWER
|
// POWER
|
||||||
{
|
{
|
||||||
activity: activities.BROAD_JUMP,
|
activity: Activity.BroadJump,
|
||||||
performance: feetToCm(105) + inchesToCm(5),
|
performance: feetToCm(105) + inchesToCm(5),
|
||||||
},
|
},
|
||||||
// ENDURANCE
|
// ENDURANCE
|
||||||
{
|
{
|
||||||
activity: activities.RUN,
|
activity: Activity.Run,
|
||||||
performance: minToMs(7) + secToMs(15),
|
performance: minToMs(7) + secToMs(15),
|
||||||
},
|
},
|
||||||
// AGILITY
|
// AGILITY
|
||||||
{
|
{
|
||||||
activity: activities.CONE_DRILL,
|
activity: Activity.ConeDrill,
|
||||||
performance: secToMs(9),
|
performance: secToMs(9),
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
const standards = new Standards(rawStandards as StandardsMap);
|
const standards = new Standards(rawStandards as ActivityStandards);
|
||||||
const levelCalculator = new LevelCalculator(standards);
|
const levelCalculator = new LevelCalculator(standards);
|
||||||
|
|
||||||
console.log(levelCalculator.calculate(player, computedPerformances));
|
console.log(levelCalculator.calculate(player, computedPerformances));
|
||||||
|
|||||||
+18
-75
@@ -1,96 +1,39 @@
|
|||||||
// -------------------------------------------------------------------------------------------------
|
import { Metrics } from "./calc";
|
||||||
// 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
|
// DATA MODELS
|
||||||
// -------------------------------------------------------------------------------------------------
|
// -------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
export interface Player {
|
export interface Player {
|
||||||
metrics: Metrics;
|
metrics: Metrics;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const activities = Object.freeze({
|
export enum Attribute {
|
||||||
BACK_SQUAT: "Back Squat",
|
Strength = "Strength",
|
||||||
DEADLIFT: "Deadlift",
|
Power = "Power",
|
||||||
BENCH_PRESS: "Bench Press",
|
Endurance = "Endurance",
|
||||||
RUN: "Run",
|
Agility = "Agility",
|
||||||
DASH: "Dash",
|
|
||||||
TREADMILL_DASH: "Treadmill Dash",
|
|
||||||
BROAD_JUMP: "Broad Jump",
|
|
||||||
CONE_DRILL: "Cone Drill",
|
|
||||||
} as const);
|
|
||||||
|
|
||||||
export type Activity = typeof activities[keyof typeof activities];
|
|
||||||
|
|
||||||
const activityAttrMap: Record<Activity, Attribute> = {
|
|
||||||
[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({
|
export enum Activity {
|
||||||
MALE: "male",
|
BackSquat = "BackSquat",
|
||||||
FEMALE: "female",
|
Deadlift = "Deadlift",
|
||||||
} as const);
|
BenchPress = "BenchPress",
|
||||||
export type Gender = typeof genders[keyof typeof genders];
|
Run = "Run",
|
||||||
|
BroadJump = "BroadJump",
|
||||||
|
ConeDrill = "ConeDrill",
|
||||||
|
}
|
||||||
|
|
||||||
// -------------------------------------------------------------------------------------------------
|
|
||||||
// Data model interfaces
|
|
||||||
// -------------------------------------------------------------------------------------------------
|
|
||||||
export interface ActivityPerformance {
|
export interface ActivityPerformance {
|
||||||
activity: Activity;
|
activity: Activity;
|
||||||
performance: number;
|
performance: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
export type Levels = Record<string, number>
|
export enum Gender {
|
||||||
|
Male = "Male",
|
||||||
export interface Metrics {
|
Female = "Female"
|
||||||
age: number;
|
|
||||||
weight: number;
|
|
||||||
gender: Gender;
|
|
||||||
};
|
|
||||||
|
|
||||||
export interface Standard {
|
|
||||||
metrics: Metrics;
|
|
||||||
levels: Levels;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export type StandardsMap = Record<
|
|
||||||
Activity,
|
|
||||||
Standard[]
|
|
||||||
>;
|
|
||||||
|
|
||||||
// -------------------------------------------------------------------------------------------------
|
// -------------------------------------------------------------------------------------------------
|
||||||
// Conversion utilities
|
// Conversion utilities
|
||||||
// -------------------------------------------------------------------------------------------------
|
// -------------------------------------------------------------------------------------------------
|
||||||
|
|||||||
+5278
-5812
File diff suppressed because it is too large
Load Diff
@@ -1,5 +1,5 @@
|
|||||||
import { LevelCalculator, Standards } from "./calculator/calc.ts";
|
import { ActivityStandards, LevelCalculator, Standards } from "./calculator/calc.ts";
|
||||||
import { ActivityPerformance, Player, StandardsMap } from "./calculator/util.ts";
|
import { ActivityPerformance, Player } from "./calculator/util.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";
|
||||||
import rawStandards from "./data/standards.json" assert { type: "json" }
|
import rawStandards from "./data/standards.json" assert { type: "json" }
|
||||||
@@ -26,7 +26,7 @@ export class ClientResponse extends Response {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const MAIN_STANDARDS = new Standards(rawStandards as StandardsMap);
|
const MAIN_STANDARDS = new Standards(rawStandards as ActivityStandards);
|
||||||
|
|
||||||
const server = Bun.serve({
|
const server = Bun.serve({
|
||||||
idleTimeout: 180,
|
idleTimeout: 180,
|
||||||
|
|||||||
Reference in New Issue
Block a user