Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feat/long-term schduler #100

Merged
merged 13 commits into from
Jul 25, 2024
2 changes: 1 addition & 1 deletion src/fsrs/algorithm.ts
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ export class FSRSAlgorithm {
this.update_parameters(params)
}

private params_handler_proxy(): ProxyHandler<FSRSParameters> {
protected params_handler_proxy(): ProxyHandler<FSRSParameters> {
// eslint-disable-next-line @typescript-eslint/no-this-alias
const _this: FSRSAlgorithm = this
return {
Expand Down
5 changes: 4 additions & 1 deletion src/fsrs/default.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ export const default_w = [
0.6468,
]
export const default_enable_fuzz = false
export const defualt_enable_short_term = true

export const FSRSVersion: string = 'v3.5.7 using FSRS V5.0'

Expand All @@ -29,7 +30,9 @@ export const generatorParameters = (
maximum_interval: props?.maximum_interval || default_maximum_interval,
w: w,
enable_fuzz: props?.enable_fuzz || default_enable_fuzz,
}
enable_short_term:
props?.enable_short_term || defualt_enable_short_term,
ishiko732 marked this conversation as resolved.
Show resolved Hide resolved
} satisfies FSRSParameters
}

/**
Expand Down
22 changes: 21 additions & 1 deletion src/fsrs/fsrs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,32 @@ import type { int } from './types'
import { FSRSAlgorithm } from './algorithm'
import { TypeConvert } from './convert'
import BasicScheduler from './impl/basic_schduler'
import LongTermScheduler from './impl/long_term_schduler'

export class FSRS extends FSRSAlgorithm {
private Schduler
constructor(param: Partial<FSRSParameters>) {
super(param)
this.Schduler = BasicScheduler
const { enable_short_term } = this.parameters
this.Schduler = enable_short_term ? BasicScheduler : LongTermScheduler
}

protected override params_handler_proxy(): ProxyHandler<FSRSParameters> {
const _this: FSRS = this satisfies FSRS
return {
set: function (target, prop, value) {
if (prop === 'request_retention' && Number.isFinite(value)) {
_this.intervalModifier = _this.calculate_interval_modifier(
Number(value)
)
} else if (prop === 'enable_short_term') {
_this.Schduler = value === true ? BasicScheduler : LongTermScheduler
}
// @ts-ignore
target[prop] = value
return true
},
}
}

/**
Expand Down
4 changes: 3 additions & 1 deletion src/fsrs/impl/basic_schduler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,8 @@ export default class BasicScheduler extends AbstractScheduler {

this.next_interval(next_again, next_hard, next_good, next_easy, interval)
this.next_state(next_again, next_hard, next_good, next_easy)
next_again.lapses += 1


const item_again = {
card: next_again,
Expand Down Expand Up @@ -268,7 +270,7 @@ export default class BasicScheduler extends AbstractScheduler {
next_easy: Card
) {
next_again.state = State.Relearning
next_again.lapses += 1
// next_again.lapses += 1

next_hard.state = State.Review

Expand Down
279 changes: 279 additions & 0 deletions src/fsrs/impl/long_term_schduler.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,279 @@
import { AbstractScheduler } from '../abstract_schduler'
import { TypeConvert } from '../convert'
import {
type Card,
type Grade,
Rating,
type RecordLogItem,
State,
} from '../models'
import type { int } from '../types'

export default class LongTermScheduler extends AbstractScheduler {
protected override newState(grade: Grade): RecordLogItem {
const exist = this.next.get(grade)

Check warning on line 14 in src/fsrs/impl/long_term_schduler.ts

View check run for this annotation

Codecov / codecov/patch

src/fsrs/impl/long_term_schduler.ts#L13-L14

Added lines #L13 - L14 were not covered by tests
if (exist) {
return exist

Check warning on line 16 in src/fsrs/impl/long_term_schduler.ts

View check run for this annotation

Codecov / codecov/patch

src/fsrs/impl/long_term_schduler.ts#L16

Added line #L16 was not covered by tests
}

const first_difficulty = this.algorithm.init_difficulty(grade)
const first_stability = this.algorithm.init_stability(grade)
ishiko732 marked this conversation as resolved.
Show resolved Hide resolved
const first_interval = 0

Check warning on line 21 in src/fsrs/impl/long_term_schduler.ts

View check run for this annotation

Codecov / codecov/patch

src/fsrs/impl/long_term_schduler.ts#L19-L21

Added lines #L19 - L21 were not covered by tests

this.current.difficulty = first_difficulty
this.current.stability = first_stability
this.current.scheduled_days = 0
this.current.elapsed_days = 0

Check warning on line 26 in src/fsrs/impl/long_term_schduler.ts

View check run for this annotation

Codecov / codecov/patch

src/fsrs/impl/long_term_schduler.ts#L23-L26

Added lines #L23 - L26 were not covered by tests

const next_again = TypeConvert.card(this.current)
const next_hard = TypeConvert.card(this.current)
const next_good = TypeConvert.card(this.current)
const next_easy = TypeConvert.card(this.current)

Check warning on line 31 in src/fsrs/impl/long_term_schduler.ts

View check run for this annotation

Codecov / codecov/patch

src/fsrs/impl/long_term_schduler.ts#L28-L31

Added lines #L28 - L31 were not covered by tests

this.next_short_term_ds(

Check warning on line 33 in src/fsrs/impl/long_term_schduler.ts

View check run for this annotation

Codecov / codecov/patch

src/fsrs/impl/long_term_schduler.ts#L33

Added line #L33 was not covered by tests
next_again,
next_hard,
next_good,
next_easy,
first_difficulty,
first_stability
)
this.next_interval(

Check warning on line 41 in src/fsrs/impl/long_term_schduler.ts

View check run for this annotation

Codecov / codecov/patch

src/fsrs/impl/long_term_schduler.ts#L41

Added line #L41 was not covered by tests
next_again,
next_hard,
next_good,
next_easy,
first_interval
)

this.next_state(next_again, next_hard, next_good, next_easy)
this.update_next(next_again, next_hard, next_good, next_easy)
return this.next.get(grade)!

Check warning on line 51 in src/fsrs/impl/long_term_schduler.ts

View check run for this annotation

Codecov / codecov/patch

src/fsrs/impl/long_term_schduler.ts#L49-L51

Added lines #L49 - L51 were not covered by tests
}

private next_short_term_ds(

Check warning on line 54 in src/fsrs/impl/long_term_schduler.ts

View check run for this annotation

Codecov / codecov/patch

src/fsrs/impl/long_term_schduler.ts#L54

Added line #L54 was not covered by tests
next_again: Card,
next_hard: Card,
next_good: Card,
next_easy: Card,
difficulty: number,
stability: number
): void {
next_again.difficulty = this.algorithm.next_difficulty(

Check warning on line 62 in src/fsrs/impl/long_term_schduler.ts

View check run for this annotation

Codecov / codecov/patch

src/fsrs/impl/long_term_schduler.ts#L62

Added line #L62 was not covered by tests
difficulty,
Rating.Again
)
next_again.stability = this.algorithm.next_short_term_stability(

Check warning on line 66 in src/fsrs/impl/long_term_schduler.ts

View check run for this annotation

Codecov / codecov/patch

src/fsrs/impl/long_term_schduler.ts#L66

Added line #L66 was not covered by tests
stability,
Rating.Again
)

next_hard.difficulty = this.algorithm.next_difficulty(

Check warning on line 71 in src/fsrs/impl/long_term_schduler.ts

View check run for this annotation

Codecov / codecov/patch

src/fsrs/impl/long_term_schduler.ts#L71

Added line #L71 was not covered by tests
difficulty,
Rating.Hard
)
next_hard.stability = this.algorithm.next_short_term_stability(

Check warning on line 75 in src/fsrs/impl/long_term_schduler.ts

View check run for this annotation

Codecov / codecov/patch

src/fsrs/impl/long_term_schduler.ts#L75

Added line #L75 was not covered by tests
stability,
Rating.Hard
)

next_good.difficulty = this.algorithm.next_difficulty(

Check warning on line 80 in src/fsrs/impl/long_term_schduler.ts

View check run for this annotation

Codecov / codecov/patch

src/fsrs/impl/long_term_schduler.ts#L80

Added line #L80 was not covered by tests
difficulty,
Rating.Good
)
next_good.stability = this.algorithm.next_short_term_stability(

Check warning on line 84 in src/fsrs/impl/long_term_schduler.ts

View check run for this annotation

Codecov / codecov/patch

src/fsrs/impl/long_term_schduler.ts#L84

Added line #L84 was not covered by tests
stability,
Rating.Good
)

next_easy.difficulty = this.algorithm.next_difficulty(

Check warning on line 89 in src/fsrs/impl/long_term_schduler.ts

View check run for this annotation

Codecov / codecov/patch

src/fsrs/impl/long_term_schduler.ts#L89

Added line #L89 was not covered by tests
difficulty,
Rating.Easy
)
next_easy.stability = this.algorithm.next_short_term_stability(

Check warning on line 93 in src/fsrs/impl/long_term_schduler.ts

View check run for this annotation

Codecov / codecov/patch

src/fsrs/impl/long_term_schduler.ts#L93

Added line #L93 was not covered by tests
stability,
Rating.Easy
)
}

protected override learningState(grade: Grade): RecordLogItem {
throw new Error('Long-term Scheduler not implemented.')

Check warning on line 100 in src/fsrs/impl/long_term_schduler.ts

View check run for this annotation

Codecov / codecov/patch

src/fsrs/impl/long_term_schduler.ts#L99-L100

Added lines #L99 - L100 were not covered by tests
}
protected override reviewState(grade: Grade): RecordLogItem {
const exist = this.next.get(grade)

Check warning on line 103 in src/fsrs/impl/long_term_schduler.ts

View check run for this annotation

Codecov / codecov/patch

src/fsrs/impl/long_term_schduler.ts#L102-L103

Added lines #L102 - L103 were not covered by tests
if (exist) {
return exist

Check warning on line 105 in src/fsrs/impl/long_term_schduler.ts

View check run for this annotation

Codecov / codecov/patch

src/fsrs/impl/long_term_schduler.ts#L105

Added line #L105 was not covered by tests
}
const interval = this.current.elapsed_days
const { difficulty, stability } = this.last
const retrievability = this.algorithm.forgetting_curve(interval, stability)
const next_again = TypeConvert.card(this.current)
const next_hard = TypeConvert.card(this.current)
const next_good = TypeConvert.card(this.current)
const next_easy = TypeConvert.card(this.current)

Check warning on line 113 in src/fsrs/impl/long_term_schduler.ts

View check run for this annotation

Codecov / codecov/patch

src/fsrs/impl/long_term_schduler.ts#L107-L113

Added lines #L107 - L113 were not covered by tests

this.next_ds(

Check warning on line 115 in src/fsrs/impl/long_term_schduler.ts

View check run for this annotation

Codecov / codecov/patch

src/fsrs/impl/long_term_schduler.ts#L115

Added line #L115 was not covered by tests
next_again,
next_hard,
next_good,
next_easy,
difficulty,
stability,
retrievability
)

this.next_interval(next_again, next_hard, next_good, next_easy, interval)
this.next_state(next_again, next_hard, next_good, next_easy)
next_again.lapses += 1

Check warning on line 127 in src/fsrs/impl/long_term_schduler.ts

View check run for this annotation

Codecov / codecov/patch

src/fsrs/impl/long_term_schduler.ts#L125-L127

Added lines #L125 - L127 were not covered by tests

this.update_next(next_again, next_hard, next_good, next_easy)
return this.next.get(grade)!

Check warning on line 130 in src/fsrs/impl/long_term_schduler.ts

View check run for this annotation

Codecov / codecov/patch

src/fsrs/impl/long_term_schduler.ts#L129-L130

Added lines #L129 - L130 were not covered by tests
}

/**
* Review next_ds
*/
private next_ds(

Check warning on line 136 in src/fsrs/impl/long_term_schduler.ts

View check run for this annotation

Codecov / codecov/patch

src/fsrs/impl/long_term_schduler.ts#L136

Added line #L136 was not covered by tests
next_again: Card,
next_hard: Card,
next_good: Card,
next_easy: Card,
difficulty: number,
stability: number,
retrievability: number
): void {
next_again.difficulty = this.algorithm.next_difficulty(

Check warning on line 145 in src/fsrs/impl/long_term_schduler.ts

View check run for this annotation

Codecov / codecov/patch

src/fsrs/impl/long_term_schduler.ts#L145

Added line #L145 was not covered by tests
difficulty,
Rating.Again
)
next_again.stability = this.algorithm.next_forget_stability(

Check warning on line 149 in src/fsrs/impl/long_term_schduler.ts

View check run for this annotation

Codecov / codecov/patch

src/fsrs/impl/long_term_schduler.ts#L149

Added line #L149 was not covered by tests
difficulty,
stability,
retrievability
)
next_hard.difficulty = this.algorithm.next_difficulty(

Check warning on line 154 in src/fsrs/impl/long_term_schduler.ts

View check run for this annotation

Codecov / codecov/patch

src/fsrs/impl/long_term_schduler.ts#L154

Added line #L154 was not covered by tests
difficulty,
Rating.Hard
)
next_hard.stability = this.algorithm.next_recall_stability(

Check warning on line 158 in src/fsrs/impl/long_term_schduler.ts

View check run for this annotation

Codecov / codecov/patch

src/fsrs/impl/long_term_schduler.ts#L158

Added line #L158 was not covered by tests
difficulty,
stability,
retrievability,
Rating.Hard
)
next_good.difficulty = this.algorithm.next_difficulty(

Check warning on line 164 in src/fsrs/impl/long_term_schduler.ts

View check run for this annotation

Codecov / codecov/patch

src/fsrs/impl/long_term_schduler.ts#L164

Added line #L164 was not covered by tests
difficulty,
Rating.Good
)
next_good.stability = this.algorithm.next_recall_stability(

Check warning on line 168 in src/fsrs/impl/long_term_schduler.ts

View check run for this annotation

Codecov / codecov/patch

src/fsrs/impl/long_term_schduler.ts#L168

Added line #L168 was not covered by tests
difficulty,
stability,
retrievability,
Rating.Good
)
next_easy.difficulty = this.algorithm.next_difficulty(

Check warning on line 174 in src/fsrs/impl/long_term_schduler.ts

View check run for this annotation

Codecov / codecov/patch

src/fsrs/impl/long_term_schduler.ts#L174

Added line #L174 was not covered by tests
difficulty,
Rating.Easy
)
next_easy.stability = this.algorithm.next_recall_stability(

Check warning on line 178 in src/fsrs/impl/long_term_schduler.ts

View check run for this annotation

Codecov / codecov/patch

src/fsrs/impl/long_term_schduler.ts#L178

Added line #L178 was not covered by tests
difficulty,
stability,
retrievability,
Rating.Easy
)
}

/**
* Review/New next_interval
*/
private next_interval(

Check warning on line 189 in src/fsrs/impl/long_term_schduler.ts

View check run for this annotation

Codecov / codecov/patch

src/fsrs/impl/long_term_schduler.ts#L189

Added line #L189 was not covered by tests
next_again: Card,
next_hard: Card,
next_good: Card,
next_easy: Card,
interval: number
): void {
let again_interval: int,
hard_interval: int,
good_interval: int,
easy_interval: int
again_interval = this.algorithm.next_interval(

Check warning on line 200 in src/fsrs/impl/long_term_schduler.ts

View check run for this annotation

Codecov / codecov/patch

src/fsrs/impl/long_term_schduler.ts#L200

Added line #L200 was not covered by tests
next_again.stability,
interval
)
hard_interval = this.algorithm.next_interval(next_hard.stability, interval)
good_interval = this.algorithm.next_interval(next_good.stability, interval)
easy_interval = this.algorithm.next_interval(next_easy.stability, interval)

Check warning on line 206 in src/fsrs/impl/long_term_schduler.ts

View check run for this annotation

Codecov / codecov/patch

src/fsrs/impl/long_term_schduler.ts#L204-L206

Added lines #L204 - L206 were not covered by tests

again_interval = Math.min(again_interval, hard_interval) as int
hard_interval = Math.max(hard_interval, again_interval + 1) as int
good_interval = Math.max(good_interval, hard_interval + 1) as int
easy_interval = Math.max(easy_interval, good_interval + 1) as int

Check warning on line 211 in src/fsrs/impl/long_term_schduler.ts

View check run for this annotation

Codecov / codecov/patch

src/fsrs/impl/long_term_schduler.ts#L208-L211

Added lines #L208 - L211 were not covered by tests

next_again.scheduled_days = again_interval
next_again.due =

Check warning on line 214 in src/fsrs/impl/long_term_schduler.ts

View check run for this annotation

Codecov / codecov/patch

src/fsrs/impl/long_term_schduler.ts#L213-L214

Added lines #L213 - L214 were not covered by tests
again_interval > 0
ishiko732 marked this conversation as resolved.
Show resolved Hide resolved
? this.review_time.scheduler(again_interval as int)
: this.review_time.scheduler(again_interval as int, false)

next_hard.scheduled_days = hard_interval
next_hard.due =

Check warning on line 220 in src/fsrs/impl/long_term_schduler.ts

View check run for this annotation

Codecov / codecov/patch

src/fsrs/impl/long_term_schduler.ts#L219-L220

Added lines #L219 - L220 were not covered by tests
hard_interval > 0
? this.review_time.scheduler(hard_interval, true)
: this.review_time.scheduler(10 as int)

next_good.scheduled_days = good_interval
next_good.due = this.review_time.scheduler(good_interval, true)

Check warning on line 226 in src/fsrs/impl/long_term_schduler.ts

View check run for this annotation

Codecov / codecov/patch

src/fsrs/impl/long_term_schduler.ts#L225-L226

Added lines #L225 - L226 were not covered by tests

next_easy.scheduled_days = easy_interval
next_easy.due = this.review_time.scheduler(easy_interval, true)

Check warning on line 229 in src/fsrs/impl/long_term_schduler.ts

View check run for this annotation

Codecov / codecov/patch

src/fsrs/impl/long_term_schduler.ts#L228-L229

Added lines #L228 - L229 were not covered by tests
}

/**
* Review/New next_state
*/
private next_state(

Check warning on line 235 in src/fsrs/impl/long_term_schduler.ts

View check run for this annotation

Codecov / codecov/patch

src/fsrs/impl/long_term_schduler.ts#L235

Added line #L235 was not covered by tests
next_again: Card,
next_hard: Card,
next_good: Card,
next_easy: Card
) {
next_again.state = State.Review

Check warning on line 241 in src/fsrs/impl/long_term_schduler.ts

View check run for this annotation

Codecov / codecov/patch

src/fsrs/impl/long_term_schduler.ts#L241

Added line #L241 was not covered by tests
// next_again.lapses += 1

next_hard.state = State.Review

Check warning on line 244 in src/fsrs/impl/long_term_schduler.ts

View check run for this annotation

Codecov / codecov/patch

src/fsrs/impl/long_term_schduler.ts#L244

Added line #L244 was not covered by tests

next_good.state = State.Review

Check warning on line 246 in src/fsrs/impl/long_term_schduler.ts

View check run for this annotation

Codecov / codecov/patch

src/fsrs/impl/long_term_schduler.ts#L246

Added line #L246 was not covered by tests

next_easy.state = State.Review

Check warning on line 248 in src/fsrs/impl/long_term_schduler.ts

View check run for this annotation

Codecov / codecov/patch

src/fsrs/impl/long_term_schduler.ts#L248

Added line #L248 was not covered by tests
}

private update_next(

Check warning on line 251 in src/fsrs/impl/long_term_schduler.ts

View check run for this annotation

Codecov / codecov/patch

src/fsrs/impl/long_term_schduler.ts#L251

Added line #L251 was not covered by tests
next_again: Card,
next_hard: Card,
next_good: Card,
next_easy: Card
) {
const item_again = {

Check warning on line 257 in src/fsrs/impl/long_term_schduler.ts

View check run for this annotation

Codecov / codecov/patch

src/fsrs/impl/long_term_schduler.ts#L257

Added line #L257 was not covered by tests
card: next_again,
log: this.buildLog(Rating.Again),
} satisfies RecordLogItem
const item_hard = {

Check warning on line 261 in src/fsrs/impl/long_term_schduler.ts

View check run for this annotation

Codecov / codecov/patch

src/fsrs/impl/long_term_schduler.ts#L261

Added line #L261 was not covered by tests
card: next_hard,
log: super.buildLog(Rating.Hard),
} satisfies RecordLogItem
const item_good = {

Check warning on line 265 in src/fsrs/impl/long_term_schduler.ts

View check run for this annotation

Codecov / codecov/patch

src/fsrs/impl/long_term_schduler.ts#L265

Added line #L265 was not covered by tests
card: next_good,
log: super.buildLog(Rating.Good),
} satisfies RecordLogItem
const item_easy = {

Check warning on line 269 in src/fsrs/impl/long_term_schduler.ts

View check run for this annotation

Codecov / codecov/patch

src/fsrs/impl/long_term_schduler.ts#L269

Added line #L269 was not covered by tests
card: next_easy,
log: super.buildLog(Rating.Easy),
} satisfies RecordLogItem

this.next.set(Rating.Again, item_again)
this.next.set(Rating.Hard, item_hard)
this.next.set(Rating.Good, item_good)
this.next.set(Rating.Easy, item_easy)

Check warning on line 277 in src/fsrs/impl/long_term_schduler.ts

View check run for this annotation

Codecov / codecov/patch

src/fsrs/impl/long_term_schduler.ts#L274-L277

Added lines #L274 - L277 were not covered by tests
}
}
1 change: 1 addition & 0 deletions src/fsrs/models.ts
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ export interface FSRSParameters {
maximum_interval: number
w: number[]
enable_fuzz: boolean
enable_short_term: boolean
}

export type RescheduleOptions = {
Expand Down