2

私は最近、シーソーフレームワークを使用して小さなマインスイーパ UI を作成しました。関連するコードはここにあります。

マインスイーパボード

基本的には今のところすべて問題なく動作していますが、唯一の問題は、エキスパート レベルでプレイすることを選択した場合のユーザー エクスペリエンスが非常に悪いことです。その理由は、セルをクリックするたびに UI 全体が再描画され、これにはかなりの時間がかかる (平均 850 ミリ秒) ためです。

再描画を担当するコードは次のとおりです。

(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)))

アイコン処理のコードは次のようになります

(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)))

だから私の質問は、これにもっと効率的にアプローチする方法は? クリックの影響を受けるセル (JButton で表される) のみを更新することを考えましたが、自動クリアが多数の隣接セルを開く場合、これにもかなりの時間がかかる場合があります。

ボードを表すためにボタン付きの mig レイアウトを使用することは、一般的に合理的な選択ですか?

4

1 に答える 1

2

を使用するclojure.core/timeと、UI ロジックのボトルネックは、(select ui [(keyword (str "#field_" idx))])ボードを更新するたびに階層内のすべてのコンポーネントをフィルター処理してシーソーを使用して名前検索を行う必要があるため、ボタンのルックアップであることがわかりました。

最も簡単な解決策は、関数をラップすることですが、ゲームを再起動するとselect-field機能しmemoizeません (新しいボタンが作成されるため、メモ化select-fieldすると前のゲームのボタンが返されます)。

別の可能な解決策は、すべてのボタンをベクトルに入れてグローバルに保持することatomです。

(def items (atom []))
(defn select-field
  [idx]
  (@items idx))

ボードの作成方法を変更します。

(defn- make-board-panel
  [snapshot]
  (let [bg    (button-group)
        [n m] (:dimension snapshot)
        buttons (into [] (for [idx (range (* n m))]
                       (make-button idx bg)))]
    (reset! items buttons)
    (mig-panel
     :constraints [(str "gap 0, wrap" n) "[]" "[]" ]
     :items       (map #(vector % "w 24px!, h 24px!") buttons))))

テスト

update-board私はあなたの体を包み込み、clojure.core/time10回プレイして次の結果を得ました(new-game 50 50 1)

修正前

"Elapsed time: 7020.756206 msecs"
"Elapsed time: 6766.130362 msecs"
"Elapsed time: 6616.715565 msecs"
"Elapsed time: 6628.383521 msecs"
"Elapsed time: 6657.386279 msecs"
"Elapsed time: 6588.50692 msecs"
"Elapsed time: 6554.704587 msecs"
"Elapsed time: 6650.864132 msecs"
"Elapsed time: 6610.557065 msecs"
"Elapsed time: 6671.02469 msecs"

修正後

"Elapsed time: 92.491489 msecs"
"Elapsed time: 60.236867 msecs"
"Elapsed time: 32.254729 msecs"
"Elapsed time: 29.551383 msecs"
"Elapsed time: 29.383067 msecs"
"Elapsed time: 25.768517 msecs"
"Elapsed time: 25.724915 msecs"
"Elapsed time: 45.869723 msecs"
"Elapsed time: 25.898016 msecs"
"Elapsed time: 26.254874 msecs"
于 2016-03-18T06:49:57.753 に答える