Update state with if on Quil - clojure

I am trying to make 10 PRINT code with Quil. I try to transform this code from the twitter post https://twitter.com/ACharLuk/status/913094845505445890 which is using luna
This is my code for it
(ns tenprint.core
(:require [quil.core :as q]
[quil.middleware :as m]))
(defn setup []
(q/frame-rate 30)
(q/color-mode :hsb)
{:x 0
:y 0
:scale 20
}
)
(defn update-state [state]
(let [x (:x state) y (:y state) s (:scale state)]
{
:x (do (+ x s) ((if (>= x q/width) 0)))
:y (do (if (>= x q/width) (+ y s)) (if (>= x q/height) (+ y s)))
:scale (+ s 0)
}
)
)
(defn draw-state [state]
(q/background 0)
(q/stroke 255)
;(q/line 0 10 10 0)
(let [x (:x state) y (:y state) s (:scale state)]
(if (> (rand) 0.5)
(q/line x y (+ x s) (+ y s))
(q/line x (+ y s) (+ x s) y)
)
)
)
(q/defsketch tenprint
:title "10PRINT"
:size [500 500]
:setup setup
:update update-state
:draw draw-state
:settings #(q/smooth 2)
:features [:keep-on-top]
:middleware [m/fun-mode]
)
And it just appears like this. I was trying to split the update of states, but it says that you must not have duplicated variables to be updated
Thank you.

Your code is in the right direction and you managed to draw the first line. It crashed when update-state was called. To fix the code I did the following things:
Clojure works with pure functions where you cannot "set" values as you seem to wanted to do in update-state (with do only the last expression is returned, not two values). To update-state you need to return a whole new state.
q/height and q/widths are functions that return the height and width, so call them (surround them by parentheses) to get the number out.
When you redraw the background in update-state the older lines are gone, so put (q/background 0) in setup.
Move (q/no-loop) from update-state to the draw-state entry-point of the program.
And stylistically I changed this:
Use associative destructuring in function parameter instead of getting values of keys in let.
Place trailing parentheses on the same line (see Clojure style guide).
Below the working version:
(ns tenprint.core
(:require [quil.core :as q]
[quil.middleware :as m]))
(defn setup []
(q/background 0) ; move setting background to setup, otherwise the state is overwritten
(q/frame-rate 30)
(q/stroke 255)
(q/color-mode :hsb)
{:x 0
:y 0
:scale 20})
(defn update-state [{:keys [x y scale] :as state}] ; destructure
;; height and width are functions you need to `(call)` to get the value
{:x (if (>= x (q/width)) 0 (+ x scale)) ; fix if-statements
:y (if (>= x (q/width)) (+ y scale) y) ; 'do' does something different than you think
:scale scale}) ; no need to add 0
(defn draw-state [{:keys [x y scale] :as state}] ; destructure
(if (>= y (q/height))
(q/no-loop)
(if (> (rand) 0.5)
(q/line x y (+ x scale) (+ y scale))
(q/line x (+ y scale) (+ x scale) y))))
(q/defsketch tenprint
:title "10 PRINT"
:size [500 500]
:setup setup
:draw draw-state
:update update-state
:features [:keep-on-top]
:middleware [m/fun-mode])

Related

Idiomatic Creation of Hash-Map

