adventofcode

Aleks 2021-12-06T04:30:20.307Z

🧵Day 6 answers thread: post your answers here

Fredrik 2021-12-06T05:26:45.308500Z

👏 1
solf 2021-12-06T05:36:42.309500Z

(defn parse [input]
  (->> input
       u/parse-comma-separated-list
       (map parse-long)))

(defn next [l]
  {0 (get l 1 0)
   1 (get l 2 0)
   2 (get l 3 0)
   3 (get l 4 0)
   4 (get l 5 0)
   5 (get l 6 0)
   6 (+ (get l 7 0) (get l 0 0))
   7 (get l 8 0)
   8 (get l 0 0)})

(->> (parse #_"3,4,3,1,2"
            (u/get-input 2021 6))
     frequencies
     (iterate next)
     (take (inc 256))
     last
     vals
     (reduce +))

🔥 1
2021-12-06T05:38:17.310800Z

Looking at the other solutions, I realize I didn't have to resort to a recursive memoized solution

Miķelis Vindavs 2021-12-06T05:44:18.311900Z

(defn step [fish]
  (->> fish
       (reduce (fn [res [k v]]
                 (if (= k 0)
                   (-> res
                       (update 6 (fnil + 0) v)
                       (assoc 8 v))
                   (update res (dec k) (fnil + 0) v)))
               {})))

(defn solve [n input]
  (->> input
       (find-ints)
       (frequencies)
       (nth-iter n step)
       (vals)
       (reduce +)))

(def solve1 (partial solve 80))
(def solve2 (partial solve 256))

👍 1
Mario C. 2021-12-06T06:18:53.312800Z

Pretty much the same as others

