Skip to content

Instantly share code, notes, and snippets.

@ericbmerritt
Created March 21, 2019 02:42
Show Gist options
  • Select an option

  • Save ericbmerritt/d37d3b6eb8e6f8a0a146388258c7802c to your computer and use it in GitHub Desktop.

Select an option

Save ericbmerritt/d37d3b6eb8e6f8a0a146388258c7802c to your computer and use it in GitHub Desktop.

Revisions

  1. ericbmerritt created this gist Mar 21, 2019.
    131 changes: 131 additions & 0 deletions index.ts
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,131 @@
    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
    });