Skip to content

Instantly share code, notes, and snippets.

@ericbmerritt
Created March 21, 2019 02:42
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save ericbmerritt/d37d3b6eb8e6f8a0a146388258c7802c to your computer and use it in GitHub Desktop.
Save ericbmerritt/d37d3b6eb8e6f8a0a146388258c7802c to your computer and use it in GitHub Desktop.
import loglevel from 'loglevel';
import { gaussian, randomBetweenMinAndMax } from './random-utils';
interface IPeriodEntry {
calories: number;
workout: boolean;
}
interface ISimEntry {
correctnessFactor: number;
period: IPeriodEntry[];
}
interface IConfig {
periodLength: number;
fastingDays: number;
targetWorkoutDays: number;
simulationCount: number;
dailyTargetCalories: number;
}
const selectPeriodDays = (
targetDays: number,
periodLength: number
): number[] => {
const realizedTarget = Math.floor(gaussian(targetDays, targetDays)());
if (realizedTarget < 1) {
return [];
} else {
return Array(realizedTarget)
.fill(false)
.map(() => randomBetweenMinAndMax(1, periodLength) - 1);
}
};
const generatePeriodWorkouts = (config: IConfig): number[] =>
selectPeriodDays(config.targetWorkoutDays, config.periodLength);
const generatePeriodFastingDays = (config: IConfig): number[] =>
selectPeriodDays(config.fastingDays, config.periodLength);
const generatePeriodCalories = (config: IConfig): number[] => {
// make a standard gaussian variable.
const standardDistribution = gaussian(
config.dailyTargetCalories,
config.dailyTargetCalories
);
return Array(config.periodLength)
.fill(0)
.map(() => Math.floor(standardDistribution()));
};
const sumPeriodCalories = (week: IPeriodEntry[]): number =>
week.reduce((acc, current) => acc + current.calories, 0);
const sumPeriodWorkouts = (week: IPeriodEntry[]): number =>
week.reduce((acc, current) => {
if (current.workout) {
return acc + 1;
} else {
return acc;
}
}, 0);
const mergeFastingAndWorkouts = (
calories: number[],
fastingDays: number[],
workoutDays: number[]
): IPeriodEntry[] => {
return calories.map(
(caloriesInstance: number, index: number): IPeriodEntry => {
const calIntstanceFastAdjusted = fastingDays.includes(index)
? 0
: caloriesInstance;
const workout = workoutDays.includes(index);
return { calories: calIntstanceFastAdjusted, workout };
}
);
};
const generatePeriodWithMetadata = (
config: IConfig,
periodTarget: number
): ISimEntry => {
const periodCalories = generatePeriodCalories(config);
const fastingDays = generatePeriodFastingDays(config);
const periodWorkouts = generatePeriodWorkouts(config);
const period = mergeFastingAndWorkouts(
periodCalories,
fastingDays,
periodWorkouts
);
const workoutDiff = Math.abs(
config.targetWorkoutDays - sumPeriodWorkouts(period)
);
const calorieDiff = Math.abs(periodTarget - sumPeriodCalories(period));
return {
correctnessFactor: workoutDiff + calorieDiff,
period
};
};
const generatePeriodSimulations = (config: IConfig): ISimEntry[] => {
const periodTarget = config.dailyTargetCalories * config.periodLength;
const periods = [];
for (let i = 0; i < config.simulationCount; i++) {
periods.push(generatePeriodWithMetadata(config, periodTarget));
}
return periods;
};
const main = (config: IConfig) => {
const sims = generatePeriodSimulations(config);
const result = sims.reduce(
(p: ISimEntry, c: ISimEntry): ISimEntry => {
if (p.correctnessFactor < c.correctnessFactor) {
return p;
} else {
return c;
}
}
);
loglevel.error(JSON.stringify(result, null, 2));
};
main({
dailyTargetCalories: 1800,
fastingDays: 2,
periodLength: 7,
simulationCount: 10000,
targetWorkoutDays: 5
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment