Skip to content

Instantly share code, notes, and snippets.

@marcmartino
Created November 5, 2018 01:30
Show Gist options
  • Save marcmartino/a4d2b9109f91305cf2d64241d77bac3f to your computer and use it in GitHub Desktop.
Save marcmartino/a4d2b9109f91305cf2d64241d77bac3f to your computer and use it in GitHub Desktop.
attempt at sm4, likely inconsistent but sm5 is significantly different so no dwelling
const answers: CardAnswer[] = [
{
"hesitationDetected": true,
"correct": true,
"timeSpent": 5000,
"date": new Date("2018-11-01T15:13:00.000Z")
},
{
"hesitationDetected": false,
"correct": false,
"timeSpent": 10000,
"date": new Date("2018-11-02T06:30:00.000Z")
},
{
"hesitationDetected": true,
"correct": true,
"timeSpent": 1000,
"date": new Date("2018-11-02T07:01:00.000Z")
}
];
interface CardAnswer {
[key: string]: (boolean | number | date)
hesitationDetected: boolean;
correct: boolean;
timeSpent: number;
date: date;
ef?: EasinessFactor;
interval?: Interval;
oi: OptimalInterval;
}
type Quality = 0|1|2|3|4|5;
type EasinessFactor = number;
type Interval = number;
type OptimalInterval = number;
const interv = (rep: number, ef: EasinessFactor /* 1.3 <= easinessFactor <= 2.5 */): Interval =>
rep === 1 ? 1
: rep === 2 ? 6
: interv(rep - 1, ef) * ef;
const ef = (prevEf: EasinessFactor, q: Quality): EasinessFactor =>
Math.max(1.3, Math.min(2.5,
prevEf + (0.1 - (5 - q) * (0.08 + (5 - q) * 0.02))))
const efFromCards = (as: CardAnswer[]): EasinessFactor =>
as.length > 1
? ef(efFromCards(as.slice(0, as.length - 1)), qual(as[as.length - 1]))
: 2.5;
const qual = (a: CardAnswer): Quality =>
a.correct
? (!a.hesitationDetected && a.timeSpent < 5000
? 5
: !a.hesitationDetected || a.timeSpent < 5000
? 4
: 3)
: (!a.hesitationDetected && a.timeSpent < 5000
? 2
: !a.hesitationDetected || a.timeSpent < 5000
? 1
: 0);
const nextOI = (prevOI: OptimalInterval, interimOI: OptimalInterval, frac: number): OptimalInterval =>
(1 - frac) * prevOI + frac * interimOI;
const primeOI = (i: interval, ef: EasinessFactor, q: Quality): OptimalInterval =>
i * i * (1 - 1 / ef) / 2 * (0.2 * q - 1);
const calcOI = (as: CardAnswers[]): OptimalInterval =>
as.length > 1
? nextOI(
calcOI(as.slice(0, as.length - 1)),
primeOI(as[as.length - 1].interval, as[as.length - 1].ef, qual(as[as.length - 1])),
.5)
: 2.5;
console.log(
answers
.reduce((as: CardAnswer[], a: CardAnswer): CardAnswer[] =>
[...as, {...a, ef: efFromCards([...as, a, ]) }], [])
.map((a: CardAnswer, x: number): CardAnswer =>
({ ...a, interval: interv(x + 1, a.ef, qual(a))}))
.reduce((as: CardAnswer[], a: CardAnswer): CardAnswer =>
[...as, {...a, oi: calcOI([...as, a, ])}], [])
.map(({ef, interval, oi}) => ({ ef, interval, oi,})),
);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment