Du kan inte välja fler än 25 ämnen Ämnen måste starta med en bokstav eller siffra, kan innehålla bindestreck ('-') och vara max 35 tecken långa.

431 lines
12KB

  1. (import comparse)
  2. (import srfi-1)
  3. (import srfi-14)
  4. (import (chicken io))
  5. (import srfi-13)
  6. (import matchable)
  7. (import fmt)
  8. (import ansi-escape-sequences)
  9. (import (chicken file))
  10. ; (define (lift fn parser)
  11. ; (bind parser (compose result fn)))
  12. (define (is-not x)
  13. (satisfies (lambda (y)
  14. (not (eqv? x y)))))
  15. (define parse-whitespace
  16. (one-or-more (is #\space)))
  17. (define skip-whitespace
  18. (skip (zero-or-more (is #\space))))
  19. (define parse-symbol
  20. (lift (compose string->symbol string-downcase list->string) (one-or-more (in char-set:letter))))
  21. (define parse-number
  22. (lift (compose string->number list->string) (one-or-more (in char-set:digit))))
  23. (define parse-string
  24. (lift list->string (enclosed-by (is #\") (one-or-more (is-not #\")) (is #\"))))
  25. (define (followed-by-consuming parser separator)
  26. (sequence* ((value parser) (_ separator))
  27. (result value)))
  28. (define (separated-by separator parser)
  29. (one-or-more (any-of (followed-by-consuming parser separator) parser)))
  30. (define parse-symbol-or-number-or-string
  31. (any-of parse-number parse-symbol parse-string))
  32. (define (completely-parse parser)
  33. (followed-by parser end-of-input))
  34. (define parse-statement
  35. (all-of skip-whitespace (separated-by parse-whitespace parse-symbol-or-number-or-string)))
  36. (define (just fn)
  37. (lambda args
  38. (fn)))
  39. (define (perhaps fn arg)
  40. (if arg
  41. (fn arg)
  42. arg))
  43. (define display-newline
  44. (compose (just newline) display))
  45. (define (display-lines ln)
  46. (perhaps (cut map display-newline <>) ln))
  47. (define (parse-input)
  48. (parse (completely-parse parse-statement) (read-line)))
  49. (define (type-of elem)
  50. (cond ((pair? elem) 'pair)
  51. ((symbol? elem) 'symbol)
  52. ((number? elem) 'number)
  53. ((char? elem) 'char)
  54. ((string? elem) 'string)
  55. ((boolean? elem) 'boolean)))
  56. (define (show str)
  57. (fmt #t (dsp (wrap-lines str))))
  58. (define (prompt str)
  59. (newline)
  60. (display str)
  61. (let ((result (read-line)))
  62. (if (equal? "" result)
  63. (prompt str)
  64. result)))
  65. (define (prompt-yn str)
  66. (newline)
  67. (display str)
  68. (let ((result (string-downcase (read-line))))
  69. (cond ((equal? "yes" result) #t)
  70. ((equal? "no" result) #t)
  71. (else (begin
  72. (newline)
  73. (display "Please answer yes or no.")
  74. (prompt-yn str))))))
  75. (define +articles-prepositions+
  76. '(a an the into on to at))
  77. (define (adventure-prompt)
  78. (let ((result (parse (completely-parse parse-statement) (prompt "> "))))
  79. (if result
  80. (let ((grug-result (filter (lambda (n) (not (member n +articles-prepositions+))) result)))
  81. (if (not (null? grug-result))
  82. grug-result
  83. (begin (display "I didn't quite understand that.")
  84. (adventure-prompt))))
  85. (begin (display "I didn't quite understand that.")
  86. (adventure-prompt)))))
  87. (define *database* '())
  88. (define (database-set name key value)
  89. (set! *database* (let loop ((kv *database*))
  90. (if (null? kv)
  91. (list (cons name (list (cons key value))))
  92. (if (equal? name (caar kv))
  93. (cons (cons name (let loop ((kv (cdar kv)))
  94. (if (null? kv)
  95. (list (cons key value))
  96. (if (equal? key (caar kv))
  97. (cons (cons key value) (cdr kv))
  98. (cons (car kv) (loop (cdr kv))))))) (cdr kv))
  99. (cons (car kv) (loop (cdr kv))))))))
  100. (define (database-get name key default)
  101. (let loop ((kv *database*))
  102. (if (null? kv)
  103. default
  104. (if (equal? name (caar kv))
  105. (let loop ((kv (cdar kv)))
  106. (if (null? kv)
  107. default
  108. (if (equal? key (caar kv))
  109. (cdar kv)
  110. (loop (cdr kv)))))
  111. (loop (cdr kv))))))
  112. (define (database-save filename)
  113. (with-output-to-file filename (cut write *database*)))
  114. (define (database-load filename)
  115. (with-input-from-file filename (lambda () (set! *database* (car (read-list))))))
  116. (define (get-all-objects)
  117. (map car *database*))
  118. (define (object-exists? object)
  119. (member object (get-all-objects)))
  120. (define (has-property? object property)
  121. (database-get object property #f))
  122. (define (toggle-flag object flag)
  123. (if (has-property? object flag)
  124. (database-set object flag #f)
  125. (database-set object flag #t)))
  126. (define (get-location object)
  127. (database-get object 'location #f))
  128. (define (set-name object name)
  129. (database-set object 'name name))
  130. (define (set-description object description)
  131. (database-set object 'description description))
  132. (define (get-name object)
  133. (database-get object 'name (symbol->string object)))
  134. (define (get-description object)
  135. (database-get object 'description "You see the swirling void of creation."))
  136. (define (get-container object)
  137. (database-get object 'container #f))
  138. (define (get-contents object)
  139. (database-get object 'contents '()))
  140. (define (set-destination object destination)
  141. (database-set object 'destination destination))
  142. (define (get-destination object)
  143. (database-get object 'destination #f))
  144. (define (set-enter-message object msg)
  145. (database-set object 'enter-message msg))
  146. (define (get-enter-message object)
  147. (database-get object 'enter-message #f))
  148. (define (get-aliases object)
  149. (database-get object 'aliases '()))
  150. (define (set-aliases object alias-list)
  151. (database-set object 'aliases alias-list))
  152. (define (add-alias object alias)
  153. (let ((aliases (get-aliases object)))
  154. (if (not (member alias aliases))
  155. (set-aliases object (cons alias aliases)))))
  156. (define (remove-alias object alias)
  157. (let ((aliases (get-aliases object)))
  158. (if (member alias aliases)
  159. (set-aliases object (remove (cut eq? alias <>) aliases)))))
  160. ;; Is development mode enabled?
  161. (define (devmode-enabled?)
  162. (has-property? 'you 'devmode))
  163. (define (toggle-devmode)
  164. (toggle-flag 'you 'devmode))
  165. ;; Is an object fixed in place (e.g. cannot be picked up?)
  166. (define (fixed? object)
  167. (has-property? object 'fixed))
  168. (define (toggle-fixed object)
  169. (toggle-flag object 'fixed))
  170. ;; Match a tag against a list of objects, checking for its tag and its aliases.
  171. (define (match-object tag objects)
  172. (let loop ((objects objects))
  173. (if (null? objects)
  174. #f
  175. (let ((taglist (cons (car objects) (get-aliases (car objects)))))
  176. (if (member tag taglist)
  177. (car objects)
  178. (loop (cdr objects)))))))
  179. (define (create-object tag name description)
  180. (set-name tag name)
  181. (set-description tag description))
  182. (define (move-object object container)
  183. (let ((prev-container (get-container object)))
  184. (database-set object 'container container)
  185. (let ((contents (get-contents container)))
  186. (if (not (member object contents))
  187. (begin
  188. (database-set container 'contents (cons object contents))
  189. (database-set prev-container 'contents (remove (cut eq? object <>) (get-contents prev-container))))))))
  190. ;; Determine the objects visible to a source object, zork-style
  191. (define (visible-objects source)
  192. (let ((result (get-container source)))
  193. (if (and result (object-exists? result))
  194. (cons (get-container source) (get-contents (get-container source)))
  195. (error "Tried to determine visible objects for object without a container."))))
  196. (define (do-command-enter tag)
  197. (let ((object (match-object tag (visible-objects 'you))))
  198. (if (not object)
  199. (show "You cannot go that way.")
  200. (let ((destination (get-destination object)))
  201. (if (not destination)
  202. (show "You cannot enter that.")
  203. (begin
  204. (move-object 'you destination)
  205. (perhaps show (get-enter-message object))
  206. (print-room-description (get-container 'you))))))))
  207. (define (print-room-description room)
  208. (newline)
  209. (display (set-text '(bold) (get-name room)))
  210. (newline)
  211. (display " ")
  212. (fmt #t (dsp (wrap-lines (get-description room))))
  213. (newline)
  214. (display "You see: ")
  215. (map (lambda (n) (display n) (display " ")) (map get-name (remove (cut eq? 'you <>) (get-contents room))))
  216. (newline))
  217. (define (do-command-save)
  218. (let ((save-name (prompt "Enter save name: ")))
  219. (if (or (not (file-exists? save-name)) (prompt-yn "That file already exists. Overwrite? "))
  220. (begin
  221. (show "Saving database, please wait...")
  222. (database-save save-name)
  223. (show "Done.")))))
  224. (define (do-command-load)
  225. (let ((save-name (prompt "Enter save file name to load: ")))
  226. (if (not (file-exists? save-name))
  227. (show "That file does not exist.")
  228. (begin
  229. (show "Loading database, please wait...")
  230. (database-load save-name)
  231. (show "Done.")))))
  232. (define (do-command-look)
  233. (print-room-description (get-container 'you)))
  234. (define (do-command-examine tag)
  235. (let ((object (match-object tag (if (devmode-enabled?)
  236. (get-all-objects)
  237. (visible-objects 'you)))))
  238. (if (not object)
  239. (show "You cannot see that here.")
  240. (show (get-description object)))))
  241. (define (do-command-inventory)
  242. (map (compose show get-name) (get-contents 'you)))
  243. (define (do-command-take tag)
  244. (if (not (symbol? tag))
  245. (show "I didn't quite understand that.")
  246. (let ((object (match-object tag (if (devmode-enabled?)
  247. (get-all-objects)
  248. (visible-objects 'you)))))
  249. (if (or (not object) (and (fixed? object) (not (devmode-enabled?))))
  250. (if object
  251. (show "That is fixed in place.")
  252. (show "You cannot see that here."))
  253. (begin
  254. (show (string-append "You get " (get-name object) "."))
  255. (move-object object 'you))))))
  256. (define (do-command-drop tag)
  257. (if (not (symbol? tag))
  258. (show "I didn't quite understand that.")
  259. (let ((object (match-object tag (get-contents 'you))))
  260. (if (not object)
  261. (show "You are not carrying that.")
  262. (begin
  263. (show (string-append "You drop " (get-name object) "."))
  264. (move-object object (get-container 'you)))))))
  265. (define (do-command-devmode)
  266. (toggle-devmode)
  267. (if (devmode-enabled?)
  268. (show "Development mode enabled.")
  269. (show "Development mode disabled.")))
  270. (define (do-command-create tag name description)
  271. (if (not (and (symbol? tag) (string? name) (string? description)))
  272. (show "I didn't quite understand that.")
  273. (if (object-exists? tag)
  274. (show "That object already exists.")
  275. (begin
  276. (create-object tag name description)
  277. (move-object tag (get-container 'you))))))
  278. (define (do-command-rename tag name)
  279. (if (not (and (symbol? tag) (string? name)))
  280. (show "I didn't quite understand that.")
  281. (if (not (object-exists? tag))
  282. (show "That object doesn't exist.")
  283. (begin
  284. (set-name tag name)))))
  285. (define (do-command-describe tag description)
  286. (if (not (and (symbol? tag) (string? description)))
  287. (show "I didn't quite understand that.")
  288. (if (not (object-exists? tag))
  289. (show "That object doesn't exist.")
  290. (begin
  291. (set-description tag description)))))
  292. ;;(define (do-command-dig direction destination)
  293. ;; (
  294. (define (do-command-exit)
  295. (show "Goodbye, see you later...")
  296. (set! *exit-adventure* #t))
  297. (define (alias-transform input)
  298. (match input
  299. (('quit) '(exit))
  300. (('i) '(inventory))
  301. (('inv) '(inventory))
  302. (('look x) `(examine ,x))
  303. (('go x) `(enter ,x))
  304. (('get x) `(take ,x))
  305. (_ input)))
  306. (define (dispatch-command input)
  307. (let ((success #t))
  308. (match input
  309. (('look) (do-command-look))
  310. (('save) (do-command-save))
  311. (('load) (do-command-load))
  312. (('devmode) (do-command-devmode))
  313. (('exit) (do-command-exit))
  314. (('enter x) (do-command-enter x))
  315. (('take x) (do-command-take x))
  316. (('drop x) (do-command-drop x))
  317. (('inventory) (do-command-inventory))
  318. (('examine x) (do-command-examine x))
  319. (('put x y) (do-command-put x y))
  320. (_ (if (devmode-enabled?)
  321. (match input
  322. (('create x y z) (do-command-create x y z))
  323. (('rename x y) (do-command-rename x y))
  324. (('describe x y) (do-command-describe x y))
  325. (('dig x y) (do-command dig x y))
  326. (_ (set! success #f)))
  327. (set! success #f))))
  328. success))
  329. (create-object 'garden "A Well-Kept Garden" "A french-style garden with topiary in the shape of various animals. A fountain gurgles happily in the middle.")
  330. (create-object 'unicorn "a frolicking unicorn" "A white unicorn, with a long spiral horn.")
  331. (create-object 'forest "A Foreboding Forest" "Tall trees bunch around a winding path.")
  332. (create-object 'trail "a trail" "A winding trail.")
  333. (add-alias 'trail 'winding)
  334. (set-enter-message 'trail "You walk along the winding trail...")
  335. (move-object 'you 'garden)
  336. (move-object 'trail 'garden)
  337. (toggle-fixed 'trail)
  338. (set-destination 'trail 'forest)
  339. (move-object 'unicorn 'garden)
  340. (define *exit-adventure* #f)
  341. (define (adventure)
  342. (let ((success (dispatch-command (alias-transform (adventure-prompt)))))
  343. (if (not success)
  344. (begin
  345. (show "I didn't quite understand that.")
  346. (adventure))
  347. (if *exit-adventure*
  348. (display *database*)
  349. (adventure)))))
  350. (print-room-description (get-container 'you))
  351. (adventure)