Skip to content

Computer Move: REPL Experimentsλ︎

Lets define a sample game board to work with, a board that is part way through a game should give us a good way to test computer moves

(def game-board-example-mid-game
       [[:cross :nought :empty]
        [:empty :nought :empty]
        [:cross :nought :empty]])

Iterate over the boardλ︎

To make a move there needs to be an empty cell on the board to make a move in.

(= :empty (get-in game-board-example-mid-game [1 0]))
;; => true

(= :empty (get-in game-board-example-mid-game [0 2]))
;; => true

lets get the co-ordinates for all the cells on the board that are empty, so we know which cells are available

(for [row    (range board-dimension)
        column (range board-dimension)
        :when  (= :empty
                  (get-in game-board-example-mid-game [row column]))]
    [row column])
;; => ([0 2] [1 0] [1 2] [2 2])

We can use the same approach, but assigning it to a local name with let, so we can do more with the result

(let [available-cells (for [row    (range 3)
                        column (range 3)
                        :when (= :empty
                                 (get-in game-board-example-mid-game [row column]))]
                    [row column])]
  available-cells)
;; => ([0 2] [1 0] [1 2] [2 2])

Did we find any empty cellsλ︎

We can check with the empty? function and therefore check if there are available cells by inverting the check with not

(empty? '([0 2] [1 0] [1 2] [2 2]))

(not (empty? '([0 2] [1 0] [1 2] [2 2])))

However there is a Clojure idiom to use seq (mentioned in the docs for empty?)

#_(seq '([0 2] [1 0] [1 2] [2 2]))
;; => ([0 2] [1 0] [1 2] [2 2])

#_(seq '())
;; => nil

So with seq if there are empty cells, those values are returned, otherwise nil is returned (which is falsey)

Randomly selecting an available cellλ︎

To get one of the co-ordinates assuming there are available cells, we can use rand-nth

#_(rand-nth '([0 2] [1 0] [1 2] [2 2]))
;; => [0 2]

Putting seq and rand-nth together we can get one of the available positions

(def available-cells-example '([1 0] [0 2] [1 2] [2 2]))

(when (seq available-cells-example)
  (rand-nth available-cells-example))
;; => [1 2]

The computer can now make moves until no more cells are available

(let [available-cells
       (for [row    (range board-dimension)
             column (range board-dimension)
             :when (=
                    :empty
                    (get-in game-board-example-mid-game [column row]))]
          [column row])

      next-move (when (seq available-cells)
                  (rand-nth available-cells))]

  (if next-move
    (str "update app-state")
    (str "display messages saying no more moves")))
;; => "update app-state"

Now we can fit this algorithm into our game code