Fork me on GitHub
#beginners
<
2023-01-24
>
Sebastian12:01:08

I have made a tic-tac-toe game in clojure, to get a feel of everything. I would be very glad if anyone could take a look at it and let me know of any improvements I dont know if this is too much spam, so i will paste the code in a reply to this

Sebastian12:01:12

(def start-board [[1 2 3]
                      [4 5 6]
                      [7 8 9]])

(defn print-board! [board]
  (println)
  (doseq [row board]
    (println row)))

(defn winner? [rows]
  (or (some #(every? (fn [c] (= c \x)) %) rows)
      (some #(every? (fn [c] (= c \o)) %) rows)))

(defn transpose [m]
  (apply mapv vector m))

(defn game-won? [board]
  (let [winner-rows? (winner? board)
        columns (transpose board)
        winner-col? (winner? columns)
        diagonal (map-indexed (fn [i row] (nth row i)) board)
        winner-diagonal (winner? [diagonal])
        anti-diagonal (reverse (map-indexed (fn [i row] (nth row i)) (reverse board)))
        winner-anti-diagonal (winner? [anti-diagonal])]
    (or winner-rows? winner-col? winner-diagonal winner-anti-diagonal)))

(defn board-full? [board]
  (every? char? (flatten board)))

(defn legal-move? [board move]
  (and (<= 1 move 9)
       (integer? (get-in board [(int (Math/ceil (quot (dec move) 3))) (mod (dec move) 3)]))))

(defn make-move [board move turn]
  (update-in board [(int (Math/ceil (quot (dec move) 3))) (mod (dec move) 3)] (constantly turn)))

(def next-turn {\x \o
                \o \x})

(defn get-move! [turn]
  (printf "%s's turn. Please select a square\n" turn)
  (println "___________")
  (Integer/parseInt (read-line)))

(defn run-game []
  (loop [board start-board
         turn \x]
    (print-board! board)
    (cond 
      (game-won? board) (format "%s is the WINNER" (next-turn turn))
      (board-full? board) "DRAW"
      :else (let [move (get-move! turn)]
                  (if (legal-move? board move)
                    (recur (make-move board move turn) (next-turn turn))
                    (do (println "Please select a valid square")
                        (recur board turn)))))))

pez12:01:13

I think this is cool and not spammy, especially since you posted the code in a reply. There is also #C053PTJE6, just FYI.

Martin Půda13:01:12

(defn winner? [rows]
  (or (some #(every? #{\x} %) rows)
      (some #(every? #{\o} %) rows)))
diagonal and anti-diagonal- you can also try this:
(mapv #(%1 %2) [[1 2 3]
                [4 5 6]
                [7 8 9]] (range 3))
=> [1 5 9]

(mapv #(%1 %2) [[1 2 3]
                [4 5 6]
                [7 8 9]] [2 1 0])
=> [3 5 7]
legal-move? and make-move- repeated code, move it into the function:
(defn move->yx [n]
  (let [d (dec n)]
    [(quot d 3) (mod d 3)]))
make-move- I guess you need assoc-in get-move!- Integer/parseInt -> parse-long

Sebastian13:01:25

Thank you very much for your feedback! I use this approach for the diagonal and anti-diagonal because i had some thought that i might want to extend the board size in the future. I've used your suggestion for legal-move and moved the repeated code into a new function. But i might have to refactor this anyways if i make it possible to change the board size Thank you again :)

Rupert (All Street)20:01:37

The code looks pretty good/idiomatic to me.

popeye18:01:43

Why (Character/isUpperCase "a") is not working in repl ?

hiredman18:01:48

user=> (type "a")
java.lang.String
user=>

didibus18:01:08

(Character/isUpperCase \a)

popeye18:01:59

is there any function to type cast to char ?

hiredman18:01:24

user=> (->> (clojure.reflect/reflect Character) :members (filter #(= 'isUpperCase (:name %))) (map :parameter-types))
([char] [int])
user=>

hiredman18:01:11

user=> (first "a")
\a
user=> (nth "a" 0)
\a
user=>

4
🙌 2
didibus18:01:39

Don't think of it as casting though, a String is not castable to a char, a String is a list of Chars, it's like if you said if we can cast a List of int to an int, it doesn't make logical sense. So start thinking of a String like a collection of Characters, a String of one element is a Collection of one Character.

2
🙇 2
peterh21:01:07

Just stumbled over something that so often got me confused as a beginner, so while it may be obvious to most professionals, I thought it may help some people to write about it: When you evaluate an expression with a very large value/sequence in the REPL or via the editor and it seems to freeze your editor/environment, remember that your value is being printed, which can have a huge impact on performance. So even though an evaluation would otherwise take just a second to compute, it could take much, much longer if it is printed directly instead of being stored in a var or reduced to a smaller value.

✔️ 8
ghadi21:01:55

...and stored in a var is faster because the repl prints the var, not the contents of the var

👍 4
ghadi21:01:01

you can control content printing with

(set! *print-length* 50)
(set! *print-level* 2) ;; depth

ghadi21:01:31

(and uncap those by setting to nil)

noob.clj23:01:27

I am trying to write identity function in anonymous function shorthand. I tried following code:

(map (fn [x] x) [1 2 3])  => (1 2 3)
(map #(%) [1 2 3]) => Error printing return value (ClassCastException) at etl/eval7963$fn (form-init6095319328514486799.clj:25).
As per my understanding both approaches should work. Although when I write anonymous function to double the value of elements, the following way it works as expected.
(map #(* % 2) [1 2 3]) => (2 4 6)
Can anyone please help me understand why #(%) is not working?

hiredman23:01:03

user=> '#(%)
(fn* [p1__2#] (p1__2#))
user=>

hiredman23:01:20

if #(+ % %) is the same as (fn [x] (+ x x)) then what is #(+ %) then what is #(+) then what is #(%)

thanks3 2
noob.clj23:01:43

You mean #(%) is trying to treat % as a function?

noob.clj23:01:44

Hmm… makes sense the error is also indicating that class java.lang.Long cannot be cast to class clojure.lang.IFn 😞 probably should’ve read that carefully.

Matthew Downey02:01:17

This confused many of us at first! Also watch out for e.g. (map #([%]) (range 3)) which, for the same reason, does not do what one might expect.

👍 2
2
Bediako George14:01:57

That won't work ... Try (map identity [1 2 3]) which is not anonymous or (map #(do %) [1 2 3]) which uses anonymous function form but is not idiomatic. This #(%) is not allowed as % is not a function, but in this case it is an integer. So for instance you can't do this (1) and expect it to execute, since 1 is not a function.

didibus16:01:37

The trick you're looking for is:

#(do %)
#(do [%])
#(do (println %) (* 2 %))