sra
sra

Reputation: 6408

Best practices for working with 2d arrays in clojure?

I am building a minesweeper where the gameboard is a 2d array.

To generate the board I need to get a 2d array, randomly place some bombs, calculate all neighboring bombs for each field, then pack all together with another value (is-revealed) into a Map that should be the final value of each field.

It seems that to do all of this, I need the help of functions like map, filter, nth or zip, but instead of working on lists, they should work on a 2d array. I already started building those, but it seems like the wrong path to go.

Is there some cool abstraction that helps me use the existing functions on the 2d array? Or are there any functions already existing, that deal with 2d arrays?

edit: The way I was doing it - before thinking to myself, that there should be a better way - is something like this:

(defn mapmap [f xxs]
  (for [xs xxs]
    (map f xs)))

(defn mapmap-coords [f xxs]
  (for [[i xs] (map-indexed list xxs)]
    (map-indexed (fn [j x] (f j i)) xs)))

(defn get-value [board x y]
  (if (or (< x 0) (< y 0) (< (dec (count board)) y))
    false
    (let [xs (nth board y)]
      (if (< (dec (count xs)) x)
        false
        (nth xs x)))))

Upvotes: 4

Views: 4386

Answers (2)

Andy Marks
Andy Marks

Reputation: 21

If you're willing to drop down into Java types for your implementation, consider using

aget will give you a more natural method for accessing the array, but you'll still need to build further abstractions to get neighbours and perform updates to (i.e., regenerate) this data structure.

Upvotes: 2

noisesmith
noisesmith

Reputation: 20194

for works for nested sequential data.

user> (def game-state
       (let [game '[[_ _ _ _]
                    [1 1 _ _]
                    [! 1 _ _]
                    [1 1 _ _]]]
         (for [[i row] (map-indexed list game)
               [j cell] (map-indexed list row)
               :when (not= '_ cell)]
              {:x i :y j :value cell})))
#'user/game-state
user> (pprint game-state)
({:x 1, :y 0, :value 1}
 {:x 1, :y 1, :value 1}
 {:x 2, :y 0, :value !}
 {:x 2, :y 1, :value 1}
 {:x 3, :y 0, :value 1}
 {:x 3, :y 1, :value 1})
nil

We can use reduce to build up the data structure again (of course any transformations to the state could have been done in between).

user> (let [empty-game (vec (repeat 4 '[_ _ _ _]))
            fill-cell (fn [game, {:keys [x y value]}] (assoc-in game [x y] value))
            game (reduce   fill-cell empty-game game-state)]
        (doseq [row game] (apply println row)))
_ _ _ _
1 1 _ _
! 1 _ _
1 1 _ _
nil

Upvotes: 2

Related Questions