4
\$\begingroup\$

I'm rather new to Clojure and so I decided to program a Connect Four for fun and learning.

The code below is a Clojure implementation of this bitboard algorithm.

The whole code can be found here: https://gist.github.com/3639220

What I'm unsure about are the functions (insert) and (bit-check-board):

(defn insert
  "Inserts symbol for given player (either 1 or 2) at specified x and
   calls bit-insert for corresponding bitboard."
  [boards x player-num]
  (let [y (get-y (boards 0) x)]
    (if (= player-num 1) ; TODO: improve
      (vector
       (assoc-in (boards 0) [y x] (player player-num))
       (bit-insert (boards 1) (- 5 y) x)
       (boards 2))
      (vector
       (assoc-in (boards 0) [y x] (player player-num))
       (boards 1)
       (bit-insert (boards 2) (- 5 y) x)))))

(defn bit-check-board ; TODO: improve
  "Checks whether given bitboard is a win."
  [bitboard]
  (let [diag1 (bit-and bitboard (bit-shift-right bitboard 6))
        hori  (bit-and bitboard (bit-shift-right bitboard 7))
        diag2 (bit-and bitboard (bit-shift-right bitboard 8))
        vert  (bit-and bitboard (bit-shift-right bitboard 1))]
    (bit-or (bit-and diag1 (bit-shift-right diag1 (* 2 6)))
            (bit-and hori  (bit-shift-right hori  (* 2 7)))
            (bit-and diag2 (bit-shift-right diag2 (* 2 8)))
            (bit-and vert  (bit-shift-right vert  (* 2 1))))))
  1. insert: Is there a better way to somehow make function calls depend on some test?
  2. bit-check-board: A lot of similar lines where just one number (and one variable name) is different.
\$\endgroup\$
2
\$\begingroup\$

The bit-check-board can be indeed improved. This is what I got by just joining together common parts, even though I guess it could be further improved:

(defn check [c x]
  (bit-and c (bit-shift-right c x)))

(defn bit-check-board
  "Checks whether given bitboard is a win."
  [bitboard]
  (let [positions [6 7 8 1]
        coords (mapv (partial check bitboard) positions)]
    (apply bit-or (map check coords (map #(* 2 %) positions)))))

Also, you can reduce the repetition in the insert function by better modularize the code and use higher order functions with a pre-calculated transformation table:

(defn insert* [board x y]
  (bit-insert board (- 5 y) x))

(defn get-transforms [num]
  (if (= 1 num)
    [identity insert* identity]
    [identity identity insert*]))

(defn insert
  "Inserts symbol for given player (either 1 or 2) at specified x and
   calls bit-insert for corresponding bitboard."
  [boards x player-num]
  (let [y (get-y (boards 0) x)
        board0 (assoc-in (boards 0) [y x] (player player-num))
        transfv (get-transforms player-num)]
    (mapv [board0 (boards 1) (boards 2)] transv)))

If you're on Clojure < 1.4 than you have to change (mapv ...) in (vector (map ...))

\$\endgroup\$
2
  • \$\begingroup\$ I understand what map(v), apply and partial do, but I just couldn't come up with what you did. My mind is not that functional it seems... I can't remember seeing identity before and for sure not in such a usage. I don't understand (mapv [board0 (boards 1) (boards 2)] transv) - mapv returns a vector of applying a vector containing the boards on a vector containing functions? o.O also: let binding is transfv and in mapv it's transv and insert* should take [board y x] instead of [board x y] and while writing that I realized that I have no clue where insert* actually get its parameters from? \$\endgroup\$ – naeg Sep 6 '12 at 12:32
  • \$\begingroup\$ Your code seems not to work, I guess you meant something similar to this: (defn insert [boards x player-num] (let [y (get-y (boards 0) x) board0 (assoc-in (boards 0) [y x] (player player-num)) funcorder (if (= 1 player-num) [identity #(bit-insert % (- 5 y) x) identity] [identity identity #(bit-insert % (- 5 y) x)])] (mapv #(% %2) funcorder [board0 (boards 1) (boards 2)]))) \$\endgroup\$ – naeg Sep 10 '12 at 20:01

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.