Coding Train, Challenge 180, Falling Sand

Caída de arena de colores

Resultado

Código

;; 180 - Falling Sand
(ns challenges.challenge-180-falling-sand
  (:require [p5]))

;; Definición de variables
;; (def state {:sizes {:w js/window.innerWidth :h js/window.innerHeight}})
(def state {:sizes {:w 800 :h 1400}})
(def width (-> state :sizes :w))
(def height (-> state :sizes :h))
(def w 5) ;; tamaño de cada celda;
(def cols (/ width w))
(def rows (/ height w))
(def rcols (range cols))
(def rrows (range rows))
(def grid (volatile! #js []))
(def matrix 6)
(def hue-value (volatile! 50))

(defn make-2darray
  "Creación de matriz con función opcional para rellenarla.
  Se le pasan dimensiones (`nrows` y `ncols`) y opcionalmente
  un callback para calcular el valor de sus elementos a partir
  del índice de cada uno de ellos `(fn [col_idx row_idx))`.
  Si no pasamos función, el fallback es rellenar con ceros."
  [ncols nrows & {:keys [f] :or {f (constantly 0)}}]
  #_(vec (repeat nrows (vec (repeat ncols 0))))
  (mapv (fn [i] (mapv #(f i %) (range nrows))) (range ncols)))

(defn make-flat-2darray
  [ncols nrows]
  (vec (repeat (* nrows ncols) 0)))

(defn setup
  []
  (let [canvas (js/createCanvas width height)]
    (.parent canvas "p5jsparent")
    (js/loadPixels))
  (js/colorMode js/HSB 360 255 255)
  (let [arr (make-2darray cols rows)]
    (println (count arr) (count (first arr))))
  (vreset! grid (clj->js (make-2darray cols rows))))

(defn mouse-pressed []
  (let [[mouse-col mouse-row] [(Math/floor (/ js/mouseX w)) (Math/floor (/ js/mouseY w))]
        extent (Math/floor (/ matrix 2))]
    ;; Pintamos de forma aletoria nuevos granos de arena alrededor del punto donde clicamos.
    (doseq [i (range (- extent) (inc extent))
            j (range (- extent) (inc extent))]
      (when (zero? (rand-int 2))
        (let [col (+ mouse-col i)
              row (+ mouse-row j)]
          (when (and (> col -1) (< col cols) (> row 1) (< row rows))
            (aset @grid col row @hue-value)))))
    (vreset! hue-value (+ 5 @hue-value))
    (when (> @hue-value 359)
      (vreset! hue-value 1))))

(comment
  (range 0 100 5)

  )

(defn draw []
  (js/background 0)
  (js/noStroke)
  (doseq [i (range cols)
          j (range rows)]
    ;; (js/stroke 255)
    ;; Podríamos ahorranos esto si el valor del grid base fuese negro en el
    ;; espacio de color que estamos usando. Pero no tiene mayor importancia
    ;; y lo simplifica mucho.
    (when (> (aget @grid i j) 0)
      (js/fill (aget @grid i j) 255 255)
      (js/square (* i w) (* j w) w)))

  ;; Para calcular el movimiento, volvemos a repasar el grid, y
  ;; si teníamos en una celda un 1, en este nuevo grid el 1 se
  ;; habrá movido hacia abajo.
  (let [next-grid (clj->js (make-2darray cols rows))]
    (doseq [i rcols j rrows]
      (let [state (aget @grid i j)]
        (when (> state 0)
          (let [below (aget @grid i (inc j))
                dir (if (zero? (rand-int 2)) 1 -1)
                next-i (+ i dir)
                prev-i (- i dir)
                next-j (inc j)]
            (cond
              (zero? below) (aset next-grid i next-j state)

              ;; Comprobamos posición bajo derecha.
              (and (pos? next-i) (< next-i cols) (zero? (aget @grid next-i next-j)))
              (aset next-grid next-i next-j state)

              ;; Comprobamos posición bajo izquierda.
              (and  (pos? prev-i) (< prev-i cols) (zero? (aget @grid prev-i next-j)))
              (aset next-grid prev-i next-j state)

              :else (aset next-grid i j state))))))
    (vreset! grid next-grid)))

Enlaces