 (defn show-top-panel
   [scores player]
   [:div.scores
-   [top-panel-button new-game! (special-char "&#9733;")]
+   [top-panel-button #(send-game-event! :new-game) (special-char "&#9733;")]
    [top-panel-button #(swap! app-state update :help not) "?"]
    (show-scores scores player)
-   [top-panel-button restart-game! (special-char "&#x21bb;")]
-   [top-panel-button cancel-last-move! (special-char "&#x21A9;")]])
+   [top-panel-button #(send-game-event! :restart) (special-char "&#x21bb;")]
+   [top-panel-button #(send-game-event! :undo) (special-char "&#x21A9;")]])
 
 (defn max-board-height []
   (- (-> (dom/getWindow) dom/getViewportSize .-height) 100))

        is-human-xf (fn [_] (not (is-ai? @current-player)))
        is-ai-xf (fn [_] (is-ai? @current-player))
        ai-events (chan 1 (comp (filter game-on-xf) (filter is-ai-xf)))
        player-events (chan 1 (comp (filter game-on-xf) (filter is-human-xf)))]
    
    (go
      (while true
        (alt!
          player-events ([coord] (update-game! play-move coord))
          ai-events ([msg] (when (= msg :ai-play) (handle-ai!)))
          (async/timeout ai-move-delay) ([_] (put! ai-events :ai-play))
          )))
    
    {:player-events player-events}
    ))

(defonce game-loop (start-game-loop))

(def send-player-event!
  (partial put! (game-loop :player-events)))


;; -----------------------------------------

(def ai-players (reaction (get-in @game [:ai-players])))
(def end-of-game (reaction (nil? @current-player)))

(defn is-ai? [player]
  (contains? @ai-players player))


;; -----------------------------------------
;; GAME LOOP
;; -----------------------------------------

(defn update-game! [f & args]
  (swap! app-state update-in [:games] conj (apply f @game args)))

(defn- handle-ai! []
  (let [ai-algo (get @ai-players @current-player)
        move (ai-algo @game @current-player)]
    (update-game! play-move move)))

(defn cancel-last-move! []
  (swap! app-state update-in [:games]
