Fork me on GitHub
#adventofcode
<
2021-11-30
>
Chase15:11:50

I've been doing some old AOC problems as a warmup and am confused about why my solution for Year 2015, Day 3 isn't working. https://adventofcode.com/2015/day/3

Chase15:11:00

(ns aoc.2015.day-03)                                                                             
                                                                                                 
(def input                                                                                       
  (slurp "resources/2015/day_03.txt"))                                                           
                                                                                                 
(def direction->move                                                                             
  {\^ [1, 0]                                                                                     
   \v [-1, 0]                                                                                    
   \> [0, 1]                                                                                     
   \< [0, -1]})                                                                                  
                                                                                                 
(def moves                                                                                       
  (map direction->move input))                                                                   
                                                                                                 
(defn part-1 [moves]                                                                             
  (loop [moves moves                                                                             
         current [0 0]                                                                           
         visited #{current}]                                                                     
    (if (seq? moves)                                                                             
      (let [[x y] current                                                                        
            [x-delta y-delta] (first moves)                                                      
            next-house [(+ x x-delta) (+ y y-delta)]]                                            
        (recur (rest moves) next-house (conj visited next-house)))                               
      (count visited))))                                                                         
                                                                                                 
(part-1 moves)                                                                                   

Chase15:11:26

This is the error I get:

; eval (root-form): (part-1 moves)                                                               
; (err) Execution error (NullPointerException) at aoc.2015.day-03/part-1 (REPL:22).              
; (err) Cannot invoke "Object.getClass()" because "x" is null                                    

Chase15:11:56

When I evaluate moves I get a seq of vecs like so: (take 5 moves) => ([0 1] [1 0] [1 0] [-1 0] [1 0])

Chase15:11:30

So I'm not sure where this whole null thing is coming from. Any tips?

potetm16:11:11

is there a dangling newline in input or something?

Chase16:11:13

I was thinking something like that could be happening but (last moves) gives me [0 -1]

potetm16:11:51

(remove some? input)

potetm16:11:24

alternatively, (remove some? moves)

Chase16:11:00

that is giving me an empty seq...

potetm16:11:42

(seq? (rest '([1 0]))) => ?

Chase16:11:07

hmmm, so that is evaluating to true huh

Chase16:11:53

but I would want that to be false right? I'm confused. haha. I thought it was idiomatic to use seq? in the if conditional on ... seqs

potetm16:11:51

You’re looking for seq I think.

Chase16:11:52

oh wait... it's idiomatic to use seq not seq? huh? lol. D'oh!

Chase16:11:10

This is what I get for having to leave clojure for a few months

Chase16:11:24

so what do you think of that solution (it works now btw)? I still struggle turning that whole loop/recur approach into a reduce (or better?) approach.

Chase16:11:40

I remember a year where you did some awesome videos going through that process

potetm16:11:27

I happened to have recently done most of the the 2015 problems. Let me get mine real quick.

potetm16:11:45

You want me to just post my solution here?

Chase16:11:26

Yeah, that would be great. Just for part 1 please. I'm about to tackle part-2.

potetm16:11:28

Hmmm… my part-1 basically covers part 2

potetm16:11:42

Alternatively, I can give you some kind of hint if you prefer.

potetm16:11:26

why not both?

Chase16:11:49

Let's do it. I already finished part-2 anyways.

Chase16:11:45

I broke out part-1 into a separate function to give me the set of houses visited so then my part-2 becomes:

(defn part-2 [moves]                                                                             
  (let [santa-moves (take-nth 2 moves)                                                           
        robo-moves  (take-nth 2 (rest moves))                                                    
        santa-houses-visited (houses-visited santa-moves)                                        
        robo-houses-visited  (houses-visited robo-moves)]                                        
    (count (clojure.set/union santa-houses-visited robo-houses-visited))))

potetm16:11:32

But there are some fundamental questions you can ask yourself when trying to figure out whether you need recursion or reduction or sequence fns.

potetm16:11:36

1. Am I working on a collection? a. If yes, then you can maybe reduce or sequence fns b. If no, then you either need loop/recur or iterate 2. Do I need to track state across members of the collection? a. If yes, then you either need reduction or partition b. If no, then you can use sequence fns 3. Do I need to control flow? a. If yes, then you probably need loop/recur b. If no, then you can use reduction

potetm16:11:10

In this case, you’re working on a collection, you do need to track state, but you don’t need to control flow (because you’re just going to process the entire input collection).

potetm16:11:27

So you know immediately that reduction is applicable.

Chase16:11:35

I like this!

potetm16:11:51

The only thing you might not know is there is a function reductions which will give you every intermediate state of a reduce operation.

Chase16:11:47

I actually used reductions for day-1 but still it didn't occur to me to use that one here. I imagine it is quite useful.

potetm16:11:02

(fwiw reductions and iterate are extremely useful for AoC and basically useless in every other context :D)

Chase16:11:15

defn part-2 [directions]                                                                        
  (let [current-floor (reductions + directions)]                                                 
    (inc                                                                                         
     (.indexOf current-floor -1))))                                                              

Chase16:11:23

it definitely made that one easy

Chase16:11:57

now I'm even more excited for AOC (well the first week at least before I burn out, lol) so I can get more tips like this

potetm16:11:30

😄 I’m not sure I plan on keeping up at all this year.

potetm16:11:49

Honestly, I didn’t even know .indexOf was implemented on sequences. That’s a good tip!

Chase16:11:16

Yeah I thought that was nifty. So in my solution though, I am processing the whole sequence first and then checking to see where the -1 is right? Is that a lot worse than continually checking for the solution as it's processing through lazily? Does that question make sense? haha

potetm16:11:40

Yeah that makes sense. The only way to know is to implement the lazy version and time it.

potetm16:11:21

If you really wanna know, you give the JIT time to warm up by running in a loop: https://github.com/potetm/advent-of-code/blob/master/src/advent/util.clj#L43-L62

potetm16:11:15

But for AoC, I generally take the attitude of, “If it finishes in a few seconds, it’s fine.”

Chase16:11:15

Yep. haha. The repl returns the answer immediately so that's good for me.

Chase16:11:27

Fun chat. Thanks for rubber ducking that issue with me. I felt my logic was solid so I was pulling my hair out a bit

potetm16:11:25

Yeah man, feel free to reach out again! It was fun for me as well.

cdeszaq19:11:38

While not as terse as some other solutions, here’s my 2015 Day 3 as well, for comparison: https://github.com/cdeszaq-org/adventofcode/blob/main/src/com/cdeszaq/adventofcode/twenty15/day3.cljs#L13-L37 Uses reductions as well and looks to be largely the same as some others, but makes liberal use of thread macros and named functions to break up the logic.

potetm19:11:56

#nomorethreaders #nomorenames

rjray18:11:23

Sadly, I won’t be able to take part this year. Grad school (MSCS program), with a major project due on the 12th and a final exam on the 17th. 😞.

tschady18:11:40

in my day, that would guarantee i finish AoC 😬

tschady18:11:21

mega nerd snipe

pez19:11:20

I like how this channel awakens to the occasion.

😁 1
Antonio Bibiano19:11:40

Hello peeps 🙂 i was trying to re-do some problems from last year and when I try this

Antonio Bibiano19:11:48

(time
 (first
  (for [x entries
        y entries
        :let [z (- 2020 x y)]
        :when (< x y z)
        :when (contains? entries z)]
    (* x y z))))

Antonio Bibiano19:11:02

it finishes in 22 ms

Antonio Bibiano19:11:14

without first it finishes in less under 1 ms

Antonio Bibiano19:11:51

Is the difference because the repl takes care of printing the resulting LazySeq and so it's not counted in the time calls?

potetm19:11:55

When you call first you realize the first element of the sequence.

potetm19:11:13

When you do not call first you realize none of the sequence.

1
Antonio Bibiano20:11:32

nice I was a bit confused because i actually saw the result 😄

delaguardo09:12:29

Actually, because of chunking, you realize first 32 elements when you call first.

potetm14:12:24

Actually, no.

potetm14:12:16

for returns a LazySeq which isn’t chunked.

delaguardo16:12:32

user=> (first (for [x (range 1000)] (do (prn x) x)))
0
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
0

potetm16:12:15

(first
    (for [i (range)]
      (do (prn i)
          (inc i))))
0
=> 1

delaguardo17:12:11

my point is that for might realize more than needed because of chunking.

potetm17:12:28

You did not say, “might.”

delaguardo17:12:12

yeah, sorry )

Antonio Bibiano19:12:09

and the difference here between @U04V4KLKC and @U07S8JGF7 output is due to what?

potetm19:12:59

@U01HY37QQUA It’s called “chunking”

Antonio Bibiano19:12:57

ah so it's probably a different code path in for?

Antonio Bibiano19:12:58

(chunked-seq? (range)) => false
(chunked-seq? (range 1000)) => true

Antonio Bibiano19:12:22

there you have it

potetm19:11:01

@nbardiuk Is the leaderboard not adventofcode-clojurians?

nbardiuk19:11:45

I've just bumped the year 😅

nbardiuk19:11:16

I think that code references adventofcode-clojurians, let me check

nbardiuk19:11:53

yep, the 217019-4a55b8eb is a code to join adventofcode-clojurians private board

👍 3
gratitude-merci 1
Chase20:11:05

so are these private leaderboards just tracking the time to complete like the main site? I don't get to them until 8-12 hours after they are posted.

Chase20:11:57

It would be cool to get something that ran benchmarks for submitted solutions. I am inexperienced in benchmarking stuff so could be cool to explore as a project idea

fingertoe21:11:26

@U9J50BY4C The really nice thing about the Clojure leaderboard isn't so much the scorekeeping -- It's that it often links to git repositories so you can compare methods used after you get done.. @U04V15CAJ did a benchmarked repo for CLJC a few years ago. It was kinda cool.

Phil Shapiro22:11:17

I wrote this code last year to benchmark my solutions. Not general, but may be interesting to some. https://github.com/pshapiro4broad/advent2020/blob/main/src/main.clj

$ clj -X main/-main
day1/part1 : "Elapsed time: 38.363232 msecs"
day1/part2 : "Elapsed time: 976.244442 msecs"
day2/part1 : "Elapsed time: 12.697295 msecs"
day2/part2 : "Elapsed time: 6.865864 msecs"
day3/part1 : "Elapsed time: 1.519996 msecs"
day3/part2 : "Elapsed time: 2.062161 msecs"
...

Chase23:11:55

@U10B0BPJP that is a good feature. I signed up.

tschady02:12:55

I put my answers in tests then use a test runner that profiles.