diff --git a/spec/clojure_ttt/board_spec.clj b/spec/clojure_ttt/board_spec.clj index dc47f90..a5eaceb 100644 --- a/spec/clojure_ttt/board_spec.clj +++ b/spec/clojure_ttt/board_spec.clj @@ -29,8 +29,11 @@ (it "should know if there is a draw" (should= true (draw? ["x"]))) - (it "should know valid positions" - (should= false (valid-position? ["x"] 0))) + (it "should know if a position is valid" + (should= true (valid-position? ["x" "-"] 1))) + + (it "should know if a position is not valid" + (should= false (valid-position? ["x" "-"] 0))) (it "should know all available positions" (should= [1 2] (available-positions ["x" "-" "-"]))) diff --git a/spec/clojure_ttt/game_spec.clj b/spec/clojure_ttt/game_spec.clj index 869de80..8058847 100644 --- a/spec/clojure_ttt/game_spec.clj +++ b/spec/clojure_ttt/game_spec.clj @@ -13,7 +13,7 @@ (it "should make a move" (should= ["o" "x" "-"] (with-in-str "1" - (make-move ["o" "-" "-"])))) + (make-move ["o" "-" "-"] "x")))) (it "does not let you make moves in invalid cells" (should= ["-"] diff --git a/spec/clojure_ttt/presenter_spec.clj b/spec/clojure_ttt/presenter_spec.clj new file mode 100644 index 0000000..22cd7c0 --- /dev/null +++ b/spec/clojure_ttt/presenter_spec.clj @@ -0,0 +1,22 @@ +(ns clojure-ttt.presenter-spec + (:require [speclj.core :refer :all] + [clojure-ttt.presenter :refer :all])) + +(describe "presenter" + (it "displays gamemode options" + (should-contain "Type 0 to play human vs human" + (with-out-str (show-gamemode-options)))) + + (it "displays the board correctly" + (should-contain "x--\nx--\n---" + (with-out-str (show-board ["x" "-" "-" "x" "-" "-" "-" "-" "-"])))) + + (it "displays the winner" + (should-contain "x has won the game!" + (with-out-str (show-round-message ["x" "x" "x" "o" "-" "-" "-" "o" "o"])))) + + (it "displays if its a draw" + (should-contain "draw" + (with-out-str (show-round-message ["x" "x" "o" + "o" "x" "x" + "x" "o" "o"]))))) diff --git a/src/clojure_ttt/board.clj b/src/clojure_ttt/board.clj index 93daba2..8249155 100644 --- a/src/clojure_ttt/board.clj +++ b/src/clojure_ttt/board.clj @@ -33,7 +33,7 @@ (true? (some #(has-win? %) (winning-lines board (size board))))) (defn draw? [board] - (not (some #{"-"} board))) + (not-any? #{"-"} board)) (defn available-positions [board] (->> (zipmap (iterate inc 0) board) @@ -41,7 +41,7 @@ (map (fn [[position cell]] position)))) (defn valid-position? [board position] - (contains? (vec (available-positions board)) position)) + (true? (some #(= position %) (available-positions board)))) (defn find-turn [board] (if (even? (count (available-positions board))) "x" "o")) diff --git a/src/clojure_ttt/computer.clj b/src/clojure_ttt/computer.clj index 396c98b..afe583d 100644 --- a/src/clojure_ttt/computer.clj +++ b/src/clojure_ttt/computer.clj @@ -43,4 +43,4 @@ (all-rounds-played board player (available-positions board))))) (defn find-best-move [board player] - (:move (minimax board player 8 ["x" "o"]))) + (:move (minimax board player (min 8 (count (available-positions board))) ["x" "o"]))) diff --git a/src/clojure_ttt/game.clj b/src/clojure_ttt/game.clj index 6d4120c..8521356 100644 --- a/src/clojure_ttt/game.clj +++ b/src/clojure_ttt/game.clj @@ -1,5 +1,7 @@ (ns clojure-ttt.game - (use [clojure-ttt.board])) + (use [clojure-ttt.board] + [clojure-ttt.computer] + [clojure-ttt.presenter])) (defn game-over? [board] (or (draw? board) (any-wins? board))) @@ -7,16 +9,37 @@ (defn- read-move [] (read-string (read-line))) -(defn make-move [board] +(defn make-move [board player] (let [move (read-move)] (cond - (valid-position? board move) (mark-board board move (find-turn board)) + (valid-position? board move) (mark-board board move player) :else board))) -(defn start-game [board] - (println board) +(defn make-move-computer [board player] + (mark-board board (find-best-move board player) player)) + +(defn play-until-over [board gamemode] + (show-round-message board) + (show-board board) (when-not (game-over? board) - (recur (make-move board)))) + (let [turn (find-turn board)] + (cond + (and (= gamemode :hvc) (= turn "o")) (recur (make-move-computer board turn) gamemode) + (= gamemode :cvc) (recur (make-move-computer board turn) gamemode) + :else (recur (make-move board turn) gamemode))))) + +(defn- ask-for-settings [board] + (show-gamemode-options) + (let [input (read-move)] + (cond + (= input 1) (play-until-over board :hvc) + (= input 2) (play-until-over board :cvc) + :else (play-until-over board :hvh)))) + +(defn ask-to-play-again [] + (show-options) + (if (= 1 (read-move)) (ask-for-settings (create-board 3)))) (defn -main [& args] - (start-game (create-board 3))) + (ask-for-settings (create-board 3)) + (ask-to-play-again)) diff --git a/src/clojure_ttt/presenter.clj b/src/clojure_ttt/presenter.clj new file mode 100644 index 0000000..9d0f7fe --- /dev/null +++ b/src/clojure_ttt/presenter.clj @@ -0,0 +1,29 @@ +(ns clojure-ttt.presenter + (use [clojure-ttt.board])) + +(defn- clear-console [] + (println "\033[H\033[2J")) + +(defn- welcome-user [] + (println "\033[35mWelcome! Please select a gamemode.") + (println "------------------------------------------")) + +(defn show-gamemode-options [] + (clear-console) + (welcome-user) + (println "\033[36mType 0 to play human vs human. \n1 to play vs a computer. \n2 to watch two computers play.")) + +(defn show-board [board] + (let [partitioned-board (map #(apply str %) (partition 3 board))] + (print "\033[31m") + (println (apply str(interpose "\n" partitioned-board))))) + +(defn show-round-message [board] + (println (str "\033[35m" "--------------------------")) + (cond + (any-wins? board) (println (str (last-move board) " has won the game!")) + (draw? board) (println "The game is a draw!") + :else (println (str (find-turn board) " please make your move.")))) + +(defn show-options [] + (println "Press 1 to play again, anything else to quit."))