adventofcode

2023-12-15T08:52:18.075939Z

Day 15 - Solutions

🧡 1
alpox 2023-12-28T16:29:55.604469Z

Very simple with array-map.

2023-12-15T08:53:00.690509Z

array-map ftw https://github.com/caseneuve/aoc2023/blob/master/day15/solution.clj

πŸ‘ 2
gabor.veres 2023-12-15T09:08:21.167619Z

Very nice and concise, @piotr.kaznowski! Mine is https://github.com/gveres/advent-of-code-2023-clojure/blob/main/src/day15.clj

borkdude 2023-12-15T10:15:40.189279Z

@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

genmeblog 2023-12-15T10:20:07.758619Z

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

borkdude 2023-12-15T10:21:48.520329Z

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

πŸ‘ 1
borkdude 2023-12-15T10:24:49.716089Z

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

borkdude 2023-12-15T10:31:36.895959Z

lol, with @tsulej’s solution I have the same problem for part 1 in CLJS

borkdude 2023-12-15T10:40:36.033919Z

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

(map int "shn-")

borkdude 2023-12-15T10:41:20.222099Z

charCodeAt to the rescue

borkdude 2023-12-15T10:44:42.857189Z

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

genmeblog 2023-12-15T11:00:31.567679Z

@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

2023-12-15T11:24:25.774169Z

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

borkdude 2023-12-15T11:25:58.290759Z

@piotr.kaznowski array-map is an optimization for small maps

borkdude 2023-12-15T11:27:24.664189Z

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

genmeblog 2023-12-15T11:30:58.275149Z

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

genmeblog 2023-12-15T11:32:34.783839Z

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

2023-12-15T11:35:04.860109Z

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

borkdude 2023-12-15T11:35:39.238949Z

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

πŸ‘ 1
2023-12-15T11:36:07.433319Z

Sure!

borkdude 2023-12-15T11:36:13.520149Z

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

borkdude 2023-12-15T11:37:14.275579Z

maybe you could also use the mutable LinkedHashMap in Java

borkdude 2023-12-15T11:39:50.349099Z

(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

borkdude 2023-12-15T11:40:51.798499Z

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

2023-12-15T12:11:16.137699Z

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
genmeblog 2023-12-15T12:13:33.392429Z

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

πŸ‘ 1
2023-12-15T12:18:06.453029Z

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

πŸ‘ 1
bhauman 2023-12-15T13:51:55.054259Z

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

Ivana 2023-12-15T15:08:10.484929Z

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

rjray 2023-12-15T17:20:41.711549Z

https://github.com/rjray/advent-2023-clojure/blob/master/src/advent_of_code/day15.clj Man, I needed a day this easy...

borkdude 2023-12-15T19:25:30.375539Z

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

πŸ‘ 1
wevrem 2023-12-15T19:55:24.336909Z

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
gabor.veres 2023-12-15T09:12:44.318859Z

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

2
Alvydas Vitkauskas 2023-12-15T13:15:00.353569Z

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

oyakushev 2023-12-15T13:23:19.569439Z

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

1
bhauman 2023-12-15T13:57:31.032829Z

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

bhauman 2023-12-15T13:59:25.325329Z

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.

roklenarcic 2023-12-15T14:49:56.891449Z

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.

roklenarcic 2023-12-15T14:52:23.123039Z

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

Ivana 2023-12-15T15:19:51.953079Z

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}

2023-12-15T18:16:24.389719Z

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

Ivana 2023-12-15T18:18:05.868699Z

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

oyakushev 2023-12-15T20:36:15.638649Z

But it actually was enough.