I'd like to create a hash-map that has n number of key-value pairs created in sets of 3 where the sets do not intersect, e.g. [(34 false) (35 false) (36 false)] && [(24 false) (25 false) (26 false)] -> {34 false 35 false 36 false 24 false 25 false 26 false}
EDIT:
To play/practice with Clojure, I'm attempting to implement an idiomatic version of the battleship board game. I decided to store the battleship coordinates in a hash-map where the keys are coordinates and the values are booleans indicating whether that section of the ship has been hit. The specific piece of code below is supposed to
Select an axis (horizontal or vertical)
Select a coordinate for the bow of the ship
"Build" the rest of the ship (3 coordinates in total) by increasing the x or y value accordingly, e.g. {"10" false "11" false "12" false}. Note the "10" translates into the second row of a matrix, first column.
Note: Before adding the ship to the hash-map of coordinates the new ship coordinates must be checked to ensure that an intersection does not exist. If it does, the ship must be "re-built."
To that end, I've created the code below. It has 2 issues:
Executing the function results in the following exception from the use of the 'acc' accumulator:
clojure.lang.LazySeq cannot be cast to clojure.lang.Associative
The result of the function is not a single hash-map, but rather a list of n hash-maps
Using idiomatic clojure, how can I achieve my goal?
(defn launch
[n]
(loop [cnt n acc {}]
(if (= cnt 0)
acc
(recur
(- cnt 1)
((fn []
(let [axis (rand-int 2)]
(if (= axis 0)
(let [x (rand-int 8) y (rand-int 10)]
(for [k (range 3)]
(assoc acc (str y (+ x k)) false)))
(let [x (rand-int 10) y (rand-int 8)]
(for [k (range 3)]
(assoc acc (str (+ y k) x) false)))))))))))
that's how i would rewrite it:
(defn create-key [axis-val i]
(if axis-val
(str (rand-int 10) (+ (rand-int 8) i))
(str (+ (rand-int 8) i) (rand-int 10))))
(defn launch [n]
(reduce (fn [acc axis]
(reduce #(assoc % (create-key axis %2) false)
acc
(range 3)))
{}
(repeatedly n #(zero? (rand-int 2)))))
in repl:
user> (launch 5)
{"40" false, "07" false, "19" false,
"46" false, "87" false, "47" false,
"41" false, "62" false, "86" false}
or (in case you don't like reduce):
(defn launch [n]
(zipmap (mapcat #(map (partial create-key %) (range 3))
(repeatedly n #(zero? (rand-int 2))))
(repeat false)))
the third variant is to use list comprehension to generate keys:
(defn launch [n]
(zipmap (for [_ (range n)
:let [axis (zero? (rand-int 2))]
i (range 3)]
(create-key axis i))
(repeat false)))
all three of them are idiomatic ones, i guess, so it's up to you to choose one, according to your own preferred programming style.
notice that the resulting keys are shuffled inside the map, because unsorted maps don't preserve order. If it is important, you should use sorted-map
What about your variant, the one generating error is this:
(for [k (range 3)] (assoc acc (str y (+ x k)) false))
it doesn't put all the keys to one map, rather it generates a seq of three items equalling (assoc acc k false):
(let [acc {}]
(for [k (range 3)] (assoc acc k false)))
;;=> ({0 false} {1 false} {2 false})
to do what you want, you use reduce:
(let [acc {}]
(reduce #(assoc %1 %2 false) acc (range 3)))
;;=> {0 false, 1 false, 2 false}
leetwinski has given a more concise answer, but I thought I would post this anyway, since I basically left your structure intact, and this may help you see the error a bit more clearly.
First, I am not sure why you were rebinding acc to the value of an anonymous function call. Your let will happily return a result; so, you should probably do some thinking about why you thought it was necessary to create an anonymous function.
Second, the problem is that for returns a lazy seq, and you are binding this to what you think is a map data structure. This explains why it works fine for cases 0 and 1, but when you use a value of 2 it fails.
Since I don't really fully understand what you're trying to accomplish, here is your original code, modified to work. Disclaimer--this is not really idiomatic and not how I would write it, but I'm posting because it may be helpful to see versus the original, since it actually works.
(defn launch
[n]
(loop [cnt n
acc {}]
(if (= cnt 0)
acc
(recur
(dec cnt)
(into acc
(let [axis (rand-int 2)]
(if (= axis 0)
(let [x (rand-int 8) y (rand-int 10)]
(map #(hash-map (str y (+ x %)) false) (range 3)))
(let [x (rand-int 10) y (rand-int 8)]
(map #(hash-map (str (+ y %) x) false) (range 3))))))))))

Increase efficiency in Clojure Mandelbrot generator

I wanted to learn Clojure and started with a Mandelbrot generator using quil, I got it to work - however it takes some time to generate images and is a massive resource hog. Any advice for how to make it faster or if you spot any un-clojure-esque parts of my code.
Core.clj
(ns frac.core
(:require [quil.core :as q])
(:require [frac.complex :refer :all]))
(def scale (atom 0.01))
(def xoff (atom -200))
(def yoff (atom -200))
(def its 50)
(defn mandelbrot [r i]
(count (take its (take-while #(<= (modu %) 2) (iterate #( add (mul % %) [r i]) [r i])))))
(defn gen []
(let [p (q/pixels)
w (q/width)
h (q/height)]
(doseq [x (range w) y (range h)]
(let [m (mandelbrot (* #scale (+ x #xoff)) (* #scale (+ y #yoff)))
c (if (= m its) 0 m)]
(aset p (+ x (* y w)) (q/color (* 1.5 c) (* 4 c) (* 5.2 c))))))
(q/update-pixels))
(defn setup []
(gen))
(defn draw [])
(defn click []
(swap! xoff #(+ (q/mouse-x) (- (/ (q/width) 2)) %))
(swap! yoff #(+ (q/mouse-y) (- (/ (q/height) 2)) %))
(gen))
(defn wheel [z]
(swap! scale #(if (pos? z) (* 1.1 %) (* 0.9 %)))
(prn #scale)
(gen))
(q/defsketch example
:title "Mandel"
:setup setup
:draw draw
:size [400 400]
:mouse-clicked click
:mouse-wheel wheel)
(defn -main [& args])
Complex.clj
(ns frac.complex)
(defn mul [z1 z2]
(let [r1 (first z1)
i1 (second z1)
r2 (first z2)
i2 (second z2)]
[(- (* r1 r2) (* i1 i2)) (+ (* r1 i2) (* r2 i1))]))
(defn add [z1 z2]
(let [r1 (first z1)
i1 (second z1)
r2 (first z2)
i2 (second z2)]
[(+ r1 r2) (+ i1 i2)]))
(defn modu [z]
(let [r (first z)
i (second z)]
(Math/sqrt (+ (* r r) (* i i)))))
Try set this:
(set! *unchecked-math* :warn-on-boxed)
and remove all warnings. Use type hints as needed.

make-keyword-map in Clojure - Idiomatic?

I have been writing some Clojure recently, and I found myself using the following pattern frequently enough:
(let [x (bam)
y (boom)]
{:x x
:y y})
So I went ahead and wrote the following macro:
(defmacro make-keyword-map [& syms]
`(hash-map ~#(mapcat (fn [s] [(keyword (name s)) s]) syms)))
With that, code now looks like:
(let [x (bam)
y (boom)]
(make-keyword-map x y)
Would this sort of macro be considered idiomatic? Or am I doing something wrong, and missing some already established pattern to deal with something of this sort?
Note, that you can also replace all of:
(let [x (bam)
y (boom)]
{:x x
:y y})
with just:
{:x (bam)
:y (boom)}
which will evaluate to the same thing.
If your let expressions depend on one another, then how about a macro like so:
(defmacro make-keyword-map [& let-body]
(let [keywords-vals (flatten (map (juxt keyword identity)
(map first (partition 2 let-body))))]
`(let ~(vec let-body)
(hash-map ~#keywords-vals))))
that way (make-keyword-map x (foo 1) y (bar 2) z (zoom x)) expands to:
(clojure.core/let [x (foo 1) y (bar 2) z (zoom x)]
(clojure.core/hash-map :x x :y y :z z))
So something like this would work:
user=> (defn foo [x] (+ x 1))
#'user/foo
user=> (defn bar [x] (* x 2))
#'user/bar
user=> (defn zoom [x] [(* x 100) "zoom!"])
#'user/zoom
user=> (make-keyword-map x (foo 1) y (bar 2) z (zoom x))
{:z [200 "zoom!"], :y 4, :x 2}
Not sure how idiomatic that is, but it also saves you a let, compared to your original example.

how would a loop with a nested return be implemented in clojure?

I'm playing around with a crafty tutorial here:
http://buildnewgames.com/introduction-to-crafty/
and am wondering how this particular function be implemented in clojurescript/clojure
var max_villages = 5;
for (var x = 0; x < Game.map_grid.width; x++) {
for (var y = 0; y < Game.map_grid.height; y++) {
if (Math.random() < 0.02) {
Crafty.e('Village').at(x, y);
if (Crafty('Village').length >= max_villages) {
return;
}
}
}
}
I know that we can have the (for []) construct but how would you get it to stop when max_villages hits 5?
Here's one approach:
(def max-villages 5)
(->> (for [x (range map-width)
y (range map-height)]
[x y])
(filter (fn [_] (< (rand) 0.02)))
(take max-villages))
Then perhaps add (map make-village-at) or something similar as the next stage of the pipeline; if it's meant to perform side effects, add a dorun or doall as the final stage to force them to happen at once (choosing one or the other depending on whether the return values are interesting).
NB. some extra vectors and random numbers may be generated due to seq chunking, it'll be less than 32 though.
A more imperative approach with a counter for comparison:
(let [counter (atom 0)]
(doseq [x (range map-width)
:while (< #counter max-villages)
y (range map-height)
:while (< #counter max-villages)
:when (< (rand) 0.02)]
(swap! counter inc)
(prn [x y]))) ; call make-village-at here
:while terminates the loop at the current nesting level when its test expression fails; :when moves on to the next iteration immediately. doseq supports chunking too, but :while will prevent it from performing unnecessary work.
Using recursion it would be something like:
(letfn [(op [x y]
(if (= (rand) 0.02)
(do
(village-at x y)
(if (>= (village-length) max-villages) true))))]
(loop [x 0 y 0]
(when (and (< x width) (not (op x y)))
(if (= (inc y) height)
(recur (inc x) 0)
(recur x (inc y))))))
That's a great tutorial!
A variation on Michael's approach (I would have just commented to his answer but I don't have enough stack power yet) would be to use Cartesian products rather than nested for loops:
;; some stub stuff to get the example to run
(ns example.core
(:use clojure.math.combinatorics))
(def max-villages 5)
(def map-width 10)
(def map-height 10)
(defn crafty-e [x y z] (print z))
;; the example, note I use doseq rather than map to empasize the fact that the loop
;; is being performed for its side effects not its return value.
(doseq [coord (take max-villages
(filter
(fn [_] (< (rand) 0.02))
(cartesian-product (range map-width) (range map-height))))]
(crafty-e :village :at coord))

Returning a value from a loop

Is there anyway i can return a value from a loop since the recursion has to be at the tail
(ns for)
(defn abc [y]
(loop [x 10]
(when (> x 2)
(if (= 2 3) (do (println "test") (recur (- x 2)))
(do (let [x (+ 1 x)
y 2] (println y) (recur (- x 2))))))))
(abc 1)
is there anyway i can return a value for the function by taking y as a parameter and updating a new value of y. However, the recur part has to be at the last line of the code hence i am unable to put y as the last line of the code.
Example
(ns for)
(defn abc [y]
(loop [x 10]
(when (> x 2)
(if (= 2 3) (do (println "test") (recur (- x 2)))
(do (let [x (+ 1 x)
y 2] (println y) (recur (- x 2)))))))
y)
(abc 1)
This would give me an error since recur has to be the last line of code. I have looked at similar questions and it says to put the return value at the end of the if loop which i tried but failed which gives me an exception thatthe recursion can only happen at the tail
I guess you meant this:
(defn abc [y]
(loop [x 10
y nil]
(if (> x 2)
(if (= 2 3)
(do (println "test")
(recur (- x 2) nil))
(do (let [x (+ 1 x)
y 2]
(println y)
(recur (- x 2) y))))
y)))
Update. Without unnecessary parts it would be
(defn abc [y]
(loop [x 10]
(if (> x 2)
(do (println 2)
(recur (- x 1)))
2)))
which is the same as
(defn abc [_]
(dotimes [_ 8] (println 2))
2)