Skip to content

Instantly share code, notes, and snippets.

@bhauman
Last active June 14, 2021 23:34
Show Gist options
  • Save bhauman/1f8b59927ff183e80bb141215e715777 to your computer and use it in GitHub Desktop.
Save bhauman/1f8b59927ff183e80bb141215e715777 to your computer and use it in GitHub Desktop.
Generating a random ceramic tile layout for my shower in CLJS
(ns ^:figwheel-hooks tilelayout.core
(:require
[goog.dom :as gdom]
[goog.crypt.base64 :as base64]
[goog.events :as gevent]
[clojure.string :as string]
[cljs.reader :refer [read-string]])
(:import [goog History]))
;; TILE LAYOUT GENERATION
(def HEIGHT 13)
(def WIDTH (+ 5 7 5))
(def colors [:arancio
:giallo
:rosso
:blu
:azzurrio
:acquamarina
:bianco
:bianco
:cenere
:cenere])
(defn taken-colors [{:keys [x y]} filled-pos-index]
(fn [col]
(let [neighbors (cond-> [{:x x :y (dec y)}
{:x (dec x) :y y}
;; don't allow identical neighbors two tiles away
{:x x :y (dec (dec y))}
{:x (dec (dec x)) :y y}]
(not (or (#{:cenere :bianco} col)
(< (rand) 0.1)))
;; dissallow identical corners
(concat [{:x (dec x) :y (dec y)}
{:x (dec x) :y (inc y)}
{:x (inc x) :y (dec y)}]))]
((->> neighbors
(keep (fn [{:keys [x y]}]
(get-in filled-pos-index [x y])))
set)
col))))
(defn neighbor-constrained-color-lookup-index
[positions avail-colors color-index-accum]
(if (empty? positions)
color-index-accum
(let [[p & ps] positions
[unused-neighbor-matching-colors [uniq-color & colors-left]]
(split-with (taken-colors p color-index-accum) avail-colors)]
(recur ps
(concat unused-neighbor-matching-colors colors-left)
(assoc-in color-index-accum [(:x p) (:y p)] uniq-color)))))
(defn gen-positions [width-range height-range]
(for [y height-range
x width-range]
{:x x :y y}))
(defn gen-vertical-striped-positions [stripe-width width-range height-range]
(mapcat #(gen-positions % height-range)
(partition-all stripe-width width-range)))
(defn random-tile-layout []
(let [positions (->>
;; IMPORTANT for even distribution order the
;; positions in vertical stripes with a width that
;; is the square root of the the number of unique
;; tiles, in this case 10 unique tiles so the width
;; of our vertical stripes is 3
(gen-vertical-striped-positions
(js/Math.trunc (js/Math.sqrt (count colors)))
(range WIDTH) (range HEIGHT))
(remove
(set (concat
;; window
(gen-positions (range 6 8) (range 1 6))
;; bench
(gen-positions (range 2 7) (range 11 13))))))]
(neighbor-constrained-color-lookup-index
positions
;; IMPORTANT for even distribution don't shuffle colors in total!
(mapcat shuffle (repeat colors))
{})))
;; DISPLAY
(declare draw-walls)
(defonce hist
(doto (History. false)
(.setEnabled true)
(gevent/listen goog.history.EventType.NAVIGATE
#(some->> (.-token %)
base64/decodeString
read-string
(draw-walls WIDTH HEIGHT)))))
(defn register-draw-event [positions-with-colors]
(when positions-with-colors
(.setToken hist (base64/encodeString (pr-str positions-with-colors)))))
(defn draw-walls [width height positions-with-colors]
(letfn [(dom [klass] (gdom/createDom "div" #js {:class klass}))]
(doto (gdom/getElement "app")
(gdom/removeChildren)
(gdom/append
(doto (gdom/createDom "button" #js{} "next")
(.addEventListener
"click"
#(register-draw-event (random-tile-layout)))))
(gdom/append
(doto (dom "wall")
(gdom/append (gdom/createTable height width false))
(gdom/append (dom "cover top-wall"))
(gdom/append (dom "cover left-wall-1"))
(gdom/append (dom "cover left-wall-2"))
(gdom/append (dom "cover right-wall-1"))
(gdom/append (dom "cover right-wall-2"))
(gdom/append (dom "cover window-cover"))
(gdom/append (dom "cover bench-cover")))))
(doseq [[{:keys [x y]} elem] (map vector
(gen-positions (range width) (range height))
(gdom/getElementsByTagName "td"))]
(gdom/append elem (dom "cell"))
(when-let [color (some-> positions-with-colors
(get-in [x y])
name)]
(set! (.-className elem) color))))
(js/console.log "Color frequency")
(->> positions-with-colors
vals
(mapcat vals)
frequencies
prn)
positions-with-colors)
(defn setup [tok]
(if (or (nil? tok) (string/blank? tok))
(random-tile-layout)
(let [data (read-string (base64/decodeString tok))]
(draw-walls WIDTH HEIGHT data)
nil)))
(register-draw-event (setup (.getToken hist)))
;; specify reload hook with ^;after-load metadata
(defn ^:after-load on-reload []
(register-draw-event (random-tile-layout)))
.wall {
font-size: 0.3em;
position:relative;
}
.cover {
position: absolute;
background-color: rgba(255,255,255,1);
}
.left-wall-1 {
top: 0px;
left: 0px;
width: 3em;
height: 600px;
}
.left-wall-2 {
top: 0px;
left: 203px;
width: 4.4em;
height: 600px;
}
.right-wall-2 {
top: 0px;
left: 748px;
width: 3em;
height: 600px;
}
.right-wall-1 {
top: 0px;
left: 539px;
width: 4.4em;
height: 600px;
}
.window-cover {
top: 39px;
left: 243px;
width: 150px;
height: 252px;
}
.top-wall {
top: 0px;
left: 0px;
width: 100%;
height: 3em;
}
.bench-cover {
top: 476px;
left: 90px;
width: 245px;
height: 110px;
}
.cell {
width: 8.5em;
height: 8.5em;
border-color: grey;
}
.arancio {
background-color: rgb(255, 137, 75);
}
.giallo {
background-color: rgb(255, 207, 92);
}
.rosso {
background-color: rgb(196, 56, 61);
}
.blu {
background-color: rgb(14,40,95);
}
.azzurrio {
background-color: rgb(62, 163, 187);
}
.acquamarina {
background-color: rgb(165, 219, 171);
}
.smeraldo {
background-color: rgb(28,85,77);
}
.bianco {
background-color: rgb(242, 241, 233);
}
.nero {
background-color: rgb(28,32,31);
}
.cenere {
background-color: rgb(232, 230, 225);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment