To learn Clojure I'm working on a little Tic Tac Toe game. After completing the first part of the game with relative ease, I've struggled trying to build an intelligent computer player.
For the test I'm writing to help guide this, I want to check that the computer picks spot 9 if it's the computer's turn and this is the board:
X | O | 3
4 | X | O
7 | 8 | 9
To begin the game, the board is defined like this, as a map with key-value pairs representing the location on the board and the contents of that space:
(def board {1 "1" 2 "2" 3 "3" 4 "4" 5 "5" 6 "6" 7 "7" 8 "8" 9 "9"})
I had a few thoughts about how to solve this problem. One was to define the winning sets like this:
(def winning-sets
[[(board 1) (board 2) (board 3)],
[(board 4) (board 5) (board 6)],
[(board 7) (board 8) (board 9)],
[(board 1) (board 4) (board 7)],
[(board 2) (board 5) (board 8)],
[(board 3) (board 6) (board 9)],
[(board 1) (board 5) (board 9)],
[(board 3) (board 5) (board 7)]])
Iterate over each set:
(for [set winning-sets]
(filter #(= symbol %) set))
But that doesn't seem right...I don't know where I would go from there. The problem I'm trying to solve can be described like this:
Tell the computer to look over the 8 winning sets and find one set that has TWO of your symbols and ONE open spot.
I'm fairly new to Clojure, so I'm just not sure I understand the best way to approach this problem. I've been looking at the ClojureDocs (checking out iterating functions like for
and loop
and case
), but haven't been able to make this work.
What would be the best way to iterate over those winning sets, currently in vector form, and find the set that has two of a specific symbol and one opening? Or would it be best to store the winning sets in a different data structure?
NOTE: I've read the responses to this question, but haven't been able to apply them to mine.
First of all, I advice you to use this structure for board position:
(def board [[1 1 0]
[0 0 0]
[1 0 1]])
Where X is 1
, O is -1
and empty cell is 0
. Board in my example has only X symbols (to simplify). Next,
(def winning-sets
'([[0 0] [0 1] [0 2]]
[[1 0] [1 1] [1 2]]
[[2 0] [2 1] [2 2]]
[[0 0] [1 0] [2 0]]
[[0 1] [1 1] [2 1]]
[[0 2] [1 2] [2 2]]
[[0 0] [1 1] [2 2]]
[[0 2] [1 1] [2 0]]))
This is "winning" sets of coordinates. You can calculate this if necessary, but for 3x3 this list is really not so big. In this terms, the answer for you question is
(defn check
[target combo]
(= (map #(count (filter (partial = %) combo)) [target 0]) '(2 1)))
(defn extract
[coords]
(apply vector (map (fn [[f s]] ((board f) s)) coords)))
(filter #(check 1 (extract %)) winning-sets)
If you execute this code in REPL, you will see
user=> (filter #(check 1 (extract %)) winning-sets)
([[0 0] [0 1] [0 2]]
[[2 0] [2 1] [2 2]]
[[0 0] [1 0] [2 0]]
[[0 0] [1 1] [2 2]])
which looks like right answer (2 horizontal lines, 1 vertical and 1 diagonal). Code is rough and there are few ways to make it more beautiful and reusable. Should I explain what's going on or code is clear enough?
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With