| Play three games on the gfx backend e98a184 nandi 11d ago | 1 | (ns glimmer-gfx.tictactoe |
| 2 | "Tic-tac-toe on the gfx backend: a 3x3 grid of buttons and one pure rule. |
| 3 | |
| 4 | The board is a vector of 9 cells, each :x, :o or nil. You are X; O replies |
| 5 | with the first free square, which is dumb on purpose -- the point here is |
| 6 | that a game is just another glimmer component." |
| 7 | (:require [glimmer.ratom :as ra] |
| 8 | [glimmer.core :as ui] |
| 9 | [glimmer-gfx.core])) |
| 10 | |
| 11 | (def ^:private lines |
| 12 | [[0 1 2] [3 4 5] [6 7 8] [0 3 6] [1 4 7] [2 5 8] [0 4 8] [2 4 6]]) |
| 13 | |
| 14 | (defn winner |
| 15 | "The mark occupying a whole line, or nil." |
| 16 | [board] |
| 17 | (some (fn [l] (let [[a b c] (map board l)] (when (and a (= a b c)) a))) lines)) |
| 18 | |
| 19 | (defn reply |
| 20 | "O's move: the first empty square. ponytail: no minimax, add one when losing |
| 21 | to this stops being funny." |
| 22 | [board] |
| 23 | (first (remove #(board %) (range 9)))) |
| 24 | |
| 25 | (defn play |
| 26 | "X takes `i`, then O replies -- unless the game is already over." |
| 27 | [board i] |
| 28 | (if (or (board i) (winner board)) |
| 29 | board |
| 30 | (let [b (assoc board i :x)] |
| 31 | (if-let [o (and (not (winner b)) (reply b))] (assoc b o :o) b)))) |
| 32 | |
| 33 | (def ^:private empty-board (vec (repeat 9 nil))) |
| 34 | (def state (ra/atom empty-board)) |
| 35 | |
| 36 | (defn- status [board] |
| 37 | (case (winner board) |
| 38 | :x "You win" |
| 39 | :o "O wins" |
| 40 | (if (every? some? board) "Draw" "Your turn"))) |
| 41 | |
| 42 | (defn- cell [board i] |
| 43 | [:button {:label (case (board i) :x "X" :o "O" " ") |
| 44 | :kind (when (= :x (board i)) :primary) |
| 45 | :on-click #(ra/swap! state play i)}]) |
| 46 | |
| 47 | (defn app [] |
| 48 | (let [board (ra/deref state)] |
| 49 | [:page {:max-width 300} |
| 50 | [:card {:spacing 8} |
| 51 | [:title {:label "Tic-tac-toe"}] |
| 52 | [:label {:label (status board)}] |
| 53 | (into [:vbox {:spacing 6}] |
| 54 | (for [row (partition 3 (range 9))] |
| 55 | (into [:hbox {:spacing 6}] (for [i row] (cell board i))))) |
| 56 | [:spacer {:size 4}] |
| 57 | [:button {:label "new game" :on-click #(ra/reset! state empty-board)}]]])) |
| 58 | |
| 59 | (defn -main [& _] |
| 60 | (ui/run app :title "tic-tac-toe" :width 300 :height 340)) |