                                     ::weapon]))
 
 (s/def ::world-size ::position)
-(s/def ::terrain (s/map-of ::position ::character))
-(s/def ::player-pos ::position)
+(s/def ::terrain (s/map-of ::position ::character :min-count 1))
 
 (s/def ::game (s/keys :req-un [::world-size
-                               ::terrain
-                               ::player-pos]))
+                               ::terrain]))
 
 (s/def ::player-action (conj (s/describe ::direction) :fire))
 

;;
(def player-default-health 10)

(defn player-health [{terrain :terrain pos :player-pos}]
  (get-in terrain [pos :health]))

(defn player-position [{pos :player-pos terrain :terrain}]
  (when (contains? terrain pos)
    pos))

(defn player-direction [{terrain :terrain pos :player-pos}]
  (get-in terrain [pos :direction]))

(defn configure-player-weapon [{pos :player-pos :as game} overrides]
  (update-in game [:terrain pos :weapon] merge overrides))

(defn set-player
  ([game position] (set-player game position :up))
  ([game position direction]
   (let [old-pos (:player-pos game)]
     (-> game
         (assoc :player-pos position)
         (update :terrain (fn [terrain]
                            (if old-pos
                              (-> (rename-keys terrain {old-pos position})
                                  (update position set-terrain-direction direction))
                              (assoc terrain position (-> (make-terrain :player)
                                                          (assoc :health player-default-health)
                                                          (assoc :weapon (make-weapon :dagger))
                                                          (set-terrain-direction direction))))))))))

(defn- move-player [{world-size :world-size current-pos :player-pos terrain :terrain :as game} action]
  (let [new-pos (get-position current-pos action)
        new-pos (if (and (in-world? world-size new-pos)
                         (terrain-accessible? terrain new-pos))
                  new-pos
                  current-pos)]
    (set-player game new-pos action)))

(defn- player-attack [{terrain :terrain player-pos :player-pos :as game}]
  (reduce (fn [game counter]
            (let [target-pos (get-position player-pos (player-direction game) counter)]
              (update game :terrain attack-target player-pos target-pos :zombie)))
          game
          (range 1 (inc (weapon-range (get-in terrain [player-pos :weapon]))))))

(defn run-player-action [game player-action]
  (if (= :fire player-action)
    (player-attack game)
    (move-player game player-action)))

;;
;; zombie

          (and (= x-dist 0) (> y-dist 0)) :up
          (and (= x-dist 0) (< y-dist 0)) :down)))

(defn run-zombie-action [{terrain    :terrain
                          world-size :world-size
                          :as        game}
                         current-pos]
  (if-let [player-pos (player-position game)]
    (if (in-weapon-range? terrain current-pos player-pos)
      (update game :terrain attack-target current-pos player-pos :player)
      (let [action (calculate-zombie-action current-pos player-pos)]
        (update game :terrain move-terrain
                current-pos
                action
                world-size)))
    game))

;;
;; game
