我正在尝试使用Clojure创建类似俄罗斯方块的游戏,但在决定游戏场地的数据结构时遇到了一些问题。我想将游戏场地定义为可变网格。单个块也是网格,但不需要是可变的。
我的第一次尝试是将网格定义为向量的向量。例如,S块看起来像这样:
:s-block {
:grids [
[ [ 0 1 1 ]
[ 1 1 0 ] ]
[ [ 1 0 ]
[ 1 1 ]
[ 0 1 ] ] ]
}
但对于简单的迭代和绘图(请参见下面的代码),这似乎相当棘手。
为了使网格可变,我的初始想法是将每一行作为引用。但是,我无法真正弄清楚如何更改行中特定单元格的值。一个选择是将每个单独的单元格作为引用而不是每行。但这感觉像一种不干净的方法。
我现在考虑使用Java数组。Clojure的aget和aset函数可能会变得简单得多。
然而,在陷入更深的困境之前,我想询问您的想法/见解。您如何推荐实现可变的二维网格?也可以分享其他替代方法。
源代码当前状态:Tetris.clj (rev452) 更新 在您的建议和自己的摆弄后,我想出了以下内容:
(defstruct grid :width :height)
(defn create-grid [w h initial-value]
(struct-map grid
:width w
:height h
:data (ref (vec (repeat (* w h) initial-value)))))
(defn create-grid-with-data [w h gdata]
(struct-map grid
:width w
:height h
:data (ref gdata)))
(defn get-grid [g x y]
(let [gdata (g :data)
idx (+ x (* (g :width) y)) ]
(gdata idx)))
(defn set-grid [g x y value]
(let [data (deref (g :data))
idx (+ x (* (g :width) y)) ]
(dosync (alter (g :data) (fn [_] (assoc data idx value))))))
(defn get-grid-rows [g]
(partition (g :width) (deref (g :data))))
我喜欢它是因为它是一个更通用的解决方案。如果它完全错误,或者可以改进,请随便说。