Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ttt with a cli #2

Open
wants to merge 37 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 36 commits
Commits
Show all changes
37 commits
Select commit Hold shift + click to select a range
9a202f8
add console
liza-pizza Aug 25, 2022
47282af
add ttt cli
liza-pizza Aug 26, 2022
4af5bdf
add validation for input
liza-pizza Aug 29, 2022
968e4f2
update read me
liza-pizza Aug 31, 2022
5a52ec0
update readme
liza-pizza Aug 31, 2022
3e945df
merge with main
liza-pizza Aug 31, 2022
942c476
formatting
liza-pizza Aug 31, 2022
789841d
add view position board and input validation
liza-pizza Aug 31, 2022
b8db7d0
add validation
liza-pizza Aug 31, 2022
91e7b60
merge with ttv
liza-pizza Aug 31, 2022
2cc4ee1
combine game over and draw functions
liza-pizza Aug 31, 2022
35f7d5e
fomatting (again)
liza-pizza Aug 31, 2022
98166db
add print winner to console
liza-pizza Sep 1, 2022
e96f0f5
formatting again
liza-pizza Sep 1, 2022
b27f9c1
remove empty matrix test
liza-pizza Sep 1, 2022
bece888
add tests for game-over?
liza-pizza Sep 1, 2022
6fe4ee2
add test for matrix to string
liza-pizza Sep 1, 2022
b6d0e81
rename matrix to string acc to style guide
liza-pizza Sep 1, 2022
bca4fc6
rename ttt ns
liza-pizza Sep 1, 2022
8ff0477
add test for update board
liza-pizza Sep 1, 2022
1d2814a
add test for update board
liza-pizza Sep 1, 2022
ddecdd2
remove place move on board
liza-pizza Sep 1, 2022
9174f36
add exit option
liza-pizza Sep 1, 2022
5dbdb5b
rename failjure handlers
liza-pizza Sep 1, 2022
5ff08f8
add failjure handlers tests
liza-pizza Sep 1, 2022
eb3d0d8
add in range test
liza-pizza Sep 1, 2022
db983f8
add ascii board
liza-pizza Sep 1, 2022
f9b9f4e
update readme
liza-pizza Sep 2, 2022
16d6b1b
add dev notes to read me
liza-pizza Sep 2, 2022
65e7a5c
Revert "update readme"
liza-pizza Sep 2, 2022
33e2764
remove cpcache
liza-pizza Sep 2, 2022
420ff2a
update readme
liza-pizza Sep 2, 2022
70733e5
[mohita/nid] Refactor ttt into more logical namespaces
nid90 Sep 7, 2022
49a4e0b
fix and refactor tests into corresponding namespaces
liza-pizza Sep 8, 2022
d18ad12
refactor user input to not use loops, generate board vars that were h…
liza-pizza Sep 9, 2022
c1d4fc6
Remove conflicts to merge with main
liza-pizza Sep 13, 2022
0f38dc1
rename pieces to players in accordance with wiki and refactor matrix …
liza-pizza Sep 15, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
46 changes: 46 additions & 0 deletions practice/ttt/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
# Tic Tac Toe

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion: Try to think of the README as the first thing a potential user might be looking at. What would that person want to see here?

Some potential questions:

  • What does this repo do?
  • How do I set this up in my machine?
  • How do I test this out?
  • How to I play the game?

Thinking from this perspective will help pen down a more thoughtful README.

### Cli interface

To start the game use `lein run`

Possible positions:

| 1 | 2 | 3 |
|-----|-----|-----|
| 4 | 5 | 6 |
| 7 | 8 | 9 |

=> lein run:

Enter a number between 1-9 to make a move

Enter h to view position board

Enter q to quit

Current board:

| e | e | e |
|-----|-----|-----|
| e | e | e |
| e | e | e |

Player is o
=> 1

Current board:

| o | e | e |
|-----|-----|-----|
| e | e | e |
| e | e | e |

Player is x

=> q

Bye bye!

### Developer notes

Run tests using `lein test`
84 changes: 84 additions & 0 deletions practice/ttt/board.clj
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
(ns ttt.board
(:refer-clojure :exclude [update])
(:require
[clojure.set]
[ttt.matrix-operations :as mo]))


(def size 3)


