Fork me on GitHub
#adventofcode
<
2020-12-31
>
Christian10:12:55

I am new to clojure and I make my way through the aoc2020 problems. I'm interested in your opinions about my day12 code. In python I would check the values and update some data, but with clojure I wanted to stay with away from mutables for the time being. There is two things I noticed. part1 and part2 are very similar but I dont know how to make it more compact. (would you prefer a github link to this?)

(ns advent-of-code.day-12
  (:require [clojure.edn :as edn]))

(defn update-position
  "returns the coordinate changes from an instruction"
  [instruction northsouth eastwest facing]
  (let [code (first (re-find #"[NSEWLRF]" instruction))
        number (edn/read-string (re-find #"\d+" instruction))]
    (cond
      (= code \N) [(+ northsouth number) eastwest facing]
      (= code \S) [(- northsouth number) eastwest facing]
      (= code \E) [northsouth (+ eastwest number) facing]
      (= code \W) [northsouth (- eastwest number) facing]
      (= code \L) [northsouth eastwest (mod (+ 360 (- facing number)) 360)]
      (= code \R) [northsouth eastwest (mod (+ 360 (+ facing number)) 360)]
      (= code \F) (cond
                    (= facing 270) [(+ northsouth number) eastwest facing]
                    (= facing 90) [(- northsouth number) eastwest facing]
                    (= facing 0) [northsouth (+ eastwest number) facing]
                    (= facing 180) [northsouth (- eastwest number) facing]))))

(defn part-1
  "Day 12 Part 1"
  [input]
  (loop
   [instructions input
    northsouth 0
    eastwest 0
    facing 0]
    (let [instruction (first instructions)
          otherinstructions (rest instructions)
          [newns, newew, newface] (update-position instruction northsouth eastwest facing)]

      (if (empty? otherinstructions)
        (+ (Math/abs newns) (Math/abs newew))
        (recur otherinstructions newns newew newface)))))

(defn update-position2
  "returns the coordinate changes from an instruction"
  [instruction northsouth eastwest shipNS shipEW]
  (let [code (first (re-find #"[NSEWLRF]" instruction))
        number (edn/read-string (re-find #"\d+" instruction))]
    (cond
      (= code \N) [(+ northsouth number) eastwest shipNS shipEW]
      (= code \S) [(- northsouth number) eastwest shipNS shipEW]
      (= code \E) [northsouth (+ eastwest number) shipNS shipEW]
      (= code \W) [northsouth (- eastwest number) shipNS shipEW]
      (= code \F) [northsouth eastwest (+ shipNS (* number northsouth)) (+ shipEW (* number eastwest))]
      (= code \L) (cond
                    (= number 90) [eastwest (- northsouth) shipNS shipEW] ;; n=a e=b => n=b e=-a) 
                    (= number 270) [(- eastwest) northsouth shipNS shipEW]
                    (= number 180) [(- northsouth) (- eastwest) shipNS shipEW])
      (= code \R) (cond
                    (= number 270) [eastwest (- northsouth) shipNS shipEW] ;; n=a e=b => n=b e=-a) 
                    (= number 90) [(- eastwest) northsouth shipNS shipEW]
                    (= number 180) [(- northsouth) (- eastwest) shipNS shipEW]))))

(defn part-2
  "Day 12 Part 2"
  [input]
  (loop
   [instructions input
    northsouth 1
    eastwest 10
    shipNS 0
    shipEW 0]
    (let [instruction (first instructions)
          otherinstructions (rest instructions)
          [newns, newew, newShipNS, newShipEW] (update-position2 instruction northsouth eastwest shipNS shipEW)]
      
      (if (empty? otherinstructions)
        (+ (Math/abs newShipNS) (Math/abs newShipEW))
        (recur otherinstructions newns newew newShipNS newShipEW)))))

misha11:12:57

It looks ok. Replacing cond with case or condp would save you a bunch of typing though. If it would be the code you had to maintain, it would make sense to replace loop with a step function, and a (->> state (iterate step) (drop-until done?) first calc-score) "pipeline", to decouple done condition from step logic, and make step function testable and replaceable, etc. But in the context of aoc - it is perfectly fine as is.

Christian12:12:20

I had case first, but it would not evaliate my code

misha13:12:17

can you show me?

Christian14:12:50

Ah, it was another day. I think I was trying to compare my value to something. Like if x is smaller than 4 and bigger than -14. and that would not work.

Max Deineko15:12:32

Apart from case and cond there's also core.match for when you'd prefer something like

(match [code (/ number 90) (<= 4 x 13)]
  [\N _ true] (do-something)
  [\R d _] (rotate-smth-by d))

👀 1
kingcode20:01:53

I use the cycle fn to do rotations. Also I find the most enjoyable part is to ‘clean up’ after part 1 and first modularize part 1 and retest, than add your part 2 tweaks on top of that. A good rule of thumb is to keep each function to 3-10 of no more than 80 columns for easier reading. Cheers!

👀 1
kingcode20:01:06

...”3-10 lines whenever possible...”, sorry

Stuart18:12:56

I'm having trouble understanding the logic for day 17

If a cube is active and exactly 2 or 3 of its neighbors are also active, the cube remains active. Otherwise, the cube becomes inactive.
    If a cube is inactive but exactly 3 of its neighbors are active, the cube becomes active. Otherwise, the cube remains inactive.

z=0
.#.
..#
###

After 1 cycle:

z=-1
#..
..#
.#.
How does top left in z=-1 turn on? It has 1 neighbour that is on (the top middle on z=0)

holymackerels18:12:30

the examples for this one are confusing, it's not keeping like [0 0] on the top left, its always centering the display around the visible active ones

holymackerels18:12:01

its mentioned but briefly, I spent way to much time trying to make sense of the examples until someone pointed it out to me: > (and the frame of view follows the active cells in each cycle)

Stuart18:12:25

i saw that and didn't understand what it meant 😞