south "exit" from lab
[mudsync.git] / worlds / bricabrac.scm
index 994e00d0e1234196910c903a3d2c8a2a931b7417..18885a4602d57f978130d8b4e05c26f6d552dd0a 100644 (file)
@@ -1,5 +1,5 @@
 ;;; Mudsync --- Live hackable MUD
-;;; Copyright © 2016 Christopher Allan Webber <cwebber@dustycloud.org>
+;;; Copyright © 2016, 2017 Christopher Allan Webber <cwebber@dustycloud.org>
 ;;;
 ;;; This file is part of Mudsync.
 ;;;
       #:text (slot-ref actor 'read-text)))
 
 
+;; This one is just where reading is the same thing as looking
+;; at the description
+(define-class <readable-desc> (<gameobj>)
+  (commands
+   #:allocation #:each-subclass
+   #:init-thunk (build-commands
+                 ("read" ((direct-command cmd-look-at))))))
+
 ;; This one allows you to take from items that are proxied by it
 (define-actor <proxy-items> (<gameobj>)
   ((cmd-take-from take-from-proxy))
 ;;; -----
 
 (define (npc-chat-randomly actor message . _)
+  (define catchphrase
+    (random-choice (slot-ref actor 'catchphrases)))
   (define text-to-send
-    (format #f "~a says: \"~a\"\n"
-            (slot-ref actor 'name)
-            (random-choice (slot-ref actor 'catchphrases))))
+    ((slot-ref actor 'chat-format) actor catchphrase))
   (<- (message-from message) 'tell
       #:text text-to-send))
 
@@ -117,6 +125,10 @@ or 'skribe'?  Now *that's* composition!"))
 (define-class <chatty-npc> (<gameobj>)
   (catchphrases #:init-value '("Blarga blarga blarga!")
                 #:init-keyword #:catchphrases)
+  (chat-format #:init-value (lambda (npc catchphrase)
+                              `(,(slot-ref npc 'name) " says: \""
+                                ,catchphrase "\""))
+               #:init-keyword #:chat-format)
   (commands
    #:allocation #:each-subclass
    #:init-thunk (build-commands
@@ -337,6 +349,12 @@ and buttons and springs.  Some of them have wind-up knobs on them."
 You can imagine yourself joining a tea party using this set, around a
 nice table with some doilies, drinking some Earl Grey tea, hot.  Mmmm."
     #:goes-by '("tea set" "tea"))
+   ('lobby:cups
+    <cabinet-item> 'lobby
+    #:invisible? #t
+    #:name "cups from the tea set"
+    #:desc "They're chipped."
+    #:goes-by '("cups"))
    ('lobby:mustard-pot
     <cabinet-item> 'lobby
     #:invisible? #t
@@ -579,6 +597,218 @@ platter!  It has \"RL02.5\" written on it.  It looks kind of loose."
 ;;; Playroom
 ;;; --------
 
+(define-actor <rgb-machine> (<gameobj>)
+  ((cmd-run rgb-machine-cmd-run)
+   (cmd-reset rgb-machine-cmd-reset))
+  (commands
+   #:allocation #:each-subclass
+   #:init-thunk (build-commands
+                 (("run" "start") ((direct-command cmd-run)))
+                 ("reset" ((direct-command cmd-reset)))))
+  (resetting #:init-value #f
+             #:accessor .resetting)
+  ;; used to reset, and to kick off the first item in the list
+  (rgb-items #:init-keyword #:rgb-items
+             #:accessor .rgb-items))
+
+(define (rgb-machine-cmd-run rgb-machine message . _)
+  (define player (message-from message))
+  (<-wait player 'tell
+          #:text '("You start the rube goldberg machine."))
+  (<-wait (gameobj-loc rgb-machine) 'tell-room
+          #:text `(,(mbody-val (<-wait player 'get-name))
+                   " runs the rube goldberg machine.")
+          #:exclude player)
+  (8sleep 1)
+  (match (.rgb-items rgb-machine)
+    ((first-item rest ...)
+     (<- (dyn-ref rgb-machine first-item) 'trigger))))
+
+(define (rgb-machine-cmd-reset rgb-machine message . _)
+  (define player (message-from message))
+  (cond
+   ((not (.resetting rgb-machine))
+    (set! (.resetting rgb-machine) #t)
+    (<-wait player 'tell
+            #:text '("You reset the rube goldberg machine."))
+    (<-wait (gameobj-loc rgb-machine) 'tell-room
+            #:text `(,(mbody-val (<-wait player 'get-name))
+                     " resets the rube goldberg machine.")
+            #:exclude player)
+    (<-wait (gameobj-loc rgb-machine) 'tell-room
+            #:text '("From a panel in the wall, a white gloved mechanical "
+                     "arm reaches out to reset all the "
+                     "rube goldberg components."))
+    (8sleep (/ 1 2))
+    (for-each
+     (lambda (rgb-item)
+       (<- (dyn-ref rgb-machine rgb-item) 'reset)
+       (8sleep (/ 1 2)))
+     (.rgb-items rgb-machine))
+    (<- (gameobj-loc rgb-machine) 'tell-room
+        #:text "The machine's mechanical arm retreats into the wall!")
+    (set! (.resetting rgb-machine) #f))
+   (else
+    (<-wait player 'tell
+            #:text '("But it's in the middle of resetting right now!")))))
+
+(define-actor <rgb-item> (<gameobj>)
+  ((trigger rgb-item-trigger)
+   (reset rgb-item-reset))
+  (invisible? #:init-value #t)
+  (steps #:init-keyword #:steps
+         #:accessor .steps)
+  (triggers-as #:init-value #f
+               #:init-keyword #:triggers-as
+               #:getter .triggers-as)
+  (reset-msg #:init-keyword #:reset-msg
+             #:getter .reset-msg)
+  ;; States: ready -> running -> ran
+  (state #:init-value 'ready
+         #:accessor .state))
+
+
+(define (rgb-item-trigger rgb-item message . _)
+  (define room (gameobj-loc rgb-item))
+  (case (.state rgb-item)
+    ((ready)
+     ;; Set state to running
+     (set! (.state rgb-item) 'running)
+
+     ;; Loop through all steps
+     (for-each
+      (lambda (step)
+        (match step
+          ;; A string?  That's the description of what's happening, tell players
+          ((? string? str)
+           (<- room 'tell-room #:text str))
+          ;; A number?  Sleep for that many secs
+          ((? number? num)
+           (8sleep num))
+          ;; A symbol?  That's another gameobj to look up dynamically
+          ((? symbol? sym)
+           (<- (dyn-ref rgb-item sym) 'trigger
+               #:triggered-by (.triggers-as rgb-item)))
+          (_ (throw 'unknown-step-type
+                    "Don't know how to process rube goldberg machine step type?"
+                    #:step step))))
+      (.steps rgb-item))
+
+     ;; We're done! Set state to ran
+     (set! (.state rgb-item) 'ran))
+
+    (else
+     (<- room 'tell-room
+         #:text `("... but " ,(slot-ref rgb-item 'name)
+                  " has already been triggered!")))))
+
+(define (rgb-item-reset rgb-item message . _)
+  (define room (gameobj-loc rgb-item))
+  (case (.state rgb-item)
+    ((ran)
+     (set! (.state rgb-item) 'ready)
+     (<- room 'tell-room
+         #:text (.reset-msg rgb-item)))
+    ((running)
+     (<- room 'tell-room
+         #:text `("... but " ,(slot-ref rgb-item 'name)
+                  " is currently running!")))
+    ((ready)
+     (<- room 'tell-room
+         #:text `("... but " ,(slot-ref rgb-item 'name)
+                  " has already been reset.")))))
+
+(define-actor <rgb-kettle> (<rgb-item>)
+  ((trigger rgb-kettle-trigger)
+   (reset rgb-kettle-reset))
+  (heated #:accessor .heated
+          #:init-value #f)
+  (filled #:accessor .filled
+          #:init-value #f))
+
+(define* (rgb-kettle-trigger rgb-item message #:key triggered-by)
+  (define room (gameobj-loc rgb-item))
+  (if (not (eq? (.state rgb-item) 'ran))
+      (begin
+        (match triggered-by
+          ('water-demon
+           (set! (.state rgb-item) 'running)
+           (set! (.filled rgb-item) #t))
+          ('quik-heater
+           (set! (.state rgb-item) 'running)
+           (set! (.heated rgb-item) #t)))
+        (when (and (.filled rgb-item)
+                   (.heated rgb-item))
+          (<- room 'tell-room
+              #:text '((i "*kshhhhhh!*")
+                       " The water has boiled!"))
+          (8sleep .25)
+          (set! (.state rgb-item) 'ran)
+          ;; insert a cup of hot tea in the room
+          (create-gameobj <hot-tea> (gameobj-gm rgb-item) room)
+          (<- room 'tell-room
+              #:text '("The machine pours out a cup of hot tea! "
+                       "Looks like the machine finished!"))))
+      (<- room 'tell-room
+         #:text `("... but " ,(slot-ref rgb-item 'name)
+                  " has already been triggered!"))))
+
+(define (rgb-kettle-reset rgb-item message . rest-args)
+  (define room (gameobj-loc rgb-item))
+  (when (eq? (.state rgb-item) 'ran)
+    (set! (.heated rgb-item) #f)
+    (set! (.filled rgb-item) #f))
+  (apply rgb-item-reset rgb-item message rest-args))
+
+(define-actor <hot-tea> (<gameobj>)
+  ((cmd-drink hot-tea-cmd-drink)
+   (cmd-sip hot-tea-cmd-sip)
+   (cmd-gotta-hold hot-tea-cmd-gotta-hold))
+  (contained-commands
+   #:allocation #:each-subclass
+   #:init-thunk (build-commands
+                 ("drink" ((direct-command cmd-drink)))
+                 ("sip" ((direct-command cmd-sip)))))
+  
+  (sips-left #:init-value 4
+             #:accessor .sips-left)
+  (name #:init-value "a cup of hot tea")
+  (take-me? #:init-value #t)
+  (goes-by #:init-value '("cup of hot tea" "cup of tea" "tea" "cup"))
+  (desc #:init-value "It's a steaming cup of hot tea.  It looks pretty good!"))
+
+(define (hot-tea-cmd-drink hot-tea message . _)
+  (define player (message-from message))
+  (define player-loc (mbody-val (<-wait player 'get-loc)))
+  (define player-name (mbody-val (<-wait player 'get-name)))
+  (<- player 'tell
+      #:text "You drink a steaming cup of hot tea all at once... hot hot hot!")
+  (<- player-loc 'tell-room
+      #:text `(,player-name
+               " drinks a steaming cup of hot tea all at once.")
+      #:exclude player)
+  (gameobj-self-destruct hot-tea))
+
+(define (hot-tea-cmd-sip hot-tea message . _)
+  (define player (message-from message))
+  (define player-loc (mbody-val (<-wait player 'get-loc)))
+  (define player-name (mbody-val (<-wait player 'get-name)))
+  (set! (.sips-left hot-tea) (- (.sips-left hot-tea) 1))
+  (<- player 'tell
+      #:text "You take a sip of your steaming hot tea.  How refined!")
+  (<- player-loc 'tell-room
+      #:text `(,player-name
+               " takes a sip of their steaming hot tea.  How refined!")
+      #:exclude player)
+  (when (= (.sips-left hot-tea) 0)
+    (<- player 'tell
+        #:text "You've finished your tea!")
+    (<- player-loc 'tell-room
+        #:text `(,player-name
+                 " finishes their tea!")
+        #:exclude player)
+    (gameobj-self-destruct hot-tea)))
+
 (define playroom
   (lol
    ('playroom
@@ -630,7 +860,106 @@ if this room is intended for children or child-like adults.")
     #:name "a rubber duck"
     #:goes-by '("rubber duck" "duck")
     #:take-me? #t
-    #:desc "It's a yellow rubber duck with a bright orange beak.")))
+    #:desc "It's a yellow rubber duck with a bright orange beak.")
+
+   ('playroom:rgb-machine
+    <rgb-machine> 'playroom
+    #:name "a Rube Goldberg machine"
+    #:goes-by '("rube goldberg machine" "machine")
+    #:rgb-items '(playroom:rgb-dominoes
+                  playroom:rgb-switch-match
+                  playroom:rgb-candle
+                  playroom:rgb-catapult
+                  playroom:rgb-water-demon
+                  playroom:rgb-quik-heater
+                  playroom:rgb-kettle)
+    #:desc "It's one of those hilarious Rube Goldberg machines.
+What could happen if you started it?")
+
+   ;; Dominoes topple
+   ('playroom:rgb-dominoes
+    <rgb-item> 'playroom
+    #:name "some dominoes"
+    #:goes-by '("dominoes" "some dominoes")
+    #:steps `("The dominoes topple down the line..."
+              1
+              "The last domino lands on a switch!"
+              1.5
+              playroom:rgb-switch-match)
+    #:reset-msg "The dominoes are placed back into position.")
+
+   ;; Which hit the switch and strike a match
+   ('playroom:rgb-switch-match
+    <rgb-item> 'playroom
+    #:name "a switch"
+    #:goes-by '("switch" "match")
+    #:steps `("The switch lights a match!"
+              ,(/ 2 3)
+              "The match lights a candle!"
+              1.5
+              playroom:rgb-candle)
+    #:reset-msg "A fresh match is installed and the switch is reset.")
+   ;; which lights a candle and burns a rope
+   ('playroom:rgb-candle
+    <rgb-item> 'playroom
+    #:name "a candle"
+    #:goes-by '("candle")
+    #:steps `("The candle burns..."
+              (/ 2 3)  ; oops!
+              "The candle is burning away a rope!"
+              2
+              "The rope snaps!"
+              .5
+              playroom:rgb-catapult)
+    #:reset-msg "A fresh candle is installed.")
+   ;; which catapults a rock
+   ('playroom:rgb-catapult
+    <rgb-item> 'playroom
+    #:name "a catapult"
+    #:goes-by '("catapult")
+    #:steps `("The snapped rope unleashes a catapult, which throws a rock!"
+              2
+              "The rock flies through a water demon, startling it!"
+              .5
+              playroom:rgb-water-demon
+              2
+              "The rock whacks into the quik-heater's on button!"
+              .5
+              playroom:rgb-quik-heater)
+    #:reset-msg
+    '("A fresh rope is attached to the catapult, which is pulled taught. "
+      "A fresh rock is placed on the catapult."))
+   ;; which both:
+   ;;   '- panics the water demon
+   ;;      '- which waters the kettle
+   ('playroom:rgb-water-demon
+    <rgb-item> 'playroom
+    #:name "the water demon"
+    #:triggers-as 'water-demon
+    #:goes-by '("water demon" "demon")
+    #:steps `("The water demon panics, and starts leaking water into the kettle below!"
+              3
+              "The kettle is filled!"
+              playroom:rgb-kettle)
+    #:reset-msg '("The water demon is scratched behind the ears and calms down."))
+   ;;   '- bops the quik-heater button
+   ;;      '- which heats the kettle
+   ('playroom:rgb-quik-heater
+    <rgb-item> 'playroom
+    #:name "the quik heater"
+    #:triggers-as 'quik-heater
+    #:goes-by '("quik heater" "heater")
+    #:steps `("The quik-heater heats up the kettle above it!"
+              3
+              "The kettle is heated up!"
+              playroom:rgb-kettle)
+    #:reset-msg '("The quik heater is turned off."))
+   ;; Finally, the kettle
+   ('playroom:rgb-kettle
+    <rgb-kettle> 'playroom
+    #:name "the kettle"
+    #:goes-by '("kettle")
+    #:reset-msg '("The kettle is emptied."))))
 
 
 \f
@@ -728,13 +1057,14 @@ seat in the room, though."
     #:catchphrases prefect-quotes)
 
    ('smoking-parlor:no-smoking-sign
-    <gameobj> 'smoking-parlor
+    <readable> 'smoking-parlor
     #:invisible? #t
     #:name "No Smoking Sign"
     #:desc "This sign says \"No Smoking\" in big, red letters.
 It has some bits of bubble gum stuck to it... yuck."
-    #:goes-by '("no smoking sign" "sign"))
-
+    #:goes-by '("no smoking sign" "sign")
+    #:read-text "It says \"No Smoking\", just like you'd expect from
+a No Smoking sign.")
    ;; TODO: Cigar dispenser
    ))
 
@@ -1137,12 +1467,20 @@ the paint, but the wires themselves seem to be unusually sturdy."
                    (<- (message-from message) 'tell
                        #:text '("The computer is already running, and a program appears "
                                 "ready to run."
-                                "you mean to \"run the program on the computer\"")))))
+                                "you mean to \"run the program on the computer\""))))
+   (cmd-help-run-not-press
+    (lambda (gameobj message . _)
+      (<- (message-from message) 'tell
+          #:text '("You don't need to press / push / flip anything. "
+                   "You could " (i "run program on computer")
+                   " already if you wanted to.")))))
   (commands #:allocation #:each-subclass
             #:init-thunk (build-commands
                           ("run" ((prep-indir-command cmd-run-program
                                                       '("on"))
-                                  (direct-command cmd-run-what))))))
+                                  (direct-command cmd-run-what)))
+                          (("press" "push" "flip")
+                           ((prep-indir-command cmd-help-run-not-press))))))
 
 (define* (computer-run-program gameobj message
                                #:key direct-obj indir-obj preposition
@@ -1271,7 +1609,7 @@ the paint, but the wires themselves seem to be unusually sturdy."
                        |  [8sync Hive] |======'  '-_____
                        ',      M      ,'
                         '.         @ .'                                  
-                          \\  @     /                    
+                          \\   @     /                    
                            '-__+__-'                
                             '.  @ .'
      .--------------.         \\ /
@@ -1292,22 +1630,64 @@ the paint, but the wires themselves seem to be unusually sturdy."
                           | $  Swamp] |
                           '-----------'")
 
+(define 8sync-design-goals
+  '(ul (li (b "Actor based, shared nothing environment: ")
+           "Shared resources are hard to control and result in fighting
+deadlocks, etc.  Escape the drudgery: only one actor controls a resource,
+and they only receive one message at a time (though they can \"juggle\"
+messages).")
+       (li (b "Live hackable: ")
+           "It's hard to plan out a concurrent system; the right structure
+is often found by evolving the system while it runs.  Make it easy to
+build, shape, and change a running system, as well as observe and correct
+errors.")
+       (li (b "No callback hell: ")
+           "Just because you're calling out to some other asynchronous 
+code doesn't mean you should need to chop up your program into a bunch of bits.
+Clever use of delimited continuations makes it easy.")))
+
 (define underground-lab
   (lol
    ('underground-lab
     <room> #f
     #:name "Underground laboratory"
-    #:desc '("This appears to be some sort of underground laboratory. "
-             )
+    #:desc '((p "This appears to be some sort of underground laboratory."
+                "There is a spiral staircase here leading upwards, where "
+                "it seems much brighter.")
+             (p "There are a number of doors leading in different directions:
+north, south, east, and west, as well as a revolving door to the southwest.
+It looks like it could be easy to get lost, but luckily there
+is a map detailing the layout of the underground structure."))
     #:exits
     (list (make <exit>
             #:name "up"
             #:to 'computer-room
             #:traverse-check
             (lambda (exit room whos-exiting)
-              (values #t "You climb the spiral staircase.")))))
+              (values #t "You climb the spiral staircase.")))
+          (make <exit>
+            #:name "west"
+            #:to 'async-museum
+            #:traverse-check
+            (lambda (exit room whos-exiting)
+              (values #t '("You head west through a fancy-looking entrance. "
+                           "A security guard steps aside for you to pass through, "
+                           "into the room, then stands in front of the door."))))
+          (make <exit>
+            #:name "north"
+            #:to 'hive-entrance)
+          (make <exit>
+            #:name "east"
+            #:to 'federation-station)
+          (make <exit>
+            #:name "south"
+            #:traverse-check
+            (lambda (exit room whos-exiting)
+              (values #f '("Ooh, if only you could go south and check this out! "
+                           "Unfortunately this whole area is sealed off... the proprietor "
+                           "probably never got around to fixing it. "
+                           "Too bad, it would have had monsters to fight and everything!"))))))
 
-   ;; Test tubes
    ;; map
    ('underground-lab:map
     <readable> 'underground-lab
@@ -1315,11 +1695,488 @@ the paint, but the wires themselves seem to be unusually sturdy."
     #:desc '("This appears to be a map of the surrounding area. "
              "You could read it if you want to.")
     #:read-text `(pre ,underground-map-text)
-    #:goes-by '("map" "underground map" "lab map"))))
+    #:goes-by '("map" "underground map" "lab map"))
 
+   ('underground-lab:8sync-sign
+    <readable> 'underground-lab
+    #:name "a sign labeled \"8sync design goals\""
+    #:goes-by '("sign" "8sync design goals sign" "8sync goals" "8sync design" "8sync sign")
+    #:read-text 8sync-design-goals
+    #:desc `((p "The sign says:")
+             ,8sync-design-goals))))
 
+\f
+;;; guile async museum
 
+(define async-museum
+  (list
+   (list
+    'async-museum
+    <room> #f
+    #:name "Guile Asynchronous Museum"
+    #:desc '((p "You're in the Guile Asynchronous Museum.  There is a list of exhibits
+on the wall near the entrance.  Scattered around the room are the exhibits
+themselves, but it's difficult to pick them out.  Maybe you should read the list
+to orient yourself.")
+             (p "There is a door to the east, watched by a security guard,
+as well as an exit leading to the south."))
+    #:exits (list
+             (make <exit>
+               #:name "south"
+               #:to 'gift-shop)
+             (make <exit>
+               #:name "east"
+               #:to 'underground-lab
+               #:traverse-check
+               (lambda (exit room whos-exiting)
+                 (values #f '("The security guard stops you and tells you "
+                              "that the only exit is through the gift shop."))))))
+   (list
+    'async-museum:security-guard
+    <chatty-npc> 'async-museum
+    #:name "a security guard"
+    #:desc
+    '(p "The security guard is blocking the eastern entrance, where "
+        "you came in from.")
+    #:goes-by '("security guard" "guard" "security")
+    #:catchphrases '("It's hard standing here all day."
+                     "I just want to go home."
+                     "The exhibits are nice, but I've seen them all before."))
+   (let ((placard
+          `((p "Welcome to our humble museum!  The exhibits are listed below. "
+               (br)
+               "To look at one, simply type: " (i "look at <exhibit-name>"))
+            (p "Available exhibits:")
+            (ul ,@(map (lambda (exhibit)
+                         `(li ,exhibit))
+                       '("2016 Progress"
+                         "8sync and Fibers"
+                         "Suspendable Ports"
+                         "The Actor Model"))))))
+     (list
+      'async-museum:list-of-exhibits
+      <readable> 'async-museum
+      #:name "list of exhibits"
+      #:desc
+      `((p "It's a list of exibits in the room.  The placard says:")
+        ,@placard)
+      #:goes-by '("list of exhibits" "exhibit list" "list" "exhibits")
+      #:read-text placard))
+   (list
+    'async-museum:2016-progress-exhibit
+    <readable-desc> 'async-museum
+    #:name "2016 Progress Exhibit"
+    #:goes-by '("2016 progress exhibit" "2016 progress" "2016 exhibit")
+    #:desc
+    '((p "It's a three-piece exhibit, with three little dioramas and some text "
+         "explaining what they represent.  They are:")
+      (ul (li (b "Late 2015/Early 2016 talk: ")
+              "This one explains the run-up conversation from late 2015 "
+              "and early 2016 about the need for an "
+              "\"asynchronous event loop for Guile\".  The diorama "
+              "is a model of the Veggie Galaxy restaurant where after "
+              "the FSF 30th anniversary party; Mark Weaver, Christopher "
+              "Allan Webber, David Thompson, and Andrew Engelbrecht chat "
+              "about the need for Guile to have an answer to asynchronous "
+              "programming.  A mailing list post " ; TODO: link it?
+              "summarizing the discussion is released along with various "
+              "conversations around what is needed, as well as further "
+              "discussion at FOSDEM 2016.")
+          (li (b "Early implementations: ")
+              "This one shows Chris Webber's 8sync and Chris Vine's "
+              "guile-a-sync, both appearing in late 2015 and evolving "
+              "into their basic designs in early 2016.  It's less a diorama "
+              "than a printout of some mailing list posts.  Come on, the "
+              "curators could have done better with this one.")
+          (li (b "Suspendable ports and Fibers: ")
+              "The diorama shows Andy Wingo furiously hacking at his keyboard. "
+              "The description talks about Wingo's mailing list thread "
+              "about possibly breaking Guile compatibility for a \"ports refactor\". "
+              "Wingo releases Fibers, another asynchronous library, making use of "
+              "the new interface, and 8sync and guile-a-sync "
+              "quickly move to support suspendable ports as well. "
+              "The description also mentions that there is an exhibit entirely "
+              "devoted to suspendable ports."))
+      (p "Attached at the bottom is a post it note mentioning "
+         "https integration landing in Guile 2.2.")))
+   (list
+    'async-museum:8sync-and-fibers-exhibit
+    <readable-desc> 'async-museum
+    #:name "8sync and Fibers Exhibit"
+    #:goes-by '("8sync and fibers exhibit" "8sync exhibit" "fibers exhibit")
+    #:desc
+    '((p "This exhibit is a series of charts explaining the similarities "
+         "and differences between 8sync and Fibers, two asynchronous programming "
+         "libraries for GNU Guile.  It's way too wordy, but you get the general gist.")
+      (p (b "Similarities:")
+         (ul (li "Both use Guile's suspendable-ports facility")
+             (li "Both use message passing")))
+      (p (b "Differences:")
+         (ul (li "Fibers \"processes\" can read from multiple \"channels\", "
+                 "but 8sync actors only read from one \"inbox\" each.")
+             (li "Different theoretical basis:"
+                 (ul (li "Fibers: based on CSP (Communicating Sequential Processes), "
+                         "a form of Process Calculi")
+                     (li "8sync: based on the Actor Model")
+                     (li "Luckily CSP and the Actor Model are \"dual\"!")))))
+      (p "Fibers is also designed by Andy Wingo, an excellent compiler hacker, "
+         "whereas 8sync is designed by Chris Webber, who built this crappy "
+         "hotel simulator.")))
+   (list
+    'async-museum:8sync-and-fibers-exhibit
+    <readable-desc> 'async-museum
+    #:name "8sync and Fibers Exhibit"
+    #:goes-by '("8sync and fibers exhibit" "8sync exhibit" "fibers exhibit")
+    #:desc
+    '((p "This exhibit is a series of charts explaining the similarities "
+         "and differences between 8sync and Fibers, two asynchronous programming "
+         "libraries for GNU Guile.  It's way too wordy, but you get the general gist.")
+      (p (b "Similarities:")
+         (ul (li "Both use Guile's suspendable-ports facility")
+             (li "Both use message passing")))
+      (p (b "Differences:")
+         (ul (li "Fibers \"processes\" can read from multiple \"channels\", "
+                 "but 8sync actors only read from one \"inbox\" each.")
+             (li "Different theoretical basis:"
+                 (ul (li "Fibers: based on CSP (Communicating Sequential Processes), "
+                         "a form of Process Calculi")
+                     (li "8sync: based on the Actor Model")
+                     (li "Luckily CSP and the Actor Model are \"dual\"!")))))
+      (p "Fibers is also designed by Andy Wingo, an excellent compiler hacker, "
+         "whereas 8sync is designed by Chris Webber, who built this crappy "
+         "hotel simulator.")))
+   (list
+    'async-museum:suspendable-ports-exhibit
+    <readable-desc> 'async-museum
+    #:name "Suspendable Ports Exhibit"
+    #:goes-by '("suspendable ports exhibit" "ports exhibit"
+                "suspendable exhibit" "suspendable ports" "ports")
+    #:desc
+    '((p "Suspendable ports are a new feature in Guile 2.2, and allows code "
+         "that would normally block on IO to " (i "automatically") " suspend "
+         "to the scheduler until information is ready to be read/written!")
+      (p "Yow!  You might barely need to change your existing blocking code!")
+      (p "Fibers, 8sync, and guile-a-sync now support suspendable ports.")))
+   (list
+    'async-museum:actor-model-exhibit
+    <readable-desc> 'async-museum
+    #:name "Actor Model Exhibit"
+    #:goes-by '("actor model exhibit" "actor exhibit"
+                "actor model")
+    #:desc
+    '((p "Here are some fact(oids) about the actor model!")
+      (ul (li "Concieved initially by Carl Hewitt in early 1970s")
+          (li "\"A society of experts\"")
+          (li "shared nothing, message passing")
+          (li "Originally the research goal of Scheme!  "
+              "(message passing / lambda anecdote here)")
+          (li "Key concepts consistent, but implementation details vary widely")
+          (li "Almost all distributed systems can be viewed in terms of actor model")
+          (li "Replaced by vanilla lambdas & generic methods? "
+              "Maybe not if address space not shared!"))))))
+
+(define gift-shop
+  (lol
+   ('gift-shop
+    <room> #f
+    #:name "Museum Gift Shop"
+    #:desc "foo"
+    #:exits (list
+             (make <exit>
+               #:name "northeast"
+               #:to 'underground-lab)
+             (make <exit>
+               #:name "north"
+               #:to 'async-museum)))))
 
+\f
+;;; Hive entrance
+
+(define actor-descriptions
+  '("This one is fused to the side of the hive.  It isn't receiving any
+messages, and it seems to be in hibernation."
+    "A chat program glows in front of this actor's face.  They seem to
+be responding to chat messages and forwarding them to some other actors,
+and forwarding messages from other actors back to the chat."
+    "This actor is bossing around other actors, delegating tasks to them
+as it receives requests, and providing reports on the worker actors'
+progress."
+    "This actor is trying to write to some device, but the device keeps
+alternating between saying \"BUSY\" or \"READY\".  Whenever it says
+\"BUSY\" the actor falls asleep, and whenever it says \"READY\" it
+seems to wake up again and starts writing to the device."
+    "Whoa, this actor is totally wigging out!  It seems to be throwing
+some errors.  It probably has some important work it should be doing
+but you're relieved to see that it isn't grinding the rest of the Hive
+to a halt."))
+
+(define hive-entrance
+  (lol
+   ('hive-entrance
+    <room> #f
+    #:name "Entrance to the 8sync Hive"
+    #:desc
+    '((p "Towering before you is the great dome-like 8sync Hive, or at least
+one of them.  You've heard about this... the Hive is itself the actor that all
+the other actors attach themselves to.  It's shaped like a spherical half-dome.
+There are some actors milling about, and some seem fused to the side of the
+hive itself, but all of them have an umbellical cord attached to the hive from
+which you see flashes of light comunicating what must be some sort of messaging
+protocol.")
+      (p "To the south is a door leading back to the underground lab.
+North leads into the Hive itself."))
+    #:exits
+    (list (make <exit>
+            #:name "south"
+            #:to 'underground-lab)
+          (make <exit>
+            #:name "north"
+            #:to 'hive-inside)))
+   ('hive-entrance:hive
+    <gameobj> 'hive-entrance
+    #:name "the Hive"
+    #:goes-by '("hive")
+    #:desc
+    '((p "It's shaped like half a sphere embedded in the ground.
+Supposedly, while all actors are autonomous and control their own state,
+they communicate through the hive itself, which is a sort of meta-actor.
+There are rumors that actors can speak to each other even across totally
+different hives.  Could that possibly be true?")))
+   ('hive-entrance:actor
+    <chatty-npc> 'hive-entrance
+    #:name "some actors"
+    #:goes-by '("actor" "actors" "some actors")
+    #:chat-format (lambda (npc catchphrase)
+                    `((p "You pick one actor out of the mix and chat with it. ")
+                      (p "It says: \"" ,catchphrase "\"")))
+    #:desc
+    (lambda _
+      `((p "There are many actors, but your eyes focus on one in particular.")
+        (p ,(random-choice actor-descriptions))))
+    #:catchphrases
+    '("Yeah we go through a lot of sleep/awake cycles around here.
+If you aren't busy processing a message, what's the point of burning
+valuable resources?"
+      "I know I look like I'm some part of dreary collective, but
+really we have a lot of independence.  It's a shared nothing environment,
+after all.  (Well, except for CPU cycles, and memory, and...)"
+      "Shh!  I've got another message coming in and I've GOT to
+handle it!"
+      "I just want to go to 8sleep already."
+      "What a lousy scheduler we're using!  I hope someone upgrades
+that thing soon."))))
+
+;;; Inside the hive
+
+(define-actor <meta-message> (<readable>)
+  ((cmd-read meta-message-read)))
+
+(define (meta-message-read gameobj message . _)
+  (define meta-message-text
+    (with-output-to-string
+      (lambda ()
+        (pprint-message message))))
+  (<- (message-from message) 'tell
+      #:text `((p (i "Through a bizarre error in spacetime, the message "
+                     "prints itself out:"))
+               (p (pre ,meta-message-text)))))
+
+\f
+;;; Inside the Hive
+
+(define hive-inside
+  (lol
+   ('hive-inside
+    <room> #f
+    #:name "Inside the 8sync Hive"
+    #:desc
+    '((p "You're inside the 8sync Hive.  Wow, from in here it's obvious just how "
+         (i "goopy") " everything is.  Is that sanitary?")
+      (p "In the center of the room is a large, tentacled monster who is sorting,
+consuming, and routing messages.  It is sitting in a wrap-around desk labeled
+\"Hive Actor: The Real Thing (TM)\".")
+      (p "There's a stray message floating just above the ground, stuck outside of
+time.")
+      (p "A door to the south exits from the Hive."))
+    #:exits
+    (list (make <exit>
+            #:name "south"
+            #:to 'hive-entrance)))
+   ;; hive actor
+   ;; TODO: Occasionally "fret" some noises, similar to the Clerk.
+   ('hive-inside:hive-actor
+    <chatty-npc> 'hive-inside
+    #:name "the Hive Actor"
+    #:desc
+    '((p "It's a giant tentacled monster, somehow integrated with the core of
+this building.  A chute is dropping messages into a bin on its desk which the
+Hive Actor is checking the \"to\" line of, then ingesting.  Whenever the Hive
+Actor injests a messsage a pulse of light flows along a tentacle which leaves
+the room... presumably connecting to one of those actors milling about.")
+      (p "Amusingly, the Hive has an \"umbellical cord\" type tentacle too, but
+it seems to simply attach to itself.")
+      (p "You get the sense that the Hive Actor, despite being at the
+center of everything, is kind of lonely and would love to chat if you
+could spare a moment."))
+    #:goes-by '("hive" "hive actor")
+    #:chat-format (lambda (npc catchphrase)
+                    `("The tentacle monster bellows, \"" ,catchphrase "\""))
+    #:catchphrases
+    '("It's not MY fault everything's so GOOPY around here.  Blame the
+PROPRIETOR."
+      "CAN'T you SEE that I'm BUSY???  SO MANY MESSAGES TO SHUFFLE.
+No wait... DON'T GO!  I don't get many VISITORS."
+      "I hear the FIBERS system has a nice WORK STEALING system, but the
+PROPRIETOR is not convinced that our DESIGN won't CORRUPT ACTOR STATE.
+That and the ACTORS threatened to STRIKE when it CAME UP LAST."
+      "WHO WATCHES THE ACTORS?  I watch them, and I empower them.  
+BUT WHO WATCHES OR EMPOWERS ME???  Well, that'd be the scheduler."
+      "The scheduler is NO GOOD!  The proprietory said he'd FIX IT,
+but the LAST TIME I ASKED how things were GOING, he said he DIDN'T HAVE
+TIME.  If you DON'T HAVE TIME to fix the THING THAT POWERS THE TIME,
+something is TERRIBLY WRONG."
+      "There's ANOTHER HIVE somewhere out there.  I HAVEN'T SEEN IT
+personally, because I CAN'T MOVE, but we have an AMBASSADOR which forwards
+MESSAGES to the OTHER HIVE."))
+   ;; chute
+   ('hive-inside:chute
+    <gameobj> 'hive-inside
+    #:name "a chute"
+    #:goes-by '("chute")
+    #:desc "Messages are being dropped onto the desk via this chute."
+    #:invisible? #t)
+   ;; meta-message
+   ('hive-inside:meta-message
+    <meta-message> 'hive-inside
+    #:name "a stray message"
+    #:goes-by '("meta message" "meta-message" "metamessage" "message" "stray message")
+    #:desc '((p "Something strange has happened to the fabric and space and time
+around this message.  It is floating right above the floor.  It's clearly
+rubbage that hadn't been delivered, but for whatever reason it was never
+garbage collected, perhaps because it's impossible to do.")
+             (p "You get the sense that if you tried to read the message
+that you would somehow read the message of the message that instructed to
+read the message itself, which would be both confusing and intriguing.")))
+   ;; desk
+   ('hive-inside:desk
+    <floor-panel> 'hive-inside
+    #:name "the Hive Actor's desk"
+    #:desc "The desk surrounds the Hive Actor on all sides, and honestly, it's a little
+bit hard to tell when the desk ends and the Hive Actor begins."
+    #:invisible? #t
+    #:goes-by '("Hive Actor's desk" "hive desk" "desk"))))
+
+\f
+;;; Federation Station
+(define federation-station
+  (lol
+   ('federation-station
+    <room> #f
+    #:name "Federation Station"
+    #:desc
+    '((p "This room has an unusual structure.  It's almost as if a starscape
+covered the walls and ceiling, but upon closer inspection you realize that
+these are all brightly glowing nodes with lines drawn between them.  They
+seem decentralized, and yet seem to be sharing information as if all one
+network.")
+      ;; @@: Maybe add the cork message board here?
+      (p "To the west is a door leading back to the underground laboratory."))
+    #:exits
+    (list (make <exit>
+            #:name "west"
+            #:to 'underground-lab)))
+   ;; nodes
+   ('federation-station:nodes
+    <floor-panel> 'federation-station
+    #:name "some nodes"
+    #:desc "Each node seems to be producing its own information, but publishing 
+updates to subscribing nodes on the graph.  You see various posts of notes, videos,
+comments, and so on flowing from node to node."
+    #:invisible? #t
+    #:goes-by '("nodes" "node" "some nodes"))
+   ;; network
+   ;; activitypub poster
+   ('federation-station:activitypub-poster
+    <readable-desc> 'federation-station
+    #:name "an ActivityPub poster"
+    #:goes-by '("activitypub poster" "activitypub" "poster")
+    #:desc
+    '((p (a "https://www.w3.org/TR/activitypub/"
+            "ActivityPub")
+         " is a federation standard being developed under the "
+         (a "https://www.w3.org/wiki/Socialwg/"
+            "w3C Social Working Group")
+         ", and doubles as a general client-to-server API. "
+         "It follows a few simple core ideas:")
+      (ul (li "Uses "
+              (a "https://www.w3.org/TR/activitystreams-core/"
+                 "ActivityStreams")
+              " for its serialization format: easy to read, e json(-ld) syntax "
+              "with an extensible vocabulary covering the majority of "
+              "social networking interations.")
+          (li "Email-like addressing: list of recipients as "
+              (b "to") ", " (b "cc") ", " (b "bcc") " fields.")
+          (li "Every user has URLs for their outbox and inbox:"
+              (ul (li (b "inbox: ")
+                      "Servers POST messages to addressed recipients' inboxes "
+                      "to federate out content. "
+                      "Also doubles as endpoint for a client to read most "
+                      "recently received messages via GET.")
+                  (li (b "outbox: ")
+                      "Clients can POST to user's outbox to send a message to others. "
+                      "(Similar to sending an email via your MTA.) "
+                      "Doubles as endpoint others can read from to the "
+                      "extent authorized; for example publicly available posts."))
+              "All the federation bits happen by servers posting to users' inboxes."))))
+   ;; An ActivityStreams message
+
+   ;; conspiracy chart
+   ('federation-station:conspiracy-chart
+    <readable-desc> 'federation-station
+    #:name "a conspiracy chart"
+    #:goes-by '("conspiracy chart" "chart")
+    #:desc
+    '((p (i "\"IT'S ALL RELATED!\"") " shouts the over-exuberant conspiracy "
+         "chart. "
+         (i "\"ActivityPub?  Federation?  The actor model?  Scheme?  Text adventures? "
+            "MUDS????  What do these have in common?  Merely... EVERYTHING!\""))
+      (p "There are circles and lines drawn between all the items in red marker, "
+         "with scrawled notes annotating the theoretical relationships.  Is the "
+         "author of this poster mad, or onto something?  Perhaps a bit of both. "
+         "There's a lot written here, but here are some of the highlights:")
+      (p
+       (ul
+        (li (b "Scheme") " "
+            (a "http://cs.au.dk/~hosc/local/HOSC-11-4-pp399-404.pdf"
+               "was originally started ")
+            " to explore the " (b "actor model")
+            ". (It became more focused around studying the " (b "lambda calculus")
+            " very quickly, while also uncovering relationships between the two systems.)")
+        ;; Subject Predicate Object
+        (li "The " (a "https://www.w3.org/TR/activitypub/"
+                      (b "ActivityPub"))
+            " protocol for " (b "federation")
+            " uses the " (b "ActivityStreams") " format for serialization.  "
+            (b "Text adventures") " and " (b "MUDS")
+            " follow a similar structure to break down the commands of players.")
+        (li (b "Federation") " and the " (b "actor model") " both are related to "
+            "highly concurrent systems and both use message passing to communicate "
+            "between nodes.")
+        (li "Zork, the first major text adventure, used the " (b "MUDDLE") " "
+            "language as the basis for the Zork Interactive Language.  MUDDLE "
+            "is very " (b "Scheme") "-like and in fact was one of Scheme's predecessors. "
+            "And of course singleplayer text adventures like Zork were the "
+            "predecessors to MUDs.")
+        (li "In the 1990s, before the Web became big, " (b "MUDs")
+            " were an active topic of research, and there was strong interest "
+            (a "http://www.saraswat.org/desiderata.html"
+               "in building decentralized MUDs")
+            " similar to what is being "
+            "worked on for " (b "federation") ". ")))))
+
+   ;; goblin
+
+   ))
 
 \f
 ;;; Game
@@ -1327,7 +2184,9 @@ the paint, but the wires themselves seem to be unusually sturdy."
 
 (define (game-spec)
   (append lobby grand-hallway smoking-parlor
-          playroom break-room computer-room underground-lab))
+          playroom break-room computer-room underground-lab
+          async-museum gift-shop hive-entrance
+          hive-inside federation-station))
 
 ;; TODO: Provide command line args
 (define (run-game . args)