5
\$\begingroup\$

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)) 
\$\endgroup\$
4
\$\begingroup\$

A few small points

  • replicate is deprecated. Use repeat instead.
  • Use - instead of _ in names.
  • The cond in str-piece compares piece against a number of constants. Use case 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 xs 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.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.