   [{:keys [ball] :as state}]
   (merge state {:ball (move ball)}))
 
-(defn hit?
-  [{x :x y :y vx :vx radius :radius :as ball}
-   {x1 :x y1 :y w :width h :height :as paddle}
-   dir]
-  (let [x2 (+ x1 w)
-        y2 (+ y1 h)]
-    (or (and (= dir :right)
-             (<= y1 y y2)
-             (< vx 0)
-             (>= radius (dist x2 y x y)))
-        (and (= dir :left)
-             (<= y1 y y2)
-             (> vx 0)
-             (>= radius (dist x1 y x y)))
-        false)))
-
 (defn collision-phase!
-  [{:keys [ball paddle-1 paddle-2 mode] :as state}]
-  (if (or (hit? ball paddle-1 :right)
-          (hit? ball paddle-2 :left))
+  [{:keys [ball paddle-1 paddle-2] :as state}]
+  (if (or (hit? paddle-1 ball)
+          (hit? paddle-2 ball))
     (let [{:keys [vx vy]} ball
           delta (rand-nth [0.3 0.5 0.7])
           vxf (if (< vx 0) dec inc)

  IMovable
  (move [{:keys [x y vx vy radius] :as ball}]
    (cond
      (= x radius)
      (assoc ball :x -100)
      ;;
      (= x (- SCALE-W radius))
      (assoc ball :x (+ SCALE-W 100))
      ;;
      :else
      (let [vy (if (< radius y (- SCALE-H radius)) vy (* -1 vy))]
        (assoc ball :x (+ x vx) :y (+ y vy) :vy vy)))))

;;-----------------------------------------------------------------------------
;; Controllable Objects
;;-----------------------------------------------------------------------------

(defprotocol IPositionable
  (position! [_ y]))

  (js/Math.sqrt (+ (sqr (- x2 x1))
                   (sqr (- y2 y1)))))

;; DOM

(defn by-id
  [id]
