This commit is contained in:
@@ -0,0 +1,72 @@
|
||||
(ns tbd.ui.tictactoe-test
|
||||
(:require [cljs.test :refer [deftest is testing]]
|
||||
[re-frame.core :as rf]
|
||||
[re-frame.db :as rf-db]
|
||||
[tbd.ui.events :as events]
|
||||
[tbd.ui.tictactoe :as ttt]))
|
||||
|
||||
(defn- board
|
||||
"A board from a string of 9 characters: x, o or anything else for an empty cell."
|
||||
[s]
|
||||
(mapv {\x :x \o :o} s))
|
||||
|
||||
(deftest winner-test
|
||||
(testing "every row, column and diagonal"
|
||||
(doseq [line [[0 1 2] [3 4 5] [6 7 8] [0 3 6] [1 4 7] [2 5 8] [0 4 8] [2 4 6]]
|
||||
player [:x :o]]
|
||||
(let [b (reduce #(assoc %1 %2 player) ttt/new-board line)]
|
||||
(is (= {:player player :line line} (ttt/winner b))))))
|
||||
(testing "no winner"
|
||||
(is (nil? (ttt/winner ttt/new-board)))
|
||||
(is (nil? (ttt/winner (board "xo.ox.xo."))))
|
||||
(is (nil? (ttt/winner (board "xxo.oxxox"))))))
|
||||
|
||||
(deftest draw-test
|
||||
(is (ttt/draw? (board "xoxxoooxx")))
|
||||
(is (not (ttt/draw? (board "xxxooxoxo"))) "a full board with a winner is not a draw")
|
||||
(is (not (ttt/draw? (board "xoxxooox.")))))
|
||||
|
||||
(deftest outcome-test
|
||||
(is (nil? (ttt/outcome ttt/new-board)))
|
||||
(is (= :draw (ttt/outcome (board "xoxxoooxx"))))
|
||||
(is (= {:player :o :line [0 4 8]} (ttt/outcome (board "oxxxoxxxo")))))
|
||||
|
||||
(deftest move-test
|
||||
(is (= :x (get (ttt/move ttt/new-board :x 4) 4)))
|
||||
(is (nil? (ttt/move (board "x........") :o 0)) "taken cell")
|
||||
(is (nil? (ttt/move ttt/new-board :x 9)) "off the board")
|
||||
(is (nil? (ttt/move ttt/new-board :x -1)) "off the board")
|
||||
(is (nil? (ttt/move (board "xxxoo....") :o 8)) "game already won"))
|
||||
|
||||
(defn- move! [& cells]
|
||||
(doseq [i cells] (rf/dispatch-sync [::events/ttt-move i])))
|
||||
|
||||
(deftest game-flow-test
|
||||
(rf/dispatch-sync [::events/initialize])
|
||||
(let [game #(let [{:keys [board turn]} (:ttt @rf-db/app-db)]
|
||||
{:board board :turn turn :outcome (ttt/outcome board)})]
|
||||
(testing "a new game"
|
||||
(is (= :x (:turn (game))))
|
||||
(is (nil? (:outcome (game)))))
|
||||
(testing "players alternate"
|
||||
(move! 0)
|
||||
(is (= :o (:turn (game))))
|
||||
(move! 3)
|
||||
(is (= [:x nil nil :o] (subvec (:board (game)) 0 4))))
|
||||
(testing "a taken cell is ignored and doesn't pass the turn"
|
||||
(move! 0)
|
||||
(is (= :x (:turn (game))))
|
||||
(is (= :o (get (:board (game)) 3))))
|
||||
(testing "X wins on the top row; the game then ignores moves"
|
||||
(move! 1 4 2)
|
||||
(is (= {:player :x :line [0 1 2]} (:outcome (game))))
|
||||
(let [before (:board (game))]
|
||||
(move! 8)
|
||||
(is (= before (:board (game))))))
|
||||
(testing "new game"
|
||||
(rf/dispatch-sync [::events/ttt-reset])
|
||||
(is (= ttt/new-board (:board (game))))
|
||||
(is (= :x (:turn (game)))))
|
||||
(testing "a draw"
|
||||
(move! 0 1 2 4 3 5 7 6 8)
|
||||
(is (= :draw (:outcome (game)))))))
|
||||
@@ -228,3 +228,52 @@
|
||||
(is (re-find #"Invalid body" (:text r)))
|
||||
(is (empty? (get-in @rf-db/app-db [:todos :items])))
|
||||
(done))))))
|
||||
|
||||
(deftest tic-tac-toe
|
||||
(open-page!)
|
||||
(async done
|
||||
(-> (call "get_tic_tac_toe" {})
|
||||
(.then (fn [r]
|
||||
(is (false? (:error? r)))
|
||||
(is (= {:board (vec (repeat 9 nil)) :status "in_progress" :turn "x"} (json (:text r))))
|
||||
(call "move_tic_tac_toe" {:cell 4})))
|
||||
(.then (fn [r]
|
||||
;; It pressed the cell, as a user would, and says it is O's turn now.
|
||||
(is (= :x (get-in @rf-db/app-db [:ttt :board 4])))
|
||||
(is (= {:board [nil nil nil nil "x" nil nil nil nil] :status "in_progress" :turn "o"}
|
||||
(json (:text r))))
|
||||
(call "move_tic_tac_toe" {:cell 4})))
|
||||
(.then (fn [r]
|
||||
(is (= {:text "cell 4 is taken" :error? true} r))
|
||||
(call "move_tic_tac_toe" {:cell 0 :player "x"})))
|
||||
(.then (fn [r]
|
||||
(is (= {:text "it is o's turn, not x's" :error? true} r))
|
||||
(is (nil? (get-in @rf-db/app-db [:ttt :board 0])) "nothing was moved")
|
||||
(call "move_tic_tac_toe" {:cell 9})))
|
||||
(.then (fn [r]
|
||||
(is (:error? r))
|
||||
(is (re-find #"0 to 8" (:text r)))
|
||||
(call "move_tic_tac_toe" {:cell 1.5})))
|
||||
(.then (fn [r]
|
||||
(is (:error? r))
|
||||
(call "move_tic_tac_toe" {:cell 0 :player "z"})))
|
||||
(.then (fn [r]
|
||||
(is (:error? r))
|
||||
(call "move_tic_tac_toe" {:cell 0 :player "o"})))
|
||||
(.then (fn [r]
|
||||
(is (= "x" (:turn (json (:text r)))))
|
||||
;; The person moves X to 3 and O to 1; the agent's X then completes the middle row.
|
||||
(rf/dispatch-sync [::events/ttt-move 3])
|
||||
(rf/dispatch-sync [::events/ttt-move 1])
|
||||
(call "move_tic_tac_toe" {:cell 5 :player "x"})))
|
||||
(.then (fn [r]
|
||||
(is (= {:board ["o" "o" nil "x" "x" "x" nil nil nil]
|
||||
:status "won" :winner "x" :line [3 4 5]}
|
||||
(json (:text r))))
|
||||
(call "move_tic_tac_toe" {:cell 8})))
|
||||
(.then (fn [r]
|
||||
(is (= {:text "the game is over: start a new game" :error? true} r))
|
||||
(call "new_tic_tac_toe_game" {})))
|
||||
(.then (fn [r]
|
||||
(is (= {:board (vec (repeat 9 nil)) :status "in_progress" :turn "x"} (json (:text r))))
|
||||
(done))))))
|
||||
|
||||
Reference in New Issue
Block a user