Finn Völkel / Jun 09 2020
with Arne Brasseur
Tic Tac Toe
This notebook exemplifies the usage of tap support together with our MIME viewers for playing Tic Tac Toe.
{:deps {hiccup {:mvn/version "1.0.5"}}}
deps.edn
Extensible Data Notation
(ns tictactoe.core)
(use hiccup.core)
1.6s
Clojure
nil
(defn game-board [states]
{:nextjournal/viewer :html
:nextjournal/tap-id :tic
:nextjournal.viewer/value
(html [:div {:class "flex flex-wrap -mx-1"}
(for [state states]
[:div {:class "my-1 px-1 w-1/3 overflow-hidden"} state])])})
0.1s
Clojure
'tictactoe.core/game-board
The code for the logic of tic tac toe is copied from @trptcolin's tictactoe-clojure repo.
(def win-sets
[[0 1 2], [3 4 5], [6 7 8], [0 3 6], [1 4 7], [2 5 8], [0 4 8], [2 4 6]])
(defn- winner-exists-on-spaces? [board spaces]
(and
(apply = (map (board %) spaces))
(not (= "#" (board (first spaces))))))
(defn- winner-on-spaces [board spaces]
(if (winner-exists-on-spaces? board spaces)
(board (first spaces))
nil))
(defn- indexed [coll]
(map vector coll (iterate inc 0)))
(defn- index-filter [pred coll]
(for [[spot i] (indexed coll) :when (pred spot)] i))
;;; public functions
(defn make-board [] (vec (repeat 9 "#")))
(defn empty-square? [square]
(= "#" square))
(defn full? [board] (not-any? empty-square? board))
(defn winner [board] (some (winner-on-spaces board %) win-sets))
(defn game-over? [board] (boolean (or (winner board) (full? board))))
(defn place-on-board [board spot mark] (assoc board spot mark))
(defn next-mark [mark] (if (= "X" mark) "O" "X"))
(defn valid-move? [board move]
(try (= (board move) "#")
(catch Exception e false)))
(defn final-message [winner]
(if (nil? winner)
"Cat's Game!"
(str winner " Wins!!!")))
(defn empty-squares [board]
(index-filter empty-square? board))
0.5s
Clojure
'tictactoe.core/empty-squares
(def :dynamic board (make-board))
(def :dynamic player "X")
0.0s
Clojure
'tictactoe.core/player
(defn play [move]
(println "It's players " player "turn!")
(println "Player " player "has chosen move" move)
(cond
(valid-move? board move)
(do
(alter-var-root (var board) place-on-board move player)
(tap> (game-board board))
(if-some [gewinner (winner board)]
(println "The winner is :" gewinner)
(do
(println "No winner yet! Continue!")
(alter-var-root (var player) next-mark))))
:else
(println "Invalid move! Try again player " player)))
0.0s
Clojure
'tictactoe.core/play
We tap
the empty game board to initialize our viewer. #
means an empty cell. X
and O
mean the cell was marked by player 1 and player 2 respectively.
(tap> (game-board board))
0.0s
Clojure
X
O
#
#
#
#
#
#
#
true
Type a number from 0 to 8 to make your move.
(play 1)
0.8s
Clojure
"X"