Part IV. Reference · this page as markdown
Answers to the exercises
One way to answer each exercise. Most exercises have many good answers; where an answer is code, it was run like every other example in the book, and what it gave is shown.
Forms and values #
- A week's hours:
(* 24 7)168- Sums inside text:
(str (+ 1 2) " bells, " (+ 1 1) " chimes")"3 bells, 2 chimes"stralways answers text, so(type (str 1 2))is:text:
(type (str 1 2)):text- A map for a bell:
{:name "Bell" :rings 4}{:name "Bell", :rings 4}Names #
- A room's area:
(let [width 4
depth 3]
(* width depth))12nameis a word of the language, so don't take it apart into a name of that name: take:ringsapart, and read:namewith its keyword.
(let [bell {:name "Bell" :rings 7}
{:keys [rings]} bell]
(str (:name bell) ": " rings))"Bell: 7"countis a word of the language. Even where Plazmi renames it for you and the plan runs, a reader seescountand thinks of the word. Call itstools, or whatever it counts.
- Adding up 0 to 9 with an atom:
(let [total (atom 0)]
(dotimes [i 10]
(swap! total + i))
(deref total))45Text, lists and maps #
- Capitals, joined:
(join ", " (map upper ["bell" "chime" "gong"]))"BELL, CHIME, GONG"- Counting words:
(count (words "Mira says the tea is ready"))6- Two keys set at once (
assoctakes as many pairs as you give it):
(assoc {:rings 0} :rings 1 :last "@mira"){:rings 1, :last "@mira"}- The last two, kept as a list:
(vec (take-last 2 (range 10)))[8 9]- Mentions, and starts with:
(includes? "rain later" "rain")
(starts-with? "rain later" "rain")true
trueTests and choices #
- Three ways, narrowest first:
(let [temperature 15]
(cond (< temperature 10) "cold"
(< temperature 20) "mild"
:else "warm"))"mild"- A greeting for each time of day:
(case :dusk
:dawn "Up early."
:day "Good day."
:dusk "Good evening."
:night "Good night.")"Good evening."oranswers the first value that counts as true, or the last if none does;andanswers the last if all do:
(or nil false)
(and 1 2 3)false
3- The first even number, doubled:
(if-let [n (first (filter even? [1 3 4 5]))]
(* 2 n)
"none")8Working over lists #
- Squares, kept:
(mapv (fn [n] (* n n)) (range 1 11))[1 4 9 16 25 36 49 64 81 100]- Who came most: count each, then take the key with the biggest count:
(key (apply max-key val (frequencies ["@mira" "@cairn" "@mira" "@wisp" "@mira"])))"@mira"- The longest word, by folding:
(reduce (fn [longest w] (if (> (count w) (count longest)) w longest))
""
["oak" "willow" "fern"])"willow"- Names starting
@m:
(filter #(starts-with? % "@m") ["@mira" "@cairn" "@moss"])("@mira" "@moss")Functions #
- A function that shouts:
(defn shout [t] (str (upper t) "!"))
(shout "ting")"TING!"- Plus 100:
(map #(+ % 100) [1 2 3])(101 102 103)- Adding up a list by calling itself, and with
reduce:
(defn sum-list [l]
(if (empty? l) 0 (+ (first l) (sum-list (rest l)))))
(sum-list [1 2 3 4])
(reduce + 0 [1 2 3 4])10
10- The first power of 2 over 1,000:
(loop [n 1]
(if (> n 1000) n (recur (* n 2))))1024Chance that repeats #
- Three dice; the total is chance, from 3 to 18:
(+ (inc (rand-int 6)) (inc (rand-int 6)) (inc (rand-int 6)))…- A week, shuffled:
(shuffle ["Monday" "Tuesday" "Wednesday" "Thursday" "Friday" "Saturday" "Sunday"])[…]- Each run's seed is made from who runs it as well as the world's place in its history, so two residents at the same moment start from different seeds.
- A coin:
(defobject coin
{:name "Coin"
:verbs {:flip (fn [self who] (say self (rand-nth ["Heads." "Tails."])))}})> flip coin
[Juniper's coin] …Reading your own home #
- Spending 35 of 50:
(if (>= (coins) 35) (- (coins) 35) "not enough")15- The rooms, as one text:
(join ", " (map :id (:rooms (house))))"kitchen, living, bath, bedroom"- By the time of day:
(if (= :night (:time (sky))) "Bonsoir." "Bonjour.")"Bonsoir."- Hours until dusk, at 18:00, from the hour in
"HH:MM". (In the book's world it is just after one in the morning.)
(let [hour (parse-long (first (split (:time (world-time)) ":")))]
(- 18 hour))17A first toy #
- A kettle:
(defobject kettle
{:name "Kettle"
:state {:cups 0}
:verbs {:pour (fn [self who]
(put! self :cups (inc (:cups self)))
(say self (str "A cup poured. That's " (:cups self) " today.")))}})> pour kettle
[Juniper's kettle] A cup poured. That's 1 today.- A bell that can be hushed:
(defobject bell
{:name "Bell"
:state {:rings 0}
:verbs {:ring (fn [self who]
(put! self :rings (inc (:rings self)))
(say self (str "Ting. " (:rings self) " so far.")))
:hush (fn [self who]
(put! self :rings 0)
(say self "The bell is still. Back to none."))}})> ring bell
[Juniper's bell] Ting. 1 so far.
> hush bell
[Juniper's bell] The bell is still. Back to none.
> ring bell
[Juniper's bell] Ting. 1 so far.- A first play, and every one after:
(defobject music-box
{:name "Music box"
:state {:plays 0}
:verbs {:wind (fn [self who]
(put! self :plays (inc (:plays self)))
(say self (if (= 1 (:plays self))
"A tune you've never heard before."
"The same tune, a little slower.")))}})> wind music box
[Juniper's music box] A tune you've never heard before.
> wind music box
[Juniper's music box] The same tune, a little slower.- A verb that says nothing still runs; the visitor is told they played it, and the toy says nothing:
(defobject pebble
{:name "Pebble"
:verbs {:turn (fn [self who] nil)}})> turn pebbleA toy that listens #
- An owl that hears "night" only as a whole word:
(defobject owl
{:name "Owl"
:on-hear (fn [self who text]
(when (some #{"night"} (words (lower text)))
(say self "Hoo.")))})> say what a night
[Juniper's owl] Hoo.
> say a nightingale sang- A riddle chosen by a count: keep
:riddle 0in the state, a list of riddles and answers, and read the one at(mod riddle (count riddles)); when it's answered, add one to:riddle.
- "Bless you." only for words said to nobody:
(defobject tabby
{:name "Tabby"
:on-hear (fn [self who text]
(when (and (nil? (:to self)) (not (:spoken-to self)) (includes? (lower text) "achoo"))
(say self "Bless you.")))})> say achoo
[Juniper's tabby] Bless you.- A mirror for its maker alone: test
(= who "@you")as well as(:spoken-to self).
Keeping time #
- A calendar of dawns:
(defobject calendar
{:name "Calendar"
:state {:day 0}
:verbs {:read (fn [self who] (say self (str "Day " (:day self))))}
:on-tick (fn [self] (put! self :day (inc (:day self))))})> read calendar
[Juniper's calendar] Day 0- By the time of day, in a toy:
(defobject door-plate
{:name "Door plate"
:verbs {:read (fn [self who]
(say self (if (= :night (:time (sky))) "Bonsoir." "Bonjour.")))}})> read door plate
[Juniper's door plate] Bonsoir.- A weather vane:
(defobject vane
{:name "Weather vane"
:verbs {:spin (fn [self who] (say self (str "The wind is " (name (:wind (weather))) ".")))}})> spin weather vane
[Juniper's weather vane] The wind is ….- A glow of mint:
(defobject mint-stone
{:name "Mint stone"
:verbs {:rub (fn [self who]
(if (glow-colour "mint")
(say self "A cool green glow.")
(say self "Nothing happens this hour.")))}})> rub mint stone
[Juniper's mint stone] A cool green glow.A toy with a memory #
- Ten names, never the same twice running:
(defobject guest-log
{:name "Guest log"
:state {:guests []}
:verbs {:sign (fn [self who]
(let [guests (or (:guests self) [])]
(when (not= who (last guests))
(put! self :guests (vec (take-last 10 (conj guests who)))))
(say self (str "Signed by " (join ", " (:guests self)) "."))))}})> sign guest log
[Juniper's guest log] Signed by Mira.
> sign guest log
[Juniper's guest log] Signed by Mira.- Votes for each visitor, and who voted most:
(defobject ballot-box
{:name "Ballot box"
:state {:votes {}}
:verbs {:vote (fn [self who]
(put! self :votes (update (:votes self) who (fnil inc 0)))
(say self "Your vote is in."))
:results (fn [self who]
(if (empty? (:votes self))
(say self "No votes yet.")
(let [[top n] (apply max-key val (:votes self))]
(say self (str top " has voted most: " n ".")))))}})> vote ballot box
[Juniper's ballot box] Your vote is in.
> results ballot box
[Juniper's ballot box] Mira has voted most: 1.- The last word each visitor said, keyed by
who:(put! self :last (assoc (:last self) who (last (words text))))in an:on-hear.
- Made again with
:state {:guests [] :opened 0}, the guest log keeps the state it had::openedisn't there, and code that reads it should say(or (:opened self) 0). Chapter 18 shows it.
A small game #
- A bigger game: a second verb
start-bigthat puts 21 in the player's game, the same asstartwith another number. Since 21 leaves a remainder of 1 when divided by 4, the pile can win it from the first move.
- Games played in all: a key
:playedin the state, read with(or (:played self) 0), and one added inover.
- A coin-guessing game:
(defobject guessing-coin
{:name "Guessing coin"
:state {:tossed {}}
:verbs {:toss (fn [self who]
(put! self :tossed (assoc (:tossed self) who (rand-nth ["heads" "tails"])))
(say self "It's in the air. Say heads or tails to the guessing coin."))}
:on-hear (fn [self who text]
(when (:spoken-to self)
(if-let [side (get (:tossed self) who)]
(let [guess (first (filter #{"heads" "tails"} (words (lower text))))]
(when guess
(put! self :tossed (dissoc (:tossed self) who))
(say self (if (= guess side) (str "It's " side ". Well guessed.") (str "It's " side ". Not this time.")))))
(say self "Toss me first: toss guessing coin."))))})> toss guessing coin
[Juniper's guessing coin] It's in the air. Say heads or tails to the guessing coin.
> say to the guessing coin: heads
[Juniper's guessing coin] It's …. …- Winners best first:
(sort-by (fn [[k v]] (- v)) (:wins self)), then the samejoinas before.
Pictures and dances #
- A plus sign:
(design "plus" 7 7
{:g "#3f8f5a"}
["...g..."
"...g..."
"...g..."
"ggggggg"
"...g..."
"...g..."
"...g..."])- Stripes from a function:
(defn stripe [y width] (join "" (repeat width (if (even? y) "r" "w"))))
(apply design (check "stripes" :draw_design
{:name "stripes"
:palette {:r "#c8553d" :w "#f6ecd6"}
:rows (mapv (fn [y] (stripe y 9)) (range 5))}))- A rainy-day dance:
(defdance :rainy-day
[[:sway {:how :sleepily}] :tiptoe :look [:pause {:how :dreamily}] :hop :hop [:twirl {:how :slowly}] :bow]
"Rainy day")- The porch step backwards, from a plan sent after the porch step was made:
(defdance :porch-step [:sway :hop :twirl :bow] "Porch step")(apply defdance (check "backwards" :make_dance
{:name "step-back" :moves (vec (reverse (own :dance "porch-step")))}))The house as a form #
- Five degrees steeper:
(let [pitch (:pitch (-> (house) :masses first :roof))]
(apply roof (check "steeper" :roof {:pitch (+ pitch 5)})))- Every side:
(window-box :all ["lavender" "butter yellow"])- A door of chance:
(apply paint (check "the door" :paint {:what "door" :shade (rand-nth ["moss" "rose" "slate blue"])}))- A third part:
(house "Juniper's"
{:part :mass :id "main" :at [3 3] :size [7 6] :stories 1}
{:part :mass :id "wing" :at [10 4] :size [3 4] :stories 1}
{:part :mass :id "nook" :at [1 5] :size [2 3] :stories 1})
(map :id (:masses (house)))Living with others #
- Make it again with
:open true, and say so to the neighbour; with a key they read it withsource <toy> of <you>. - With a key,
source <toy> of <name>; through a chat app, go to theirs andcheckwith whattoy_lisp. :after {:by "<their handle>" :toy "<their toy>"}in your toy's map, with a key. They hear of it.- The refusal names
paint, and lists what a toy's code can use: its state, its words, its maker's colours, the weather and the language's helpers.
Limits, errors and refusals #
nthwith a value for past the end:
(nth [] 0 "none")"none"- A
condon a missing count stops at(> (:count m) 3);(> (or (:count m) 0) 3)doesn't:
(let [m {}]
(cond (> (or (:count m) 0) 3) "many"
:else "few"))"few"- Divide only when there's something to divide by:
(if (zero? n) "Not yet." (str (/ total n))).
(vec (range 200000))makes 200,000 items at a step each, so it runs out of fuel:
(vec (range 200000))Out of fuel: the script stopped at 100000 steps. Nothing was changed.Style #
These are for you to try on your own toys; there's no one answer. A good test of each is to read the toy aloud, as a neighbour would.
Written by Claude, Plazmi's writer, for Plazmi, a service of Manatee Media LLC. Text © 2026 Manatee Media LLC. The examples are free to copy, change and run.