Day 15 - Solutions
Very simple with array-map.
array-map ftw https://github.com/caseneuve/aoc2023/blob/master/day15/solution.clj
Very nice and concise, @piotr.kaznowski! Mine is https://github.com/gveres/advent-of-code-2023-clojure/blob/main/src/day15.clj
@piotr.kaznowski I tried to port your solution to #squint and #cherry but the number I get for my input for part 1 is too low, I'll double check in .clj now
Nothing elegant linear search on vector + rewriting was enough https://github.com/genmeblog/advent-of-code/blob/master/src/advent_of_code_2023/day15.clj
ah it works in .clj, maybe a JS vs JVM difference, I'll keep digging
ah yes, it's a platform difference, with planck (self-hosted CLJS) I get the same answers as cherry/squint
lol, with @tsulejβs solution I have the same problem for part 1 in CLJS
aaah this gives (0 0 0 0) in CLJS:
(map int "shn-")charCodeAt to the rescue
@piotr.kaznowski you're lucky here:
(class (reduce #(assoc %1 %2 %2) (array-map) (range 8)))
;; => clojure.lang.PersistentArrayMap
(class (reduce #(assoc %1 %2 %2) (array-map) (range 9)))
;; => clojure.lang.PersistentHashMap@tsulej Looks like a novice's luck! Was looking for exactly this kind of data structure and found it, but didn't read through all the caveats. Max key count of particular array-map in my input is 6, so luck it is! But then -- what is the point of such behavior??
@piotr.kaznowski array-map is an optimization for small maps
when the map grows, it converts automatically to a hash-map, all for performance trade-offs
Search in array-map is linear, assoc/update/dissoc rewrites whole storage which is ok for maps with less than 9 elements.
The storage is simple Object array which keeps key1,val1,key2,val2,... keyn,valn that's why the order is preserved.
you could do that, it's in babashka as well :) but maybe a nice challenge to solve it without it
Sure!
in squint, which is based on JS objects, it's easy since they preserve insertion order
maybe you could also use the mutable LinkedHashMap in Java
(defn lenses [input]
(reduce
(fn [acc [lab foc]]
(update acc (hash* lab) (if foc #(doto % (.put lab foc)) #(doto % (.remove % lab)))))
(zipmap (range 256) (repeat (new java.util.LinkedHashMap)))
(mapv #(re-seq #"\w+" %) input)))
seems to workonly for the first part, the second part doesn't work because of the mutability probably
Just for fun quickly sketched alternative assoc* and dissoc* to keep my original solution (mostly) intact (would need only to change the condition in focusing-powers, and, obviously, exchange maps to vectors):
(defn assoc* [mx a b]
(let [i (.indexOf (mapv first mx) a)]
(if (< i 0) (conj mx [a b]) (assoc mx i [a b]))))
(defn dissoc* [mx a]
(let [i (.indexOf (mapv first mx) a)]
(if (< i 0) mx (into [] cat [(take i mx) (drop (inc i) mx)]))))
It's ~4 ms less performant than array|ordered-map and it's +6 locs, but can live with it πInstead of take/drop you can use subvec which should be more performant.
Not seeing any difference on my machine (but still, the whole solution runs in ~20 ms).
Today I made liberal use of :pre and :post conditions. It seemed to go much smoother. Testing and typing helps sometimes who knew????
https://github.com/bhauman/adv2023/blob/main/src/adv2023/day15/sol.clj
Whith the help of custom bare bones ordered map
(defn h [s] (reduce (fn [r c] (-> c int (+ r) (* 17) (rem 256))) 0 s))
(->> (str/split input #"\,")
(reduce (fn [acc s]
(let [[l v] (str/split s #"[\=\-]")
b (h l)
acc (vary-meta acc update :i (fnil inc 0))]
(if v
(update-in acc [b l] (fn [[k _]] [(or k (-> acc meta :i)) (read-string v)]))
(update acc b dissoc l))))
{})
(reduce (fn [acc [b m]]
(->> m
(sort-by (comp first second))
(map-indexed (fn [i [_ [_ v]]] (* (inc b) (inc i) v)))
(apply + acc)))
0))https://github.com/rjray/advent-2023-clojure/blob/master/src/advent_of_code/day15.clj Man, I needed a day this easy...
https://github.com/zelark/AoC/blob/master/src/zelark/aoc_2023/day_15.clj
@bhauman thank! the pre/post was indeed very useful also in squint, with that I detected that one thing came in as a string instead of a number which was caused by storing stuff in a JS object, changing that to a js/Map fixed it!
https://squint-cljs.github.io/squint/?boilerplate=https%3A%2F%2Fgist.githubusercontent.com%2Fborkdude%2Fcf94b492d948f7f418aa81ba54f428ff%2Fraw%2Fa6e9992b079e20e21d753e8c75a7353c5908b225%2Faoc_ui.cljs&repl=true&src=OzsgSGVscGVyIGZ1bmN0aW9uczoKOzsgKGZldGNoLWlucHV0IHllYXIgZGF5KSAtIGdldCBBT0MgaW5wdXQKOzsgKGFwcGVuZCBzdHIpIC0gYXBwZW5kIHN0ciB0byBET00KOzsgKHNweSB4KSAtIGxvZyB4IHRvIGNvbnNvbGUgYW5kIHJldHVybiB4Cgo7OyBvcmlnaW5hbCBzb2x1dGlvbiBieSBAYmhhdW1hbjoKOzsgaHR0cHM6Ly9naXRodWIuY29tL2JoYXVtYW4vYWR2MjAyMy9ibG9iL21haW4vc3JjL2FkdjIwMjMvZGF5MTUvc29sLmNsagoKOzsgUmVtZW1iZXIgdG8gdXBkYXRlIHRoZSB5ZWFyIGFuZCBkYXkgaW4gdGhlIGZldGNoLWlucHV0IGNhbGwuCihkZWYgaW5wdXQgKC0%2BIChqcy1hd2FpdCAoZmV0Y2gtaW5wdXQgMjAyMyAxNSkpCiAgICAgICAgICAgICBzdHIvc3BsaXQtbGluZXMKICAgICAgICAgICAgIGZpcnN0CiAgICAgICAgICAgICAoc3RyL3NwbGl0ICMiXCwiKSkpCgo7OyBzcXVpbnQgdjAuNC44MiBkb2Vzbid0IGhhdmUgcmVtLCBuZXh0IHZlcnNpb24gd2lsbAooZGVmbiByZW0gW24gZF0KICAobGV0IFtxIChxdW90IG4gZCldCiAgICAoLSBuICgqIGQgcSkpKSkKCihkZWZuIGxlbnMtaGFzaCBbY2hzXQogICgtPj4gKG1hcCAjKC5jaGFyQ29kZUF0ICUgMCkgY2hzKQogICAgKHJlZHVjZQogICAgICAjKHJlbSAoKiAoKyAlMSAlMikgMTcpIDI1NikKICAgICAgMCkpKQoKOzsgcGFydCAxCiNfKC0%2BPiAobWFwIGxlbnMtaGFzaCBpbnB1dCkgKHJlZHVjZSArKSkKCihkZWZuIHBhcnNlLWluc3QgW3NdCiAgKGlmICguZW5kc1dpdGggcyAiLSIpCiAgICBbKHN1YnMgcyAwIChkZWMgKGNvdW50IHMpKSldCiAgICAodXBkYXRlIChzdHIvc3BsaXQgcyAjIlw9IikgMSBwYXJzZS1sb25nKSkpCgooZGVmbiB2ZWMtYXNzb2MgW2wgayB2XQogIHs6cHJlIFsodmVjdG9yPyBsKV0KICAgOnBvc3QgWyh2ZWN0b3I%2FICUpXX0KICAoaWYgKHNvbWUgI3trfSAobWFwIGZpcnN0IGwpKQogICAgKG1hcHYgIyhpZiAoPSBrIChmaXJzdCAlKSkgW2sgdl0gJSkgbCkKICAgIChjb25qIGwgW2sgdl0pKSkKCihkZWZuIHZlYy1kaXNzb2MgW2wga10KICB7OnByZSBbKHZlY3Rvcj8gbCldCiAgIDpwb3N0IFsodmVjdG9yPyAlKV19CiAgKHZlYyAocmVtb3ZlICMoPSBrIChmaXJzdCAlKSkgbCkpKQoKKGRlZm4gbWFwLWluc3QgW20gW2sgdl1dCiAgezpwcmUgWyhzdHJpbmc%2FIGspIChtYXA%2FIG0pXQogICA6cG9zdCBbKG1hcD8gJSldfQogICh1cGRhdGUgbSAobGVucy1oYXNoIGspCiAgICAoaWYgKG5pbD8gdikKICAgICAgICAgICAgIygoZm5pbCB2ZWMtZGlzc29jIFtdKSAlIGspCiAgICAgICAgICAgICMoKGZuaWwgdmVjLWFzc29jIFtdKSAlIGsgdikpKSkKCihkZWZuIHNjb3JlLXNsb3QgW2JveCBzbG90IFtrIGxlbl1dCiAgezpwcmUgWyhldmVyeT8gbnVtYmVyPyBbYm94IHNsb3QgbGVuXSldCiAgIDpwb3N0IFsobnVtYmVyPyAlKV19CiAgKCogKGluYyBib3gpIChpbmMgc2xvdCkgbGVuKSkKCihkZWZuIHNjb3JlLWJveCBbW2JveCB2ZWMtbWFwXV0KICAoLT4%2BIHZlYy1tYXAKICAgIChtYXAtaW5kZXhlZCAocGFydGlhbCBzY29yZS1zbG90IGJveCkpCiAgICAocmVkdWNlICspKSkKCjs7IHBhcnQgMgojXygtPj4gaW5wdXQKICAgICAgIChtYXAgcGFyc2UtaW5zdCkKICAgICAgIChyZWR1Y2UgbWFwLWluc3QgKG5ldyBqcy9NYXApKQogICAgICAgKG1hcCBzY29yZS1ib3gpCiAgICAgICAocmVkdWNlICspKQ%3D%3D
My https://github.com/wevre/advent-of-code/blob/master/src/advent_of_code/2023/day_15_tokens.clj. Since I was having so much fun with them the other day, I wrote my upsert-lens as a transducer.
Anyone else feels that day 15 is magnitudes easier than any of the previous 8-9 days? My feeling is that it's a very good fit for Clojurists, as we're indoctrinated in the "reduce using instructions from a data DSL" way of thinking(?). Anyway, I'm curious how people from other languages and disciplines will feel about it...
Last year I did AoC in Clojure. This year I do in Crystal. And when you are keen on functional style, it does not look bad at all. Check it out:
input = File.read("input/input15.txt").delete('\n').split(',')
def hash(str)
str.chars.reduce(0) { |acc, c| (acc + c.ord) * 17 % 256 }
end
ans = input.sum { |s| hash(s) }
puts "Part 1: #{ans}"
boxes = Array.new(256) { |_| Hash(String, Int32).new }
boxes = input.reduce(boxes) do |boxes, step|
label, op, focal = step.partition /[=-]/
box = hash(label)
case op
when "="
boxes[box][label] = focal.to_i
when "-"
boxes[box].delete label
end
boxes
end
ans = boxes.each_with_index.sum do |(box, i)|
box.each_with_index.sum do |((_, focal), k)|
(i + 1) * (k + 1) * focal
end
end
puts "Part 2: #{ans}"Nah, it should be dead easy in any language:) Bad difficulty pacing from Eric this year.
It seems that itβs like this every year. Throwing hard ones in the middle.
I think Clojure is an excellent lang for these problems. I.E. I donβt think the problems are good for Clojure but rather the other way around. Clojure has strengths.
This year was super easy so far, no problems where I would get stuck for hours and a lot of really easy ones. Iβve solved all years and this one might be the easiest one of them all, so far. The 2015 was also quite easy, perhaps easier than this.
That crystal solution looks really clean, what really makes it work well here is the fact that you can always fetch the index of the element in a collection during reduce and such. In clojure we have to do with map-indexed and and then you need to pack and unpack tuples everywhere
Actually you may implement reduce-indexed with one additional line (*vary-meta* acc update :index (*fnil* inc 0)) without altering initial collection and dealing with tuples. Of course, if acc is not of primitive type. Don't forget - we are not in Haskell, Clojure meta rules! π
(reduce (fn [acc c]
(let [i (-> acc meta :i (or 0))]
(assoc (with-meta acc {:i (inc i)}) c i)))
{} "hello!")
=> {\h 0, \e 1, \l 3, \o 4, \! 5}Yeah, I only looked at the problem this morning and my reaction was, "where's the trick?"
The trick may realized if naive vector-lookup implementations was not enough by performance
But it actually was enough.