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"
Runtimes (1)