#lang racket
(define *nodes* '((living-room (you are in the living-room.
a wizard is snoring loudly on the couch.))
(garden (you are in a beautiful garden.
there is a well in front of you.))
(attic (you are in the attic.
there is a giant welding torch in the corner.))))
(define (describe-location location nodes)
(cadr (assq location nodes)))
(define *edges* '((living-room (garden west door)
(attic upstairs ladder))
(garden (living-room east door))
(attic (living-room downstairs ladder))))
(define (describe-path edge)
`(there is a ,(caddr edge) going ,(cadr edge) from here.))
(define (describe-paths location edges)
(apply append (map describe-path (cdr (assq location edges)))))
(define *objects* '(whiskey bucket frog chain))
(define *object-locations* '((whiskey living-room)
(bucket living-room)
(chain garden)
(frog garden)))
(define (objects-at loc objs obj-loc)
(letrec ((is-at? (lambda (obj)
(eq? (cadr (assoc obj obj-loc)) loc))))
(filter is-at? objs)))
(define (describe-objects loc objs obj-loc)
(letrec ((describe-obj (lambda (obj)
`
(you see a
,obj on the
floor.
)))) (apply append (map describe-obj (objects-at loc objs obj-loc)))))
(define *location* 'living-room)
(define (look)
(append (describe-location *location* *nodes*)
(describe-paths *location* *edges*)
(describe-objects *location* *objects* *object-locations*)))
(define (walk direction)
(letrec ((correct-way? (lambda (edge)
(eq? (cadr edge) direction))))
(let ((next (findf correct-way? (cdr (assq *location* *edges*)))))
(if next
(begin (set! *location* (car next))
(look))
'(you cannot go that way.)))))
(define-syntax push
(syntax-rules ()
((_ item place) (set! place (cons item place)))))
(define (pickup object)
(cond ((memq object (objects-at *location* *objects* *object-locations*))
(push (list object 'body) *object-locations*)
`(you are now carrying the ,object))
(else '(you cannot get that.))))
(define (inventory)
(cons 'items- (objects-at 'body *objects* *object-locations*)))
(define (have object)
(memq object (cdr (inventory))))
(define (game-repl)
(let ((cmd (game-read)))
(unless (eq? (car cmd) 'quit)
(game-print (game-eval cmd))
(game-repl))))
(define (game-read)
(let ((cmd (read (open-input-string (string-append "(" (read-line) ")")))))
(let ((quote-it (lambda (x)
`(quote ,x))))
(cons (car cmd) (map quote-it (cdr cmd))))))
(define *allowed-commands* '(look walk pickup inventory))
(define (game-eval sexp)
(if (memq (car sexp) *allowed-commands*)
(eval sexp)
'(i do not know that command.)))
(define (tweak-text lst caps lit)
(let loop ((lst lst) (caps caps) (lit lit) (acc '()))
(if (null? lst)
(reverse acc)
(let ((item (car lst))
(rst (cdr lst)))
(cond ((char=? item #\space) (loop rst caps lit (cons item acc)))
((memv item '(#\! #\? #\.)) (loop rst #t lit (cons item acc)))
((char=? item #\") (loop rst caps (not lit) acc))
(lit (loop rst #f lit (cons item acc)))
(caps (loop rst #f lit (cons (char-upcase item) acc)))
(else (loop rst #f #f (cons (char-downcase item) acc))))))))
(define (game-print lst)
(display (list->string (tweak-text (string->list (string-trim (format "~a" lst) #px"\\(+|\\)+|\\ +" #:left? #t #:right? #t)) #t #f)))
(newline))