Fork me on GitHub
#adventofcode
<
2020-12-04
>
pez00:12:20

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)) 
       (range) 
       (repeat dx') 
       (cycle (range dy'))))
¯\(ツ)

pez01:12:44

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

pez01:12:53

(defn landing [dx line]
  (->> line
       (seq)
       (repeat)
       (flatten)
       (drop dx)
       (first)))

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

(comment
  (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 * $)))

pez02:12:21

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

pez02:12:17

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

misha05:12:56

1. replace

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

misha05:12:51

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

misha05:12:45

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

pez07:12:37

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

pez07:12:17

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

pez08:12:08

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.

misha08:12:45

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

misha08:12:23

e.g.:

...
.....
.......
.........
...........
.............

misha08:12:25

if you mod your Xs, your lookups will be:

...
.....
.......
...
.....
.......

misha08:12:17

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

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

pez08:12:52

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

pez08:12:30

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.

pez02:12:13

I updated my input-fetcher to read the cookie from a file instead, like @plexus does in his bash script: https://gist.github.com/PEZ/bcd23b9bc099b8b21a41c8c9de78a0f4

rutledgepaulv05:12:22

that second part was a lot of minutiae haha

☝️ 3
plexus05:12:06

Live stream for day 4 starting soon https://youtu.be/K3f10iwxtOw

Vincent Cantin07:12:28

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

plexus09:12:59

yes, I know

Joe12:12:44

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

plexus12:12:08

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

plexus12:12:26

basically it matches everything that conceptually is a newline

Joe12:12:48

Ah nice, thanks

Vincent Cantin05:12:44

I woke up too late today, bye bye leaderboard 😢

rutledgepaulv05:12:48

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

plexus06:12:22

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

rjray06:12:36

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

djblue06:12:47

Parsing and validation feels too much like real work 😆

😆 12
alekszelark06:12:50

so true, and so boring

plexus07:12:06

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
kenj07:12:14

80% of my part 1 solution is parsing 😞

plexus06:12:03

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

plexus06:12:10

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.

djblue06:12:48

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

djblue06:12:15

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

❤️ 6
alekszelark06:12:54

Yeah, I loved it.

alekszelark06:12:55

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

alekszelark06:12:00

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

spfeiffer07:12:40

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.

fingertoe07:12:45

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.

nbardiuk07:12:46

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

erwinrooijakkers08:12:22

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.

plexus08:12:30

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.

fingertoe07:12:27

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

nbardiuk07:12:53

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 https://github.com/listba/advent-of-code-2020/blob/master/clojure/src/aoc_2020/days/04.clj

😍 6
sheepy 3
plexus08:12:59

interesting that you reach to core.match for this!

motform13:12:03

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
erwinrooijakkers08:12:08

Missed my alarm, tedious puzzle luckily everything worked first try

nbardiuk08:12:00

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

misha08:12:57

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

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

misha08:12:51

over-engineering, over-engineering everywhere facepalm

pez22:12:51

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

https://redpenguin101.github.io/posts/2020_12_04_aoc2020_day4.html - clojure.spec seems tailor-made for this 🙃. No-one else seemed tempted though? Just not worth it for such a simple use case?

👍 12
nbardiuk12:12:17

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

erwinrooijakkers13:12:01

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. https://github.com/green-coder/advent-of-code-2020/blob/master/src/aoc/day_4.clj

👀 3
benoit12:12:18

My solution to Day 4 with clojure.spec: https://github.com/benfle/advent-of-code-2020/blob/main/day4.clj

9
👍 3
❤️ 3
motform13:12:56

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. https://github.com/motform/advent-of-clojure/blob/master/src/advent-of-clojure/2020/four.clj

motform13:12:22

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

Stuart14:12:49

My day 4. Not a great solution, but it worked in the end. https://gist.github.com/stuartstein777/7b69f6f39fe44669c77e935ba1249aec

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

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

3
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!

tschady17:12:51

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. https://github.com/tschady/advent-of-code/blob/master/src/aoc/2020/d04.clj

tschady17:12:57

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.

tschady17:12:09

(like ::hgt needs refactoring)

alekszelark17:12:39

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

alekszelark17:12:57

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

tschady17:12:40

much better, updated, thanks!

Jeff Evans17:12:53

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

nbardiuk18:12:14

yes, if there is a key it has a value

pez19:12:00

If there is a will there is a way.

💯 3
st3fan17:12:54

Ohh I should have used spec for this one

st3fan17:12:36

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

👀 3
aw_yeah 3
st3fan17:12:59

Day 5 Answers Thread .. Post your answers here

mchampine05:12:09

;; part 1
(def input (str/split-lines (slurp "resources/day05.data")))
(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
rjray06:12:24

Definitely more verbose than the previous one 🙂. This is my post-submission cleaned-up version: https://github.com/rjray/advent-2020-clojure/blob/master/src/advent_of_code/day05bis.clj

fingertoe07:12:31

https://github.com/jreighley/aoc2020/blob/master/src/day5.clj. Kinda messy — I always feel clever when I can map gibberish data to functions.

Dos10:12:50

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

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

👍 3
alekszelark11:12:21

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

https://redpenguin101.github.io/posts/2020_12_05_aoc2020_day5.html - I didn't pick up on the 'trick', very cool though! Even without it it can be solved very concisely.

Joe13:12:57

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

st3fan15:12:51

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

st3fan15:12:45

Hmm subvec

st3fan15:12:47

Ohh reduce clever I did a recursive loop

st3fan15:12:27

omg a binary decode

st3fan15:12:31

Should have recognized that

jculp16:12:55

(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)
     sort
     (partition 2 1)
     (filter my-seat?)
     first
     first
     inc)
;; => 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?

alekszelark16:12:58

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

alekszelark16:12:24

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

mchampine16:12:27

;; 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/day05.data"))))
      p2 (ffirst (drop-while #(apply = %)
           (partition 2 (interleave (iterate inc (first sc)) sc))))]
  {:part1 (last sc) :part2 p2})

