Fork me on GitHub
#adventofcode
<
2021-12-02
>
alekszelark04:12:36

🧵 Day 2 answers thread: post your answers here

R.A. Porter05:12:08

A bit inelegant, but it got the job done.

👍 3
mchampine06:12:58

Some wasted effort using maps, and could be cleaner but it got the job done.

👍 3
tylerw06:12:52

Here’s mine:

(def input (->> (u/day-input-source 2)
                (into [] (map (comp edn/read-string #(str "{:" % "}"))))))

(defn t1
  [input]
  (let [combined (reduce (fn [acc x] (merge-with + acc x)) input)
        {:keys [forward up down]} combined]
    (* forward (- down up))))

(defn t2
  [input]
  (let [f (fn [acc action]
            (let [[direction x] (-> action seq first)]
              (case direction
                :down    (update acc :aim + x)
                :up      (update acc :aim - x)
                :forward (-> acc
                             (update :horiz + x)
                             (update :depth + (* (:aim acc) x))))))
        init (zipmap [:horiz :depth :aim] (repeat 0))
        {:keys [horiz depth]} (reduce f init input)]
    (* horiz depth)))

👍 3
nbardiuk08:12:43

My first implementation was similar to alekszelark, this one is already refactored https://github.com/nbardiuk/adventofcode/blob/master/2021/src/day02.clj

👏 2
Antonio Bibiano08:12:40

I also went with a vector instead of a map because I think the argument destructuring makes it quite clear anyway

(defn answer [[distance depth]]
  (* distance depth))

(answer
 (reduce
  (fn [[distance depth] [direction step]]
    (case direction
      :forward [(+ distance step) depth]
      :down [distance (+ depth step)]
      :up [distance (- depth step)]))
  [0 0]
  input))


(answer
 (reduce
  (fn [[distance depth aim] [direction step]]
    (case direction
      :forward [(+ distance step) (+ depth (* aim step)) aim]
      :down [distance depth (+ aim step)]
      :up [distance depth (- aim step)]))
  [0 0 0]
  input))

👍 4
Callum Oakley08:12:21

part-2 aim is identical to part-1 depth, so the same reduction works for both

(defn parse [s]
  (->> s (re-seq #"\w+") (map read-string) (partition 2)))

;; This covers parts 1 and 2: for part-1 treat aim as depth and ignore dep
(defn dive [commands]
  (reduce (fn [[hor aim dep] [op x]]
            (case op
              forward [(+ hor x) aim (+ (* aim x) dep)]
              down [hor (+ aim x) dep]
              up [hor (- aim x) dep]))
          [0 0 0]
          commands))

(defn part-1 []
  (let [[hor dep _] (->> "input/2021/02" slurp parse dive)]
    (* hor dep)))

(defn part-2 []
  (let [[hor _ dep] (->> "input/2021/02" slurp parse dive)]
    (* hor dep)))

👍 5
wow 1
ajk08:12:35

(defn parse-input [input]
  (map (fn [line]
         (let [[direction amount] (string/split line #" ")]
           [(keyword direction)
            (Integer/parseInt amount)]))
       (string/split-lines input)))

(def input (parse-input (slurp "aoc-day-2.txt")))
(defn part-one [commands]
  (loop [commands commands
         horizontal-position 0
         vertical-position 0]
    (if-let [[direction amount] (first commands)]
      (case direction
        :forward (recur (rest commands)
                        (+ horizontal-position amount)
                        vertical-position)
        :up (recur (rest commands)
                   horizontal-position
                   (- vertical-position amount))
        :down (recur (rest commands)
                     horizontal-position
                     (+ vertical-position amount)))
      (* horizontal-position vertical-position))))
(defn part-two [commands]
  (loop [commands commands
         aim 0
         horizontal-position 0
         depth 0]
    (if-let [[direction amount] (first commands)]
      (case direction
        :forward (recur (rest commands)
                        aim
                        (+ horizontal-position amount)
                        (+ depth (* amount aim)))
        :up (recur (rest commands)
                   (- aim amount)
                   horizontal-position
                   depth)
        :down (recur (rest commands)
                     (+ aim amount)
                     horizontal-position
                     depth))
      (* horizontal-position depth))))

👍 1
Stuart09:12:17

(defn parser [l]
  (let [[dir v] (str/split l #" ")]
    {:dir dir
     :v (Integer/parseInt v)}))

; part 1
(->> (f/read-all-lines-and-parse puzzle-input parser)
     (reduce (fn [[x y] {:keys [dir v]}]
               (condp = dir
                 "forward" [(+ x v) y]
                 "up" [x (- y v)]
                 "down" [x (+ y v)])) [0 0])
     (reduce * 1))

; part 2
(->> (f/read-all-lines-and-parse puzzle-input parser)
     (reduce (fn [[x y aim] {:keys [dir v]}]
               (condp = dir
                 "forward" [(+ x v) (+ y (* aim v)) aim]
                 "up"      [x y (- aim v)]
                 "down"    [x y (+ aim v)])) [0 0 0])
     (take 2)
     (reduce * 1))

👍 1
Joe10:12:40

https://github.com/RedPenguin101/aoc2021/blob/main/clojure/src/aoc2021/day02.clj

(defn parse [line]
  (let [x (Long/parseLong (re-find #"\d+" line))]
    (case (first line)
      \f [x 0]
      \u [0 (- x)]
      \d [0 x])))

(->> input
     (map parse)
     (apply map +)
     (apply *))
;; => 2073315

(defn proc [[x y aim] [p d-aim]]
  [(+ x p)
   (+ y (* p aim))
   (+ aim d-aim)])

(->> input
     (map parse)
     (reduce proc [0 0 0])
     (take 2)
     (apply *))
;; => 1840311528

👍 3
Antonio Bibiano10:12:53

What do you think about parsing the file like this:

(->> "path"
     slurp
     (format "{%s}")
     read-string)

Stuart10:12:29

As I understand it, its probably fine for Advent of Code, but you wouldn't want to read untrusted input with (read-string) in real world stuff as it can execute code.

Antonio Bibiano11:12:30

ah right! it's right there in the docs :man-facepalming:

nbardiuk11:12:37

As I understand clojure.edn/read-string is safer alternative and works for AoC fine since input is just data, no function definitions, etc.

yes 1
nooga11:12:43

I’m just pasting the input data directly into my code i.e. (def input '(..data here..)) 😄

😉 1
genmeblog12:12:38

btw. thanks @borkdude, I learned that case works on symbols without quoting...

potetm14:12:52

Interesting to see everyone’s slight variations on the exact same thing 😄

potetm14:12:39

@U0105JJK1L3 Did one novel thing in that he resolved to a direction tuple (e.g. [-1 0]) in the first step, which eliminated later switches.

potetm14:12:06

Also good to see how much mine looks like @borkdude’s. Apparently I’m not too far off the beaten path 😄

Stuart14:12:23

How do people decide wether to use case , condor condp ?

potetm15:12:23

case - closed value dispatch cond - conditional logic dispatch condp - never

Felipe Cortez15:12:33

haven't profiled it, but I imagine case is always faster so I use it when I can, cond when case isn't enough, condp when cond isn't enough condp when it's cleaner than cond

borkdude15:12:56

I considered making the most hacky version for 2 day by rewriting the input to s-expressions, (forward 2), etc and then making functions for forward, etc so I could just run the program. ;)

💯 3
potetm15:12:06

I literally have no idea what condp is good for.

borkdude15:12:22

I never use condp, it's dead to me ;)

Felipe Cortez15:12:36

@borkdude same! (wrt the lispy rewriting)

potetm15:12:39

I’ve seen people who make messes use it. But that’s it.

potetm15:12:14

@borkdude That sounds like the best kind of over-engineering tbh.

Felipe Cortez15:12:32

I recently finished https://beautifulracket.com/ and it seems like Racket really encourages this

Felipe Cortez15:12:06

read strings, turn them into s-exps somehow, write your DSL logic

kpav15:12:02

Did it last night, but here it is (not at all clean or clojurey, but im still learning!) https://github.com/kwpav/advent-of-code-2021/blob/master/src/advent_of_code_2021/day02.clj

R.A. Porter16:12:07

I couldn't resist reimplementing part 1 after @borkdude mentioned it...

👌 2
nooga16:12:14

Note that this won’t work in Clojure. for the one who tells me why. I still don’t have proper assoc/`update` and destructuring so it looks a bit weird 🤷

borkdude16:12:05

@UJEK1RGJ0 case doesn't expect you to quote the symbols

1
2
borkdude16:12:15

they are literals, not evaluated

borkdude16:12:52

so in clojure you're basically matching against (quote forward)

borkdude16:12:56

(case 'foo
  foo 1) ;;=> 1

Chase17:12:21

https://github.com/Chase-Lambert/aoc-clojure/blob/main/src/aoc/2021/day_02.clj Oh man, some of the answers here are great. I could not figure out how to use reduce for part-2 because I kept thinking I needed a 2 arity function but I have to track 3 things so I had to resort to my usual ugly loop/recur method

Chase17:12:59

They made the connection that aim for part 2 could just be your depth for part 1.

Andrew Byala17:12:57

I refactored mine to create a "mover" function which knows how to move forward, down, and up for both parts, and then plugged the mover into a common solver function that uses reduce. Curious about anyone's thoughts. https://github.com/abyala/advent-2021-clojure/blob/main/src/advent_2021_clojure/day02.clj

Ben Sless17:12:36

Another sexpr oriented solution is to just compile the instructions:

(reduce comp (map (fn [[d n]] (fn [sub] (move sub d n))) moves))

Ben Sless17:12:14

If you compile it, you just get function(s). Why does it matter when you do it?

Mario C.17:12:56

My solution for day2. Maybe too much abstraction :thinking_face: 😅

(def puzzle-input
  (->> (str/split (input "day2.txt") #"\n")
       (mapv (fn [instruction]
               (let [[direction steps] (str/split instruction #" ")]
                 [(keyword direction) (read-string steps)])))))

(def instruction-map
  {:forward [(fn [{:keys [x] :as current-position} steps]
               (assoc current-position :x (+ x steps)))]
   :down    [(fn [{:keys [y] :as current-position} steps]
               (assoc current-position :y (+ y steps)))]
   :up      [(fn [{:keys [y] :as current-position} steps]
               (assoc current-position :y (- y steps)))]})

(def instruction-map-z
  {:forward [(fn [{:keys [x] :as current-position} steps]
               (assoc current-position :x (+ x steps)))
             (fn [{:keys [y z] :as current-position} steps]
               (assoc current-position :y (+ y (* z steps))))]
   :down    [(fn [{:keys [z] :as current-position} steps]
               (assoc current-position :z (+ z steps)))]
   :up      [(fn [{:keys [z] :as current-position} steps]
               (assoc current-position :z (- z steps)))]})

(defn move-sub [instruction-map input]
  (reduce (fn [current-position [direction steps]]
            (reduce (fn [cp f] (f cp steps))
                    current-position
                    (get instruction-map direction)))
          {:x 0 :y 0 :z 0}
          input))

(defn solution [instruction-map input]
  (let [{:keys [x y]} (move-sub instruction-map input)]
    (* x y)))

(comment
  ;; Part 1
  (solution instruction-map puzzle-input)
  ;; Part 2
  (solution instruction-map-z puzzle-input))

Antonio Bibiano19:12:31

love the blogging approach 🙂

tschady19:12:24

@U016C0EGHUN which tool do you use to process your source to create that blog? i was looking at marginalia but it didn’t quite fit.

Ben Sless20:12:09

Solved with awk for fun

BEGIN {
    x=0;
    y=0;
    a=0
    #
    t["forward"]=0;
    t["up"]=-1;
    t["down"]=1;
    #
    h["forward"]=1;
    h["up"]=0;
    h["down"]=0;
    #
    v["forward"]=1;
    v["up"]=0;
    v["down"]=0;
}
{
    a = a + t[$1]*$2
    x = x + h[$1]*$2;
    y = y + v[$1]*$2*a;
}
END {
    print x*y;
}

Ben Sless20:12:18

minified!

BEGIN {x=0; y=0; a=0 t["f"]=0; t["u"]=-1; t["d"]=1; h["f"]=1; h["u"]=0; h["d"]=0; v["f"]=1; v["u"]=0; v["d"]=0;} {c=substr($1,1,1) a = a + t[c]*$2 x = x + h[c]*$2; y = y + v[c]*$2*a;} END {print x*y;}

😱 1
😍 1
Ben Sless20:12:47

Fits in a tweet

Ben Sless20:12:25

Reminds me of the annoying questions from intro to C, "do it without conditionals"

borkdude20:12:49

and now write an awk interpreter that fits in a tweet. so we can combine both tweets using tweet-def?

Ben Sless20:12:29

I think I'll first need to bootstrap it with tweet-def itself as a dep, then just chain it, but that's cheating

Ben Sless20:12:31

Which reminds me I wanted to solve it with core.logic

ordnungswidrig21:12:34

Babashka and pointless (threading style)

# part one
pbpaste | bb -I -e '(->> *input* (partition 2) (reduce (fn [[x y] [c v]] (case (keyword (name c)) :forward [(+ x v) y] :down [x (+ y v)] :up [x (- y v)])) [0 0]) (apply *))'
# part two
pbpaste | bb -I -e '(->> *input* (partition 2) (reduce (fn [[x y a] [c v]] (case (keyword (name c)) :forward [(+ x v) (+ y (* a v)) a] :down [x y (+ a v)] :up [x y (- a v)])) [0 0 0]) butlast (apply *))'

euccastro23:12:22

my solution to day2. I'll read your solutions now; I know there are some crazy people who avoided using reduce 🙂 https://github.com/euccastro/advent-of-code-2021/blob/main/day2.clj

euccastro00:12:22

yeah, I had forgotten about the joys of using re-seq or wrapping the whole input to read it as edn...

Sam Adams03:12:08

@tws thanks! I’m interspersing the code with markdown line-comments like ;; # Header, like one does with [Clerk](https://github.com/nextjournal/clerk), and then I have a little [script](https://github.com/samharad/samharad.github.io/blob/master/src/script/aoc.clj) for transforming the namespace into a markdown file. Then [Jekyll](https://jekyllrb.com/) takes it from there.

Tero Matinlassi21:12:41

Little bit late to the game, but here goes, nevertheless…

(ns day2
  (:require [clojure.string]))

(comment
  ; part 1
  (->> "input"
       slurp
       clojure.string/split-lines
       (map #(clojure.string/split % #" "))
       (map (fn [[command value]] [command (Integer/parseInt value)]))
       (reduce (fn [[pos depth] [command value]]
                 (case command
                   "forward" [(+ pos value) depth]
                   "down" [pos (+ depth value)]
                   "up" [pos (- depth value)]))
               [0 0])
       (apply *))
  ;=> 1698735
  )

(comment
  ; part 2
  (->> "input"
       slurp
       clojure.string/split-lines
       (map #(clojure.string/split % #" "))
       (map (fn [[command value]] [command (Integer/parseInt value)]))
       (reduce (fn [[pos depth aim] [command value]]
                 (case command
                   "forward" [(+ pos value) (+ depth (* aim value)) aim]
                   "down" [pos depth (+ aim value)]
                   "up" [pos depth (- aim value)]))
               [0 0 0])
       (take 2)
       (apply *))
  ;=> 1594785890
  )

potetm14:12:05

(It’s technically a solution, but I doubt most can read awk.)

👀 1
roelof14:12:11

I cannot read and understand it

potetm14:12:01

You want me to explain them?

potetm14:12:26

day-2 is pretty straightforward

roelof14:12:25

you mean the awk syntax. No need to

potetm14:12:01

Too late

😂 1
potetm14:12:03

# For lines that start with "u", subtract the second token (i.e. the number)
# from variable "d".
/^u/ {d-=$2}
# For lines that start with "d", add the second token (i.e. the number)
# to variable "d".
/^d/ {d+=$2}
# For lines that start with "f", add the second token (i.e. the number)
# to variable "h".
/^f/ {h+=$2} 
# At the end, print the product of "d" and "h"
END {print d*h}

👏 2
roelof14:12:00

😢 again too late

Stuart14:12:51

its like someone saw regex and thought, that should be an entire programming language in of itself.

Stuart14:12:06

I can't tell yet if I love it or hate it

potetm14:12:01

Oh awk is actually awesome.

potetm14:12:34

I use it less than I used to, but you can really do a lot of work w/ it if you want.

potetm14:12:47

Well… circumstantially.

Stuart14:12:26

People at my work when they saw some CLojure were like "NOPE!, NOT HAPPENING"

Stuart14:12:37

I can't imagine if I showed something I'd written in AWK or APL

Stuart14:12:18

Hell, they even said no chance to F#

roelof14:12:21

also I also not. Can read clojure a little bit and some ruby

Stuart14:12:00

I've basically realised they aren't willing to entertain trying to learn something unless the syntax basically looks like C

potetm14:12:35

Wild to me how close-minded some people are.

potetm14:12:16

Like, if I see something I don’t like, I just say, “Yeah I don’t really like that or want to spend my time on that.” But to respond, “NOOO. THAT’S TERRIBLE. ARE YOU KIDDING?” is nuts.

roelof14:12:57

im afraid a lot of people are closed minded. Even if I look at the dicussion take a vaccin or not. Looks like there are 2 sides and both said there are right and will not listen to the other side

potetm14:12:02

Yeah just a general lacking of curiosity/willingness to be ignorant.

potetm14:12:18

Willingness to be ignorant is actually a sort of superpower imo.

roelof14:12:29

or believing a source on facebook more then a expert

Stuart14:12:52

To be fair, they do seem to be happy to hack away on just completely awful code, mainly because I don't think they know any better.

potetm14:12:08

See. The real problem is that usually when people are “just hacking away on completely awful code” very little (or nothing) is actually getting done.

Stuart14:12:13

like a program we had internally that downloaded a zip file, and extracted it to disc and ran an exse in the folder was 1000+ lines of code.

potetm14:12:34

1000 lines???

Stuart14:12:46

yes, thats not a typo, it was over 1000 lines of code

potetm14:12:53

idk, I’ll give a benefit of doubt

potetm14:12:04

things that actually run forever are often more complicated than one might assume

potetm14:12:21

error checking/recovery can occupy a lot of space

roelof15:12:47

that is right

potetm14:12:10

Oh @tws’s Day 2 is real nice.

👏 1
❤️ 3
💯 1
potetm14:12:34

I think you win. 😄

potetm14:12:41

You’re supposed to sum the partition. You’ve clearly seen some math trick that I don’t see.

Stuart14:12:00

if you have A B C D E F and you partition it by 3 and a step of 1 you get (A B C) (B C D) (C D E) etc

Stuart14:12:15

so only 1 is actually different

Stuart14:12:29

so you can jsut take diff of 1st item and 3rd item

Stuart14:12:43

i'm guessing thats what his offset of dropping 3 is doing ?

tschady14:12:44

The picture in the problem showed me. The B and C lined up, don't need to add them.

👏 4
potetm14:12:47

I like your style.

borkdude15:12:04

very clever :)

alekszelark18:12:25

@U07S8JGF7 thanks for sharing @tws’s solution, it’s really neat and smart.

aw_yeah 2
tschady18:12:13

this is how I build my empire

Chase17:12:56

parse-long is getting so much usage already. You can tell who saw the 1.11.0-alpha3 update post and who didn't. Haha

👀 1
🙂 1
👍 2
nbardiuk18:12:28

I am waiting for usage of clojure.java.math. I've just grepped my last year solution and there are only 3 cases of Math

roelof18:12:25

I did not see that post. Is there a important change then ?

roelof18:12:26

aha, that is why that post. parse-long is new 🙂

tschady17:12:57

i take my AOC way too seriously to use anything alpha.

💯 2
😆 7
borkdude20:12:27

I added the new parse-long, parse-boolean, etc functions from clojure 1.11 in #babashka 0.6.8 in case anyone wants to play with that for AoC :)

babashka 3
❤️ 2
👀 1
ordnungswidrig21:12:18

I just used -I and let bb to the parsing 😉

borkdude21:12:25

haha awesome!

Vincent Cantin06:12:13

Do you think we could ask a parse-binary in the next Clojure release?

borkdude10:12:45

For today's solution it would have been nice if parse-long supported the radix argument

R.A. Porter21:12:40

Interesting observation that doesn't mean anything (though there's probably some small degree of correlation with something). A quick scan down the list of current members of the Clojurians private leaderboard (code in this channel's description if you've not joined) shows that there are a fair number of supporters of AoC, all in the top third.

tschady22:12:46

thanks for the reminder!

R.A. Porter22:12:32

Certainly not my intent, but 👍

euccastro23:12:39

this year I'll do these about 12 hours after the puzzles get unveiled, since I'm doing them with a friend in the EST timezone and there's less of a chance that the problems will bug me into work hours 🙂