【问题标题】:Appropriate representation of a 2D game board in ClojureClojure 中 2D 游戏板的适当表示
【发布时间】:2012-04-17 21:19:01
【问题描述】:

我正在使用 Clojure 开发一个小游戏作为学习练习。我想我已经确定了在任何特定时间将游戏状态表示为“可移动”列表和“地形”(棋盘方格)的 2D 向量向量。

在 95% 的时间里,我希望检查 2D 矢量似乎适合的特定正方形中的碰撞。但在少数情况下,我需要另辟蹊径——找到符合某些条件的单元格的 (x,y) 位置。第一次尝试是这样的:

(defn find-cell-row [fn row x y]
  (if (empty? row) nil
    (if (fn (first row)) [x y]
      (find-cell-row fn (rest row) (inc x) y))))

(defn find-cell [fn grid y]
  (if (empty? grid) nil
    (or (find-cell-row fn (first grid) 0 y)
        (find-cell (rest grid) (inc y)))))

(def sample [[\a \b \c][\d \e \f]])
(find-cell #(= % \c) sample 0) ;; => [2 0]

我尝试了一些更简洁的地图索引,但它很快就变得丑陋了,仍然没有给我想要的东西。是否有更惯用的方法来进行此搜索,或者使用不同的数据结构可能会更好地为我服务?也许是地图 { [x y] -> cell }?使用地图来表示矩阵对我来说感觉很不对 :)

【问题讨论】:

  • 你可以使用 Map 但是使用像 cons-cell 这样的不可变数据结构的优点之一是它可以很容易地执行类似 MINI-MAX (en.wikipedia.org/wiki/Minimax) 的算法,因为任何操作基本上将克隆板。另一方面,我发现 car/consing 通过单元格很烦人,通常会求助于一些索引结构(数组或映射)。
  • 看起来 Clojure 很好地支持使用 assoc-in 来“编辑”一个单元格(即创建一个新的不可变结构并改变值)。这个游戏中没有人工智能,但我确实希望能够“倒回”到以前的时间,所以不可变结构真的很方便。
  • 不确定你对 assoc-in 的意思,但是 clojure 中的“普通”地图是作为功能(不可变)树实现的,所以当你修改地图时,你会得到一个新的实例,它与以前的事例。我将 map {[x y] -> cell} 用于 dfs 中的结构,并且效果很好。但是,感觉很“奇怪”,所以我将这个问题添加为书签,看看是否有更好的...
  • clojuredocs.org/clojure_core/clojure.core/assoc-in。所以我可以调用(assoc-in sample [0 2] \z) 来创建样本副本,其中\c 更改为\z。感谢你们的反馈。
  • assoc-in 允许进入嵌套数据结构的路径。它将即时创建缺少的中间节点,因此 (assoc-in {} [:a :b :c] 3) => {:a {:b {:c 3}}}

标签: data-structures clojure


【解决方案1】:

对于这种事情,嵌套向量是很正常的,如果你使用 for 理解,扫描它既不难也不难看:

(let [h 5, w 10]
  (first
   (for [y (range h), x (range w)
         :let [coords [y x]]
         :when (f (get-in board coords))]
     coords)))

【讨论】:

  • 太棒了,谢谢。稍微玩一下,我想我有一个解决方案我真的很满意(defn find-cell [pred s] (first (for [[y row] (map-indexed vector s) [x cell] (map-indexed vector row) :when (pred cell)] [x y]))) 这种语言太有趣了
  • 我假设你在做这些map-indexed 的事情是为了提高效率还是什么?我真的建议您在使代码更复杂以提高速度之前进行配置文件或基准测试 - 我很确定new添加所有这些临时集合的成本比索引向量要高得多。
  • 一点也不……只是个人喜好。我也在 Java 中使用 Iterables 而不是 for(int i = 0...)。
【解决方案2】:

如果使用普通向量,那么您可以使用所有“常用”函数,并且可以根据需要提取 [x y]。

(def height 3)
(def width 3)

(def s [\a \b \c \d \e \f \g \h \i])

(defn ->xy [i]
    [(mod i height) (int (/ i height))])

(defn find-cell 
    "returns a vector of the [x y] co-ords of cell when
     pred is true"
    [pred s]
    (let [i (first (keep-indexed #(when (pred %2) %1) s))]
      (->xy i)))

(find-cell #(= \h %) s)
;=> [1 2]

(defn update-cells 
    "returns an updated sequence s where value at index i
     is replaced with v. Allows multiple [i v] pairs"
    [s i v & ivs]
    (apply assoc s i v ivs))

(update-cells s 1 \z)
;=> [\a \z \c \d \e \f \g \h \i]

(update-cells s 1 \p 3 \w)
;=> [\a \p \c \w \e \f \g \h \i]

【讨论】:

  • 知道这与用于与副本共享数据的地图相比如何吗?如果您在可能回溯的过程中进行大量小更改,哪个更节省内存?我怀疑这两种东西都是 32 组的,但没有任何数字。
  • 结构共享适用于所有持久数据结构 AFAIK,不确定矢量与地图的低级细节。另一个好处是优化了对向量的索引访问
  • 谢谢!这很有意义,而且我从你的例子中学到了一些额外的东西:)
  • 没问题。有关使用简单数据结构的更多理由,请参阅stackoverflow.com/questions/6016271/…...
猜你喜欢
  • 2012-06-13
  • 2014-01-21
  • 2012-12-16
  • 1970-01-01
  • 1970-01-01
  • 2021-09-23
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多