; https://xn--rpa.cc/irl/term.html
(define-module (depre main)
#:use-module (ice-9 textual-ports))
;; Random seed
(set! *random-state* (random-state-from-platform))
;; Code makes heavy use of functional code and parameters for state.
(define max-100 (lambda (x) (if (> x 100) 100 x)))
(define %depression (make-parameter 50 max-100))
(define %tiredness (make-parameter 50 max-100))
(define (nth n lst)
(if (> n (length lst))
(error "Chosen element longer than provided list")
(let loop ((lst lst)
(n n))
(if (= 1 n)
(car lst)
(loop (cdr lst) (- n 1))))))
;; Function combinations: they define the state transitions
(define (combine . functions)
"Calls given functions in given order."
(lambda ()
(let loop ((fs functions))
(when (not (null? fs))
((car fs))
(loop (cdr fs))))))
;; Transition by chance, according to the state
;; Example:
;; - You are too tired for that, so you do this instead
;; - Or you actually do it
(define (f-or-tired f tired)
(lambda ()
(if (< (random 100) (%tiredness)) (f) (tired))))
(define (f-or-depressed f depressed)
(lambda ()
(if (< (random 100) (%depression)) (f) (depressed))))
;; State alteration functions for easy access
(define (tire)
(lambda () (%tiredness (+ (%tiredness) 5))))
(define (rest)
(lambda () (%tiredness (- (%tiredness) 5))))
(define (depress)
(lambda () (%depression (+ (%depression) 5))))
(define (cheer-up)
(lambda () (%depression (- (%depression) 5))))
;; Simple I/O
(define (answer message)
(lambda ()
(newline)
(display message)
(get-line (current-input-port))))
(define (bar percent)
"A simple 12 character bar for life-like variable representation"
(define len 22)
(string-tabulate
(lambda (x)
(cond
((= x 0) #\[)
((= x (- len 1)) #\])
((>= (/ percent (/ 100 len)) x) #\=)
(else #\space)))
len))
(define (read-number)
(string->number (get-line (current-input-port))))
;; Basic state representation and creation: A state is a function that knows
;; how to jump to other states through a menu.
(define* (state heading menu #:optional (hide-status #f))
(lambda ()
(when (not hide-status)
(newline)
(display "Depression:\t")
(display (bar (%depression)))
(newline)
(display "Tiredness:\t")
(display (bar (%tiredness))))
(newline)
(display heading)
(newline)
(map (lambda (opt num)
(display (string-append " " (number->string num) "- " (car opt)))
(newline))
menu (iota (length menu) 1))
(display "You choose > ")
(let loop ((chosen (read-number)))
(if (and (number? chosen) (<= chosen (length menu)))
((cdr (nth chosen menu)))
(begin
(display "Wrong answer. Try again > ")
(loop (read-number)))))))
;; States:
;; Some macro magic for indirection that enables recursion.
;; I don't really know how it works (yet!)
(define %states (make-hash-table))
(define-macro (register-state name f)
`(hash-set! %states ,name ,f))
(define-macro (to-state name)
`(lambda () ((hash-ref %states ,name))))
(register-state
'morning-1
(state "It's early in the morning. You have time to do whatever you want right now. What do you want to do?"
(list
(cons "Work."
(rest))
(cons "Relax."
(rest)))))
(register-state
'morning-2
(state "It's late in the morning. You still have some time before having lunch. What do you want to do?"
(list (cons "nope" (rest)))))
(register-state
'wake-up
(state "Good morning. It's time to wake up, what do you want to do?"
(list (cons "Wake up and start your day."
(f-or-tired
(combine
(answer "You just woke up, had your shower, had some breakfast and now you are ready for anything.")
(cheer-up)
(tire)
(to-state 'morning-1))
(combine
(answer "You are too tired for waking up now. You stay at bed instead and your day starts in mid morning.")
(depress)
(rest)
(to-state 'morning-2))))
(cons "Stay in bed"
(combine
(answer "You rest for a little bit more, but you feel bad because you have a hard time waking up like a normal person")
(depress)
(rest)
(to-state 'morning-2))))))
(register-state
'start
(state "Hi, game starts"
(list
(cons "Start game"
(combine
(answer "Let's start, then. Press [ENTER] to continue.")
(to-state 'wake-up)))
(cons "Quit game"
(answer "Good bye!")))
#:hide-status))
((to-state 'start))