I recently created a little minesweeper UI using the seesaw framework which is basically a nifty clojure wrapper around swing. The related code can be found here.
Basically everything works fine so far, the only problem is that the user experience is quite bad when you choose to play on expert level. The reason is that on every click on a cell the whole ui is repainted and this takes quite long (on average 850 millis).
The code that is responsible for the repainting is the following:
(defn- update-fields
[cell-states]
(doseq [[idx state] (map-indexed vector cell-states)
:let [field (select-field idx)]]
(config! field :icon (icons/cell-icons state))))
(defn- update-board
[snapshot face]
(do
(change-smiley face)
(update-fields (:cells snapshot))
(repaint! ui)))
The code for the icon handling looks the following
(ns minesweeper.icons
(:require
[clojure.java.io :as io]
[clojure.string :as str]
[seesaw.icon :as icon]))
(def ^:private cell-icons-path "minesweeper/icons/cell")
(def ^:private face-icons-path "minesweeper/icons/face")
(defn- file-name
[file]
(str/replace-first
(.getName file) #"\.[^.]+$" ""))
(def ^:private init-icons
(memoize
(fn [res]
(let [parent (rest (file-seq (io/file (io/resource res))))]
(reduce
#(assoc %1 (keyword (file-name %2)) (icon/icon %2))
{}
parent)))))
(defn cell-icons
[id]
(let [icons (init-icons cell-icons-path)]
(get icons id)))
(defn face-icons
[id]
(let [icons (init-icons face-icons-path)]
(get icons id)))
So my question is, how to approach this more efficiently? I thought about only updating the cells (which are represented by JButtons) that are affected by a click but in case auto-clear opens a lot of adjacent cells this may also take quite a while.
Is it in general a reasonable choice to use a mig layout with buttons to represent the board?