flashsync / src /sm2_algorithm.gleam
Dhurgh's picture
Permanently remove build artifacts from history
d0b8e8f
Raw
History Blame Contribute Delete
2.26 kB
import gleam/int
import gleam/float
import gleam/string
import gleam/order
pub type ReviewGrade {
Complete
Incorrect
Difficult
Acceptable
Easy
Perfect
}
pub fn grade_to_int(grade: ReviewGrade) -> Int {
case grade {
Incorrect -> 0
Difficult -> 1
Acceptable -> 3
Easy -> 4
Perfect -> 5
Complete -> 2
}
}
pub type SpacedRepetitionState {
SpacedRepetitionState(ease_factor: Float, interval: Int, repetitions: Int, next_review_date: String)
}
pub fn initial_state() -> SpacedRepetitionState {
SpacedRepetitionState(ease_factor: 2.5, interval: 1, repetitions: 0, next_review_date: get_future_date(1))
}
pub fn calculate_next_review(state: SpacedRepetitionState, grade: ReviewGrade) -> SpacedRepetitionState {
let grade_num = grade_to_int(grade)
let new_ease_factor = case grade_num {
n if n < 3 -> 1.3
n -> {
let base = state.ease_factor +. {0.1 -. {5.0 -. int.to_float(n)} *. {0.08 +. {5.0 -. int.to_float(n)} *. 0.02}}
case base <. 1.3 {
True -> 1.3
False -> base
}
}
}
let #(new_interval, new_reps) = case grade_num {
n if n < 3 -> #(1, 0)
_ -> case state.repetitions {
0 -> #(1, 1)
1 -> #(3, 2)
_ -> {
let new_int = int.to_float(state.interval) *. new_ease_factor |> float.round
#(int.max(new_int, 1), state.repetitions + 1)
}
}
}
SpacedRepetitionState(ease_factor: new_ease_factor, interval: new_interval, repetitions: new_reps, next_review_date: get_future_date(new_interval))
}
pub fn calculate_mastery_percentage(state: SpacedRepetitionState) -> Float {
let base = int.to_float(state.repetitions) /. 5.0 *. 100.0
case base >. 100.0 {
True -> 100.0
False -> base
}
}
pub fn should_review(current: String, next: String) -> Bool {
case string.compare(current, next) {
order.Lt -> False
_ -> True
}
}
pub fn get_future_date(days: Int) -> String {
"2026-03-" <> int.to_string(int.min(days + 19, 31))
}
pub fn estimate_total_reviews(target: Float) -> Int {
case target <. 2.0 {
True -> 8
False -> case target <. 2.5 {
True -> 7
False -> 5
}
}
}
pub fn calculate_optimal_schedule(_total: Int) -> List(Int) {
[1, 3, 7, 14, 30, 60, 120, 365]
}