 
 (defn init! []
   (mount-root))
+
+(st/instrument)

(ns tester.core
  (:require [reagent.core :as reagent]
            [clojure.test.check.generators]
            [cljs.spec.impl.gen :as gen]
            [cljs.spec :as s]))



(defonce app-db (reagent/atom (initial-state)))

(s/fdef positions-to-move
        :args (s/cat :current-position ::position
                     :move-direction ::direction)
        :ret (s/coll-of ::position))

(defn dec-pos
  [x]
  (if (pos? (dec x))
    (dec x)
    0))

(defn positions-to-move
  [[x y] move-direction]
  (case move-direction
    ::up    (map (fn [new-y] [x new-y]) (range (inc y) board-size))
    ::down  (map (fn [new-y] [x new-y]) (reverse (range (dec-pos y))))
    ::right (map (fn [new-x] [new-x y]) (range (inc x) board-size))
    ::left  (map (fn [new-x] [new-x y]) (reverse (range (dec-pos x))))))

(s/fdef next-position
        :args (s/cat :board ::game-board :positions-to-check (s/coll-of ::position))
        :ret ::position)

(defn next-position
  [board positions-to-check]
  (or (first (filter board positions-to-check)) (last positions-to-check)))

(defn board->position-board
  [board direction]
  (zipmap (keys board) (map #(positions-to-move % direction) (keys board))))

(s/fdef move-direction
        :args (s/cat :board ::game-board :direction ::direction)
        :ret ::game-board)

(defn sort-next-positions
  [nps direction]
  (case direction
    ::up (sort-by (fn [[x y] _] y) nps)
    ::down (reverse (sort-by (fn [[x y] _] y) nps))
    ::right (sort-by (fn [[x y] _] x) nps)
    ::left (reverse (sort-by (fn [[x y] _] x) nps))))

(defn move-direction
  [board direction]
  (let [positions (board->position-board board direction)
        next-positions (map (fn [[k v]] [k (next-position board v)]) positions)
        only-moves (filter (fn [[k v]] (not-empty v)) next-positions)
        sorted-next-positions (sort-next-positions only-moves direction)
        updated-board (reduce (fn [b [p1 p2]] (update (dissoc b p1) p2 + (b p1))) board sorted-next-positions)
        ]
    updated-board
    ))

(defn update-state
  [state [event-type & params]]
  (case event-type
    ::move-direction (let [[direction] params] (update state ::game-board #(move-direction % direction)))))

(defn dispatch-event!
  [event]
  (swap! app-db update-state event)
  (println @app-db)
  (s/assert ::app-db @app-db)  )

(def key-code->direction
  {38 ::up
