patternMinor
Clojure Minesweeper from scratch
Viewed 0 times
scratchfromclojureminesweeper
Problem
In order to exercise and learn Clojure, I decided to write a simple Minesweeper game from scratch. I'd consider myself as a Clojure novice and would be thankful if somebody could do a review or give me feedback on the code.
The full repository can be found here but I'd be also happy if somebody could have a look on the core functionalities at least:
board.clj
```
(ns minesweeper.board
(:use [clojure.pprint]))
(defn empty-board
"Create a rectangular empty board of
the specified with and height"
[w h]
(vec (repeat w (vec (repeat h {})))))
(defn to-coords
"Transform the board cells into coordinates"
([board]
(to-coords board (constantly true)))
([board pred]
(let [w (count board)
h (count (first board))]
(for [x (range w) y (range h) :when (pred (get-in board [x y]))]
[x y]))))
(defn neighbour-cells
"Locate neighbour cells based on coordinates [x y],
respecting board width and height"
[board [x y]]
(let [w (count board)
h (count (first board))]
(for [dx (map (partial + x) [-1 0 1])
dy (map (partial + y) [-1 0 1])
:when (and (or (not= x dx) (not= y dy))
(> w dx -1)
(> h dy -1))]
[dx dy])))
(defn warnings-freq [board]
"Count the number of nearby mines"
(let [mines (to-coords board :mine)
warnings (mapcat (partial neighbour-cells board) mines)]
(frequencies
(remove (set mines) warnings))))
(defn random-mines
[board start-pos]
(-> (set (to-coords board))
(disj start-pos)
(shuffle)))
(defn place-mines
"Place n mines randomly on the board"
[board mine-count start-pos]
(let [mines (take mine-count
(random-mines board start-pos))]
(reduce
(fn [m k]
(assoc-in m k {:mine true}))
board
mines)))
(defn place-warnings
"Place warnings on a mines' neighbour cells"
[board]
(let [mine-counts (warnings-freq board)]
(reduce-kv
(fn [m k v]
The full repository can be found here but I'd be also happy if somebody could have a look on the core functionalities at least:
board.clj
```
(ns minesweeper.board
(:use [clojure.pprint]))
(defn empty-board
"Create a rectangular empty board of
the specified with and height"
[w h]
(vec (repeat w (vec (repeat h {})))))
(defn to-coords
"Transform the board cells into coordinates"
([board]
(to-coords board (constantly true)))
([board pred]
(let [w (count board)
h (count (first board))]
(for [x (range w) y (range h) :when (pred (get-in board [x y]))]
[x y]))))
(defn neighbour-cells
"Locate neighbour cells based on coordinates [x y],
respecting board width and height"
[board [x y]]
(let [w (count board)
h (count (first board))]
(for [dx (map (partial + x) [-1 0 1])
dy (map (partial + y) [-1 0 1])
:when (and (or (not= x dx) (not= y dy))
(> w dx -1)
(> h dy -1))]
[dx dy])))
(defn warnings-freq [board]
"Count the number of nearby mines"
(let [mines (to-coords board :mine)
warnings (mapcat (partial neighbour-cells board) mines)]
(frequencies
(remove (set mines) warnings))))
(defn random-mines
[board start-pos]
(-> (set (to-coords board))
(disj start-pos)
(shuffle)))
(defn place-mines
"Place n mines randomly on the board"
[board mine-count start-pos]
(let [mines (take mine-count
(random-mines board start-pos))]
(reduce
(fn [m k]
(assoc-in m k {:mine true}))
board
mines)))
(defn place-warnings
"Place warnings on a mines' neighbour cells"
[board]
(let [mine-counts (warnings-freq board)]
(reduce-kv
(fn [m k v]
Solution
This is excellent. You have nice small functions, intent is clear, and the docstrings are helpful.
These are very minor suggestions:
Might be slightly more obvious as
or
update is 1.7 only, but you could also use update-in [:flag]
I think this
(especially since it is repeated 2x)
These are very minor suggestions:
(update-in board coords
#(assoc % :flag (not (:flag %)))Might be slightly more obvious as
(update-in board (conj coords :flag) not))or
(update-in board coords update :flag not)update is 1.7 only, but you could also use update-in [:flag]
I think this
pred would be better promoted to a defn, and perhaps called something like boom(letfn [(pred [m] (and (:mine m) (:explored m)))](especially since it is repeated 2x)
(disp/fire) might just be a multimethod?Code Snippets
(update-in board coords
#(assoc % :flag (not (:flag %)))(update-in board (conj coords :flag) not))(update-in board coords update :flag not)(letfn [(pred [m] (and (:mine m) (:explored m)))]Context
StackExchange Code Review Q#105448, answer score: 2
Revisions (0)
No revisions yet.