Fork me on GitHub

I spent a lot of time counting all the trees passed vertically for when the sled goes 1 right, 2 down. I don’t know why I started to think I should do that when I didn’t do it horizontally… Anyway, if someone ever needs a sequence of x-deltas for a instructions like “x right, y down, forever”, here goes:

(defn dxs [dx' dy']
  (map (fn [line dx dy] 
         (* (+ line dy) dx)) 
       (repeat dx') 
       (cycle (range dy'))))


I’m a bit stuck on day 3, step 2. I get the right answer for the sample data, but too low for the real input. Not sure how I should go about debugging it. My solution looks like so, so far. (In the reply.)


(defn landing [dx line]
  (->> line
       (drop dx)

(defn count-trees [[dx dy] woods]
  (->> woods
       (take-nth dy)
       (map landing (map #(* % dx) (range))) ; is (take-nth dx (range)) better?
       (filter #{\#})

  (def input (util/fetch-input 3))
  (as-> input $
    (s/split-lines $)
    (map #(count-trees % $) [[1 2] [3 1] [5 1] [7 1] [1 2]])
    (apply * $)))


Found it! I fed it [1 2] instead of [1 1] for the first slope….


It takes about half a sec on my pretty fast MacBook.


1. replace

with (cycle) for probably x2 speedup 2. replace (cycle) with (mod dx line-len) for next x5-x10 speedup


3. replace drop, first with nth for 3rd speedup


(let [s (str/join (range 1000))]
  (time (nth s 2000))
  (time (first (drop 2000 s))))
"Elapsed time: 0.01144 msecs"
"Elapsed time: 0.264392 msecs"


Sweet! I got a x10 when replacing (seq) (repeat) (flatten) with (cycle).


> 2. replace (cycle) with (mod dx line-len) for next x5-x10 speedup Not following here.


Replacing drop, first with nth gained me x1.2-ish, but I’ll of course keep that and celebrate the speed gain, since it is WAY clearer.


nth is linear lookup. with cycle, each next line does longer and longer linear lookups





if you mod your Xs, your lookups will be:



(time (nth (cycle "abcd") 100000))
"Elapsed time: 7.717277 msecs"
=> \a

(time (nth "abcd" (mod 100000 (count "abcd"))))
"Elapsed time: 0.093352 msecs"
=> \a


Gotcha. Down to 2.5 msec now. That’s x200 in total!


This is super helpful to me. I often reach for mod quickly. This time it was much easier for me to visualize it if I actually repeated the pattern indefinitely. But I should of course have remembered/realized to translate it to mod once it worked.


I updated my input-fetcher to read the cookie from a file instead, like @plexus does in his bash script:


that second part was a lot of minutiae haha

☝️ 3

Live stream for day 4 starting soon

Vincent Cantin07:12:28

oh … I feel stupid, I did not think about that (str/split real-input #"\R\R")


yes, I know


What does #"\R\R" match on? From context it looks like a double carriage return, but is it different from #"\r\r" ? says \R is just an escaped R, which it clearly isn't in this case.


it matches "\r" "\n" "\r\n" or a number of unicode newline characters. It does not backtrack between "\r\n", so #"\R\R" will match "\r\r" but not "\r\n" (it will match "\r\n\r\n")


basically it matches everything that conceptually is a newline


Ah nice, thanks

Vincent Cantin05:12:44

I woke up too late today, bye bye leaderboard 😢


yeah i went to bed early and didn't do yesterday's problem until right before tonight's so also dropped 😞


meh... the leaderboards are really the last of my concerns 🙂


Argh. Tonight's took much longer than it should. I'm much more rusty than I thought.


Parsing and validation feels too much like real work 😆

😆 12

so true, and so boring


I was just thinking "if this doesn't work from the first try it's going to suck to debug", but luckily it did

🙂 3

80% of my part 1 solution is parsing 😞


yeah can't say day 4 was particularly interesting... just tediously converting rules into code


so far the puzzles have been surprisingly easy though... was it like this in the past? it's been two years since I participated so maybe I remember it wrong, I guess it'll get harder in a few days.


Last year, for some of the puzzles, we built up a little byte code VM and our inputs were programs, it was pretty cool


We even got to chain the programs together via input/output ports

❤️ 6

Yeah, I loved it.


Last year, the first a bit challenging puzzle for me was on day 10, then day 12.


Day 22, part 2 was pretty hard for the most of the participants.


Yes, it gets harder, and perhaps this year they toned it down a little bit, because i remember last year the were some frustrated participants.


It seems to me the past years I have done it, it gets steeper pretty fast after the first 3 or 4 days. It also tends to go back an re-factor off of your old solutions, so it’s hard to jump right in if you get behind.


last year day 3 was already challenging. If you look at stats you can find where people have dropped off. Like days 3, 7, 18


Last year became really challenging for me, but first four days were similar to this year. It was interesting last year with puzzle inputs being programs in a fictional language called intcode you fed in various ways to your own interpreter(s) that you build up over the days with various stories around it (having a robot that runs on a computer that has to perform certain tasks by feeding it input and the coordinates it spit out in return will display a word which is your answer, stuff like that). Also a puzzle that could best be solved using trigonemetry and one with fast fourier transforms. Was magical and instructive.


I'm glad someone did :rolling_on_the_floor_laughing:

Vincent Cantin08:12:51

I am happy I spent time to get familiar with it, it might be useful one day.


That was way harder than it needed to be.. 😉. I really need to learn my destructuring better..


Today is a bit boring, but I've spend some creativity on refactoring parsing rules

Ben List07:12:18

Not super happy with what I came up with today. But I'm still new to clojure

😍 6
sheepy 3

interesting that you reach to core.match for this!


always fun to see core.match! Also, if you want to destructure map keys into vars with the same name, you can use the handy :keys syntax (defn validate-passport-fields [{:keys [ecl pid eyr hcl byr iyr hgt]} ,,,)

💯 3
Ben List14:12:14

I'm a scala dev by trade so pattern matching calls to me, and great tip thanks!

moneyparrot 3

Missed my alarm, tedious puzzle luckily everything worked first try


AOC made bad job generating invalid passports 😆 you code proves that • all years fields are actually numbers • height does not have other text than number and unit of measure • documents do not have keys other than explicitly specified


(remove #(not-empty (dissoc % "hgt" "byr" "eyr" "iyr" "ecl" "hcl" "cid")))
(defn parse-int [s else]
    (Integer/parseInt s 10)
    (catch Exception e else)))

(parse-int "foo" -1)
=> -1
would fix that


over-engineering, over-engineering everywhere facepalm


I think it is super nice. I learn a lot by looking at it. One thing makes me wonder “why?” though, and that is your between? function. Is (partial between? 1920 2002) more readable to you than #(<= 1920 % 2020)?

Joe11:12:35 - clojure.spec seems tailor-made for this 🙃. No-one else seemed tempted though? Just not worth it for such a simple use case?

👍 12

I've given up on specs, but when I got a wrong answer regretted it. Spec's explain would be very useful to debug which rule invalidated an entry


I was in a hurry. Would have loved to use Malli or spec

Vincent Cantin13:12:11

I solve the puzzle using Minimallist, a library inspired by Malli and Spec.

👀 3

My solution to Day 4 with clojure.spec:

👍 3
❤️ 3

Great to see someone use spec! My solution is very boring, I’m sure parse-passport and valid-hgt? could be more terse, but it was a bit to boring to refactor.


Also, general question! Dones anyone have any examples of fancy ways of doing validation? Rule based systems?


My day 4. Not a great solution, but it worked in the end.

Narve Saetre14:12:37

I am really glad I used spec for part 1 😄 First practical use of spec for me:

(def text (slurp "adv4.input.txt"))
;(def text "ecl:gry pid:860033327 eyr:2020 hcl:#fffffd\nbyr:1937 iyr:2017 cid:147 hgt:183cm\n\niyr:2013 ecl:amb cid:350 eyr:2023 pid:028048884\nhcl:#cfa07d byr:1929\n\nhcl:#ae17e1 iyr:2013\neyr:2024\necl:brn pid:760753108 byr:1931\nhgt:179cm\n\nhcl:#cfa07d eyr:2025 pid:166559648\niyr:2011 ecl:brn hgt:59in")

(defn parse-line [l]
  (let [
        psa (string/split l #"[\s+]")
        pa (map #(string/split %1 #"\:") psa)
        ma (map (fn [p] {(first p) (second p)}) pa)
        m (apply merge ma)]

(def entries (map parse-line (string/split text #"\n\n")))

(s/def ::byr (s/and string? #(<= 1920 (bigint %1)) #(>= 2002 (bigint %1))))
(s/def ::iyr (s/and string? #(<= 2010 (bigint %1)) #(>= 2020 (bigint %1))))
(s/def ::eyr (s/and string? #(<= 2020 (bigint %1)) #(>= 2030 (bigint %1))))
(s/def ::hgt (s/and string? (s/or ::in #(re-matches #"\d{2}in" %1) ::cm #(re-matches #"\d{3}cm" %1))))
(s/def ::hcl (s/and string? #(re-matches #"\#[0-9a-f]{6}" %1)))
(s/def ::ecl #{"amb" "blu" "brn" "gry" "grn" "hzl" "oth"})
(s/def ::pid (s/and string? #(re-matches #"[0-9]{9}" %1)))

(s/def ::passport
  (dict {"ecl" ::ecl
         "pid" ::pid
         "eyr" ::eyr
         "hcl" ::hcl
         "byr" ::byr
         "iyr" ::iyr
         "hgt" ::hgt}))

(def v (count (filter #(s/valid? ::passport %1) entries)))

(println "Valid: " v " total " (count entries))

Vincent Cantin15:12:28

@USAGL3YUS As the topic says, please put spoilers in thread replies. Thank you.

Vincent Cantin15:12:52

links to code are also ok

Ben List17:12:20

fyi you can do (<= min num max) instead of (and (<= min num) (>= max num)

Narve Saetre19:12:46

Right, sorry i'll use thread replies or links next time.

Narve Saetre19:12:12

Thanks for the hint, @U01CNM6254M - I saw that in other submissions. Lots of improvements to make!


another spec sol’n. To handle part1 being less specific, I would up with a non s/keys spec for it. Any ideas there? I didn’t want to start undef’ing specs for half the problem.


i have unit tests that verify part-1 and part-2 are still correct as I refactor and optimize, so I need both parts to work at same time.


(like ::hgt needs refactoring)


btw, you could a bit simplify the spec for part 1


(every? (partial contains? %) required-fields) would work


much better, updated, thanks!

Jeff Evans17:12:53

are you assuming for day4, part1 that the “values” are always non-empty?


yes, if there is a key it has a value


If there is a will there is a way.

💯 3

Ohh I should have used spec for this one


One thing I learned today is (<= 150 height 193) - which is a nice readable short form for (and (…) (…))

👀 3
aw_yeah 3

Day 5 Answers Thread .. Post your answers here


;; part 1
(def input (str/split-lines (slurp "resources/")))
(def fblrbits {\F 0 \B 1 \L 0 \R 1})

(defn seatcode [v]
  (-> (apply str (map fblrbits v)) (Integer/parseInt 2)))

(def codes (map seatcode input))
(apply max codes)

;; part 2
(let [dc (partition 2 1 (sort codes))]
  (inc (ffirst (filter (fn [[a b]] (= 2 (- b a))) dc))))
Explanation: Part 1: FBLR code is just binary. Convert to 1's and 0's and then to base 10. Part 2, You’re looking for a gap. Sort the codes and make pairs of (code, nextcode). Where that difference is 2, your seat number is in-between them, so add one to the first value.

🤯 9
👍 6
👏 6

Definitely more verbose than the previous one 🙂. This is my post-submission cleaned-up version:

fingertoe07:12:31 Kinda messy — I always feel clever when I can map gibberish data to functions.


(def l->b {\B 1 \F 0 \R 1 \L 0})
(def powers-of-2 (iterate (partial * 2) 1))

(->>  s
      (map l->b)
      (map * powers-of-2)
      (apply +))

👍 3

My final decode is

(defn decode [code]
  (-> (str/escape code {\F 0 \L 0 \B 1 \R 1})
      (Long/parseLong 2)))
That’s all you need 🙂

🙌 9
👍 3
Joe12:12:56 - I didn't pick up on the 'trick', very cool though! Even without it it can be solved very concisely.


I'm also linking to and trying to summarize and comment on other peoples solutions on that page, hope no-one minds


Not super proud of part 2 - I think that could have a simpler solution


Hmm subvec


Ohh reduce clever I did a recursive loop


omg a binary decode


Should have recognized that


(ns advent.day5
  (:require [advent.core :as core]
            [clojure.string :as s]))

(defn seat-id [boarding-pass]
  (-> boarding-pass
      (s/replace #"[FL]" "0")
      (s/replace #"[BR]" "1")
      (Integer/parseInt 2)))

(->> (core/input-file "day5.txt")
     (map seat-id)
     (reduce max))
;; => 998

(defn my-seat? [[a b]]
  (= 2 (- b a)))

(->> (core/input-file "day5.txt")
     (map seat-id)
     (partition 2 1)
     (filter my-seat?)
;; => 676
I thought the partition for part 2 was clever, but wondering if theres a better way to do the filter, first, first, inc chain?


@U0ESCTA1E yes, as Arne showed you could use some instead.


(some (fn [[lo hi]] (when (= (+ lo 2) hi) (inc lo))))


;; day 5 super golf
(let [ec #(-> (str/escape % {\F 0 \B 1 \L 0 \R 1}) 
                (Integer/parseInt 2))
      sc (sort (map ec (str/split-lines 
                (slurp "resources/"))))
      p2 (ffirst (drop-while #(apply = %)
           (partition 2 (interleave (iterate inc (first sc)) sc))))]
  {:part1 (last sc) :part2 p2})


some+when is a useful pattern. I vastly prefer it nowadays over filter+first

👍 9
Ben List18:12:02

my day 5, didnt pick up on the trick until I was nearly done but still fairly happy with the result (I did a scala solution using the trick afterwards)

Ola Sikström23:12:54

Interesting to see all different/similar solutions 🙂 Here's my take Used bit-shifting instead of the '2r' trick, but same idea.


I am extremely jealous of @U06B54Y95’s solution

🙂 3

Day 6 Answers Thread .. Post your answers here


(->> (slurp "input6.txt")
     ((fn [v] (str/split v #"\n\n")))
     (map str/split-lines)
     (map (fn [xs] (-> xs str/join (str/split #""))))
     (map frequencies)
     (map keys)
     (map count)
     (apply +))

(->> (slurp "input6.txt")
     ((fn [v] (str/split v #"\n\n")))
     (map str/split-lines)
     (map (fn [xs] [(count xs) (-> xs str/join (str/split #""))]))
     (map (fn [[count xs]] (filter #(= count %) (vals (frequencies xs)))))
     (map count)
     (apply +))

😍 3

Mine looks similar to others..

;; part 1
(def input
  (->> (slurp "resources/")
       (partition-by empty?)
       (take-nth 2)))

(apply + (map #(count (set (apply str %))) input))

;; part 2
(defn groupcnt [l]
   (for [x (set (apply str l))
         :when (every? #(str/includes? % (str x)) l)] x)))

(apply + (map groupcnt input))


And I used union for the part 1, and intersection for the part 2. Just went with the same data structure for the both parts.

👍 6
Vincent Cantin07:12:14

I had that intuition as well, but the timer told me to write first and think later 😛

⏲️ 6
🏃 3
🚤 3

I rewrote my part 1 after noticing the same thing about the set operations


I want more of these easy ones.

  (def input (util/fetch-input 6))

  ; step 1
  (->> (clojure.string/split input #"\R\R")
    (map #(clojure.string/replace % #"\R" ""))
    (map #(clojure.string/split % #""))
    (map set)
    (map count)
    (apply +))

  ; step 2
  (->> (clojure.string/split input #"\R\R")
       (map clojure.string/split-lines)
       (#(for [answers %]
           (->> answers
                (map set)
                (apply clojure.set/intersection))))
       (map count)
       (apply +)))


;; part 1
(defn count-answers [s]
  (->> (mapcat #(str/split % #"") s)
       (into #{})

;; part 2
(defn count-answers-2 [s]
  (let [num-people (count s)
        combined (apply str s)
        answer-frequencies (frequencies combined)]
    (->> (filter (fn [[_ v]] (= num-people v)) answer-frequencies)

;; shared
(as-> (slurp "puzzle-inputs/2020/day6") o
      (str/split o #"\R\R")
      (map #(str/split % #"\n") o)
      (map count-answers-2 o)
      #_(map count-answers o)
      (reduce + o))


I did something really silly at first lol -- forgot you could just (set "string") --


Check the pinned threads people - no spoilers in the main channel


My solution using spec… probably poorly since I just recently learned it 🙂


My day-4 solution. I have a feeling I could have used spec for the parsing, but I’m such a newbie with spec so what do I know.


I've been using re-seq with capturing groups pretty heavily in some of these, just in case someone wasn't aware of it! Can be a really nice way of parsing the input sometimes if it follows a simple pattern. Less string splitting and you get a seq of the values you're interested in right away.


this would have simplified parsing on day 4 for me!


I used it for my day 4 parser, which ended up like so:

(defn parse [card]
  (->> (clojure.string/replace card #"\s" " ")
    (re-seq #"(\S+):(\S+)")
    (map rest)
     (fn [m [k v]]
       (assoc m
              (keyword k)


I just refactored mine from a giant nasty looking threading macro doing all sorts of terrible hack n slash

(defn parse-passport-chunk [s]
  "key/val pairs separated by whitespace into a map"
  (into {} (map (fn [[_ key val]] [(keyword key) val])
                (re-seq #"(\w+):(\S+)" s))))

(defn input->passports [s]
  "Raw input -> map of passwords w/ keyword keys"
  (->> (str/split s #"\n\n")
       (map parse-passport-chunk)))


Yeah, ours are pretty similar now. And I can also get rid of that (map rest) and do what you do there:

(defn parse [card]
  (->> (clojure.string/replace card #"\s" " ")
    (re-seq #"(\S+):(\S+)")
     (fn [m [_ k v]]
       (assoc m
              (keyword k)


given how your regex is defined, I don’t think there’s even a need to do the replace call to normalize the whitespace, since it will be thrown out by re-seq regardless


Yeah, I realized that when I saw your code. 😃


thanks for mentioning re-seq, I remember when I first encountered it and then suddenly I use it literally everywhere. Another tip: with destructuring, you get really smooth group matches to bound names: EG from my day2 of Advent:

(let [[_ lower upper letter pass] (first (re-seq #"([0-9]+)-([0-9]+) ([a-zA-Z]): (.+)" line))]
  ;; do stuff with lower, upper, letter, pass


@U4TGNN19D If you use re-matches you don’t even need the first:

(let [[_ pmin pmax pc password]
      (re-matches #"(\d+)-(\d+) ([a-z]): ([a-z]+)" s)])

Joe12:12:56 - I didn't pick up on the 'trick', very cool though! Even without it it can be solved very concisely.