I am learning clojure this week end and just starting to develop a simple checkers engine. I currently coded some basic functions to create the board and display it. I would love to get some feedback !
(ns sandbox.checkers)
(def standard-size 10)
(defn empty-board [size]
(vec (replicate size (vec (replicate size :empty)))))
(defn str-piece [piece]
(cond
(= piece :empty) " "
(= piece :white_man) "O"
(= piece :black_man) "0"))
(defn str-board [board]
(str "\n"
(clojure.string/join
"\n"
(map
#(clojure.string/join " | " (map str-piece %))
board))))
(defn print-board [board]
(println (str-board board)))
(defn fill-white [size]
(apply comp
(map
(fn [pos]
(fn [board] (assoc-in board pos :white_man)))
(for [x (range size)
y (range (inc (/ size 2)) size)
:when (or (and (even? x) (even? y))
(and (odd? x) (odd? y)))]
[y x]))))
(defn fill-black [size]
(apply comp
(map
(fn [pos]
(fn [board] (assoc-in board pos :black_man)))
(for [x (range size)
y (range 0 (dec (/ size 2)))
:when (or (and (even? x) (even? y))
(and (odd? x) (odd? y)))]
[y x]))))
(defn starting-board [size]
((fill-black size) ((fill-white size) (empty-board size))))
(print-board (starting-board standard-size))
1 Answer 1
A few small points
replicate
is deprecated. Userepeat
instead.- Use
-
instead of_
in names. - The
cond
instr-piece
comparespiece
against a number of constants. Usecase
instead. - Simplify the
:when
conditions to(= (even? x) (even? y))
This gives us
(ns sandbox.checkers)
(def standard-size 10)
(defn empty-board [size]
(vec (repeat size (vec (repeat size :empty)))))
(defn str-piece [piece]
(case piece
:empty " "
:white-man "O"
:black-man "0"))
(defn str-board [board]
(str "\n"
(clojure.string/join
"\n"
(map
#(clojure.string/join " | " (map str-piece %))
board))))
(defn print-board [board]
(println (str-board board)))
(defn fill-white [size]
(apply comp
(map
(fn [pos]
(fn [board] (assoc-in board pos :white-man)))
(for [x (range size)
y (range (inc (/ size 2)) size)
:when (= (even? x) (even? y))]
[y x]))))
(defn fill-black [size]
(apply comp
(map
(fn [pos]
(fn [board] (assoc-in board pos :black-man)))
(for [x (range size)
y (range 0 (dec (/ size 2)))
:when (= (even? x) (even? y))]
[y x]))))
(defn starting-board [size]
((fill-black size) ((fill-white size) (empty-board size))))
(print-board (starting-board standard-size))
Edited subsequently:
There are simpler ways to generate the lists of squares for fill-white
and fill-black
.
For a given row y
, the required x
s are (range (mod y 2) size 2)
. The functions become
(defn fill-white [size]
(apply comp
(map
(fn [pos] (fn [board] (assoc-in board pos :white-man)))
(for [y (range (inc (/ size 2)) size)
x (range (mod y 2) size 2)]
[y x]))))
(defn fill-black [size]
(apply comp
(map
(fn [pos] (fn [board] (assoc-in board pos :black-man)))
(for [y (range 0 (dec (/ size 2)))
x (range (mod y 2) size 2)]
[y x]))))
A thorough reworking
The fill...
functions construct and apply a sequence of functions, one function for each change to the empty board.
It is simpler to describe the repetitive pattern using functions cycle
and take
, with vec
to convert to vectors:
(defn starting-board [size]
(let [block (fn [rows piece]
(let [source (cycle [piece :empty])
row-pair (map (comp vec (partial take size))
[source (rest source)])]
(take rows (cycle row-pair))))
block-size (dec (quot size 2))]
(vec (concat (block block-size :black-man)
(block 2 :empty)
(block block-size :white-man)))))
Notice that the same row vectors are used again and again, not copies. This works in Clojure because vectors (and other native data structures) are immutable.
Explore related questions
See similar questions with these tags.