Fork me on GitHub
#adventofcode
<
2023-12-15
>
Piotr Kaznowski08:12:18

Day 15 - Solutions

๐Ÿงต 1
borkdude10:12:40

@UUAKPEMJ9 I tried to port your solution to #C03U8L2NXNC and #C03QZH5PG6M but the number I get for my input for part 1 is too low, I'll double check in .clj now

borkdude10:12:48

ah it works in .clj, maybe a JS vs JVM difference, I'll keep digging

๐Ÿ‘ 1
borkdude10:12:49

ah yes, it's a platform difference, with planck (self-hosted CLJS) I get the same answers as cherry/squint

borkdude10:12:36

lol, with @U1EP3BZ3Qโ€™s solution I have the same problem for part 1 in CLJS

borkdude10:12:36

aaah this gives (0 0 0 0) in CLJS:

(map int "shn-")

borkdude10:12:20

charCodeAt to the rescue

borkdude10:12:42

https://squint-cljs.github.io/cherry/?boilerplate=https%3A%2F%2Fgist.githubusercontent.com%2Fborkdude%2Fcf94b492d948f7f418aa81ba54f428ff%2Fraw%2Fa6e9992b079e20e21d753e8c75a7353c5908b225%2Faoc_ui.cljs&amp;repl=true&amp;src=OzsgSGVscGVyIGZ1bmN0aW9uczoKOzsgKGZldGNoLWlucHV0IHllYXIgZGF5KSAtIGdldCBBT0MgaW5wdXQKOzsgKGFwcGVuZCBzdHIpIC0gYXBwZW5kIHN0ciB0byBET00KOzsgKHNweSB4KSAtIGxvZyB4IHRvIGNvbnNvbGUgYW5kIHJldHVybiB4Cgo7OyBSZW1lbWJlciB0byB1cGRhdGUgdGhlIHllYXIgYW5kIGRheSBpbiB0aGUgZmV0Y2gtaW5wdXQgY2FsbC4KOzsgSGVscGVyIGZ1bmN0aW9uczoKOzsgKGZldGNoLWlucHV0IHllYXIgZGF5KSAtIGdldCBBT0MgaW5wdXQKOzsgKGFwcGVuZCBzdHIpIC0gYXBwZW5kIHN0ciB0byBET00KOzsgKHNweSB4KSAtIGxvZyB4IHRvIGNvbnNvbGUgYW5kIHJldHVybiB4Cgo7OyBSZW1lbWJlciB0byB1cGRhdGUgdGhlIHllYXIgYW5kIGRheSBpbiB0aGUgZmV0Y2gtaW5wdXQgY2FsbC4KKGRlZiBpbnB1dCAoLT4%2BIChqcy1hd2FpdCAoZmV0Y2gtaW5wdXQgMjAyMyAxNSkpCiAgICAgICAgICAgICBzdHIvdHJpbSkpCgooZGVmbiBwYXJzZSBbZGF0YV0gKHN0ci9zcGxpdCBkYXRhICMiLCIpKQoKKGRlZm4gaGFzaCogW2N4XQogIChyZWR1Y2UKICAgIChmbiBbYWNjIGNoXQogICAgICAocmVtICgqIDE3ICgrIGFjYyAoIz8oOmNsaiBpbnQKICAgICAgICAgICAgICAgICAgICAgICAgICAgIDpjbGpzIC5jaGFyQ29kZUF0KSBjaAogICAgICAgICAgICAgICAgICAgICAgICAgIz8oOmNsanMgMCkpKSkgMjU2KSkgMCBjeCkpCgooZGVmbiBsZW5zZXMgW2lucHV0XQogIChyZWR1Y2UKICAgIChmbiBbYWNjIFtsYWIgZm9jXV0KICAgICAgKHVwZGF0ZSBhY2MgKGhhc2gqIGxhYikgKGlmIGZvYyAjKGFzc29jICUgbGFiIGZvYykgIyhkaXNzb2MgJSBsYWIpKSkpCiAgICAoemlwbWFwIChyYW5nZSAyNTYpIChyZXBlYXQgKGFycmF5LW1hcCkpKQogICAgKG1hcHYgIyhyZS1zZXEgIyJcdysiICUpIGlucHV0KSkpCgooZGVmbiBmb2N1c2luZy1wb3dlcnMgW2xlbnNlc10KICAodmVjIChrZWVwCiAgICAgICAgIChmbiBbW2JveCBzbG90c11dCiAgICAgICAgICAgKHdoZW4tbGV0IFt2cyAodmFscyBzbG90cyldCiAgICAgICAgICAgICAoYXBwbHkgKyAobWFwLWluZGV4ZWQgKGZuIFtpIHZdICgqIChpbmMgYm94KSAoaW5jIGkpIChwYXJzZS1sb25nIHYpKSkgdnMpKSkpCiAgICAgICAgIGxlbnNlcykpKQoKKGNvbW1lbnQKICAobGV0IFtpbnB1dCAoLT4%2BIGlucHV0IChyZS1zZXEgIyJbXixdKyIpIHZlYyldCiAgICB7OnBhcnQxIChhcHBseSArIChtYXAgaGFzaCogaW5wdXQpKQogICAgIDpwYXJ0MiAoYXBwbHkgKyAoZm9jdXNpbmctcG93ZXJzICh2ZWMgKGxlbnNlcyBpbnB1dCkpKSl9KSk%3D

