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
2020-12-09T03:24:25.422Z

Polling for yesterday's solution: PC people :bmo: vs. IP people :bananadance:

4
5
djblue 2020-12-09T05:25:04.423300Z

I'm going to rename my repo to advent-of-for 😆

😂 6
2020-12-09T05:57:11.423700Z

Day 9 Answers Thread - Post your answers here

pez 2020-12-09T10:21:55.444900Z

Not cleaned up at all.

(defn parse [input]
  (->> input
       (re-seq #"\d+")
       (map #(Integer/parseInt %))))

(comment
  (def input (util/fetch-input 9))
  ; step 1
  (->> input
       (parse)
       (partition 26 1)
       (keep #(apply (fn [& nums]
                       (let [code     (butlast nums)
                             checksum (last nums)
                             sums (for [a (butlast code)
                                        b (rest code)
                                        :let [sum (+ a b)]]
                                    sum)]
                         (when-not ((set sums) checksum)
                           checksum))) %))

       (first))

  ; step 2
  (def weakness 127)
  (def weakness 36845998)
  (->> input
       (parse)
       ((fn dig [weakness data]
          (loop [remaining data]
            (let [nums (reduce (fn [acc num]
                                 (let [new-acc (conj acc num)
                                       sum (apply + new-acc)]
                                   (cond
                                     (= sum weakness) (reduced new-acc)
                                     (> sum weakness) (reduced nil)
                                     :else new-acc)))
                               []
                               remaining)]

              (if nums
                nums
                (recur (rest remaining)))))) weakness)
       ((fn [nums] [(apply min nums) (apply max nums)]))
       (apply +)))

benoit 2020-12-09T11:46:44.448600Z

Nothing very exciting today either ... https://github.com/benfle/advent-of-code-2020/blob/main/day9.clj

motform 2020-12-09T12:03:03.449100Z

It seems like brute-force was the name of the game. Missed an opportunity to juxt though! https://github.com/motform/advent-of-clojure/blob/master/src/advent-of-clojure/2020/nine.clj

st3fan 2020-12-09T12:08:59.449800Z

Yup brute force

st3fan 2020-12-09T12:10:41.450Z

Oh! recur without a loop is allowed?

2020-12-09T12:13:57.450200Z

@st3fan Yes it works with functions as well.

motform 2020-12-09T12:14:49.450400Z

It goes to the latest fn-def, which can be #(), letfn, loop, defn or fn (afaik those are the main ones). I always thought of loop as a nicer proxy for a nested recur-fn a scheme

Björn Ebbinghaus 2020-12-09T12:54:13.451400Z

How long does your solutions take? I need 1,3s and it really bugs me, that I don’t know why it takes so long..

2020-12-09T12:55:47.451800Z

What is the algorithmic complexity of your code? Mine is O(n^2) and it is faster than your. O(n) might be possible also.

Joe 2020-12-09T12:59:09.452Z

Mine is 150ms for part 1, 35ms for part 2. Only using time though, so might be inaccurate. That's also with the input already loaded into memory.

nbardiuk 2020-12-09T13:35:44.452600Z

10ms and 35ms when the input string is already in the memory using time

2020-12-09T13:37:53.452800Z

For part2, if all the numbers are positive, it might be possible to solve it by doing one pass over the collection, adding in front when the sum is less, and removing from behind when the sum is greater.

👍 4
pez 2020-12-09T14:37:34.453400Z

I think that means that all numbers in my input are positive. 😃

tws 2020-12-09T14:59:03.453600Z

meh: https://github.com/tschady/advent-of-code/blob/master/src/aoc/2020/d09.clj

☝️ 2
1
🎉 2
2020-12-09T15:33:45.454800Z

Yes, that's what I meant.

2020-12-09T15:35:27.455200Z

The sum could be a rolling sum ... you add in front and substract in the back.

tws 2020-12-09T16:02:48.455500Z

yes, i know i’m resumming too much, that’s for a phantom refactor that may never happen 🙂

Ben List 2020-12-09T17:28:05.456900Z

not my best work but gets the job done https://github.com/listba/advent-of-code-2020/blob/master/clojure/src/aoc_2020/days/09.clj

2020-12-09T17:33:22.457200Z

@nbardiuk, you solution is awesome! But I noticed that has-sum should be called has-not-sum 🙂

nbardiuk 2020-12-09T17:46:25.457400Z

That is right! I have renamed it couple times and never noticed the missing negation

2020-12-09T18:20:20.457800Z

Also (apply max-key count) could be just first 🤐

tws 2020-12-09T18:25:49.458Z

@vincent.cantin refactored to be a rolling sum: https://github.com/tschady/advent-of-code/blob/master/src/aoc/2020/d09.clj

👍 1
R.A. Porter 2020-12-09T18:44:53.458700Z

Definitely needs some cleanup: https://github.com/coyotesqrl/advent2020/blob/master/src/coyotesqrl/advent2020.clj#L464 but I was not completely disgusted by what I wrote.

markw 2020-12-09T19:38:18.459300Z

Not the most efficient, but I think it’s reasonably clean: https://github.com/Solaxun/aoc2020_clj/blob/main/src/aoc2020_clj/day9.clj

2020-12-10T02:35:18.012100Z

@tws This is my new optimized version, super fast. https://github.com/green-coder/advent-of-code-2020/blob/master/src/aoc/day_9.clj#L52-L65

pwojnowski 2020-12-10T07:25:52.019400Z

For the record: https://github.com/pwojnowski/advent-of-code/blob/master/src/aoc/aoc2020.clj#L258

👍 1
2020-12-09T05:58:02.424100Z

Just solved today’s puzzle, but it’s a bit dirty, so I’m going to refactor it before publishing ^_^

rjray 2020-12-09T06:14:25.424300Z

Grrr. Got my first wrong submission this time, on part 1. Last year I reached day 12 before I made a wrong submission. Ah well. clojure.math.combinatorics helps again... https://github.com/rjray/advent-2020-clojure/blob/master/src/advent_of_code/day09.clj

mchampine 2020-12-09T06:15:46.424600Z

Mine’s quick-n-dirty also, but whatever.. [edit] Cleaned up a bit.

(def input
  (map #(BigInteger. %)
       (str/split-lines (slurp "resources/day09.data"))))

;; part 1 (refactored for to set/when/not-any via nbardiuk)
(defn none-valid? [blk]
  (let [ps (set (butlast blk))
        n (last blk)]
    (when (not-any? #(ps (- n %)) ps) n)))

@(def sol-p1 (some none-valid? (partition 26 1 input)))
;; => 400480901

;; part 2
(defn findn [n] (filter #(= sol-p1 (apply + %)) (partition n 1 input)))

(->> (ffirst (remove empty? (map findn (iterate inc 2))))
     (#(+ (apply max %) (apply min %))))
;; => 67587168N

👍 1
kenj 2020-12-09T06:30:12.426200Z

bruted it with code I’m not too proud of 🤕

fingertoe 2020-12-09T06:33:26.426800Z

Using reductions + as a set has to be a terrible idea, but it worked.. https://github.com/jreighley/aoc2020/blob/master/src/day9.clj

nbardiuk 2020-12-09T07:07:20.429400Z

https://github.com/nbardiuk/adventofcode/blob/master/2020/src/day09.clj today I've used partition juxt reductions it was fun

💯 1
👏 5
plexus 2020-12-09T07:35:30.430200Z

I think you can get some reuse by keeping the program state up until the point where you hit the changed instruction, and reusing that. Still, not sure if it would really be worth it.

plexus 2020-12-09T07:36:54.431200Z

Recording for day 9 https://youtu.be/7gp4MZdCDQc used a nested loop/recur for part 2, I may try to clean that up a bit later today 🙂

👍 1
2020-12-09T07:43:08.431800Z

My solution, after a lot of clean up. https://github.com/green-coder/advent-of-code-2020/blob/master/src/aoc/day_9.clj#L19-L48 I made a first-for macro.

2020-12-09T07:44:20.432100Z

After some heavy cleanup, I made a version which is using only the for loop. https://github.com/green-coder/advent-of-code-2020/blob/master/src/aoc/day_9.clj#L19-L48 Edit: not totally, there is still a reduce inside.

2020-12-09T08:41:57.434600Z

I decided to write some functions with the purpose of saving time of some commonly used operations. Here is my new group-by. Feedback welcome:

(defn group-by
  "Same as clojure.core/group-by, but with some handy new arities which apply
   custom map & reduce operations to the elements grouped together under the same key."
  ([kf coll]
   (group-by kf identity conj [] coll))
  ([kf vf coll]
   (group-by kf vf conj [] coll))
  ([kf vf rf coll]
   (group-by kf vf rf (rf) coll))
  ([kf vf rf init coll]
   (persistent!
    (reduce
     (fn [ret x]
       (let [k (kf x)
             v (vf x)]
         (assoc! ret k (rf (get ret k init) v))))
     (transient {}) coll))))

2020-12-09T08:42:28.434700Z

Testing expressions:

(group-by first [[:a 1] [:a 2] [:b 3] [:a 4] [:b 5]])
(group-by first second [[:a 1] [:a 2] [:b 3] [:a 4] [:b 5]])
(group-by first second + [[:a 1] [:a 2] [:b 3] [:a 4] [:b 5]])
(group-by first second + 10 [[:a 1] [:a 2] [:b 3] [:a 4] [:b 5]])

=> {:a [[:a 1] [:a 2] [:a 4]], :b [[:b 3] [:b 5]]}
=> {:a [1 2 4], :b [3 5]}
=> {:a 7, :b 8}
=> {:a 17, :b 18}

2020-12-09T09:09:01.437100Z

I made this group-by because usually the part which is in the val after the group-by is often the target of further computation.

2020-12-09T09:11:02.438800Z

Another problem I want to address is comp on functions. The way I think about the operations to apply is the reverse of what I need to write.

(defn comp-> [& args]
  (apply comp (reverse args)))

#_ ((comp str inc) 17) ; => "18"
#_ ((comp-> inc str) 17) ; => "18"
Feedback welcome.

🤘 1
imre 2020-12-09T09:29:43.439800Z

@vincent.cantin I recommend taking a look at https://github.com/cgrand/xforms - there are lots of goodies in there that help build stuff like this but it's all transducer-based

👍 1
imre 2020-12-09T09:31:37.440Z

I used it in a lot of places in my solutions so far https://github.com/imrekoszo/advent-2020/

👍 1
2020-12-09T09:32:44.440300Z

It is a good library. With this tree-seq transducer, you may have used it at one more place too. https://github.com/cgrand/xforms/issues/20

imre 2020-12-09T09:34:18.440800Z

Nice one, that 🙂

2020-12-09T09:34:48.441Z

on the shiny bag day

imre 2020-12-09T09:35:40.441200Z

I don't think I got that far just yet

imre 2020-12-09T09:35:47.441400Z

I'm 3 days behind rn

2020-12-09T09:36:49.441600Z

Side note: The functions I past in the channel are targeted at saving typing time. They are not necessarily the right choice for quality code.

2020-12-09T09:38:24.441800Z

Until now, performance was not the issue in all the puzzles, so lazy collections are still in the game.

imre 2020-12-09T09:38:58.442200Z

I prefer using transducers even then. Easy to go back to lazyland from there

2020-12-09T09:54:19.443800Z

One last function: the equivalent of partition, but with vector input and a sequence of subvec as output.

(defn partitionv
  ([n vec-coll]
   (partitionv n n vec-coll))
  ([n step vec-coll]
   (for [i (range n (inc (count vec-coll)) step)]
     (subvec vec-coll (- i n) i))))

#_ (partitionv 3 (vec (range 10)))
#_ (partitionv 3 1 (vec (range 10)))
The idea is to keep collections which are indexable (i.e. vectors) while having the benefits of partition

2020-12-09T10:00:15.444Z

@plexus you may like that one

plexus 2020-12-09T10:01:04.444200Z

oh yeah that's nice

erwinrooijakkers 2020-12-09T10:17:29.444700Z

ah nice

erwinrooijakkers 2020-12-09T10:22:07.445100Z

perhaps nice addition to medley: https://github.com/weavejester/medley/blob/master/src/medley/core.cljc

erwinrooijakkers 2020-12-09T10:29:08.445400Z

maybe more logical to return a vec of subvecs?

2020-12-09T11:19:49.446Z

the lazyness on the outter sequence could be useful, I want to keep it.

👍 1
2020-12-09T11:26:39.447700Z

I will also make a variation partition-indexes which returns the indexes of the possible sub-vectors.

nbardiuk 2020-12-09T12:29:10.450700Z

I've build similar function, which returns sequence of vectors, but does not require input to be a vector https://gist.github.com/nbardiuk/ec16046263734c795a82d33dcf83fb81#file-moving-avarage-clj-L4-L8 but in my case it would be probably simpler to just use partition (I didn't know it has step argument)

2020-12-09T18:28:58.458400Z

Day 10 Answers Thread - Post your answers here

1
Dosbol 2020-12-10T08:02:39.020100Z

{0 1}
{1 1}
{4 1}
{5 1, 6 1, 7 1}
{6 1, 7 2, 10 1}
{7 1, 10 2, 11 1, 12 1}
{10 1, 11 2, 12 3, 15 1}
{11 1, 12 3, 15 3, 16 1}
{12 1, 15 3, 16 3, 19 1}
{15 1, 16 3, 19 4}
{16 1, 19 7}
{19 8}

  (defn next-numbers [x]
    (remove nil? [(when (some #{(+ x 1)} data) (+ x 1))
                  (when (some #{(+ x 2)} data) (+ x 2))
                  (when (some #{(+ x 3)} data) (+ x 3))]))

  (defn next-ways [m]
    (reduce-kv
     (fn [m k v]
       (-> (reduce (fn [m key] (update m key (fnil + 0) v))
                   m
                   (next-numbers k))
           (update k - v)
           (->> (filter (fn [[_ y]] (not= y 0)))
                (into {}))))
     m
     (dissoc m maximum)))

  (loop [ways {0 1}]
    (if (= (keys ways) (list maximum))
      (ways maximum)
      (recur (next-ways ways))))

❤️ 2
pez 2020-12-10T09:08:00.024800Z

Trying to not look at your solutions just yet, but seems best to ask this here. Part 2 seems like a math problem to me. When there is a string of distances 1, I can remove some subsets of them and still have a “bridge”. Does that make sense to you people?

oxalorg (Mitesh) 2020-12-10T09:31:37.028300Z

My day 10 solution, but I ported my python solution as-is. I am not satisfied with this though and maybe there's a more "clojurey" / "functional" way to do this. I'll go through some of the above solution to check alterntative methods: https://github.com/oxalorg/aoc/blob/main/src/day10.clj

oxalorg (Mitesh) 2020-12-10T09:33:01.028700Z

@pez I can probably give you a small hint: It's not a math problem, it's a memory problem. Can you find out a way to mix and match such that you reduce unnecessary computations.

erwinrooijakkers 2020-12-10T09:39:11.029Z

I calculated in three parts using brute force https://github.com/transducer/adventofcode/blob/master/src/adventofcode/2020/day10.clj Cut the coll in three parts (finding snips where I could cut the adapters, if there’s a gap of 2 in between you don’t miss any) then multiplying the separate answers Not proud of this using an atom to count. But it works. Will try to improve if I have more time. Also wanting to fully automate.

erwinrooijakkers 2020-12-10T09:45:30.029500Z

I read btw that the creator of AoC is not a fan of checking in your inputs, he prefers not making them public

👍 1
erwinrooijakkers 2020-12-10T09:49:36.029900Z

@mitesh a you can also brute force using memoize?

erwinrooijakkers 2020-12-10T09:49:43.030100Z

Cutting of parts you already did?

pez 2020-12-10T09:52:48.030300Z

Part of it can be seen as a math problem, it seems, @mitesh. Here’s a “solution”:

(->> input
       (parse)
       (sort)
       (cons 0)
       (#(concat % [(+ 3 (last %))]))
       (partition 2 1)
       (map #(- (second %) (first %)))
       (partition-by identity)
       (keep #(seq (filter #{1} %)))
       (map count)
       (map {1 1
             2 2
             3 4
             4 7})
       (apply *))

👏 1
🙏 1
pez 2020-12-10T09:57:08.030800Z

Using the longer example in the calendar, after the keep there I have this: ((1 1 1 1) (1 1 1 1) (1 1 1) (1 1) (1 1 1 1) (1) (1 1 1 1)) which is the strings of 1 distances I was thinking about. So after mapping count over them I have (4 4 3 2 4 1 4). I then map those over the “magic” function that tells me how many combinations each string contributes with. Then multiplying all those.

pez 2020-12-10T09:58:50.031100Z

I have yet to figure out the “magic” function, but don’t expect a blocker there.

oxalorg (Mitesh) 2020-12-10T09:59:36.031600Z

@erwinrooijakkers That is a very nice solution! If you can try and generalize your part cutting solution in a way where you don't have to cut, rather just progressively find solutions to larger subsets then you will have discovered Dynamic programming!

oxalorg (Mitesh) 2020-12-10T10:00:47.031800Z

@pez That is an interesting take on this, I'll have to dig deeper to understand it haha

erwinrooijakkers 2020-12-10T10:01:27.032Z

haha funny you say that talked about dynamic programming last night with a friend and realized i did not know what it was and that i would find out more today

🦜 2
pez 2020-12-10T10:02:19.032200Z

I will have to check that out too! 😃

erwinrooijakkers 2020-12-10T10:02:27.032400Z

so you start in the small, then go up to a larger problem, adding the sub amounts?

erwinrooijakkers 2020-12-10T10:02:35.032600Z

like the opposite of memoization

erwinrooijakkers 2020-12-10T10:03:30.032800Z

with memoization you start in the large and memoize subparts, with dynamic programming you start in the small and go to the large, i have to find out more after work 🙂

👍 1
erwinrooijakkers 2020-12-10T10:23:36.034600Z

@mitesh the problem with the cutting is that for just bare multiplication the subparts needs to be separated on the three jumps. e.g., From the example:

(count-paths [1 4 5 6 7 10 11 12 15 16 19])
;; => 8
and when cutting on a three jump (from 7 to 10) you can just multiply solutions to both parts:
(* (count-paths [1 4 5 6 7])
   (count-paths [10 11 12 15 16 19]))
;; => 8
But when cutting between 10 and 11, you miss a few solutions:
(* (count-paths [1 4 5 6 7 10])
   (count-paths [11 12 15 16 19]))
;; => 4
So not sure how I can prevent this cutting part or maybe if there’s some logic to the type of cutting and the factor of paths you miss

oxalorg (Mitesh) 2020-12-10T10:27:48.036100Z

Don't think about cutting the input randomly. Put a bit more stress on the problem and figure out a pattern in which dividing the problem into smaller parts gives you something concrete (the ability to avoid future computations)

👍 1
nbardiuk 2020-12-10T10:29:28.036400Z

I've cut input by difference of 3, then counted connections inside each group. Since 3 is the longest possible link, only one item from group can reach next group

💯 1
pez 2020-12-10T12:11:18.037400Z

So the “magic” function/map in my step 2 “solution” above was really just the needed 2-combinations + 1. So then this is my real solution:

(defn ! [n]
  (reduce *' (range 1 (inc n))))

(defn combinations [n r]
  (if (= 1 n)
    0
    (let [n! (! n)
          r! (! r)
          r-n! (! (- n r))]
      (/ n! (* r! r-n!)))))

(->> parsed-input
     (sort)
     (cons 0)
     (#(concat % [(+ 3 (last %))]))
     (partition 2 1)
     (map #(- (second %) (first %)))
     (partition-by identity)
     (keep #(seq (filter #{1} %)))
     (map count)
     (map #(inc (combinations % 2)))
     (apply *))

🤯 4
🎉 3
pez 2020-12-10T12:14:23.037600Z

Given parsed input it runs in just above 1ms on my machine fed my input data. I guess that decent. Anyone see any speed gains I could collect? @misha, maybe?

oxalorg (Mitesh) 2020-12-10T12:55:52.037800Z

This is beautiful @pez ! So basically you're finding out how many ways can we arrange consecutive adapters until we have a jump which is a 3 volt difference. Since a 3 volt difference can't be reached by any adapter in the previous consecutive list (except for the last one) so that won't affect the number of arrangements.

👍 1
oxalorg (Mitesh) 2020-12-10T12:56:37.038100Z

You definitely did it the math way 😄

pez 2020-12-10T13:00:46.038500Z

Thanks! Yes, like that. When solving step 1, I had stuff like this before I applied frequencies:

(1 1 1 1 3 1 1 1 1 3 3 1 1 1 3 1 1 3 3 1 1 1 1 3 1 3 3 1 1 1 1 3)
And those strings of 1s looked like bridges to me.

👏 1
pez 2020-12-10T13:01:57.038700Z

But then I needed to ask my daughter for help with figuring out the pattern. Then wikipedia helped me with the math. 😃

😇 1
oxalorg (Mitesh) 2020-12-10T13:02:12.038900Z

That was great thinking, not going to lie I don't think I would've noticed this! 🙈

pez 2020-12-10T13:04:57.039500Z

I think it was because I think so literally about the problem as it is stated. It made me miss those binary numbers when finding a seat on that plane, but spot this bridging thing instead. Haha.

💯 1
😆 1
oxalorg (Mitesh) 2020-12-10T13:05:05.039700Z

One catch in this solution would be if the requirement of using all adapters were loosened and the AOC input had something like this: [2 4 7] So there would be two ways to arrange this: [2 4 7] and [2 7] Maybe something can be done with the bridges to divide them even further? Or just count the number of 2 and multiply the result by n^2 as every 2 would lead to two possible solutions

nbardiuk 2020-12-10T13:05:12.039900Z

oh, I didn't notice that there are only differences in 1 and 3, I assumed that there should be 2s somewhere 😮

oxalorg (Mitesh) 2020-12-10T13:06:02.040100Z

@nbardiuk Yes I didn't realise that either, just went back and checked they've said "need to use all adapters" and they've conveniently left input which can lead to a difference of 2

👍 1
benoit 2020-12-10T13:32:34.041500Z

A solution to Day 10. https://github.com/benfle/advent-of-code-2020/blob/main/day10.clj

👏 1
🤘 2
benoit 2020-12-10T13:36:57.041900Z

path-count(n) = path-count(n+1) + path-count(n+2) + path-count(n+3)

2020-12-10T14:04:54.044900Z

Brilliantly!

misha 2020-12-10T14:42:58.046300Z

@pez I don't think 1ms needs any speeding up. I have a similar solution wrt splitting by "magic" [3 3] pattern, but my "combinations" function is a dirty loop, I have left over from the morning

misha 2020-12-10T14:47:05.046500Z

(def minus (fnil - ##Inf ##Inf))

(defn split [xs]
  (let [threes (fn [[a b c]] (= 3 (minus a b) (minus b c)))]
    (->> xs
      (partition-all 3 1)
      (partition-by threes)
      (map (partial map first))
      (partition-all 2)
      (map (partial apply concat)))))

(->> xs split (map p1) (reduce *))

misha 2020-12-10T14:52:05.047400Z

so test input gets split like this: https://gist.github.com/akovantsev/782045b0f49a5d3875c0c42c11776ed4

pez 2020-12-10T15:00:47.048Z

It’s wonderful!

Ben List 2020-12-10T15:33:18.053200Z

struggled with part 2 for a little bit, but memoization saved the day. Pretty happy with my end result https://github.com/listba/advent-of-code-2020/blob/master/clojure/src/aoc_2020/days/10.clj

👍 1
genmeblog 2020-12-10T16:30:34.056400Z

Finally! 2-3ms for part 2 with input sorted in descending order. https://github.com/genmeblog/advent-of-code/blob/master/src/advent_of_code_2020/day10.clj#L17-L31

rjray 2020-12-10T17:55:01.075300Z

This took me over 2 1/2 hours. I basically had part 2 correct, but I had the memoize at the wrong lexical level and I wasn’t getting any benefit from it. Looking at @vincent.cantin’s solution showed me what I had wrong. At least I can take some solace in knowing I was about 96% there… https://github.com/rjray/advent-2020-clojure/blob/master/src/advent_of_code/day10bis.clj

tws 2020-12-10T18:01:31.076800Z

Not as clean as the awesome O(n) above, but this was my first idea - to isolate the areas of multiple paths (runs of 1), compute the ways through them (kind of like a change making algo), then multiply those together. https://github.com/tschady/advent-of-code/blob/master/src/aoc/2020/d10.clj

mchampine 2020-12-10T18:14:12.083400Z

I found an interesting pattern for part 2. I haven’t waded through all the other solutions to see if someone discovered this, but: Looking at the diffs pattern (first and last value with moving window of 3) you can simply count the number of runs of 2's. Runs of 3 = 7, runs of 2 = 4, runs of 1 = 2, then take the product. [edit]. Just discovered @hobosarefriends posted a question on the main thread earlier that references the prime factorization. So, yeah, some others were onto this idea also.. Maybe there are more. Note: runs in “Elapsed time: 0.5174 msecs”

;; part 2
(defn adapter-combos [inp]
  (->> (sort (concat inp [0 (+ 3 (apply max inp))]))
       (partition 3 1)
       (map (fn [[a _ c]] (- c a)))
       (partition-by identity)
       (keep {'(2 2 2) 7 '(2 2) 4 '(2) 2})
       (reduce *)))
(adapter-combos input)
;; => 5289227976704

tws 2020-12-10T18:28:57.086900Z

@mchampine i wrote a fn to calculate those magic 7, 4, 2 https://github.com/tschady/advent-of-code/blob/master/src/aoc/2020/d10.clj#L23-L29

👍 1
Joe 2020-12-10T18:32:08.087200Z

Dang, tough part 2, spent a good half-hour with pen and paper before I touched the keyboard. https://github.com/RedPenguin101/aoc2020/blob/main/day10.clj- but it is fast, < 2ms.

R.A. Porter 2020-12-10T19:01:05.088100Z

I’m not going to say how long part 2 took me. And it’s not like it’s the prettiest of solutions…would be really short if I’d found a graphing library to count all paths in a DAG for me. https://github.com/coyotesqrl/advent2020/blob/master/src/coyotesqrl/advent2020.clj#L550

👏 1
Joe 2020-12-10T19:37:21.088400Z

https://redpenguin101.github.io/posts/2020_12_10_aoc2020_day10.html. Looking forward to going through other peoples solutions for this one!

🤘 1
pez 2020-12-10T20:39:23.089200Z

I got very different results from time so tried criterium quick-bench which reports:

Evaluation count : 4350 in 6 samples of 725 calls.
             Execution time mean : 137,221776 µs
    Execution time std-deviation : 3,615336 µs
   Execution time lower quantile : 134,692566 µs ( 2,5%)
   Execution time upper quantile : 143,297341 µs (97,5%)
                   Overhead used : 6,391737 ns

Found 1 outliers in 6 samples (16,6667 %)
	low-severe	 1 (16,6667 %)
 Variance from outliers : 13,8889 % Variance is moderately inflated by outliers

pez 2020-12-10T20:46:15.089700Z

I get 18 µs for the smallest sample input and 43 µs for the larger.

2020-12-10T23:10:21.110200Z

(ns day10.core
  (:require [clojure.string :as str]))

(def input (slurp "src/day10/input.txt"))

(defn add-device [coll]
  (cons (+ 3 (apply max coll)) coll))

(def adapters (-&gt;&gt; input
                   (str/split-lines)
                   (map #(Long. %))
                   (add-device)
                   (cons 0)
                   (sort)))

;; Part 1
(defn part1 []
  (let [freqs (-&gt;&gt; adapters
                   (partition 2 1)
                   (map (fn [[x y]] (- y x)))
                   (frequencies))]
    (* (get freqs 1) (get freqs 3))))

;; Part 2
(defn part2 []
  (let [graph (-&gt;&gt; adapters
                   (partition-all 4 1)
                   (map (fn [[x &amp; xs]] [x (vec (remove #(&lt; 3 (- % x)) xs))]))
                   (into {}))
        vs (reverse adapters)
        goal (first vs)]
    (loop [[v &amp; vs] (rest vs)
           v-&gt;count {goal 1}]
      (let [n (apply + (map v-&gt;count (get graph v)))]
        (if (seq vs)
          (recur vs (assoc v-&gt;count v n))
          n)))))
(time (part2)) "Elapsed time: 0.496946 msecs" For part 2, I made the numbers into a DAG (where the neighbours of a number are the numbers with a diff of 3 or smaller). Did a little bit of googling for efficient graph algorithms and found out that a property of a topologically sorted graph (which is what you get when you sort the input numbers) is that you can find the number of paths really efficiently using dynamic programming. Basically I keep a map (`v->count`) of the number of paths from which you can reach the goal from the given vertex. The number of paths to reach the goal from the goal is 1. Then I go to the next vertex in the topological sorting and look at its neighbours (only the goal in this case), and store the number of paths in the map. I then keep doing this until I reach the start. Since we're going backwards, the map will already contain the number of paths for all the neighbours we're checking, and the last neighbour sum (the neighbours of the start vertex) is the answer!

👍 1
1
❤️ 1
2020-12-11T02:22:59.115100Z

day 10 part 2, dirty solution

(as-&gt; (slurp "puzzle-inputs/2020/day10") o
        (str/split-lines o)
        (map #(Integer/parseInt %) o)
        (sort o)
        (concat [0] o)
        (map - (rest o) o)
        (partition-by identity o)
        (filter #(some #{1} %) o)
        (map #(case (count %) 1 1 2 2 3 4 4 7) o)
        (reduce * 1 o))

markw 2020-12-11T04:52:10.116900Z

Late to the party - didn’t find the math trick, went with memoization:

(defn adapter-fits? [current-adapter other-adapter]
  (&lt; current-adapter other-adapter (+ 4 current-adapter)))

(defn possible-arrangements [root adapters]
  (if-let [choices (seq (filter #(adapter-fits? root %) adapters))]
    (reduce + (map #(possible-arrangements % adapters) choices))
    1))

(def possible-arrangements (memoize possible-arrangements))

2020-12-09T20:12:29.460200Z

what's the best way to do a cartesian product but without the order?

markw 2020-12-09T20:28:21.460900Z

what do you mean by without the order?

misha 2020-12-09T20:44:40.461500Z

(for [x (range 3) y (range 3)] [x y])
=&gt; ([0 0] [0 1] [0 2] [1 0] [1 1] [1 2] [2 0] [2 1] [2 2])

2020-12-09T21:05:42.463Z

yeah I mean having [0 1] but not [1 0] for example

2020-12-09T21:06:07.463700Z

so imagining a square, taking the diagonal and the lower or upper half

tws 2020-12-09T21:07:08.464100Z

combination instead of permutation?

imre 2020-12-09T21:08:19.464400Z

ha. Is this for day 1?

imre 2020-12-09T21:11:18.467200Z

Cause that did bug me with day 1 where you kinda need elements taken for the combinations of indices. Not being able to find an existing implementation, I wrote a transducer for it but its performance is shite when compared to the for-based approach: https://github.com/imrekoszo/advent-2020/blob/master/src/imrekoszo/advent2020/day1_alternatives.clj#L102

Lars Nilsson 2020-12-09T21:15:01.469700Z

I used (for [i (range size) j (range i size)] ...) to get a triangle of indices (where size if the count of the collection)

markw 2020-12-09T21:15:06.469900Z

@andrea.crotti There is more than one way to do it, but as mentioned above that’s combinations vs permutations. Another way is in the for macro to make sure that one index is > than another, e.g. 1(for [i xs j xs :when (> j i))1

markw 2020-12-09T21:16:04.470900Z

basically you want your inner loop to always start at 1 + the outer loop var

raicotop 2020-12-09T21:16:28.471200Z

as @markw mentions:

(for [x (range 5)
      y (range 5)
      :when (&lt; x y)]
  [x y])
;; =&gt; ([0 1] [0 2] [0 3] [0 4] [1 2] [1 3] [1 4] [2 3] [2 4] [3 4])

markw 2020-12-09T21:16:47.471500Z

Yep - I think @plexus mentioned this in the most recent video

raicotop 2020-12-09T21:18:52.472800Z

That's where I saw it too I guess. Came in handy for day 9.

misha 2020-12-09T21:20:18.473400Z

lacks diagonal: [0 0] – [4 4]

markw 2020-12-09T21:21:50.474300Z

if you need diagonals you can just add a condition to the :when

markw 2020-12-09T21:22:23.475200Z

:when (or ( = x y) (&lt; x y))

Lars Nilsson 2020-12-09T21:22:28.475400Z

Or change &lt; to &lt;=, I would imagine...

markw 2020-12-09T21:22:34.475700Z

oh yeah

misha 2020-12-09T21:22:37.475900Z

&lt;=? :kappa:

Lars Nilsson 2020-12-09T21:25:26.477600Z

Using the when clause will make the for comprehension iterate over all 5*5 indices though (perhaps a small cost compared to the work overall...)

markw 2020-12-09T21:26:08.478200Z

if you want efficient generation i would use combinatorics and tack on the diagonals explicitly

Lars Nilsson 2020-12-09T21:26:09.478300Z

It just won't generate an entry for the failing conditions.

2020-12-09T21:27:50.479300Z

cool nice

Lars Nilsson 2020-12-09T21:27:50.479400Z

As I mentioned above, I did it using the two argument version of range to get the triangular shape of indices.

👍 1
markw 2020-12-09T21:29:57.480600Z

Missed the fact that you started the second loop at i rather than i+1 which would have made it effectively the same as combinations

Lars Nilsson 2020-12-09T21:32:29.481600Z

In my actual for sequence I do have :let and :when for other purposes, just not the range/index checking.

Lars Nilsson 2020-12-09T21:33:42.482600Z

I feel somewhat stupid about my day 1, part 2 implementation where I just added another index instead of doing something else.

misha 2020-12-09T21:34:14.483200Z

(time (do (doall (for [x (range 1000) y (range 1000) :when (&lt;= x y)] [x y])) nil))
"Elapsed time: 40.609524 msecs"
=&gt; nil
(time (do (doall (for [x (range 1000) y (range x 1000)] [x y])) nil))
"Elapsed time: 27.957203 msecs"

markw 2020-12-09T21:34:20.483400Z

oh yeah i brute forced that one O^N(3)

markw 2020-12-09T21:34:30.483600Z

i’m not going to be clever on day 1 😛

misha 2020-12-09T21:35:55.484700Z

Depends on what your goals are for this puzzle thing. And there are a bunch of conflicting ones there.

Lars Nilsson 2020-12-09T21:36:01.485Z

(time (do (doall (for [x (range 1000) y (range 1000) :when (&lt;= x y)] [x y])) nil))
"Elapsed time: 65.1356 msecs"
(time (do (doall (for [x (range 1000) y (range x 1000)] [x y])) nil))
"Elapsed time: 53.8159 msecs"

Lars Nilsson 2020-12-09T21:37:33.485700Z

Never mind my duplicate experiment. I misread yours completely and thought it did not include the x offset for y... Time to fetch my glasses.

2020-12-09T21:38:27.486600Z

I'm using loom for problem 7 https://github.com/aysylu/loom and it worked out nicely for the first part of the problem

2020-12-09T21:38:56.487500Z

I also added the number of bags contained as the weight of each edge, so I thought it would be easy to do part b of the exercise

2020-12-09T21:39:15.488Z

but I can't find anything useful to do that

misha 2020-12-09T21:39:37.488900Z

> it worked out nicely for the first part of the problem famous last words™

Lars Nilsson 2020-12-09T21:40:16.490Z

Thanks for the heads-up on loom.. I might need to look at that for some non-aoc stuff.

2020-12-09T21:40:17.490100Z

in theory I just want to be able to sum all the paths that I generate with a DFS search for example (multiplying the weights)

misha 2020-12-09T21:40:17.490300Z

someone shared loom solution here

2020-12-09T21:41:03.490700Z

it's pretty much just

(-&gt;&gt; "shiny gold"
       (la/bf-traverse (get-graph))
       count
       ;; removing the node itself
       dec)
once you have the graph

2020-12-09T21:41:14.491100Z

but yeah getting the actual weights to do something seems to be another matter

misha 2020-12-09T21:42:02.491800Z

there is a loom conj presentation https://www.youtube.com/watch?v=wEEutxTYQQU

misha 2020-12-09T21:44:16.493400Z

> Aysylu Greenberg - Loom and Graphs in Clojure

2020-12-09T21:44:17.493500Z

yeah I think I saw that some time ago, but well I kind of know how I could do it I guess. I need to find in the whole dfs which are the terminal nodes, for them generate all the paths, then get the weights, multiply them and sum it all together

2020-12-09T21:44:32.494400Z

would be nice if there was an easier way though

misha 2020-12-09T21:45:17.495100Z

sounds like one ugly loop would do just fine here :kappa:

2020-12-09T21:45:26.495300Z

hehe yeah that would work I guess

2020-12-09T21:45:34.495800Z

was trying to avoid that since that's all I'm doing apparently

misha 2020-12-09T21:46:02.496400Z

yeah, story of my aoc life for 5 years

Lars Nilsson 2020-12-09T21:48:08.497100Z

Day 7 was some ugly for comprehension over recursive calls for me.

misha 2020-12-09T21:49:22.498200Z

but yeah, downloading a bunch of ugly loop s from clojars might seem more idiomatic :)

2020-12-09T22:17:44.498700Z

mm dammit I think I'm close

(defn n-bags-inside [gr starting]
  (let [edges (lg/out-edges gr starting)]
    (if (empty? edges)
      1
      (apply +
             (for [e edges]
               (* (lg/weight gr e)
                  (n-bags-inside gr (second e))))))))
at least it works for this simple graph
;; desired = 16
(def sample-g
  (lg/weighted-digraph
   [:a :b 2]
   [:a :d 10]
   [:b :c 3]))

2020-12-09T22:18:30.499Z

but aoc doesn't agree with me

2020-12-09T22:29:59.499900Z

mm weird my result is exactly (actual-result / 2 + 1) using the example from the instructions

2020-12-09T22:30:21.000400Z

but of course if I (dec (* 2 current)) it still doesn't work 😄

2020-12-09T22:30:29.000800Z

so probably just a coincidence

2020-12-09T22:31:18.001500Z

I think the problem is maybe the graph generated because the algorithm I think it's correct, bah I'll see tomorrow

markw 2020-12-09T22:40:04.007800Z

I have been revisiting some prior years problems I gave up on… https://adventofcode.com/2018/day/20 is really stumping me. Is it possible to parse something like ENWWW(NEEE|SSE(EE|N)) using recursive descent into the following paths? Basically each | is an optional fork in the path, which starts from the point in each prior group formed by (. The instructions give some more detail, but the above should yield these three paths: • ENWWWNEEE • ENWWWSSEEE • ENWWWSSEN I feel like this should be simple yet I’ve been stuck on it for an embarrassingly long amount of time. I recently abandoned the recursive approach dealing with each symbol in-turn for a token-by-token approach, which is getting be closer but bleh.

2020-12-09T22:40:50.007900Z

that's quite neat thanks

Lars Nilsson 2020-12-09T22:54:15.008200Z

Cheating to use instaparse?

markw 2020-12-09T23:00:51.008500Z

that’s next, first i want to understand how to do it though

st3fan 2020-12-09T23:07:07.010300Z

Did a bunch of reading on graphs and depth first search and I think I finally have all the pieces I need to solve day 7

st3fan 2020-12-09T23:07:26.010600Z

Totally out of my comfort zone, but it is a good learning exercise

👍 2
❤️ 2
st3fan 2020-12-09T23:07:48.011Z

Got some code snippets in a file that need to work together and then …