Fork me on GitHub

sick. I learned about the args to partition from this

🎉 1

Done with day3 but it’s too ugly to post before some cleanup. :)

🙌 1

and the part two has a pitfall ^_^


hah did everyone copy and paste their code for part2?


@U067R559Q what pitfall did you notice?


@U06B54Y95 actually it wasn’t a pitfall, I just didn’t read the description carefully.


@U067R559Q Yeah, when the problem text is that long and involved, it’s so easy to get some little detail wrong. So to me it had several potential pitfalls there! Not to mention the usual risks of off-by-ones and edge cases. I couldn’t use max-key vs min-key in part 2 because they’re not complements like > and <= are. @U1NLKFVC4 I copy/pasted on my first pass, but couldn’t stand to look at it. 🙂


🧵 Day 3 answers thread: post your answers here

Vincent Cantin06:12:30

Ugly code ...

(ns aoc2021.day3
  (:require [ :as io]
            [clojure.string :as str]
            [clojure.edn :as edn]))

(defn parse-binary [s]
  (edn/read-string (str "2r" (str/triml s))))

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

;; Part 1
(let [s (for [index (-> input first count range)
              :let [{zeroes \0
                     ones   \1} (->> input
                                     (map (fn [bin] (nth bin index)))
          (if (< zeroes ones)
            [\1 \0]
            [\0 \1]))
      gamma (->> s
                 (map first)
      epsilon (->> s
                   (map second)
  (* gamma epsilon))

;; Part 2
(defn most-common-bit [coll index]
  (let [{zeroes \0
         ones   \1} (->> coll
                         (map (fn [x] (nth x index)))
    (if (<= zeroes ones) \1 \0)))

(defn least-common-bit [coll index]
  (let [{zeroes \0
         ones   \1} (->> coll
                         (map (fn [x] (nth x index)))
    (if (<= zeroes ones) \0 \1)))

(let [[[oxygen] _] (->> [input 0]
                        (iterate (fn [[s index]]
                                   (let [bit (most-common-bit s index)]
                                     [(filter (fn [n]
                                                (= (nth n index) bit))
                                      (inc index)])))
                        (drop-while (comp not #{1} count first))
      [[co2] _] (->> [input 0]
                     (iterate (fn [[s index]]
                                (let [bit (least-common-bit s index)]
                                  [(filter (fn [n]
                                             (= (nth n index) bit))
                                   (inc index)])))
                     (drop-while (comp not #{1} count first))
  (* (parse-binary oxygen)
     (parse-binary co2)))

👏 1

(defn read-data []
  (->> (slurp "data/day3.txt")
       (map (fn [s]
              (->> (seq s)
                   (map #(- (int %) (int \0))))))))

(defn to-decimal [ns]
  (read-string (apply str "2r" ns)))

(defn part1 []
  (let [data   (read-data)
        n      (count data)
        counts (apply map + data)
        v1     (map #(if (< % ( * n 0.5)) 1 0) counts )
        v2     (map #(if (< % ( * n 0.5)) 0 1) counts )]
    (* (to-decimal v1)) (to-decimal v2)))

(defn bit-to-keep-o2 [ones zeroes]
   (= ones zeroes) 1
   (> ones zeroes) 1
   :else           0))

(defn bit-to-keep-co2 [ones zeroes]
   (= ones zeroes) 0
   (< ones zeroes) 1
   :else           0))

(defn part2-rating [f ratings]
  (loop [rs ratings
         n  0]
    (if (= 1 (count rs))
      (to-decimal (first rs))
      (let [counts   (->> rs
                        (map #(nth % n))
                        (reduce +))
            keep-bit (f counts (- (count rs) counts))]
        (recur (filter #(= (nth  % n) keep-bit ) rs)
               (inc n))))))

(defn part2 []
  (let [data (read-data)]
    (* (part2-rating bit-to-keep-o2 data)
       (part2-rating bit-to-keep-co2 data))))
Any improvements welcome.


Btw, I found out that to get nice code coloring, start by clicking the lightning bolt in the lower left of the composition box, then select “create a text snippet”. It supposedly has language auto-detect but I haven’t tried it. It also doesn’t color it right away - takes a minute or so after you post!

💯 1
Antonio Bibiano09:12:01

this is my solution

; part 1
(defn rate [key-compare input]
    (->> input
         (apply map vector)
         (map frequencies)
         (map #(key (apply key-compare val %)))

(def gamma (rate max-key large))
(def epsilon (rate min-key large))

(* (Integer/parseInt gamma 2) (Integer/parseInt epsilon 2))

; part 2

(defn rate [comparison input]
  (loop [pos 0 
         input input]
    (if (= 1 (count input))
      (first input)
      (let [{zeros \0 ones \1} (group-by #(nth % pos) input)]
        (if (comparison (count zeros) (count ones))
          (recur (inc pos) ones)
          (recur (inc pos) zeros))))))

(def oxygen (rate <= large))

(def co2 (rate > large))

(* (Integer/parseInt oxygen 2) (Integer/parseInt co2 2))

👍 3
Callum Oakley12:12:53

surprised I don’t see anybody above using bit-test bit-set etc!

👍 2
nice 1

I used bit-not and bit-and in part 1

☝️ 2
Joe12:12:00 Haven't read any other solutions yet, but whenever there's one of these binary problems I always feel like I've missed a trick by not actually using any bitwise operators. Like that plane seating problem, I forget which year.

👏 3
R.A. Porter14:12:11

Even if I remove all the comment blocks, I'm still pretty long today. So a link instead - Like @U1EP3BZ3Q, I also used bit-and and bit-not in part 1. Both parts today, in fact, have that inverse, mirror symmetry. I used filter/`remove` to get to the O2/CO2 values.

👍 1

I have the feeling this could be a one-or-two liner so it's probably more verbose than necessary: But it runs in insignificant time with bb (60ms including startup).

👍 2

Here’s mine. I used core.matrix and didn’t end up using it much. I have the feeling there’s a function I’m missing that would’ve made things easier.


Looking forward to reading others’ solutions later to compare, but gotta do some real work first 🙂


I'll paste my solution here. I feel like I made it too complicated. I'm still too reliant on loops - Feedback is very welcome

tschady16:12:14 after a vain search for bitwise brilliance (deeper than bit-test and bit-and-not I just went with strings. I like my part1 (no temporary variables), but p2 still needs work.


My unreadable solution of part 2, making each bitstring a function that pushes a counter up or down and adds itself to a set, the sign of counter then picks next set of bitstrings to inspect.

(defn pick [l test]
  (let [make-fn (fn [idx f]
                  (fn [[n m]] [(f n) (update m f (fnil conj #{}) idx)]))
        to-weird-format (fn [a]
                          (map-indexed #(mapv (partial make-fn %1) (mapv {\1 inc \0 dec} %2)) a))
        pos-or-neg (fn [xs idx]
                     (reduce #(%2 %1) [0 {}] (map #(nth % idx) xs)))
        l2 (to-weird-format l)]
    (loop [candidates l2,  bit-index 0]
      (let [[n m] (pos-or-neg candidates bit-index)
            next-indices (if (test n) (m inc) (m dec))]
        (if (< 1 (count next-indices))
          (recur (reduce #(conj %1 (nth l2 %2)) [] next-indices)
                 (inc bit-index))
          (nth l (first next-indices)))))))

(defn sol [input-string]
  (apply * (map #(Integer/parseInt % 2)
                (map #(pick (str/split-lines input-string) %) [(partial <= 0) (partial > 0)]))))


a little late to the party but I finally found time to do it today!

👏 1
karol22:12:48 - here is mine. somehow managed to reuse most of the extract logic from part 1. But accessing things positionally in Clojure always feels kind of dirty to me 😄

Andrew Byala22:12:28

I loved the day 3 puzzle. Here’s a link to my blog about my solution and its many refactorings, with a link to the source at the top. I really enjoyed making composable functions, like most-common-bit, least-common-bit, one-pass-bit-check, and multi-pass-bit-check that all built up my gamma, epsilon, O2, and CO2 calculations. Very fun day!

👏 1

Oh man, part 2 ate me alive. I don't know why I struggled so much but that's kind of sad this early in the game. Haven't had a chance to process and clean it up. I just can't figure out how to think in terms of reduce.


I'm looking forward to reading these answers and figuring out how to approach this in a more simple manner. Things got ugly quick! lol


my solution: I have the feeling that I left some mathematical insight on the table, but this is much cleaner than the first thing that I got to work


oh yeah, frequencies would have helped in part one


TIL (or was reminded of) %& in function literals


@U067R559Q great one! part 1 esp. blew my mind. this is maybe evil, but (sort-by (juxt second first)) could be (sort-by reverse) in this case?


Day 3, part 1 (I'll post part 2 when I'm not utterly ashamed of it 😆)

(defn get-key-with-highest-val [m]
  (if (>= (m \1 0) (m \0 0))

(defn flip-bits [s]
  (map (fn [i] ({\1 \0 \0 \1} i)) s))

(defn binary-str->int [s]
  (Integer/parseInt s 2))

(defn char-seq->str [cs]
  (->> (map str cs)
       (str/join "")))

;; part 1
(let [input (->> (f/read-all-lines-and-parse "puzzle-inputs/2021/day3" parser)
                 (apply map vector)
                 (map frequencies))
      γ-bin (->> input
                 (map get-key-with-highest-val))
      ε     (->> γ-bin
      γ     (->> γ-bin
  (* ε γ))


OK, I'm still ashamed of it but :man-gesturing-no: it's 2am here...

(defn has-v-at-idx [idx v xs]
  (= v (nth xs idx)))

(defn parser [l]
  (map identity l))

(defn reduce-binary-over-f [input f]
  (loop [input input, idx 0]
    (if (= 1 (count input))
      (-> (first input)
      (let [bits-at-idx (nth (apply map vector input) idx)
            most-common (->> (frequencies bits-at-idx)
        (recur (filter (partial has-v-at-idx idx most-common) input)
               (inc idx))))))

(let [input (f/read-all-lines-and-parse "puzzle-inputs/2021/day3" parser)
      oxygen-generator (reduce-binary-over-f input identity)
      oxygen-scrubber (reduce-binary-over-f input #({\1 \0 \0 \1} %))]
  (* oxygen-generator oxygen-scrubber))


you don't need to wrap the map in a function literal; it's already a function as is


What do you mean ?


#({\1 \0 \0 \1} %) could be just {\1 \0 \0 \1}


that part 2 solution looks fine to me, nothing to be ashamed of


ah okay! I see, thank you!


kudos on the greek chars for part 1! 😄


Basically the same as 3-4 others I saw.


I’m kinda convinced there’s no pretty way to do Part 2.

Sam Adams04:12:11

Maybe tomorrow I’ll benchmark a frequencies -based solution too

Sam Adams04:12:39

@U067R559Q I might be misreading but I think your gamma and epsilon ratings are flip-flopped 🙂


@U016C0EGHUN you’re right, I was polishing the code and mixed them up. But yesterday evening I refactored the code and there is no even names, just one single thread :star-struck:

👏 1

@U65FN6WL9 revers won’t help here

; Execution error (IllegalArgumentException) at (REPL:47).
; Don't know how to create ISeq from: clojure.core$reverse


@U067R559Q it seems you have the arguments swapped in your experiment? anyway, you're right it doesn't work, but for a reason that is surprising to me: clojure doesn't know how to compare lists (while it can compare vectors):

(sort '[(3 4) (1 2)])

clojure.lang.PersistentList cannot be cast to java.lang.Comparable

(sort-by (comp vec reverse) {:a 3 :b 1})
;; => ([:b 1] [:a 3])


of course once you have to (comp vec reverse) it stops being that much prettier


Yes, the actual exception is “clojure.lang.PersistentList cannot be cast to java.lang.Comparable”

👍 1

> I loved the day 3 puzzle.  Here’s a link to my blog about my solution > and its many refactorings, with a link to the source at the top.  I > really enjoyed making composable functions, like most-common-bit, least-common-bit, one-pass-bit-check, and multi-pass-bit-check that all built up my gamma, epsilon, O2, and CO2 calculations.  Very fun day! @U01HHBJ56J1 this was a great read and a nice solution, thanks!


oh, I can do str/join instead of apply str. nice!


looking into the details of clojure list/vector comparison I realized that I could shorten the following thread-last sequence in my solution:

(group-by #(nth % idx))
                (sort-by key)
                (map second)
                (sort-by count)
(where I felt clever for relying on the stability of sort-by so the order in the first sort would be a tie-breaker in the second one) to just
(group-by #(nth % idx))
because vectors will first be compared by length and then lexicographically on ties, which is exactly what I want

👏 1

proud of part 2 - now I can read your solutions and go aha 7 times 😛

Cora (she/her)03:12:03

again a very boring solution by me


Why do you @U04V15CAJ do this in your Day 3 solution?

(if more-ones? 
  (conj bits 1) (conj bits 0))
I don't understand why you are shifting the bits to the left with another 0 or 1 on it...


eh, lemme check


honest answer: I don't remember, day 3 is already too long ago ;)


alright, never mind. thanks anyway


this is not bit shifting per se, it is gradually building up the answer. for every position, determine if that column has more ones or zeros, and append accordingly either 1 or 0 to the answer

👍 1

I'm in that horrible situation where my code runs on the test data for day 3, and produces correct answer, and runs on the full data without crashing, but the answer is wrong... gah


Maybe you’ve missed this “If `0` and `1` are equally common, keep values with a `1` in the position being considered.” and this “If `0` and `1` are equally common, keep values with a `0` in the position being considered.”


hmmm. That will be it, I assumed since the page doesnt explain what to do in the event of a tie that its set to never occur, thanks!


It explains (for part 2): "If `0` and `1` are equally common, keep values with a `1` in the position being considered."


ah, im on part 1!


ah! do you have a tie in your dataset? (I don't have, it may happen in part 2)


I don't, as even putting that condition I'm getting back same answer


epsilon is always just gamma with the bits flipped right?


omg, i was debugging and had a rogue (take 3) in! argh! That I forgot to take out once I was using the real data. IT works and I have part 1 completed.

🎉 7

wall of text today 😄

Ben Sless13:12:18

I didn't do part 2 only due to too long didn't read

😆 4
Noah Bogart14:12:24

meme from reddit:

🧵 1

At least you wouldn't have the leaderboard filled in 2 minutes!

😂 1

TIL about: seems very useful for AOC

Ben Sless16:12:49

if your ints are sequential, you can just use a vector

Jeff Evans16:12:51

does anyone else find themselves more interested in refactoring part1 so that it can be solved using part2 code, rather than going for sheer speed?

Mario C.17:12:04

Yes, I am actually focusing on this, this year. I try to write part 1 in such a way that it can be re-used or easily refactored for part2. The less effort required for part 2 means I did a decent job with part 1. Major refactor means I may have made it to specific.


Yeah. I find no joy in just doing the quickest thing, it's far more fun to have unit tests and nice abstractions. Also fun to properly benchmark multiple approaches etc.


This is my first year tracking my progress on a private leaderboard, so I hacked up a pretty gross solution to part 1. After I read part 2, it didn't feel right to do the same. I did refactor part 1 and then came back and finished part 2 this morning. I'm pretty sure I'll end up refactoring it a couple more times.


Rich always solves part 2 while hammoking on part 1

Jeff Evans17:12:27

awesome. sounds like I am in good company, then 😁


I mean, I can't be the only person with GitHub Actions running my tests and generating Marginalia docs, right? Right?

😮 1

All in all, I think speed is rather useless.. Instead, I want to do it badly first, so I can build buckets to put the “how to do it right” knowledge into..

Antonio Bibiano19:12:28

I like to get to a solution as fast as i can and then spend the rest of the day thinking about better ways, that allows me to learn a lot from other people solution throughout the day with no guilt :D

👏 4

+1 to get solution as quickly as you can and then if you find it interesting enough optimise it 😄


Stats show a dip today on part2, almost half of people decided to skip it


I can only assume it was the big wall of text. I know I couldnt process that while I was half asleep last night.


Yeah, I waisted time debugging code while the problem was in my reading skils


I managed to get part 1 done during a meeting today at work, just haven't had time yet today to even look at part 2


And I'm going out tonight, so will have to do part 2 tomorrow before I do day 4


That a good point, tomorrow is a weekend, people will have plenty of time to catch up


for me at least, part 2 was a considerable step up in difficulty from part 1


just finished day3, it's 1:46 am here and I'm pretty drunk. Very not proud of my code