genmeblog11:12:31

@UUAKPEMJ9 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

Piotr Kaznowski11:12:25

@U1EP3BZ3Q 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??

borkdude11:12:58

@UUAKPEMJ9 array-map is an optimization for small maps

borkdude11:12:24

when the map grows, it converts automatically to a hash-map, all for performance trade-offs

genmeblog11:12:58

Search in array-map is linear, assoc/update/dissoc rewrites whole storage which is ok for maps with less than 9 elements.

genmeblog11:12:34

The storage is simple Object array which keeps key1,val1,key2,val2,... keyn,valn that's why the order is preserved.

Piotr Kaznowski11:12:04

Thanks, @U04V15CAJ, @U1EP3BZ3Q. What do you think about replacing it with ordered-map from flatland.ordered.map ?

borkdude11:12:39

you could do that, it's in babashka as well :) but maybe a nice challenge to solve it without it

๐Ÿ‘ 1
borkdude11:12:13

in squint, which is based on JS objects, it's easy since they preserve insertion order

borkdude11:12:14

maybe you could also use the mutable LinkedHashMap in Java

borkdude11:12:50

(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 work

borkdude11:12:51

only for the first part, the second part doesn't work because of the mutability probably

Piotr Kaznowski12:12:16

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 ๐Ÿ˜„

๐Ÿ‘ 2
genmeblog12:12:33

Instead of take/drop you can use subvec which should be more performant.

๐Ÿ‘ 1
Piotr Kaznowski12:12:06

Not seeing any difference on my machine (but still, the whole solution runs in ~20 ms).

๐Ÿ‘ 1
bhauman13:12:55

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

Ivana15:12:10

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))

borkdude19:12:30

