adventofcode

Happy Advent 2020! Please put answers in the pinned threads or create one if it does not exist yet. | https://github.com/adventofcode-clojurians/adventofcode-clojurians | Join the private leaderboard with code 217019-4a55b8eb
Average-user 2020-12-11T00:51:42.111Z

Gonna take a closer look to it, but probably tomorrow

st3fan 2020-12-11T02:18:24.112600Z

Can someone help me with the following (may contain a spoiler)

st3fan 2020-12-11T02:18:32.112700Z

(frequencies (map #(* -1 (apply - %)) (partition 2 1 [0 1 4 5 6 7 10 11 12 15 16 19 22])))
;; => {1 7, 3 5}

(defn jolt-distribution [jolts]
  (frequencies (map #(* -1 (apply - %)) (partition 2 1 jolts))))

(jolt-distribution [0 1 4 5 6 7 10 11 12 15 15 19 22])
;; => {1 6, 3 4, 0 1, 4 1} ???

st3fan 2020-12-11T02:18:58.112900Z

I am pretty tired, so maybe I just donโ€™t see it - but why do I get a different result .. the function is exactly what the expression above it does

st3fan 2020-12-11T02:19:57.113100Z

Maybe I should restart the REPL

st3fan 2020-12-11T02:20:42.113300Z

I donโ€™t get it ๐Ÿ˜•

2020-12-11T02:20:45.113500Z

two 15s

st3fan 2020-12-11T02:20:51.113700Z

whaaat

2020-12-11T02:20:56.113900Z

look at line 4

st3fan 2020-12-11T02:20:57.114100Z

Sigh ๐Ÿ™‚

2020-12-11T02:20:59.114300Z

12 15 15 19

2020-12-11T02:21:07.114500Z

(Y)

st3fan 2020-12-11T02:21:24.114700Z

Hope I can solve part 1 before I pass out ๐Ÿ™‚

st3fan 2020-12-11T02:21:39.114900Z

TY

2020-12-11T02:26:36.115300Z

today was tough

st3fan 2020-12-11T02:38:13.115500Z

Uhoh now I am afdraid of what to get for part 2 ๐Ÿ™‚

st3fan 2020-12-11T02:50:52.116Z

I need to get better at thinking in recursion

๐Ÿข 2
2020-12-11T02:52:58.116400Z

are you onto part 2?

Average-user 2020-12-11T06:26:04.117500Z

How long take your programs for day 11?

2020-12-11T06:50:52.118Z

Good morning !

2020-12-11T06:51:15.118600Z

If you find it difficult today, just remember: โ€œCโ€™est la vie !โ€ (thatโ€™s life)

๐ŸŽฎ 2
plexus 2020-12-11T07:21:19.119Z

Part 2 of day 11 on the real input runs in just under a second for me. Probably could be a lot faster but good enough for me ๐Ÿ™‚

plexus 2020-12-11T07:22:15.119900Z

Pleasantly surprised that my initial attempt at part 2 actually finished in under a second, I was expecting a similar scenario as yesterday... "back to the drawing board"

๐Ÿ‘ 1
โž• 1
๐Ÿ˜† 3
๐ŸŽ‰ 2
Average-user 2020-12-11T13:35:24.130900Z

Yup, I decided to do it in haskell yesterday and I really don't want to add mutation to it.

plexus 2020-12-11T16:12:24.137200Z

Tried the "precompute 8 layers" approach, haven't finished it yet, but calculating the layers takes about 1ms per layer or 8-10ms for all ten. Still feels like it should be possible to do that faster.

2020-12-11T16:13:27.137400Z

Can we see how you did?

plexus 2020-12-11T16:19:44.138300Z

I'll push it, trying to wrap it up. Also surprising find, looking up in 2-dimensional array is twice as slow as lookup in a two-dimensional vector

1
plexus 2020-12-11T16:22:07.138500Z

(defn getxy ^long [grid x y]
  (.nth ^clojure.lang.PersistentVector (.nth ^clojure.lang.PersistentVector grid x) y))

(defn agetxy [^"[[J" arr ^long x ^long y]
  (java.lang.reflect.Array/get (java.lang.reflect.Array/get arr x) y))

(let [dim 1000
      arr (array-grid dim dim)]
  (time
   (doseq [x (range dim)
           y (range dim)]
     (agetxy arr x y))))
;; This takes ~150ms

(let [dim 1000
      arr (mapv vec (array-grid dim dim))]
  (time
   (doseq [x (range dim)
           y (range dim)]
     (getxy arr x y))))
;; This only take ~75ms

plexus 2020-12-11T16:22:53.138700Z

seems there not really any way to generate direct array lookup bytecode

plexus 2020-12-11T17:09:44.143100Z

@vincent.cantin https://github.com/lambdaisland/aoc_2020/blob/main/src/lambdaisland/aoc_2020/puzzle11_alt.clj Here it is, a lot of effort, and in the end still slower than my first solution. Then I went back and just ported the optimized nested vector lookup, and it cut the time for my original solution in half. So now I'm at 500ms for part 2, or about 1.5s with the "8 layers" approach

๐ŸŽ‰ 1
2020-12-11T17:17:09.144Z

I think that the lack of eccentricity of the input data implies that the simple implementation is working fast enough.

2020-12-11T17:18:07.144200Z

The optimization is only saving some time when the lookup of the neighbors is costly, and it does not seem to be the case here.

2020-12-11T17:21:20.144400Z

I give up on trying to improve my solution any further, it makes the code look like ugly C, itโ€™s unreadable and not interesting.

2020-12-11T17:21:44.144700Z

Thank you for spending the time to look into it.

2020-12-11T17:25:14.144900Z

A last cheap optimization to be considered: having the grid on a 1 dimensional vector. PRO: faster look up CONS: need to manually write code to make sure that the col index is correct + it looks ugly as hell. โ€ฆ tried and rolled back :face_vomiting:

2020-12-12T04:13:27.164400Z

@plexus I fixed your array access function, it should look like that instead:

(defn agetxy ^long [^"[[J" arr x y]
  (aget ^"[J" (aget arr x) y))

2020-12-12T04:14:34.164600Z

The benchmarking has a problem too, the result of the operation needs to be used, otherwise the JVM will just drop the body using dead code elimination.

2020-12-12T04:15:37.164800Z

Probably, the DCE is triggered when the read expression is fully understood statically and thatโ€™s why the reflective calls where not removed - but they were slow anyway.

2020-12-12T04:30:45.165200Z

@plexus This is the correct way to bench, and even with the addition and transducing process overhead, it is still faster than your previous fastest.

(set! *unchecked-math* :warn-on-boxed)

(defn agetxy ^long [^"[[J" arr x y]
  (aget ^"[J" (aget arr x) y))

(let [dim 1000
      arr (make-array Long/TYPE dim dim)
      _ (doseq [x (range dim)
                y (range dim)]
          #_ (aset ^"[[J" arr x y 1) ;; super slow - don't use this!
          (aset ^"[J" (aget ^"[[J" arr x) y 1))
      result (time
               (transduce (map (fn [x]
                                 (transduce (map (fn [y]
                                                   (agetxy arr x y)))
                                            +
                                            (range dim))))
                          +
                          (range dim)))]
  (prn result))

plexus 2020-12-11T07:23:21.120300Z

Day 11 answers thread - Post your answers here

nbardiuk 2020-12-11T09:08:37.127500Z

my solution runs in 3s and 6s, I am very curious to learn from the thread to find better data structures or approach https://github.com/nbardiuk/adventofcode/blob/master/2020/src/day11.clj

๐Ÿ‘ 3
misha 2020-12-11T10:09:51.129Z

cache first-seen xys, and remove dots (floor) from the map. @nbardiuk

๐Ÿ‘ 2
genmeblog 2020-12-11T13:36:33.131200Z

My runs in 0.6s and 1.2s with index of seen chairs. https://github.com/genmeblog/advent-of-code/blob/master/src/advent_of_code_2020/day11.clj

2020-12-11T13:44:13.131600Z

My messy original solution (a cleaned up and faster version is coming later) For the part 2, it scans the whole data in 8 directions in O(n) before updating the data in O(n) https://github.com/green-coder/advent-of-code-2020/blob/1df1043c19017850257d403000d9b6f5568fddee/src/aoc/day_11.clj

2020-12-11T13:57:20.133200Z

Here is my solution. The key is โ€œall decisions are based on theย number of occupied seatsโ€ฆโ€ https://github.com/zelark/AoC-2020/blob/master/src/zelark/aoc_2020/day_11.clj

benoit 2020-12-11T14:23:08.133600Z

My solution to Day 11. https://github.com/benfle/advent-of-code-2020/blob/main/day11.clj

erwinrooijakkers 2020-12-11T14:28:49.134Z

๐ŸŽจ 3
๐Ÿคฏ 2
โค๏ธ 2
๐Ÿ˜ 2
๐Ÿฆœ 4
plexus 2020-12-11T15:27:04.135200Z

TFW you think you'll speed it up by changing arrays in-place, and it's 100 times slower than updating vectors

๐Ÿ˜… 3
2020-12-11T15:29:01.135400Z

Did you transient / persistent! a lot?

2020-12-11T16:00:37.135800Z

@vincent.cantin I tried, but it didnโ€™t help much

2020-12-11T16:03:54.136Z

I benched my solution with Criterium: part 1 โ€” 882ms, and part 2 โ€” 772ms.

๐Ÿš€ 2
2020-12-11T16:07:55.136200Z

Can you bench my part 1 and tell me how fast it takes on your computer? (I think my computer is way slower than everybody else) https://github.com/green-coder/advent-of-code-2020/blob/01fc1785a26872c165302c463e26caceb93e82ff/src/aoc/day_11.clj

2020-12-11T16:08:14.136500Z

Sure!

๐Ÿ‘ 1
2020-12-11T16:08:31.136700Z

In this version, I tried to leverage the transducers.

2020-12-11T16:17:28.137600Z

Part 1 โ€”ย 512.213845 ms Part 2 โ€” 2.664164 sec

2020-12-11T16:18:10.137900Z

Thank you ! It seems that your computer is exactly twice faster than mine.

markw 2020-12-11T17:36:19.145200Z

As concise as I could get it, but it does take ~20s to run both parts https://github.com/Solaxun/aoc2020_clj/blob/main/src/aoc2020_clj/day11.clj

2020-12-11T18:47:20.146400Z

(time (solve adjacent-count 4)) "Elapsed time: 1488.883654 msecs" (time (solve seen-count 5)) "Elapsed time: 2997.948211 msecs" I was pretty happy with the functional composition in this one! (And the completely unnecessary selections from math.combinatorics) https://github.com/Dexterminator/advent-of-code-2020/blob/main/src/day11/core.clj

๐Ÿ‘ 2
pez 2020-12-11T21:33:43.149800Z

pez 2020-12-11T21:33:43.150Z

nbardiuk 2020-12-11T21:43:25.151900Z

after incorporating examples and suggestions I've managed to reduce runtime from 2s,6s to 0.6s,0.9s. What helped is to precompute near seats for each seat and split map of seat states into 3 sets (occupied, floor, seats)

1
pez 2020-12-11T21:46:48.152200Z

@misha:I remove floor cells from the map.

pez 2020-12-11T22:56:22.158500Z

So if I memoize my neighbour-of function, I get 3 secs for step 2. But can only do it once then, because on the second round it never finishes.

(def neighbours-of-
  (fn
    [{:keys [floor size]} seat]
    (let [[rows cols] size]
      (->> [[-1 -1] [0 -1] [1 -1] [-1 0] [1 0] [-1 1] [0 1] [1 1]]
           (keep (fn look [direction]
                   (loop [delta direction]
                     (let [[new-r new-y] (map + seat delta)]
                       (when-not (or (neg? new-r)
                                     (neg? new-y)
                                     (> new-r rows)
                                     (> new-y cols))
                         (if (floor [new-r new-y])
                           (recur (map + delta direction))
                           [new-r new-y]))))))
           (into #{})))))

(def neighbours-of
  (memoize neighbours-of-))

(defn occupied-neighbours-of
  [ferry seat]
  (->> (neighbours-of (select-keys ferry [:floor :size]) seat)
       (filter #((:occupied-seats ferry) %))
       (into #{})))

pez 2020-12-12T00:05:34.158900Z

Iโ€™m down to 2 secs now, with cached neighbours/first-seens. Canโ€™t see where I have gains to make. I might have choosen the wrong data structure for the grid.

pez 2020-12-12T00:05:34.159100Z

(defn parse-seats [rows chs]
  (->> rows
       (map-indexed (fn [y row]
                      (map-indexed (fn [x this-ch]
                                     (when (chs this-ch)
                                       [x y]))
                                   row)))
       (apply concat)
       (remove nil?)
       (into #{})))

(defn parse [input]
  (->> input
       (re-seq #"[.L#]+")
       ((fn parse-ferry
          [rows]
          (let [floor (parse-seats rows #{\.})]
            {:floor          floor
             :occupied-seats (parse-seats rows #{\#})
             :all-seats      (clojure.set/difference (parse-seats rows #{\# \. \L}) floor)
             :neighbours-of  {}
             :size           [(count (first rows)) (count rows)]
             :generations    0})))))

(defn neighbours-of-1
  [seat]
  (->> [[-1 -1] [0 -1] [1 -1] [-1 0] [1 0] [-1 1] [0 1] [1 1]]
       (map (fn [direction]
              (map + seat direction)))
       (into #{})))

(defn occupied-neighbours-of-1
  [ferry seat]
  (->> (neighbours-of-1 seat)
       (filter (fn [c]
                 ((:occupied-seats ferry) c)))
       (into #{})))

(defn neighbours-of
  [{:keys [floor size]} seat]
  (let [[rows cols] size]
    (->> [[-1 -1] [0 -1] [1 -1] [-1 0] [1 0] [-1 1] [0 1] [1 1]]
         (keep (fn look [direction]
                 (loop [delta direction]
                   (let [[new-r new-y] (map + seat delta)]
                     (when-not (or (neg? new-r)
                                   (neg? new-y)
                                   (> new-r rows)
                                   (> new-y cols))
                       (if (floor [new-r new-y])
                         (recur (map + delta direction))
                         [new-r new-y]))))))
         (into #{}))))

(defn occupied-neighbours-of
  [ferry seat]
  (->> (get-in ferry [:neighbours-of seat])
       (clojure.set/intersection (:occupied-seats ferry))))

(defn render [ferry] ; TODO: This is broken
  (let [[rows cols] (:size ferry)
        pixels (for [row (range rows)
                     col (range cols)]
                 (cond
                   ((:floor ferry) [row col]) "."
                   ((:occupied-seats ferry) [row col]) "#"
                   :else                            "L"))]
    (->> pixels
         (partition 10)
         (map #(apply str %)))))

(defn next-ferry
  [ferry]
  (-> ferry
      (update :occupied-seats
              (fn [_]
                (->> (:all-seats ferry)
                     (filter (fn [seat]
                               (let [num-occupied-neighbours (count (occupied-neighbours-of ferry seat))]
                                 (cond
                                   (and (not ((:occupied-seats ferry) seat))
                                        (zero? num-occupied-neighbours))
                                   true

                                   (and ((:occupied-seats ferry) seat)
                                        (>= num-occupied-neighbours 5))
                                   false

                                   :else
                                   ((:occupied-seats ferry) seat)))))
                     (into #{}))))
      (update :generations inc)))

(defn cache-neighbours [ferry]
  (update ferry
          :neighbours-of
          #(into
            %
            (->> (:all-seats ferry)
                 (map (fn [seat]
                        {seat (neighbours-of ferry seat)}))
                 (into #{})))))

(comment
  (def input (util/fetch-input 11))

  (time (->> input
             (parse)
             (cache-neighbours)
             ((fn stabilize [ferry]
                (loop [old-ferry ferry]
                  (let [new-ferry (next-ferry old-ferry)]
                    (if (= (:occupied-seats new-ferry)
                           (:occupied-seats old-ferry))
                      old-ferry
                      (recur new-ferry))))))
             (:occupied-seats)
             (count))))

Joe 2020-12-12T17:46:59.197700Z

Would anyone be able to give me some clues as to why my Day 11 Part 1 solution is so slow? I'm caching neighbours, but I'm still getting >2 seconds. I'm doing a fair amount of set operations, maybe that's it? https://github.com/RedPenguin101/aoc2020/blob/main/day11-2.clj

genmeblog 2020-12-12T18:00:13.199800Z

Part 2 final stage (probably filpped): https://raw.githubusercontent.com/genmeblog/advent-of-code/master/images/advent_of_code_2020/day11_far_222.jpg

Ben List 2020-12-12T19:23:13.207300Z

definitely not my best work, but i never cared for game of life when I had to do it in college either https://github.com/listba/advent-of-code-2020/blob/master/clojure/src/aoc_2020/days/11.clj

plexus 2020-12-11T07:24:10.120500Z

Not gonna win at code golf but at least it works ๐Ÿ™‚ https://github.com/lambdaisland/aoc_2020/blob/main/src/lambdaisland/aoc_2020/puzzle11.clj

๐Ÿ‘ 5
2020-12-11T07:35:02.120900Z

Impressive

2020-12-11T07:39:29.121100Z

I did not believe that the simple approach would be fast enough, so I spent a lot of time optimizing the algorithm and writing it down.

erwinrooijakkers 2020-12-11T07:42:45.121700Z

Nice pictures

erwinrooijakkers 2020-12-11T07:43:28.121900Z

I read on the subreddit that itโ€™s indeed a tribonacci sequence, but not sure what that means for the solution

plexus 2020-12-11T07:43:54.122200Z

One more optimized approach I can think of is doing a single pass over the layout for each of the 8 directions, so you end up with 8 data structures with precomputed values for what the next chair in that direction is. I hope that makes sense ๐Ÿ™‚

2020-12-11T07:44:08.122400Z

that's what I did.

๐Ÿ‘ 1
plexus 2020-12-11T07:44:25.122600Z

And, how fast is it?

2020-12-11T07:44:57.122900Z

a couple of seconds ... but my computer is a mac book air from 2011

plexus 2020-12-11T07:45:31.123100Z

hmmm that still feels like it should be faster

2020-12-11T07:45:37.123300Z

Note to self: learn the mutating operators ... assoc! and friends.

erwinrooijakkers 2020-12-11T07:45:56.123500Z

Second part (just executing how it is stated) takes 2 minutes but well, https://github.com/transducer/adventofcode/blob/master/src/adventofcode/2020/day11.clj. Not sure how to improve the speed.

2020-12-11T07:46:01.123800Z

I used an atom as acc inside a double for loop

plexus 2020-12-11T07:46:12.124Z

yeah transients can help... not sure how much

2020-12-11T07:47:05.124200Z

Doing the optimised algorithm only with immutable datastructure in limited time is very hard.

2020-12-11T07:47:43.124400Z

I only thought about atom because I usually don't do mutable things. It's time to change that.

2020-12-11T07:48:31.124600Z

Other note for myself: add a reverse-range function to my tool ns

plexus 2020-12-11T07:49:29.124800Z

or just use a step of -1?

2020-12-11T07:50:07.125Z

(range (dec n) -1 -1) .. with the adrenaline of the rush, a reverse-range function is wiser.

plexus 2020-12-11T07:50:23.125200Z

I might have a stab at doing the precomputing later, I feel like it should be possible to make it very fast without funky stuff like transitiens

2020-12-11T07:50:44.125500Z

The key is not to have to write 8 functions.

plexus 2020-12-11T07:51:23.125700Z

yeah, makes sense. I might write two, one for diagonal one for straight

2020-12-11T07:52:32.125900Z

the transient stuff is a good idea for opening your next stream

plexus 2020-12-11T07:52:45.126100Z

meh...

plexus 2020-12-11T07:53:06.126300Z

I've never come across a real life need for transients

2020-12-11T07:53:33.126500Z

but people in AoC may need it soon.

2020-12-11T07:54:24.126700Z

.. and that would be a good demonstration of the versatility of Clojure.

mchampine 2020-12-11T07:59:35.126900Z

Part 1, kinda crufty.

;; get neighbor coords
(defn addpairs [[x1 y1] [x2 y2]] [(+ x1 x2) (+ y1 y2)])
(def surround [[1 0] [1 1] [0 1] [-1 1] [-1 0] [-1 -1] [0 -1] [1 -1]])
(defn neighbors [posn] (map addpairs (repeat posn) surround))

;; get / set seat value
(defn getxy [brd [x y]] (get (get brd y) x))

(defn setxy [brd [x y] v]
  (let [row (get brd y)
        newrow (apply str (assoc (vec row) x v))]
    (assoc brd y newrow)))

(defn countneighbors? [brd seat]
  (count (filter #{\#} (map (partial getxy brd) (neighbors seat)))))

(defn update1seat [brd seat]
  (let [seatstatus (getxy brd seat)
        adjseats (countneighbors? brd seat)]
    (case seatstatus
      \L (if (= 0 adjseats) \# \L)
      \# (if (>= adjseats 4) \L \#)
      \. \.)))

(defn step1 [brd]
  (let [dimx (count (first brd))
        dimy (count brd)
        allseats (for [y (range dimy) x (range dimx)] [x y])
        updatedseats (map (partial update1seat brd) allseats)]
    (vec (map (partial apply str) (partition dimx updatedseats)))))

(->> input
     (iterate step1)
     (partition 2 1)
     (drop-while #(apply not= %))
     ffirst
     (apply str)
     (filter #{\#})
     count)
;; => 2412

๐Ÿ‘ 1
โ›ณ 1
2020-12-11T12:09:50.130400Z

๐Ÿ˜ฑ I replaced get-in by 2 consecutive get and I cut my runtime by half !!!

๐Ÿ˜ฎ 1
2020-12-11T13:46:58.132400Z

My bed book this weekend will be http://clojure-goes-fast.com/blog/

2020-12-11T13:49:36.132700Z

as well as https://github.com/bsless/clj-fast

2020-12-11T16:23:20.139100Z

From slower to faster:

(swap! acc update row assoc col c)

(swap! acc assoc-in [row col] c)

(swap! acc (fn [grid]
             (update grid row (fn [row]
                                (assoc row col c))))))))

pez 2020-12-11T21:17:31.147900Z

My solution for Day 11, step 2 takes 6 seconds to runโ€ฆ But at least I have the stars.

๐Ÿ˜ 4
2020-12-11T21:36:11.151300Z

I've been getting some good mileage out of iterate in some of the problems. Haven't gotten the opportunity to use it that much before, it's so nice when the problen fits! I started writing the body for a loop/recur and then realised that I could just feed it to iterate and it turned out much nicer

๐Ÿค˜ 3
2020-12-11T21:50:46.152600Z

Ah nice yeah I forgot about iterate

2020-12-11T21:51:12.153500Z

I feel like I should use a proper matrix library instead of the usual vector of vectors

2020-12-11T21:51:29.154Z

What's the best library for this kind of stuff atm?

2020-12-11T22:00:13.155800Z

@andrea.crotti The ones I've seen (like core.matrix) are a bit more math focused I'd say. One of the main matrix processing tools in core is https://clojuredocs.org/clojure.walk/prewalk

2020-12-11T22:00:37.156300Z

I don't really think either is very useful for today's problem though since you need to keep track of indices and stuff

2020-12-11T22:02:02.156900Z

If you find a way to use one of the walk functions in a nice way in the problem I'd love to hear it! That was the ugliest part of my solution

โž• 1
markw 2020-12-11T22:46:52.158400Z

@vincent.cantin may the lisp gods forgive this abomination inspired by your comment:

(defmacro update-in-faster [m [k & ks] f & args]
  (if ks
    `((fn [m#]
        (assoc m# ~k (update-in-faster (m# ~k) ~ks ~f ~@args)))
      ~m)
    `(update ~m ~k ~f ~@args)))

๐Ÿ‘ 1
๐Ÿฆœ 1