#lang racket/gui
(require srfi/13 srfi/14)
(define *nodes* '((居間 (あなたは魔法使いの館の居間にいる。魔法使いはソファでいびきをかいている。))
(庭 (あなたは美しい庭にいる。目の前に井戸がある。))
(屋根裏 (あなたは屋根裏にいる。部屋の隅に巨大な溶接バーナーがある。))))
(define (describe-location location nodes)
(second (assq location nodes)))
(define *edges* '((居間 (庭 西 に向かう ドア)
(屋根裏 上 に続く 梯子))
(庭 (居間 東 に向かう ドア))
(屋根裏 (居間 下 に続く 梯子))))
(define (describe-path edge)
`(ここから ,(second edge) ,(third edge) ,(fourth edge) がある。))
(define (describe-paths location edges)
(apply append (map describe-path (cdr (assq location edges)))))
(define *objects* '(ウィスキー バケツ カエル 鎖))
(define *object-locations* '((ウィスキー 居間)
(バケツ 居間)
(鎖 庭)
(カエル 庭)))
(define (objects-at loc objs obj-locs)
(letrec ((at-loc? (lambda (obj)
(eq? (second (assq obj obj-locs)) loc))))
(filter at-loc? objs)))
(define (describe-objects loc objs obj-loc)
(letrec ((describe-obj (lambda (obj)
`(床に ,obj がある。))))
(apply append (map describe-obj (objects-at loc objs obj-loc)))))
(define *location* '居間)
(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? (second edge) direction))))
(let ((next (findf correct-way (cdr (assq *location* *edges*)))))
(if next
(begin (set! *location* (car next))
(look))
'(そちらには行けない。)))))
(define (pickup object)
(cond ((memq object (objects-at *location* *objects* *object-locations*))
(set! *object-locations* (cons `(,object body) *object-locations*))
`(あなたは ,object を持っている。))
(else '(それは持てない。))))
(define (inventory)
(cons 'アイテム- (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) (map car *allowed-commands*))
(eval `(,(cdr (assq (car sexp) *allowed-commands*)) ,@(cdr sexp)))
'(そんなコマンドは知らない。)))
(define (tweak-text lst caps lit)
(let loop ((item (car lst)) (rst (cdr lst)) (caps caps) (lit lit) (acc '()))
(cond
((null? rst) (reverse acc))
((char=? item #\space) (loop (car rst) (cdr rst) caps lit acc))
((memq item '(#\! #\? #\。)) (loop (car rst) (cdr rst) #t lit (cons item acc)))
((char=? item #\") (loop (car rst) (cdr rst) caps (not lit) acc))
(lit (loop (car rst) (cdr rst) #f lit (cons item acc)))
(caps (loop (car rst) (cdr rst) #f lit (cons item acc)))
(else (loop (car rst) (cdr rst) #f #f (cons item acc))))))
(define (game-print lst)
(display (list->string (tweak-text (string->list (string-trim (format "~a" lst) (char-set-adjoin char-set:whitespace #\( #\)))) #t #f)))
(newline))