plexus17:12:17

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) https://github.com/listba/advent-of-code-2020/blob/master/clojure/src/aoc_2020/days/05.clj

Ola Sikström23:12:54

Interesting to see all different/similar solutions 🙂 Here's my take https://github.com/teppix/advent-of-code-2020/blob/main/src/advent/day05.clj Used bit-shifting instead of the '2r' trick, but same idea.

devn01:12:57

I am extremely jealous of @U06B54Y95’s solution

🙂 3
st3fan17:12:23

Day 6 Answers Thread .. Post your answers here

Dos05:12:53

(->> (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
mchampine06:12:51

Mine looks similar to others..

;; part 1
(def input
  (->> (slurp "resources/day06.data")
       str/split-lines
       (partition-by empty?)
       (take-nth 2)))

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

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

(apply + (map groupcnt input))

alekszelark06:12:14

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. https://github.com/zelark/AoC-2020/blob/master/src/zelark/aoc_2020/day_06.clj

👍 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
🚤 3
kenj07:12:04

I rewrote my part 1 after noticing the same thing about the set operations https://gist.github.com/KennyMonster/ef878c0a789cebe2df3e572a11803f01

pez09:12:45

I want more of these easy ones.

(comment
  (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 +)))

Stuart16:12:49

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

;; 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)
         (count))))

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

neeasade22:12:56

I did something really silly at first lol -- forgot you could just (set "string") -- https://github.com/neeasade/AdventOfCode/commit/aa941beeb54407bde2b7a00c6678d34345eff80c

st3fan18:12:01

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

kenj18:12:19

My solution using spec… probably poorly since I just recently learned it 🙂 https://gist.github.com/KennyMonster/a51c03c53403e76413aa2fe4d61d57a4

pez22:12:08

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. https://gist.github.com/PEZ/42375289c032e43ee6dd8cd40b3c1b42

curlyfry23:12:06

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.

kenj23:12:02

this would have simplified parsing on day 4 for me!

3
pez23:12:24

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)
    (reduce
     (fn [m [k v]]
       (assoc m
              (keyword k)
              v))
     {})))

kenj23:12:58

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

pez23:12:22

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+)")
    (reduce
     (fn [m [_ k v]]
       (assoc m
              (keyword k)
              v))
     {})))

kenj23:12:58

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

pez23:12:51

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

neeasade04:12:27

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
  )

kenj05:12:41

@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

https://redpenguin101.github.io/posts/2020_12_05_aoc2020_day5.html - I didn't pick up on the 'trick', very cool though! Even without it it can be solved very concisely.