Skip to main content
Notice removed Draw attention by CommunityBot
Bounty Ended with no winning answer by CommunityBot
Notice added Draw attention by u6f6o
Bounty Started worth 100 reputation by u6f6o
Notice removed Draw attention by CommunityBot
Bounty Ended with no winning answer by CommunityBot
Notice added Draw attention by u6f6o
Bounty Started worth 50 reputation by u6f6o
Tweeted twitter.com/#!/StackCodeReview/status/646937076978593793
deleted 24 characters in body; edited title
Source Link
Jamal
  • 35.2k
  • 13
  • 134
  • 238

Clojure minesweeperMinesweeper from scratch

In order to exercise and learn clojureClojure, I decided to write a simple minesweeperMinesweeper game from scratch. I'd consider myself as a clojureClojure novice/noob and would be thankful if somebody could do a review or give me feedback on the code.

Thx in advance!

Clojure minesweeper from scratch

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/noob and would be thankful if somebody could do a review or give me feedback on the code.

Thx in advance!

Clojure Minesweeper from scratch

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.

Source Link
u6f6o
  • 291
  • 1
  • 7

Clojure minesweeper from scratch

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/noob 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]
       (assoc-in m k {:warn v}))
     board
     mine-counts)))


(defn explore-field
  "Explore single field on the board"
  [board coords]
  (update-in board coords conj {:explored true}))


(defn handle-flag
  "Handles set and remove of a flag"
  [board coords]
  (update-in board coords
             #(assoc % :flag (not (:flag %)))))


(defn game-started?
  "At least one field explored?"
  [board]
  (pos? (count (to-coords board :explored))))


(defn game-lost?
  "Any mine exploded?"
  [board]
  (letfn [(pred [m] (and (:mine m) (:explored m)))]
    (pos? (count (to-coords board pred)))))


(defn game-won?
  "All fields cleared?"
  [board]
  (letfn [(pred [m] (or (:mine m) (:explored m)))]
    (= (to-coords board pred)
       (to-coords board))))

game.clj

(ns minesweeper.game
  (:require [minesweeper.board :as board]
            [minesweeper.dispatch :as disp]))


(def levels { :beginner     { :rows 8,  :cols 8,  :mines 10 }
              :intermediate { :rows 16, :cols 16, :mines 40 }
              :expert       { :rows 30, :cols 16, :mines 99 }})

(def ^:private level (atom {}))
(def ^:private board (atom []))


(defn- new-game
  [data]
  (let [new-level (:level data)]
    (do
      (reset! level new-level)
      (reset! board (board/empty-board
                     (:rows new-level)
                     (:cols new-level)))
      (disp/fire :game-initialized data))))


(defn- start-game
  [board mine-count start-pos]
  (-> board
      (board/place-mines mine-count start-pos)
      (board/place-warnings)))


(defn- explore
  [board data]
  (let [mine-count (:mines (:level data))
        position   (vector (:row data) (:col data))]
    (if (not (board/game-started? board))
      (-> board
          (start-game mine-count position)
          (board/explore-field position))
      (board/explore-field board position))))


(defn- explore-field
  [data]
  (let [board (swap! board explore data)
        attrs (get-in board (vector (:row data) (:col data)))
        data  (assoc data :attrs attrs)]
    (cond
     (board/game-won? board)   (disp/fire :game-won data)
     (board/game-lost? board)  (disp/fire :game-lost (assoc data :board board))
     :else                     (disp/fire :uncover-field data))))


(defn- handle-flag
  [data]
  (let [position (vector (:row data) (:col data))]
    (do
      (swap! board (partial board/handle-flag) position)
      (disp/fire :uncover-field data))))


(disp/register :explore-field #'explore-field)
(disp/register :handle-flag #'handle-flag)
(disp/register :new-game #'new-game)

Thx in advance!