This commit is contained in:
Dominic Ferrando
2025-11-08 19:53:39 -05:00
parent 8adb1c4566
commit f12453434e
2 changed files with 124 additions and 51 deletions
+70 -31
View File
@@ -220,7 +220,10 @@ export type StandardsConfig = {
enableGeneration: boolean;
difficultyModifier: number;
peakAge: number;
stretch: number;
stretch: {
upper: number;
lower: number;
};
}
>;
};
@@ -246,7 +249,10 @@ export class Standards {
difficultyModifier:
activity === Activity.BroadJump ? 0.05 : 0,
peakAge: 27,
stretch: 0,
stretch: {
upper: 0,
lower: 0,
},
},
]),
) as Record<Activity, StandardsConfig["activity"][Activity]>;
@@ -264,15 +270,15 @@ export class Standards {
// STRETCH
for (const activity of Object.values(Activity)) {
const BASE_MAX_LEVEL = 5;
const newLevelCount = Math.max(
Math.round(this.cfg.activity[activity].stretch),
0,
);
function expDecayModel([A, B, C]: number[]) {
return (i: number) => A * Math.exp(-B * i) + C;
}
if (!this.cfg.activity[activity].enableGeneration) {
continue;
}
for (const gender of Object.values(Gender)) {
const standardsByGender = this.activityStandards[
activity
@@ -327,29 +333,43 @@ export class Standards {
// UPDATE THE DATA
for (const standard of standards) {
const newLevels: Levels = {};
const newLowerLevelCount = Math.max(
Math.round(
this.cfg.activity[activity].stretch.lower,
),
0,
);
const newUpperLevelCount = Math.max(
Math.round(
this.cfg.activity[activity].stretch.upper,
),
0,
);
let prev = standard.levels["1"];
for (const i of range(newLevelCount).reverse()) {
const oldLevels = standard.levels as Levels;
let prev = oldLevels["1"];
for (const i of range(newLowerLevelCount).reverse()) {
const level = i + 1;
const ratio = getDecayRatio(-i);
const ratio = getDecayRatio(-i - 1);
prev = isIncreasing ? prev / ratio : prev * ratio;
newLevels[level] = Math.round(prev);
}
const oldLevels = standard.levels as Levels;
for (const i of range(BASE_MAX_LEVEL)) {
const level = i + 1;
newLevels[level + newLevelCount] = oldLevels[level];
newLevels[level + newLowerLevelCount] =
oldLevels[level];
}
prev = standard.levels[BASE_MAX_LEVEL];
for (const i of range(newLevelCount)) {
prev = oldLevels[BASE_MAX_LEVEL];
for (const i of range(newUpperLevelCount)) {
const level =
BASE_MAX_LEVEL + newLevelCount + (i + 1);
BASE_MAX_LEVEL + newLowerLevelCount + (i + 1);
const ratio = getDecayRatio(level - 1);
prev = isIncreasing ? prev * ratio : prev / ratio;
newLevels[level] = Math.round(prev);
}
standard.levels = newLevels;
}
}
@@ -796,12 +816,16 @@ export class Standards {
if (i == 0) {
return levels;
}
const levelKeys = Object.keys(levels)
.map(Number)
.sort((a, b) => a - b);
const newLevels: Levels = {};
let j = 1;
for (let k = 0; k < Object.keys(levels).length; ++k) {
const currLevel = Object.keys(levels)[k];
const nextLevel = Object.keys(levels)[k + 1];
for (let k = 0; k < levelKeys.length; ++k) {
const currLevel = levelKeys[k];
const nextLevel = levelKeys[k + 1];
newLevels[j++] = levels[currLevel];
@@ -813,28 +837,43 @@ export class Standards {
return this.expandLevels(newLevels, i - 1);
}
private compressLevels(levels: Levels, targetLevel: number): Levels {
const levelsAmount = Object.keys(levels).length;
if (levelsAmount < targetLevel) {
private compressLevels(levels: Levels, targetLvl: number): Levels {
const keys = Object.keys(levels)
.map(Number)
.sort((a, b) => a - b);
const lvlCnt = keys.length;
if (targetLvl <= 0) {
throw new Error("Target levels amount must be a positive integer");
}
if (lvlCnt === 0) {
return {};
}
if (targetLvl === 1) {
return { 1: levels[keys[0]] };
}
if (lvlCnt < targetLvl) {
throw new Error(
"Target levels amount must be greater than or equal to the current levels amount",
"Target levels amount must be less than or equal to the current levels amount",
);
}
const ratio = levelsAmount / targetLevel;
// Linear resample
const compressedLevels: Levels = {};
for (let i = 0; i < targetLevel; ++i) {
const ratioIndex = i * ratio;
const lowerIndex = Math.floor(ratioIndex);
const upperIndex = Math.ceil(ratioIndex);
for (let targetIndex = 0; targetIndex < targetLvl; ++targetIndex) {
const sourcePos = (targetIndex * (lvlCnt - 1)) / (targetLvl - 1);
const lowerValue = levels[lowerIndex + 1];
const upperValue = levels[upperIndex + 1];
const sourceIndexLower = Math.floor(sourcePos);
const sourceIndexUpper = Math.min(sourceIndexLower + 1, lvlCnt - 1);
const weight = ratioIndex - lowerIndex;
compressedLevels[i + 1] =
lowerValue + (upperValue - lowerValue) * weight;
const interpolationWeight = sourcePos - sourceIndexLower;
const lowerValue = levels[keys[sourceIndexLower]];
const upperValue = levels[keys[sourceIndexUpper]];
compressedLevels[targetIndex + 1] =
lowerValue + (upperValue - lowerValue) * interpolationWeight;
}
return compressedLevels;
+54 -20
View File
@@ -50,9 +50,16 @@
peakAge: String(
defaultStandards.cfg.activity[activity].peakAge,
),
stretch: String(
defaultStandards.cfg.activity[activity].stretch,
),
stretch: {
upper: String(
defaultStandards.cfg.activity[activity]
.stretch.upper,
),
lower: String(
defaultStandards.cfg.activity[activity]
.stretch.lower,
),
},
},
];
}),
@@ -83,7 +90,10 @@
difficultyModifier:
+input.cfg.activity[activity].difficultyModifier,
peakAge: +input.cfg.activity[activity].peakAge,
stretch: +input.cfg.activity[activity].stretch,
stretch: {
lower: +input.cfg.activity[activity].stretch.lower,
upper: +input.cfg.activity[activity].stretch.upper,
},
},
]),
),
@@ -147,22 +157,6 @@
bind:value={input.cfg.global.maxLevel}
/>
</label>
<label>
<span class="label mb-1">Stretch</span>
<input
class="input w-full"
type="number"
min="0"
max="10"
step="1"
bind:value={
input.cfg.activity[selected.activity].stretch
}
disabled={!selected.cfg.activity[selected.activity]
.enableGeneration}
/>
</label>
</div>
</fieldset>
@@ -262,6 +256,46 @@
.enableGeneration}
/>
</label>
<fieldset class="fieldset bg-base-200 p-3 max-w-xl">
<legend class="fieldset-legend text-sm font-semibold">
Stretch
</legend>
<label>
<span class="label mb-1">Lower</span>
<input
class="input w-full"
type="number"
min="0"
max="10"
step="1"
bind:value={
input.cfg.activity[selected.activity]
.stretch.lower
}
disabled={!selected.cfg.activity[
selected.activity
].enableGeneration}
/>
</label>
<label>
<span class="label mb-1">Upper</span>
<input
class="input w-full"
type="number"
min="0"
max="10"
step="1"
bind:value={
input.cfg.activity[selected.activity]
.stretch.upper
}
disabled={!selected.cfg.activity[
selected.activity
].enableGeneration}
/>
</label>
</fieldset>
</div>
</fieldset>
</div>