(def position-to-coordinate
(->> (for [x (range size)
y (range size)]
[x y])
(map-indexed (fn [i coord] [(inc i) coord]))
(into {})))


(def empty-board
(mapv vec (partition size (repeat (* size size) :e))))


(def valid-positions
(keys position-to-coordinate))


(def positions-string
(mo/matrix->string (partition size (sort valid-positions))))


(def valid-game-pieces-set #{:o :x :e})

(def game-pieces-set #{:o :x})

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

question: Should we consider :x and :o as players rather than pieces? Even the wiki thinks of it that way. What do you think?



(defn to-string
[board]
(mo/matrix->string (mapv #(mapv name %) board)))


(defn empty-coordinate?
[board coordinate]
(= :e (get-in board coordinate)))


(defn valid?
[board]
(and (mo/square-matrix? board)
(clojure.set/subset? (set (flatten board)) valid-game-pieces-set)))


(defn update
[board coordinate game-piece]
(when (valid? board)
(assoc-in board coordinate game-piece)))


(defn winner-of-collection

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

question: What does collection refer to?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the function can find the winner of any collection as opposed to a 3x3 board. Used the word collection to generalize

[coll]
(let [unique-val (set coll)]
(when (and
(= 1 (count unique-val))
(not (:e unique-val)))
(first unique-val))))


(defn winners-of-seqs
[matrix]
(->> matrix
(mo/get-all-possible-seqs)
(map winner-of-collection)
(remove nil?)))


(defn winning-game-piece
[board]
(when (= 1 (count (winners-of-seqs board)))
(first (winners-of-seqs board))))


(defn winner
[board]
(when (valid? board)
(winning-game-piece board)))
8 changes: 8 additions & 0 deletions practice/ttt/core.clj
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
(ns ttt.core
(:gen-class)
(:require [ttt.game :as game]
[ttt.board :as board]))

(defn -main
[]
(game/play board/empty-board))
63 changes: 63 additions & 0 deletions practice/ttt/game.clj
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
(ns ttt.game
(:require
[failjure.core :as f]
[ttt.board :as board]
[ttt.user-input :as user-input]))


(def player-order
(take 9 (cycle board/game-pieces-set)))


(defn over?
[board player-sequence]
(or (board/winner board)
(empty? player-sequence)))


(defn process-parsed-command
[parsed-command board player-sequence]
(case parsed-command
"h" (do
(println "\nPosition board" board/positions-string "\n")
[board player-sequence])

"q" [nil player-sequence]

(let [player (first player-sequence)]
(if (board/empty-coordinate? board parsed-command)
[(board/update board parsed-command player) (rest player-sequence)]
(do (println "Enter an empty position")
[board player-sequence])))))


(defn process-command
[command board player-sequence]
(f/attempt-all [valid-command (user-input/valid-command? command)
parsed-command (user-input/parse-command valid-command)]
(process-parsed-command parsed-command board player-sequence)
(f/when-failed [e]
(println (f/message e))
[board player-sequence])))


(defn play
[board]
(println "Enter a number between 1-9 to make a move \nEnter h to view position board \nEnter q to quit")
(loop [board board
player-sequence player-order]
(cond
(or (nil? board))
(println "Bye bye!")

(over? board player-sequence)
(if-let [winner (board/winner board)]
(println "Winner is" (name winner))
(println "It's a draw"))

:else
(do (println "Current board:" (board/to-string board)
"\nPlayer is" (name (first player-sequence)))
(let [command (read-line)
[new-board new-player-sequence] (process-command command board player-sequence)]
(recur new-board new-player-sequence))))))
8 changes: 8 additions & 0 deletions practice/ttt/matrix_operations.clj
Original file line number Diff line number Diff line change
Expand Up @@ -47,3 +47,11 @@
(transpose-matrix matrix)
(get-diagonals-of-matrix matrix)))


(defn matrix->string
[matrix]
(apply str (for [row matrix]
(str
(apply str "\n"
(interpose " " row))))))

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nitpick: Using a thread last macro might make the section more readable:

(->>  row
     (interpose " ")
     (apply str "\n")
     str)))

Also, do we need to str at the end if we're doing apply str in the previous step?


76 changes: 76 additions & 0 deletions practice/ttt/test/board_test.clj
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
(ns board-test
(:require
[clojure.test :refer :all]
[ttt.board :refer :all]))


(def test-empty-board
[[:e :e :e]
[:e :e :e]
[:e :e :e]])


(deftest empty-coordinate-test
(testing "Returns true"
(testing "when position is empty"
(is (= true (empty-coordinate? test-empty-board [1 1])))))
(testing "Returns false"
(testing "when position is not empty"
(is (= false (empty-coordinate? [[:e :e :e]
[:e :o :e]
[:e :e :e]] [1 1]))))))


(deftest valid-test
(testing "Returns true"
(testing "when board is a square matrix and all pieces are valid"
(is (= true (valid? test-empty-board)))))
(testing "Return false"
(testing "when board is not square matrix and all pieces are valid"
(is (= false (valid? [[:e :e :e]]))))
(testing "when board is square matrix and all pieces are not valid"
(is (= false (valid? [[:e :e :e]
[:e :e :e]
[:e :e 1]]))))))


(deftest update-test
(testing "Returns correctly updated board"
(testing "when board is valid"
(is (= [[:e :e :e]
[:e :o :e]
[:e :e :e]] (update test-empty-board [1 1] :o))))))


(deftest winner-of-collection-test
(testing "With all :e elements"
(is (= nil (winner-of-collection [:e :e :e]))))

(testing "With all :x elements "
(is (= :x (winner-of-collection [:x :x :x]))))

(testing "With 2 :x elements"
(is (= nil (winner-of-collection [:x :x :o])))))


(deftest winning-game-piece-test

(testing "Returns winning game piece "
(testing "when winner is along a row"
(is (= :o (winning-game-piece [[:e :x :e]
[:o :o :o]
[:x :e :x]]))))
(testing "when winner is along column"
(is (= :x (winning-game-piece [[:x :e :o]
[:x :e :e]
[:x :e :o]]))))
(testing "when winner is across diagonal"
(is (= :x (winning-game-piece [[:x :e :e]
[:o :x :e]
[:o :e :x]])))))

(testing "Returns nil "
(testing "when there is a draw"
(is (= nil (winning-game-piece [[:x :e :o]
[:x :x :e]
[:o :x :o]]))))))
30 changes: 30 additions & 0 deletions practice/ttt/test/game_test.clj
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
(ns game-test
(:require
[clojure.set]
[clojure.test :refer :all]
[ttt.game :refer :all]))


(def test-player-sequence (cycle #{:o :x}))


(deftest over-test
(testing "Returns winner "
(testing "when there is a winner in board"
(is (= :o (over? [[:e :x :e]
[:o :o :o]
[:x :e :x]] (take 2 test-player-sequence))))))
(testing "Returns true"
(testing "when there is a draw"
(is (= true (over? [[:x :o :x]
[:x :o :o]
[:o :x :x]] (take 0 test-player-sequence))))))
(testing "Returns false"
(testing "when there no winner with moves left"
(is (= false (over? [[:x :e :o]
[:e :x :e]
[:o :x :o]] (take 3 test-player-sequence)))))
(testing "when the board is empty"
(is (= false (over? [[:e :e :e]
[:e :e :e]
[:e :e :e]] (take 9 test-player-sequence)))))))
12 changes: 8 additions & 4 deletions practice/ttt/test/matrix_operations_test.clj
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,8 @@
[2 5 8]
[3 6 9]]
(transpose-matrix test-square-matrix))))
(testing "With 2x1 matrix"
(is (= [[0 2]] (transpose-matrix [[0] [2]]))))
(testing "With empty matrix"
(is (= [[]] (transpose-matrix [[]])))))
(testing "With 1x2 matrix"
(is (= [[0 2]] (transpose-matrix [[0] [2]])))))


(deftest primary-diag-coordinates-test
Expand Down Expand Up @@ -58,3 +56,9 @@
(testing "With 1x2 matrix"
(is (= false (square-matrix? test-1-by-2-matrix)))))


(deftest matrix-to-string-test
(testing "With 3x3 matrix"
(is (= "\n1 2 3\n4 5 6\n7 8 9" (matrix->string test-square-matrix))))
(testing "With 1x2 matrix"
(is (= "\n1 2" (matrix->string test-1-by-2-matrix)))))
Loading