                   :else new-y)]
       (assoc paddle :y new-y))))
 
-;;-----------------------------------------------------------------------------
+(defprotocol IHittable
+  (hit? [_ ball]))
+
+(extend-type Paddle
+  IHittable
+  (hit? [{x1 :x y1 :y w :width h :height side :side :as paddle}
+          {x :x y :y vx :vx radius :radius :as ball}]
+    (let [x2 (+ x1 w)
+        y2 (+ y1 h)]
+    (or (and (= side :right)
+             (<= y1 y y2)
+             (< vx 0)
+             (>= radius (dist x2 y x y)))
+        (and (= side :left)
+             (<= y1 y y2)
+             (> vx 0)
+             (>= radius (dist x1 y x y)))
+        false))))
+
 ;; Game State
-;;-----------------------------------------------------------------------------
 
 (def objects
   {:background (GameBackground.)

  (draw! [_ ctx]
    (.save ctx)
    (aset ctx "textAlign" "center")
    ;;
    (aset ctx "font" "60px Helvetica")
    (aset ctx "fillStyle" "peru")
    (.fillText ctx "Welcome to Pong!" MID_W 100)
    ;;
    (aset ctx "font" "italic 14px Helvetica")
    (aset ctx "fillStyle" "slategray")
    (.fillText ctx "single player" MID_W 150)
    ;;
    (aset ctx "font" "20px Helvetica")
    (aset ctx "fillStyle" "dodgerblue")
    (.fillText ctx "Press the spacebar to start." MID_W 300)
    ;;
    (aset ctx "font" "italic 12px Helvetica")
    (aset ctx "fillStyle" "slategray")
    (.fillText ctx "Click and drag the mouse pointer near a paddle to move that paddle." MID_W 370)
    (.fillText ctx "[ Spacebar ] to pause ." MID_W 410)
    (.fillText ctx "[ Escape ] to quit." MID_W 430)
    (.restore ctx)))

          score2 (if p2? (inc score2) score2)
          s1 (assoc score-1 :score score1)
          s2 (assoc score-2 :score score2)
          mode (if (or (>= score1 WIN_SCORE) (>= score2 WIN_SCORE))
                 :game-over
                 (:mode state))
          ball (if p2?
