Так как в другом вопросе ОП я предложил другую структуру данных для игровой сетки - а именно вектор векторов - я испытываю желание показать, как бы я решил эту проблему с этим представлением. Для целей этой проблемы, мне кажется, проще всего использовать 0
и 1
для представления состояний ячеек сетки. Адаптация кода для случая более сложной структуры ячеек сетки (возможно, карты, содержащей число или логическое значение где-то внутри) не создаст проблем.
Обсуждается эта функция:
(defn check-position-valid [field-grid block]
(let [grid-rect (subgrid field-grid
@(block :x)
(-> block :grid :width)
@(block :y)
(-> block :grid :height))
block-rect (-> block :grid :data)]
(and grid-rect
(not-any? pos?
(mapcat #(map (comp dec +) %1 %2)
grid-rect
block-rect)))))
Я удалил карту структуры grid
; вместо этого все сетки являются простыми векторами векторов. Обратите внимание, что наличие явных ключей :width
и :height
не обязательно может сильно повлиять на производительность, поскольку векторы Clojure сохраняют количество своих членов (как и многие другие коллекции Clojure). Нет особой причины не иметь их, хотя я просто нашел, что проще обходиться без них. Это влияет на мою терминологию ниже: слово «сетка» всегда относится к вектору векторов.
Следующее создает сетку, на которой работают другие функции; также наслаждайтесь функцией распечатки бонуса:
(defn create-grid
([w h] (create-grid w h 0))
([w h initial-value]
(let [data (vec (map vec (repeat h (repeat w initial-value))))]
data)))
(defn print-grid [g]
(doseq [row g]
(apply println row)))
Ключом к вышеуказанной версии check-position-valid
является эта функция, которая дает в качестве подсетки данной сетки:
(defn subgrid
"x & y are top left coords, x+ & y+ are spans"
[g x x+ y y+]
(if (and (<= (+ x x+) (count g))
(<= (+ y y+) (count (first g))))
(vec
(map #(subvec % x (+ x x+))
(subvec g y (+ y y+))))))
subvec
объявляется строкой документации как операция O (1) (с постоянным временем), которая очень быстрая, поэтому она также должна быть довольно быстрой. Выше он используется для извлечения окна в заданную сетку, которая сама является сеткой (и может быть напечатана с помощью print-grid
). check-position-valid
берет такое окно в сетку и проверяет его рядом с сеткой блока, чтобы определить, находится ли блок в правильной позиции.
Предполагается, что совершенно бессмысленные значения аргументов (отрицательные x
, x+
, y
, y+
) не будут возникать, однако в случае, если окно "выпирает" из сетки справа или внизу возвращается nil
вместо индекса subvec
вне границ.
Наконец, определение current-block
можно использовать с приведенным выше:
(def current-block
{:grid [[0 1 0]
[0 1 0]
[0 1 1]])
:x (ref 0)
:y (ref 0)})
И некоторые вспомогательные функции (которые все возвращают сетки):
(defn get-grid [g x y]
(get-in g [y x]))
(defn set-grid [g x y v]
(assoc-in g [y x] v))
(defn swap-grid [g x y f & args]
(apply update-in g [y x] f args))
(defn get-grid-row [g y]
(get g y))
(defn set-grid-row [g y v]
(assoc g y (vec (repeat (count (g 0)) v))))
(defn get-grid-col [g x]
(vec (map #(% x) g)))
(defn set-grid-col [g x v]
(vec (map #(assoc-in % [x] v) g)))
Последние четыре можно использовать для быстрого построения тестовой сетки, как это делается (2
s и 3
s не имеют смысла в связи с приведенным выше кодом, как он написан в настоящее время, но они служат для иллюстрации того, что бывает):
user> (print-grid (set-grid-row (set-grid-col (create-grid 6 10) 1 2) 0 3))
3 3 3 3 3 3
0 2 0 0 0 0
0 2 0 0 0 0
0 2 0 0 0 0
0 2 0 0 0 0
0 2 0 0 0 0
0 2 0 0 0 0
0 2 0 0 0 0
0 2 0 0 0 0
0 2 0 0 0 0
nil