Escape the Trolls, or just walking through a maze Clojure beginner, looking to improve this code I submitted for Escape the Trolls, a Weekly Programming challenge over in reddit. No trolls yet, just walking through a maze for now.

(ns ctrollmaze.core
  (:gen-class)
  (:require [clojure.string :as str]))

(def sample-maze
"#####################################
# #       #       #     #         # #
# # ##### # ### ##### ### ### ### # #
#       #   # #     #     # # #   # #
##### # ##### ##### ### # # # ##### #
#   # #       #     # # # # #     # #
# # ####### # # ##### ### # ##### # #
# #       # # #   #     #     #   # #
# ####### ### ### # ### ##### # ### #
#     #   # #   # #   #     # #     #
# ### ### # ### # ##### # # # #######
#   #   # # #   #   #   # # #   #   #
####### # # # ##### # ### # ### ### #
#     # #     #   # #   # #   #     #
# ### # ##### ### # ### ### ####### #
# #   #     #     #   # # #       # #
# # ##### # ### ##### # # ####### # #
# #     # # # # #     #       # #   #
# ##### # # # ### ##### ##### # #####
# #   # # #     #     # #   #       #
# # ### ### ### ##### ### # ##### # #
# #         #     #       #       # #
#X###################################")

(defrecord Maze [width
                 height
                 grid])

(defn flatten-coords
  [maze x y]
  (let [width (:width maze)]
    (+ x (* y width) y)))

(defn token-at
  [maze x y]
  (let [grid (:grid maze)]
    (nth grid (flatten-coords maze x y))))

(defn is-token?
  [maze x y given-token]
  (let [token (token-at maze x y)]
    (= given-token token)))

(defn is-wall?
  [maze x y]
  (is-token? maze x y \#))

(defn is-walkable?
  [maze x y]
  (some (partial is-token? maze x y) '(\space \X)))

(defmulti gen-maze (fn [strategy] (:strategy strategy)))

(defmethod gen-maze :static
  [strategy]
  (-&gt;Maze (:width strategy) (:height strategy) (into [] (seq sample-maze))))

(defn move-player
  [direction player]
  (case direction
    "a" (assoc player :dir \&lt; :x (dec (:x player)))
    "d" (assoc player :dir \&gt; :x (inc (:x player)))
    "w" (assoc player :dir \^ :y (dec (:y player)))
    "s" (assoc player :dir \v :y (inc (:y player)))
    player))

(defn gen-static-maze
  []
  (gen-maze { :strategy :static :width 37 :height 23 }))

(defn place-player
  ([maze player] (place-player maze player player))
  ([maze player' player]
   (println player' player)
   (let [{:keys [grid width height]} maze
         x' (:x player')
         y' (:y player')
         dir' (:dir player')
         x (:x player)
         y (:y player)]
     (-&gt;Maze width height (assoc grid
                                 (flatten-coords maze x y) \space
                                 (flatten-coords maze x' y') dir')))))


(defn pretty-print
  [maze]
  (let [grid (:grid maze)]
    (println (str/join grid))))

(defn is-game-finished?
  [maze]
  (-&gt;&gt; (:grid maze)
       (some #{\X})
       (boolean)
       (not)))

(defn play-maze
  []
  (loop [player { :dir \&gt; :x 1 :y 1 }
         maze (place-player (gen-static-maze) player)
         input-dir nil]
    (let [player' (move-player input-dir player)]
      (if (is-walkable? maze (:x player') (:y player'))
        (let [maze' (place-player maze player' player)]
          (pretty-print maze')
          (if (is-game-finished? maze')
            true
            (recur player' maze' (read-line))))
        (recur player maze (read-line))))))

(defn -main
  "I don't do a whole lot ... yet."
  [&amp; args]
  (when (play-maze)
    (println "Congratulations, you won!")))


Version 1.1: No longer does the output lag behind one move.
Looks clean enough IMO, separate functions, a record for the maze -
perhaps create one for the player too?


When run from the terminal the (println player' player) is just
noise, I'd remove it.  Also, the maze should be printed once before
any commands are entered.
A lot of the lets can be inlined as they're only used once.
I'd perhaps pass coordinates in pairs instead of single numbers, then
you save a parameter every time and keep those two values that belong
together in one place.  OTOH the update code will be a little bit more
complex.
For is-game-finished? the threading macro -&gt;&gt; looks like overkill,
it's not like the expression is very complicated.
The return value of play-maze doesn't really matter, does it, so
maybe just drop the true and use when instead.





Perhaps add a way to clear the screen before printing the maze, then
at least on the terminal it's less cluttered.
Perhaps also switch to reading single characters so you don't have to
constantly press the Enter key.