Files
kiss-vscode/projects/habit-puzzle-game/source/HabitModel.kiss

129 lines
6.3 KiB
Plaintext

(prop :Array<Entry> dailyEntries [])
(prop :Array<Entry> bonusEntries [])
(prop :Array<Entry> todoEntries [])
(prop :Array<RewardFile> rewardFiles [])
(defNew [&prop :String textFile]
(let [s (Stream.fromFile textFile)
&mut lastHeader ""]
// TODO could be whileLet
(loop
(case (s.takeLine)
((Some "DAILY")
(set lastHeader "DAILY"))
((Some "BONUS")
(set lastHeader "BONUS"))
((Some "TODO")
(set lastHeader "TODO"))
((Some "FILES")
(set lastHeader "FILES"))
((when (apply = (concat ["-"] (line.split ""))) (Some line))
(continue))
((Some "") (continue))
// Types won't unify with the next case, so this is its own:
((when (= lastHeader "FILES") (Some line))
(rewardFiles.push
(let [parts (line.split " ")
startingPoints (Std.parseInt (parts.pop))
path (parts.join " ")]
(objectWith path startingPoints))))
((Some line)
(.push
(case lastHeader
("DAILY" dailyEntries)
("BONUS" bonusEntries)
("TODO" todoEntries)
(otherwise (throw "bad header")))
(object
type
(case lastHeader
("BONUS" Bonus)
("TODO" Todo)
("DAILY"
(case (line.split ":")
([noColon]
(Daily
// all days of week
(collect (range 7))
// never done before
""))
([::&mut preColon ...afterColon]
(set line (afterColon.join ":"))
(Daily
// Days of week specified by abbreviation:
(sort (filter
[
// disambiguate Th from T and Su from S:
(when (contains preColon "Th") {(set preColon (StringTools.replace preColon "Th" "")) 4})
(when (contains preColon "Su") {(set preColon (StringTools.replace preColon "Su" "")) 0})
(when (contains preColon "M") 1)
(when (contains preColon "T") 2)
(when (contains preColon "W") 3)
(when (contains preColon "F") 5)
(when (contains preColon "S") 6)
]))
// Last date completed after that:
(ifLet [[days date] (preColon.split " ")]
date
"")))
(otherwise (throw "bad line"))))
(otherwise (throw "bad header")))
labels
(for l (line.split "/")
(object
label (StringTools.trim (StringTools.replace l "|" ""))
points (count (l.split "") ->c (= c "|")))))))
(otherwise (break))))))
(method :Int totalPoints []
(apply + (for l (flatten (for e (the Array<Entry> (concat dailyEntries bonusEntries todoEntries)) e.labels)) l.points)))
(function :String stringify [:Entry e]
"$(ifLet [(Daily days lastDayDone) e.type]
(+
(.join (for day days
(case day
(0 "Su")
(1 "M")
(2 "T")
(3 "W")
(4 "Th")
(5 "F")
(6 "S")
(otherwise (throw "bad day")))) "")
" "
lastDayDone
": ")
"")$(.join (for label e.labels
"${label.label} $(* "|" label.points)") "/")")
(function :String stringifyRewardFile [:RewardFile rewardFile]
"${rewardFile.path} ${rewardFile.startingPoints}")
(method :Void save []
(localVar &mut content "DAILY\n-----\n")
(+= content (.join (map dailyEntries stringify) "\n") "\n")
(+= content "\nBONUS\n-----\n")
(+= content (.join (map bonusEntries stringify) "\n") "\n")
(+= content "\nTODO\n----\n")
(+= content (.join (map todoEntries stringify) "\n") "\n")
(+= content "\nFILES\n-----\n")
(+= content (.join (map rewardFiles stringifyRewardFile) "\n") "\n")
(File.saveContent textFile
content))
// With rotating entries, the active one is the first one with the lowest score:
(function :EntryLabel activeLabel [:Entry e]
(let [lowScore (apply min (for label e.labels label.points))]
(doFor label e.labels (when (= lowScore label.points) (return label)))
(throw "no active?!")))
(function todayString []
(let [d (Date.now)] "$(d.getDate)-$(+ 1 (d.getMonth))-$(d.getFullYear)"))
(function isActive [:Entry e]
(case e.type
((Daily days lastDayDone)
(and !(= lastDayDone (todayString)) (contains days (.getDay (Date.now)))))
(otherwise true)))