; https://xn--rpa.cc/irl/term.html (define-module (depre main) #:use-module (ice-9 textual-ports)) ;; Clean indentation from strings (eval-when (expand load eval) (read-enable 'hungry-eol-escapes)) ;; 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 40 max-100)) (define *tiredness* (make-parameter 40 max-100)) (define *day* (make-parameter 1)) ;; Control SIGINT (sigaction SIGINT (lambda (sig) (exit-with-message))) ;; Exit message (define (exit-with-message) (newline) (display "---------------------------") (newline) (display "Exiting game.") (newline) (display (string-append "Days played: " (number->string (*day*)))) (newline) (newline) (display "This game tries to show the internal dialog of a depressed person \ that is alone at home, working as a freelance, on a phd or any job \ that allows to work on an independent way.\n\ Somehow, it tries to express the life of those who suffer in \ silence, alone in their caves. Hopefully, this game made you \ empathize with them. \n\ If you feel identified by what it is shown here, it might be time \ to review your internal dialog and treat yourself better. You \ deserve to be loved.\n") (newline) (display "Thanks for playing.") (newline) (exit)) (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 ;; NOTE: The functions try with 120 to leave some chance to randomness even if ;; you are tired or depressed to the limit (define (f-or-tired f tired) (lambda () (if (< (random 120) (*tiredness*)) (f) (tired)))) (define (f-or-depressed f depressed) (lambda () (if (< (random 120) (*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)))) (define (tire+) (lambda () (*tiredness* (+ (*tiredness*) 8)))) (define (rest+) (lambda () (*tiredness* (- (*tiredness*) 8)))) (define (depress+) (lambda () (*depression* (+ (*depression*) 8)))) (define (cheer-up+) (lambda () (*depression* (- (*depression*) 8)))) (define (next-day) (lambda () (*day* (+ 1 (*day*))))) ;; Simple I/O (define (answer message) (lambda () (newline) (display message) (when (eof-object? (get-line (current-input-port))) (exit-with-message)))) (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 (day-banner) (lambda () (newline) (display "---------------------------") (newline) (display (string-append "Day " (number->string (*day*)))) (newline))) (define (read-number) (let ((line (get-line (current-input-port)))) (if (eof-object? line) (exit-with-message) (string->number line)))) ;; 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 'start (state "Hi, game starts" (list (cons "Start game" (combine (answer "Let's start, then. Press [ENTER] to continue. \ Exit with [Ctrl-D].") (to-state 'wake-up))) (cons "Quit game" (answer "Good bye!"))) #:hide-status)) (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 '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." (f-or-tired (combine (answer "You worked for a couple of hours.") (tire) (to-state 'morning-2)) (combine (answer "You are too tired to work. You do nothing for a \ couple of hours and you feel bad for wasting your \ time.") (depress+) (to-state 'morning-2)))) (cons "Relax." (combine (answer "You do nothing for a couple of hours and feel guilty \ because you didn't work.") (depress) (rest) (to-state 'morning-2)))))) (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 "Work." (f-or-depressed (f-or-tired (combine (answer "You worked for a couple of hours.") (tire) (cheer-up) (to-state 'lunch)) (combine (answer "You are too tired to work. Not that long ago you \ were able to work for longer time. You are getting \ worse.") (depress+) (to-state 'lunch))) (combine (answer "It's almost time to have lunch... Maybe it's better \ just to leave work for later.") (depress) (to-state 'lunch)))) (cons "Relax." (combine (answer "You do nothing for a couple of hours. Is this what you \ call \"working hard\"?") (depress) (rest+) (to-state 'lunch)))))) (register-state 'lunch (state "You have something for lunch. What do you want to do now?" (list (cons "Work." (f-or-depressed (f-or-tired (combine (answer "You worked for a couple of hours.") (tire) (to-state 'evening)) (combine (answer "You are too tired to work. As always.") (depress+) (to-state 'evening))) (combine (answer "Work? What for? It's meaningless. Just let your life \ pass while asking yourself why you are doing nothing.") (depress) (to-state 'evening)))) (cons "Relax." (f-or-depressed (combine (answer "You do nothing. Sometimes it's good for you.") (depress) (rest) (to-state 'evening)) (combine (answer "You can't just relax. You are worthless if you don't \ work. \ You overwork and spend the whole afternoon working in \ bad conditions.") (tire+) (to-state 'dinner))))))) (register-state 'evening (state "You still have some time until dinner. What do you want to do?" (list (cons "Work." (f-or-depressed (combine (answer "You work. It's late so it's really tiring and you have \ a hard time concentrating.") (tire+) (to-state 'dinner)) (combine (answer "You work because what else you could do? There's \ nothing else in your life.\ You work in bad conditions and you skip dinner because \ you are obsessed with your job.") (depress) (tire+) (day-banner) (to-state 'wake-up)))) (cons "Relax at home." (f-or-depressed (combine (answer "You relax at home. You stayed at home the whole day. \ Again.") (depress+) (to-state 'dinner)) (combine (answer "You are bored, you play around with work related stuff \ until it's time to have dinner.") (tire) (to-state 'dinner)))) (cons "Go outside." (f-or-depressed (combine (answer "You go outside for a walk.") (cheer-up+) (cheer-up) (to-state 'dinner)) (combine (answer "It's already dark outside. You don't feel like going \ outside. You wait doing nothing until dinner time.") (depress) (to-state 'dinner))))))) (register-state 'dinner (state "Time for dinner. What do you want to do afterwards?" (list (cons "Work" (f-or-tired (combine (answer "You work for some time and then go to bed.") (cheer-up) (tire) (next-day) (day-banner) (to-state 'wake-up)) (combine (answer "You are too tired to work. You watch some TV while \ you could be doing something productive. The story \ of your life.") (depress) (rest) (next-day) (day-banner) (to-state 'wake-up)))) (cons "Relax" (f-or-depressed (combine (answer "You manage to relax watching a film. And go to bed \ afterwards.") (day-banner) (to-state 'wake-up)) (combine (answer "You didn't work enough during the day and you want \ to relax. You should be working.\n \ You work until it's late and then go to bed.") (tire+) (depress) (next-day) (day-banner) (to-state 'wake-up))))))) ((to-state 'start))