{-# LANGUAGE NumericUnderscores #-}
-- | A tiny demonstration of the FSRS-7 scheduler: grade one card 'Good' every
-- time it comes due, and print what the model makes of it.
module Main (main) where
import Data.Time.Calendar (fromGregorian)
import Data.Time.Clock (NominalDiffTime, UTCTime (..))
import Numeric (showFFloat)
import Text.Printf (printf)
import FSRS
main :: IO ()
main = do
printf "FSRS-7 — %d parameters\n\n" parameterCount
putStrLn (unwords [showFFloat Nothing w "" | w <- parametersToList defaultParameters])
putStrLn ""
putStrLn " # elapsed | state | stability | difficulty | next | R at due"
putStrLn "----------------------------------------------------------------------------"
go 1 epoch (newCard epoch)
where
reviews = 10 :: Int
go :: Int -> UTCTime -> Card -> IO ()
go n now card
| n > reviews = pure ()
| otherwise = do
let (card', entry) = reviewCard defaultScheduler card Good now
memory = logMemoryAfter entry
due = cardDue card'
printf
"%2d %10s | %-10s | %10.4f | %10.4f | %-9s | %.4f\n"
n
(humanDuration (realToFrac (logElapsedDays entry * 86400) :: NominalDiffTime))
(show (cardState card'))
(memoryStability memory)
(memoryDifficulty memory)
(humanDuration (logInterval entry))
(maybe 1 id (cardRetrievability defaultScheduler card' due))
go (n + 1) due card'
epoch :: UTCTime
epoch = UTCTime (fromGregorian 2026 1 1) 0
humanDuration :: NominalDiffTime -> String
humanDuration dt
| seconds < 60 = printf "%.0fs" seconds
| seconds < 3_600 = printf "%.0fm" (seconds / 60)
| seconds < 86_400 = printf "%.1fh" (seconds / 3_600)
| otherwise = printf "%.2fd" (seconds / 86_400)
where
seconds = realToFrac dt :: Double