This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
Hello everyone 👋 I wanna generate seeded pseudo random value in cljs. I haven't found anything useful in cljs ecosystem. Should I just go for some js library?
This seems interesting https://github.com/clojure/test.check/blob/test.check-1.0.0/src/main/clojure/clojure/test/check/random.cljs
@U050PJ2EU Thanks! 🙏 This is what I've been looking for:
(int (rndm/rand-long (rndm/make-random))) => random int
(int (rndm/rand-long (rndm/make-random 123))) => always the same int
Well I got carried away making a rule-30 based rng 🙂. Super simple toy impl, wouldn't use in prod:
(require 'clojure.edn)
(defn dec-2-bin
[d]
(let [bits (-> d (bit-shift-right 0) (.toString 2) (->> (mapv #(clojure.edn/read-string %))))
bc (- 32 (count bits))
pad (->> 0 repeat (take bc) vec)]
(into pad bits)))
(def rands (atom {}))
(defn mk-rule-30-random-int [& [seed]]
(let [seed (or seed (inc (last (sort (keys @rands)))) 0)]
(swap! rands assoc seed 0)
(fn [n]
(swap! rands update seed inc)
(let [current-seed (+ seed (get @rands seed))]
(->> (dec-2-bin current-seed)
((fn [bits] (concat (->> 0 repeat (take 32)) bits (->> 0 repeat (take 32)))))
(iterate #(mapv {[1 1 1] 0 [1 1 0] 0 [1 0 1] 0 [1 0 0] 1 [0 1 1] 1 [0 1 0] 1 [0 0 1] 1 [0 0 0] 0}
(partition 3 1 (repeat 0) (cons 0 %))))
(take (+ 64 (mod current-seed 16)))
last
(drop (+ 32 (mod current-seed 8)))
(take 32)
(apply str)
(#(-> %
(js/parseInt 2)
(mod n))))))))
(def random-int-a (mk-rule-30-random-int))
(random-int-a 10) ;=> 6
(def random-int-b (mk-rule-30-random-int))
(random-int-b 10) ;=> 9
(def random-int-8 (mk-rule-30-random-int 8))
(random-int-8 10) ;=> 5
(def random-int-16 (mk-rule-30-random-int 16))
(random-int-16 120) ;=> 98
(def random-int-32 (mk-rule-30-random-int 32))
(random-int-32 3) ;=> 1
Well, that version right there doesn't always produce the same int, for a given input, but you could easily update it to do that
Here we go, this one always produces 8:
(defn mk-rule-30-random-int [& [seed]]
(let [seed (or seed (inc (last (sort (keys @rands)))) 0)]
(swap! rands assoc seed 0)
(fn [n & [new-seed]]
(when-not new-seed
(swap! rands update seed inc))
(let [current-seed (or new-seed (+ seed (get @rands seed)))]
(println :seed seed :current-seed current-seed)
(->> (dec-2-bin current-seed)
((fn [bits] (concat (->> 0 repeat (take 32)) bits (->> 0 repeat (take 32)))))
(iterate #(mapv {[1 1 1] 0 [1 1 0] 0 [1 0 1] 0 [1 0 0] 1 [0 1 1] 1 [0 1 0] 1 [0 0 1] 1 [0 0 0] 0}
(partition 3 1 (repeat 0) (cons 0 %))))
(take (+ 64 (mod current-seed 16)))
last
(drop (+ 32 (mod current-seed 8)))
(take 32)
(apply str)
(#(-> %
(js/parseInt 2)
(mod n))))))))
(def random-int-a (mk-rule-30-random-int))
(random-int-a 10 255) ;=> 8
Okay, my final submission to the cljs prng competition:
(defn hash-r30 [seed]
(let [generations (-> seed (mod 16) (+ 64))
index (-> seed (mod 8) (+ 32))
bits (-> seed (bit-shift-right 0) (.toString 2) (->> (mapv #(js/parseInt %))))
bc (- 32 (count bits))
pad (->> 0 repeat (take bc) vec)
padded-bits (into pad bits)
initial-conditions (concat (->> 0 repeat (take 32)) padded-bits (->> 0 repeat (take 32)))
rule-30 #(mapv {[1 1 1] 0 [1 1 0] 0 [1 0 1] 0 [1 0 0] 1 [0 1 1] 1 [0 1 0] 1 [0 0 1] 1 [0 0 0] 0}
(partition 3 1 (repeat 0) (cons 0 %)))
decimalize #(js/parseInt (apply str %) 2)
extract-int #(->> % (take generations) last (drop index) (take 32) decimalize)
new-rand (->> initial-conditions (iterate rule-30) extract-int)]
new-rand))
(defn mk-random-int [& {:keys [seed]}]
(let [rands (atom {})
seed (or seed (-> @rands keys sort last inc) 0)]
(swap! rands assoc seed 0)
(fn [& [n & {new-seed :seed step :step :as rargs}]]
(let [[new-seed n] (if (= :seed n)
[rargs nil]
[(+ seed new-seed) n])
relative-seed (+ 1 step new-seed)]
(when-not step
(swap! rands update relative-seed inc))
(let [current-seed (if step
relative-seed
(->> relative-seed (get @rands)))
current-relative (+ relative-seed current-seed)
new-rand (hash-r30 current-relative)
mod-rand (if-not (int? n)
new-rand
(mod new-rand n))]
mod-rand)))))
(def random-int (mk-random-int :seed 360)) ; <- global seed changes all values below
(random-int 10 :seed 235 :step 8) ;=> Always 5 ; (+ 360 235 8)
(random-int 10 :seed 235 :step 10) ;=> Always 4 ; (+ 360 235 10)
(random-int 10 :seed 255) ;=> Positive Integer under 10 ; (+ 360 255 1)
(random-int 10 :step 257) ;=> Always 7 ; (+ 360 1 257)
(random-int -10) ;=> Negative Integer over -10 ; (+ 360 1 1)
(random-int :seed 255) ;=> Positive Integer ; (+ 360 255 1)
(random-int 10) ;=> Positive Integer ; <- (+ 360 1 1)
:rolling_on_the_floor_laughing: @U050PJ2EU you are 👑
Thanks man. I was trying to boast at a party last night that I just wrote a prng but nobody seemed to care 🙂 That's public domain if you want to turn it into something you want to support. I'm not sure about the cryptographic integrity of rule-30, but from what I understand it is usable. I see some examples online take a vertical sampling, perhaps that's better. You could also use a wider state (initial conditions) or a longer period (generations) to get more diffusion out of it.