@U064J0EFR 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&amp;repl=true&amp;src=OzsgSGVscGVyIGZ1bmN0aW9uczoKOzsgKGZldGNoLWlucHV0IHllYXIgZGF5KSAtIGdldCBBT0MgaW5wdXQKOzsgKGFwcGVuZCBzdHIpIC0gYXBwZW5kIHN0ciB0byBET00KOzsgKHNweSB4KSAtIGxvZyB4IHRvIGNvbnNvbGUgYW5kIHJldHVybiB4Cgo7OyBvcmlnaW5hbCBzb2x1dGlvbiBieSBAYmhhdW1hbjoKOzsgaHR0cHM6Ly9naXRodWIuY29tL2JoYXVtYW4vYWR2MjAyMy9ibG9iL21haW4vc3JjL2FkdjIwMjMvZGF5MTUvc29sLmNsagoKOzsgUmVtZW1iZXIgdG8gdXBkYXRlIHRoZSB5ZWFyIGFuZCBkYXkgaW4gdGhlIGZldGNoLWlucHV0IGNhbGwuCihkZWYgaW5wdXQgKC0%2BIChqcy1hd2FpdCAoZmV0Y2gtaW5wdXQgMjAyMyAxNSkpCiAgICAgICAgICAgICBzdHIvc3BsaXQtbGluZXMKICAgICAgICAgICAgIGZpcnN0CiAgICAgICAgICAgICAoc3RyL3NwbGl0ICMiXCwiKSkpCgo7OyBzcXVpbnQgdjAuNC44MiBkb2Vzbid0IGhhdmUgcmVtLCBuZXh0IHZlcnNpb24gd2lsbAooZGVmbiByZW0gW24gZF0KICAobGV0IFtxIChxdW90IG4gZCldCiAgICAoLSBuICgqIGQgcSkpKSkKCihkZWZuIGxlbnMtaGFzaCBbY2hzXQogICgtPj4gKG1hcCAjKC5jaGFyQ29kZUF0ICUgMCkgY2hzKQogICAgKHJlZHVjZQogICAgICAjKHJlbSAoKiAoKyAlMSAlMikgMTcpIDI1NikKICAgICAgMCkpKQoKOzsgcGFydCAxCiNfKC0%2BPiAobWFwIGxlbnMtaGFzaCBpbnB1dCkgKHJlZHVjZSArKSkKCihkZWZuIHBhcnNlLWluc3QgW3NdCiAgKGlmICguZW5kc1dpdGggcyAiLSIpCiAgICBbKHN1YnMgcyAwIChkZWMgKGNvdW50IHMpKSldCiAgICAodXBkYXRlIChzdHIvc3BsaXQgcyAjIlw9IikgMSBwYXJzZS1sb25nKSkpCgooZGVmbiB2ZWMtYXNzb2MgW2wgayB2XQogIHs6cHJlIFsodmVjdG9yPyBsKV0KICAgOnBvc3QgWyh2ZWN0b3I%2FICUpXX0KICAoaWYgKHNvbWUgI3trfSAobWFwIGZpcnN0IGwpKQogICAgKG1hcHYgIyhpZiAoPSBrIChmaXJzdCAlKSkgW2sgdl0gJSkgbCkKICAgIChjb25qIGwgW2sgdl0pKSkKCihkZWZuIHZlYy1kaXNzb2MgW2wga10KICB7OnByZSBbKHZlY3Rvcj8gbCldCiAgIDpwb3N0IFsodmVjdG9yPyAlKV19CiAgKHZlYyAocmVtb3ZlICMoPSBrIChmaXJzdCAlKSkgbCkpKQoKKGRlZm4gbWFwLWluc3QgW20gW2sgdl1dCiAgezpwcmUgWyhzdHJpbmc%2FIGspIChtYXA%2FIG0pXQogICA6cG9zdCBbKG1hcD8gJSldfQogICh1cGRhdGUgbSAobGVucy1oYXNoIGspCiAgICAoaWYgKG5pbD8gdikKICAgICAgICAgICAgIygoZm5pbCB2ZWMtZGlzc29jIFtdKSAlIGspCiAgICAgICAgICAgICMoKGZuaWwgdmVjLWFzc29jIFtdKSAlIGsgdikpKSkKCihkZWZuIHNjb3JlLXNsb3QgW2JveCBzbG90IFtrIGxlbl1dCiAgezpwcmUgWyhldmVyeT8gbnVtYmVyPyBbYm94IHNsb3QgbGVuXSldCiAgIDpwb3N0IFsobnVtYmVyPyAlKV19CiAgKCogKGluYyBib3gpIChpbmMgc2xvdCkgbGVuKSkKCihkZWZuIHNjb3JlLWJveCBbW2JveCB2ZWMtbWFwXV0KICAoLT4%2BIHZlYy1tYXAKICAgIChtYXAtaW5kZXhlZCAocGFydGlhbCBzY29yZS1zbG90IGJveCkpCiAgICAocmVkdWNlICspKSkKCjs7IHBhcnQgMgojXygtPj4gaW5wdXQKICAgICAgIChtYXAgcGFyc2UtaW5zdCkKICAgICAgIChyZWR1Y2UgbWFwLWluc3QgKG5ldyBqcy9NYXApKQogICAgICAgKG1hcCBzY29yZS1ib3gpCiAgICAgICAocmVkdWNlICspKQ%3D%3D

๐Ÿ‘ 1
wevrem19:12:24

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.

๐Ÿ‘ 1
alpox16:12:55

Very simple with array-map.

gabor.veres09:12:44

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...

truestory 2
Alvydas Vitkauskas13:12:00

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}"

oyakushev13:12:19

Nah, it should be dead easy in any language:) Bad difficulty pacing from Eric this year.

clj-easy 1
bhauman13:12:31

It seems that itโ€™s like this every year. Throwing hard ones in the middle.

bhauman13:12:25

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.

roklenarcic14:12:56

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.

roklenarcic14:12:23

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

Ivana15:12:51

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}

stand18:12:24

Yeah, I only looked at the problem this morning and my reaction was, "where's the trick?"

Ivana18:12:05

The trick may realized if naive vector-lookup implementations was not enough by performance

oyakushev20:12:15

But it actually was enough.