 (defn add-move
   "Takes a game, a move function (a function that, given a board and
   some other value, translates that value to a chess move), a
-  a value representing a move, and an optional node id as input, and
-  returns an updated game where the move has been added as the last
-  child at the given node. If no node id is supplied, the current node
-  of the game is used."
-  [game move-function move node-id]
+  a value representing a move, a node id, and a boolean that says whether the
+  existing moves at the current nodes should be removed, and returns an
+  updated game where the move has been added as the last child at the given
+  node. If no node id is supplied, the current node of the game is used."
+  [game move-function move node-id remove-existing-moves?]
   (let [z (-> (game-zip game node-id)
-              (zip-add-move move-function move)
+              (zip-add-move move-function move remove-existing-moves?)
               zip/down
               zip/rightmost)]
     (assoc game :root-node (zip/root z)

(defn add-san-move
  "Adds a move in short algebraic notation to a game at a given node
  id. The move is added as the last child. If no node id is supplied,
  the current node of the game is used."
  [game san-move & {:keys [node-id]}]
  (add-move game board/move-from-san san-move
            (or node-id (-> game :current-node :node-id))))


(defn add-uci-move
  "Adds a move in UCI notation to a game at a given node id. The move is
  added as the last child. If no node id is supplied, the current node of the
  game is used."
  [game uci-move & {:keys [node-id]}]
  (add-move game board/move-from-uci uci-move
            (or node-id (-> game :current-node :node-id))))


(defn add-map-move
  "Adds a map move (see documentation for clj-chess.board/board-to-map) to a
  game at a given node id. The move is added as the last child. If no node id
  is supplied, the current node of the game is used."
  [game map-move & {:keys [node-id]}]
  (add-move game board/move-from-map map-move
            (or node-id (-> game :current-node :node-id))))


(defn add-move-sequence
  "Like add-move, but adds a sequence of moves rather than a single move. This
  is faster than calling add-move multiple times, because we don't have to
  unzip and zip the tree for each added move."
  [game move-function moves node-id]
  (let [z (-> (game-zip game node-id)
              (zip-add-move-sequence move-function moves))]
    (assoc game :root-node (zip/root z)
                :current-node (zip/node z))))


(defn- zip-add-move
  "Takes a zipper, a move function (a function that, given a board
  and some other value, translates that value to a chess move),
  a value representing a move, and a boolean that says whether any previous
  moves at the current location should be removed, and returns a new zipper
  for a modified game tree where the move has been added at the zipper
  location."
  [zipper move-function move remove-existing-moves?]
  (let [board (-> zipper zip/node :board)]
    (zip/append-child (if remove-existing-moves?
                        (zip-remove-children zipper)
                        zipper)
                      {:board (board/do-move board
                                             (move-function board move))
                       :node-id (generate-id)})))

(def ^:private zip-add-san-move #(zip-add-move %1 board/move-from-san %2 false))
(def ^:private zip-add-uci-move #(zip-add-move %1 board/move-from-uci %2 false))
(def ^:private zip-add-plain-move #(zip-add-move %2 (fn [_ m] m) %2 false))

(defn- zip-add-move-sequence
  "Takes a zipper, a move function (a function that, given a board and some
  other value, translates that value to a chess move), a sequence of
  values representing chess moves, and a boolean that says whether any
  previous moves at the current node should be removed, and returns a new
  zipper for a modified game tree where the moves have been added at the
  zipper location."
  [zipper move-function moves remove-existing-moves?]
  (reduce (fn [z m] 
            (-> (zip-add-move z move-function m remove-existing-moves?)
                (zip/down)
                (zip/rightmost)))
          zipper
          moves))

(def ^:private zip-add-san-move-sequence 
  #(zip-add-move-sequence %1 board/move-from-san %2 false))

(def ^:private zip-add-uci-move-sequence
  #(zip-add-move-sequence %2 board/move-from-uci %2 false))

(defn- zip-add-key-value-pair
  [zipper key value]
