JSFiddle - React, Tailwind, and code Playground
HTML
<!DOCTYPE html>
<html>
<head>
<script src="https://cdn.jsdelivr.net/npm/scittle-hoplon@0.0.1/dist/scittle.js" type="application/javascript"></script>
<script src="https://cdn.jsdelivr.net/npm/scittle-hoplon@0.0.1/dist/scittle.javelin.js" type="application/javascript"></script>
<script src="https://cdn.jsdelivr.net/npm/scittle-hoplon@0.0.1/dist/scittle.hoplon.js" type="application/javascript"></script>
<script type="application/x-scittle">
(require
'[hoplon.core :as h]
'[javelin.core :refer [cell cell= defc defc= dosync]])
(def transpose (partial apply map vector))
(def diagonal (partial map (comp first drop) (range)))
(def indexed (partial map-indexed vector))
(def new-game (vec (repeat 9 nil)))
(defc game new-game)
(defc undos ())
(defc= rows (partition 3 game))
(defc= columns (transpose rows))
(defc= diagonals [(diagonal rows) (diagonal (map reverse rows))])
(defc= runs (concat rows columns diagonals))
(defc= winner (->> runs
(map set)
(remove #(contains? % nil))
(filter #(= 1 (count %)))
ffirst))
(defc= moves (->> (indexed game)
(filter (comp nil? second))
(map first)))
(defc= no-moves? (not (seq moves)))
(defc= over (cond winner (str winner " won!")
no-moves? "Cat's game."))
(defn undo! []
(when (seq @undos)
(reset! game (peek @undos))
(swap! undos pop)))
(defn ai! []
(when-not @over
(swap! game assoc (rand-nth @moves) "O")))
(defn play! [i j]
(let [idx (+ (* i 3) j)]
(when (and (not @over) (nil? (get @game idx)))
(swap! undos conj @game)
(swap! game assoc idx "X"))))
(defn reset-game! []
(reset! game new-game)
(reset! undos ()))
(h/body
(h/div
(h/table :class "tictac"
(h/for-tpl [[i row] (cell= (indexed rows))]
(h/tr
(h/for-tpl [[j x] (cell= (indexed row))]
...
CSS
body {
font-family: sans-serif;
color: #333;
}
pre {
padding: 10px;
background-color: black;
color: chartreuse;
}
table.tictac {
border-collapse: collapse;
border: 5px solid black;
margin-right: 20px;
}
table.score {
border-collapse: collapse;
padding-left: 20px;
}
table.score th {
padding: 5px;
border-bottom: 2px solid black;
}
table.score td {
padding: 5px;
}
table.tictac td {
border: 5px solid black;
cursor: pointer;
width: 1.3em;
height: 1.3em;
text-align: center;
vertical-align: middle;
font: bold 36px sans-serif
}