(def puzzle-input
  (mapv read-string (str/split (input "day6.txt") #",|\n")))

(defn apply-fish-creation [index state]
  (if (= index 0)
    (-> (update state 8 (constantly (get state index)))
        (update 6 (partial + (get state index))))
    state))

(defn update-state [state]
  (->> (reduce (fn [{:keys [state carry-over]} index]
                 {:state      (-> (apply-fish-creation index state)
                                  (update index (constantly carry-over)))
                  :carry-over (get state index)})
               {:carry-over 0
                :state      state}
               (range 8 -1 -1))
       (:state)))

(defn lantern-state [fish-states]
  (reduce (fn [state f-state]
            (update state f-state inc))
          (vec (repeat 9 0)) fish-states))

(defn part-one [puzzle-input days]
  (let [state (lantern-state puzzle-input)]
    (apply + (reduce (fn [s _] (update-state s)) state (range days)))))


(comment
  ;; day1
  (part-one puzzle-input 80)
  ;; day2
  (part-one puzzle-input 256))

misha 2021-12-06T06:27:15.313Z

@dromar56

(take (inc 256))
     last
->
(drop 256)
(first)

AC 2021-12-06T06:42:06.313200Z

sigh.. I wasted way too much time reading about exponential growth functions for part 2 before the "aha" moment came.

peterc 2021-12-06T07:41:43.314Z

I wish I implemented @dromar56 solution for speed, simple and gets it done

❤️ 1
nbardiuk 2021-12-06T08:10:47.315200Z

I was very happy to find/remember that clojure has memoize, otherwise I would waste another hour caching intermediate results https://github.com/nbardiuk/adventofcode/blob/master/2021/src/day06.clj

1
👏 3
4
Antonio Bibiano 2021-12-06T08:40:33.316Z

this is my solution for today

Antonio Bibiano 2021-12-06T08:40:43.316200Z

(def small "3,4,3,1,2")
(def large (slurp "/home/antbbn/aoc_2021/input_6.txt"))

(defn parse [input]
  (as-> input _
    (re-seq #"\d+" _)
    (map #(Integer/parseInt %) _)
    (frequencies _)
    (mapv #(get _ % 0) (range 9))))

(defn step [fishes]
  (conj (update (subvec fishes 1) 6 + (first fishes))
        (first fishes)))


(reduce + (nth (iterate step (parse small)) 80))
(reduce + (nth (iterate step (parse large)) 80))
(reduce + (nth (iterate step (parse small)) 256))
(reduce + (nth (iterate step (parse large)) 256))

👍 5
👍🏻 1
👏 2
👌 2
😮 1
🤩 2
Antonio Bibiano 2021-12-06T08:41:30.316400Z

was pretty cool to keep thinking of a way to avoid creating the list of fishes 🙂

2021-12-06T10:58:14.321700Z

Great that 1.11.0-alpha3 came right before AoC 🙂 I've been heavily using the new core functions, parse-long , update-keys , update-vals etc. My solution for today: https://github.com/rap1ds/advent-of-code-2021/blob/main/src/day6.clj

1
gabo 2021-12-06T11:44:58.327Z

This day is a nice little one https://gist.github.com/galuque/25e5adf15976ea36023cf69af43c7310

(ns io.github.galuque.aoc-2021.day-6
  (:require [ :as io]
            [clojure.string :as str]))

(def input (->> "day_6/input.txt"
                io/resource
                slurp))

(def sample-input "3,4,3,1,2")

(def init-ages (into [] (repeat 9 0)))

(defn ages-vec [coll [v f]]
  (update coll v + f))

(defn parse-input [input]
  (->> (str/split (str/trim input) #",")
       (map parse-long)
       frequencies
       (reduce ages-vec init-ages)))

(defn day-passed [ages]
  (reduce conj [(subvec ages 1 7)
                (+ (nth ages 7) (nth ages 0))
                (nth ages 8)
                (nth ages 0)]))

(defn count-fish [days ages]
  (->> ages
       (iterate day-passed)
       (take (inc days))
       last
       (apply +)))

(def part-1 (partial count-fish 80))

(comment 
  
  (part-1 (parse-input sample-input))
  ;; => 5934
  
  (part-1 (parse-input input))
  ;; => 374994
  )

(def part-2 (partial count-fish 256))

(comment

  (part-2 (parse-input sample-input))
  ;; => 26984457539

  (part-2 (parse-input input))
  ;; => 1686252324092

  )

genmeblog 2021-12-06T11:58:04.327600Z

and my solution... I don't know why it's so obfuscated :/, very close to @nbardiuk. Also love @antbbn step function. So clever! https://github.com/genmeblog/advent-of-code/blob/master/src/advent_of_code_2021/day06.clj

Aleks 2021-12-06T12:33:45.328900Z

Almost forgot about memoize https://github.com/zelark/AoC-2021/blob/main/src/zelark/aoc_2021/day_06.clj

Aleks 2021-12-06T12:43:29.330Z

@https://app.slack.com/team/U01HY37QQUA, I am amazed by your solution 🤩

❤️ 1
➕ 1
Joe 2021-12-06T13:04:36.332800Z

Another fun one, but I'm sure there's a more elegant to implement the method I tried in part 2: https://github.com/RedPenguin101/aoc2021/blob/main/clojure/src/aoc2021/day06.clj https://github.com/RedPenguin101/aoc2021/blob/main/day06.md

2021-12-06T13:10:53.333800Z

My solution: https://github.com/green-coder/advent-of-code/blob/master/src/aoc_2021/day_6.clj I used the new update-keys function, yeah ^_^ 🎉

👍 1
tschady 2021-12-06T13:15:30.334400Z

i admit to stealing @antbbn’s init-state function, I originally used reduce-kv . my next-gen was original https://github.com/tschady/advent-of-code/blob/main/src/aoc/2021/d06.clj

👍 1
tschady 2021-12-06T13:18:09.334700Z

checkout my coworkers thinking: > This problem can also be done as a matrix multiplication of an “evolution” matrix `M`: > { > {0, 1, 0, 0, 0, 0, 0, 0, 0}, > {0, 0, 1, 0, 0, 0, 0, 0, 0}, > {0, 0, 0, 1, 0, 0, 0, 0, 0}, > {0, 0, 0, 0, 1, 0, 0, 0, 0}, > {0, 0, 0, 0, 0, 1, 0, 0, 0}, > {0, 0, 0, 0, 0, 0, 1, 0, 0}, > {1, 0, 0, 0, 0, 0, 0, 1, 0}, > {0, 0, 0, 0, 0, 0, 0, 0, 1}, > {1, 0, 0, 0, 0, 0, 0, 0, 0}, > } > times a vector `v[0..8]` where `v[i]` indicates how many fish with internal timer value `i`. Then the problem reduces to computing `M⁸⁰⋅v` then summing the elements in the resulting vector. > > That technique is especially interesting if the number of days is super large, because you can raise a matrix to an arbitrary power much more quickly than actually multiplying that many times.

🤯 7
❤️ 5
✔️ 1
👌 2
Antonio Bibiano 2021-12-06T13:23:27.335200Z

@tws very nice the next-gen with the thread first i also thought about using a let statement to avoid calling first two times, do you think that's better practice?

tschady 2021-12-06T13:25:43.335500Z

whenever it helps describe the domain i do it.

Antonio Bibiano 2021-12-06T13:32:54.336200Z

ah yeah i'll keep that in mind

Antonio Bibiano 2021-12-06T13:34:25.336400Z

@tsulej but with your solution you can now make a nice animation of fishes slowly gestating and giving birth to new fishes :D

🐟 2
genmeblog 2021-12-06T13:38:49.336600Z

not enough pixels 🙂

2021-12-06T14:43:52.341200Z

I initially started with just trying to add to vectors... THis worked for part1 and failed miserably for part 2

(defn puzzle-input []
  (->> (slurp "puzzle-inputs/2021/day6")
       (u/str-split #",")
       (mapv #(Integer/parseInt %))
       (frequencies)
       #_(into (sorted-map))))

(defn reset [idx-to-rest xs]
  (vec (reduce (fn [acc i] (assoc acc i 6)) xs idx-to-rest)))

(defn spawn [to-spawn xs]
  (vec (concat xs (repeat to-spawn 8))))

(loop [input (puzzle-input), day 0]
  (prn day)
  (if (= day 256)
    (count input)
    (let [to-spawn (keep-indexed #(when (zero? %2) %1) input)
          updated (->> input 
                       (mapv dec)
                       (reset to-spawn)
                       (spawn (count to-spawn)))]
      (recur updated (inc day)))))
I then tried to use transients, but I couldn't get this to work at all. I kept getting type mismatches between lazy-seq and transients. I couldn't understand why, so gave up with this. I need to learn more about transients. Then I realised I could just keep track of the number of fishes at each life stage in a hash-map like others here have done.
(loop [stages (puzzle-input)
       days 0]
  (if (= days 256)
    (->> stages
         vals
         (reduce +))    
    (recur {0 (stages 1 0)
            1 (stages 2 0)
            2 (stages 3 0)
            3 (stages 4 0)
            4 (stages 5 0)
            5 (stages 6 0)
            6 (+ (stages 7 0) (stages 0 0))
            7 (stages 8 0)
            8 (stages 0 0)}
           (inc days))))
Couldn't figure out a way to do this with reduce-kv , so just did it the long winded way.

2021-12-06T14:44:46.342300Z

ALthough seeing the final result, I doubt transients would even help much, the vector would be HUGE

2021-12-06T15:21:37.343300Z

Got it with iterate too, but I don't like this solution as much

(defn update-fish [stages]
  (->> (map (fn [n]
                  (case n
                    8 {8 (stages 0 0)}
                    6 {6 (+ (stages 7 0) (stages 0 0))}
                    {n (stages (inc n) 0)}))
                (range 0 9))
       (into {})))

(->> (puzzle-input) 
     (iterate update-fish)
     (take 257)
     (last)
     (vals)
     (reduce +))
I don't like the take 257, is their a way to do this where you "overwrite" the stages each time and dont end up with a big list. Like a function that keeps a function f over and over n times and feeds its output back in as its input, without making the collection?

Callum Oakley 2021-12-06T15:27:22.343700Z

@qmstuart you could (apply comp (repeat 256 f)), but since take is lazy, you’re not actually retaining the full list in memory anyway

👍 1
mchampine 2021-12-06T16:10:02.344300Z

👏 3
euccastro 2021-12-06T16:51:43.346800Z

there's probably a more lucid shortcut using math, but I got this working quickly and I didn't need to change it for part 2 or clean it up later: https://github.com/euccastro/advent-of-code-2021/blob/main/day6.clj

pavlosmelissinos 2021-12-06T16:57:24.347600Z

Has anyone found a solution that runs in O(1)? Or at least in linear time to the number of fish states? I feel like it should be possible.

Aleks 2021-12-06T16:58:11.347900Z

Just played a bit with @mchampine solution 🤩

(defn step [m]
  (-> (mapv m [1 2 3 4 5 6 7 8 0])
      (update 6 (fnil + 0 0) (m 0))))

(defn fish [input n]
  (let [fish-seq (iterate step (frequencies input))]
    (reduce + (nth fish-seq n))))

👏 5
Callum Oakley 2021-12-06T16:59:04.348200Z

@pavlos the matrix solution @tws posted above is O(log(n)) with repeated squaring to do the exponentiation

👍 2
euccastro 2021-12-06T17:04:46.348900Z

I think my solution is missing a declare but it worked in the repl because I somehow defined the name to something nonrecursive first? 😛

Sam Adams 2021-12-06T17:27:43.349700Z

I like the memoized per-fish solutions, totally different than how I thought about it. My rotating-array solution: https://samadams.dev/2021/12/06/advent-of-code-day-6.html

2021-12-06T18:07:36.350700Z

gave up on trying to do it mathematically and just ran the simulation instead. before the clean up:

(defn one [population]
  (reduce-kv (fn [m k _v]
               (assoc m k
                      (cond (= 6 k) (+ (get population 7)
                                       (get population 0))
                            (= 8 k) (get population 0)
                            :else   (get population (inc k)))))
             {}
             population))

(defn init [m] (merge (zipmap (range 9) (repeat 0)) m))

(reduce + (vals (last (take 257 (iterate one (init (frequencies input)))))))

Miķelis Vindavs 2021-12-06T18:16:39.351200Z

@qmstuart not that it matters too much in this case but (->> (drop 256) (first)) should be faster than (->> (take 257) (last)) because last is always O(n)

👍 3
Miķelis Vindavs 2021-12-06T18:17:09.351500Z

for this use case I have a small helper function which avoids seqs altogether

(defn nth-iter [^long n f x]
  (if (pos? n)
    (recur (dec n) f (f x))
    x))

Miķelis Vindavs 2021-12-06T18:18:27.351700Z

so (->> seed (iterate f) (drop n) (first)) becomes (->> seed (nth-iter n f))

mchampine 2021-12-06T18:40:58.352200Z

@zelark > “(mapv m [1 2 3 4 5 6 7 8 0])” Awesome, love it! So clever that your version of the step function works with the output of frequencies AND a vector (because in this case they’re both functions from number to value - and that number can just as well be an index). Very concise code!

🙌 1
borkdude 2021-12-06T22:01:54.356500Z

Better late than never. https://gist.github.com/borkdude/b5cf0e9d2d8ab7c678d88e27a3357b33#file-aoc21_06-clj

1
borkdude 2021-12-06T22:04:04.356700Z

Both run under a millisecond with babashka.

2021-12-06T23:43:22.359Z

https://github.com/kfirmanty/advent-of-code-2021/blob/main/src/day6.clj starting day6 rather late into a night wasn't good idea as it seems this is the first tricky one advent of code this year wasted a bit time trying to devise a formula of growth of single fish to be then used for others but then somehow clicked that all the fishes behave the same based on their state and not position on the array so it doesn't need to be preserved in whole

🙌 1
euccastro 2021-12-07T00:20:22.359600Z

as usual I got envious of your frequencies-based solutions, so I did two things: • make a note to ask myself "what would frequencies do?" in every AoC day going forward, and • try and make a frequencies-based solution that rotates only some reference index, not the frequencies data structure itself

euccastro 2021-12-07T00:20:29.359800Z

so here is it: https://github.com/euccastro/advent-of-code-2021/blob/main/day6b.clj

euccastro 2021-12-07T00:21:08.360200Z

it borrows liberally from various of the other solutions

kevinc 2021-12-07T04:55:35.365700Z

pleased with how brief it came out 🙂 https://github.com/kconner/advent-of-code/blob/master/2021/6b.clj

👏 5
Benjamin 2021-12-06T09:50:15.318200Z

can somebody give a tip for day6 part2

🙌 1
Benjamin 2021-12-06T09:52:07.318300Z

like how to make an efficient algorithm

Benjamin 2021-12-06T10:02:04.318900Z

now I'm trying to think about each fish in isolation, instead of the list of fish. I guess there should be a function f(age,t) -> fish count

Björn Ebbinghaus 2021-12-06T10:14:48.319200Z

The tip is: frequencies

➕ 5
peterc 2021-12-06T10:36:31.319400Z

there's an iterative approach that does not require brute-force

Miķelis Vindavs 2021-12-06T10:59:41.322700Z

The order of the fish doesn’t matter, and there’s only ever 9 types of fish. Which means you don’t have to store all of them individually

☝️ 1
genmeblog 2021-12-06T11:11:54.325300Z

my hint: cache is your friend

Benjamin 2021-12-06T12:33:26.328700Z

did something with frequencies that is around 5ms 🙂

Björn Ebbinghaus 2021-12-06T13:09:32.333300Z

@benjamin.schwerdtner Nice! Here is my solution, if you are interested: https://github.com/MrEbbinghaus/advent-of-code/blob/master/2021/day06.clj#L26

Benjamin 2021-12-06T13:27:13.335700Z

sick @mroerni

kevinc 2021-12-07T04:52:58.365Z

My tip: Don't follow the explanation too literally. And, you can eliminate unnecessary steps if you start with the end result you need to calculate, and build backward toward the input. https://github.com/kconner/advent-of-code/blob/master/2021/6b.clj

hanyuone 2021-12-06T09:59:41.318800Z

Antonio Bibiano 2021-12-06T10:49:45.319700Z

maybe a nested map

(defn bingo-mark [board draw]
  (map (fn [row]
         (map
          (fn [[col _]]
            [col (= col draw)])
          row))
       board)) 

Miķelis Vindavs 2021-12-06T10:56:05.320200Z

Nested vectors are usually not very convenient, perhaps try using a hashmap where the key is a tuple of coords [x y] and the value is the count.

✔️ 2
peterc 2021-12-06T10:56:51.320400Z

If you want to use a nested vector, something like this will work:

(defn board-coords [n]
  (for [x (range n)
        y (range n)]
    [x y]))

(def coords (board-coords 5))

(defn bingo-mark [board mark]
  (->> coords
       (filter #(= mark (get-in board (vec %))))
       (reduce #(assoc-in %1 %2 true) board)))

💯 2
Miķelis Vindavs 2021-12-06T10:57:23.320700Z

But you could do

(for [row (range SIZE)
      col (range SIZE)]
   ...)
edit: oh, jinx

peterc 2021-12-06T10:57:46.321Z

The point is that you dont want to rebuild the vector(s) each time (like in the nested map solution)

peterc 2021-12-06T10:59:37.322500Z

But yeah, a hash-map is much more convenient!

➕ 1
hanyuone 2021-12-06T11:17:51.325500Z

i ended up doing this instead:

(defn find-draw
  [board draw]
  (let [coords (for [x (range SIZE) y (range SIZE)] [x y])]
    (->> coords
      (filter (fn [[x y]] (= (get-in board [x y 0]) draw)))
      first)))

(defn bingo-mark
  "Given a Bingo board and a drawn number, 'mark' that number on the board."
  [board draw]
  (if-let [[row col] (find-draw board draw)]
    (assoc-in board [row col 1] true)
    board))

hanyuone 2021-12-06T11:22:02.325700Z

seems a bit more idiomatic - not sure about a hashmap, wouldn't it make more sense to have a 1d vector in that case then?

Miķelis Vindavs 2021-12-06T11:26:28.325900Z

In this case it probably doesn’t matter too much, but in other years on tasks where the “board” can grow in all directions and is usually quite sparse (e.g. the Conway’s game of life type puzzles) a hashmap is much much faster. But probably a matter of taste 🙂

hanyuone 2021-12-06T11:27:11.326200Z

i see, thank you!

tschady 2021-12-06T13:56:14.337700Z

for updating nested data structures, i suggest you use a purpose built library. my fav is https://github.com/redplanetlabs/specter . How much time it saves you helps get over the non-clojurey feel: https://github.com/tschady/advent-of-code/blob/main/src/aoc/2021/d04.clj#L17-L20

tschady 2021-12-06T13:57:02.338Z

that navigator says ALL cards, ALL rows, ALL columns, then find our number.

2021-12-06T10:58:44.322300Z

OH dear, my sln for part 2 is running very, very slowly. It was fastish up to day 110 ish, then started really creeping along at day 120ish, now its ticking up a day every 10 seconds or so...

Miķelis Vindavs 2021-12-06T11:01:29.323300Z

Would you like some hints on how to speed it up?

2021-12-06T11:02:52.323600Z

Not yet, I have an idea of using a hashmap to store the number of fish at each state, e.g. start would be

{0 0
 1 1
 2 1
 3 2
 4 1
 5 0
 6 0
 7 0
 8 0}
THen update each val by dec, then spawn just can update the number of 8s to the number of zeros each time. Is this dumb?

Miķelis Vindavs 2021-12-06T11:03:13.323800Z

I’d say you’re on the right track! 🙂

2021-12-06T11:03:36.324Z

can you iterate through a hash map to update each val ?

2021-12-06T11:03:42.324200Z

Is their a core function to do this?

Miķelis Vindavs 2021-12-06T11:04:17.324400Z

you can create a new map

(->> old-map
     (map (fn [[old-key old-val]]
            [new-key new-val]))
     (into {}))

2021-12-06T11:04:31.324600Z

thanks!

Miķelis Vindavs 2021-12-06T11:05:31.324900Z

or if you want a bit more control over the result, you can use reduce

(->> old-map
     (reduce (fn [new-map [old-key old-val]]
                (assoc new-map ...))
             {})

Björn Ebbinghaus 2021-12-06T13:00:55.332400Z

@qmstuart You can map over a map. Tip:

[3 42] -> {2 42}
[0 13] -> {6 13, 8 13}

tschady 2021-12-06T14:02:04.339400Z

see reduce-kv

borkdude 2021-12-06T20:10:20.353300Z

See solution is a few milliseconds, even in bb. https://twitter.com/mknoszlig/status/1467768132521627648

borkdude 2021-12-06T20:11:12.353700Z

I was stuck in a foolish solution of exploding the list like it was graphically presented in the puzzle :)

sebastian 2021-12-06T21:52:17.356Z

I am renaming the keys in the hashmap. does part 2 in 1.5ms

lread 2021-12-06T23:07:52.358100Z

Eek, the problem description drew me into a brute force solution. But I guess that was its fiendish aim! I was actually mired until I got the frequencies tip here. Bah!

borkdude 2021-12-06T23:11:01.358300Z

Same

borkdude 2021-12-06T23:11:33.358500Z

But now both answers run under 1ms in bb :)

2
🚀 1
rmprescott 2021-12-07T01:00:36.360900Z

What editor is this? I'm guessing it's displaying "f(" for "#("

2021-12-07T01:04:13.361100Z

You can do this in vscode, with prettify symbols plugin. Can setup your own substitutions.

sebastian 2021-12-07T07:20:54.372300Z

spacemacs with a fancy symbol plugin. an you are right.

nbardiuk 2021-12-06T14:00:06.339100Z

The exponential growth. Brightness represents internal timer of a fish, new fishes are added to the tail of spiral

1
🐡 2
🐬 1
🐟 1
🍥 1
🎣 1
2
🤩 6
7
tschady 2021-12-06T14:03:20.339700Z

source? love to see it.

nbardiuk 2021-12-06T14:05:40.339900Z

it's quilclojure2d code with some magic numbers https://github.com/nbardiuk/adventofcode/blob/master/2021/dev/sketches/day06.clj

2
Benjamin 2021-12-06T14:14:56.340500Z

pogg

genmeblog 2021-12-06T20:37:27.354200Z

Glad you've reached for Clojure2d @nbardiuk Yay!

nbardiuk 2021-12-06T20:41:00.354500Z

I am very glad I've migrated to Clojure2d, I could not setup quil to run fast on my machine and Clojure2d just worked. The performance difference was very big. Thank you for the library

1