Fork me on GitHub

Day 8 - Solutions

norman05:12:28 Dumb mistake on part 1 where I tested if all trees in the direction were visible, not just if the one tree was visible. I had to go back to the sample input and write the map visual to catch the mistake. Lots of time lost. Wheee

Callum Oakley06:12:17 breaking out the grid helper functions from previous years 😌

💪 1

A bit verbose as always. This felt like an ur-aoc-problem, the kind of thing an AI would generate on prompt (not in a bad way, mind you).

R.A. Porter13:12:02

Ugly as sin, and I realize this is not the first year that I've used meta to track information like this in a grid:

Mark Wardle13:12:42

Oooo using clerk is nice @U01GXCWSRMW very pretty. Here is my effort, which seems overlong compared to the problem at hand.


I used a map with [x y] as keys.. and then just a bunch of threading (like most of this month has been so far) got to pull out medley's take-upto which I find I use surprisingly often.. I wonder why it's not in clojure.core

👍 1
Michael Ducharm14:12:52

couldn't get yesterday's, so glad I was able to get today's. Starting to think that maybe it wasn't a good idea to pick AoC as my first introduction to clojure haha

👏 3

definitely late to the game today... with a rather ugly entry: now to read your solutions and realise what trick I missed! 😁


Okay, for one: Every now and then I'm reminded how in problems like this it's much nicer and neater to move unit distances from some position, rather than keep track of long lists of positions to check. And I think, "That's really nice and neat!" Then some time later it just leaks out of my ears, apparently. 🙃


another TI(re-)L: range can take a 'step'! ty @UA2U3KW0L :))


@U018D6NKRA4 np! I still feel weird when I have to inc/dec the end

Stuart13:12:23 Finally. So, last night around 9pm. I started on part 2. I was sure what I had was right and would work, and then was debugging it till just past 1am... I just couldn't find what I had wrong. I re-read the question and realised I had skipped the paragraph.

To measure the viewing distance from a given tree, look up, down, left, and right from that tree; stop if you reach an edge or at the first tree that is the same height or taller than the tree under consideration. (If a tree is right on the edge, at least one of its viewing distances will be zero.)

The Elves don't care about distant trees taller than those found by the rules above;
sigh Anyway, I solved it by creating a grid like this of height and co-ordinate.
[[[3 [0 0]] [0 [0 1]] [3 [0 2]] [7 [0 3]] [3 [0 4]]]
 [[2 [1 0]] [5 [1 1]] [5 [1 2]] [1 [1 3]] [2 [1 4]]]
 [[6 [2 0]] [5 [2 1]] [3 [2 2]] [3 [2 3]] [2 [2 4]]]
 [[3 [3 0]] [3 [3 1]] [5 [3 2]] [4 [3 3]] [9 [3 4]]]
 [[3 [4 0]] [5 [4 1]] [3 [4 2]] [9 [4 3]] [0 [4 4]]]]
Then I could just deal with each row left -> right, because even after rotating the grid and reversing rows, I still had the original cell co-ordinates.


  (:require [clojure.string :refer [split]]))

(let [nd    (mapv #(mapv (comp parse-long str) %) (split (slurp "../") #"\n"))
      td    (apply mapv vector nd)
      [my mx] [(dec (count nd)) (dec (count td))]
      views (fn [y x] [(reverse (take x (nd y))) (drop (inc x) (nd y))
                       (reverse (take y (td x))) (drop (inc y) (td x))])
      yxvs  (for [y (range 1 my) x (range 1 mx)] [y x ((nd y) x)])
      fn1   (fn [a [y x v]]
              (if (some #(every? (partial > v) %) (views y x))
                (inc a) a))
      fn2   (fn [a [y x v]]
              (let [dist #(if (< %2 v) (inc %) (reduced (inc %)))]
                (max a (->> (views y x)
                            (map #(reduce dist 0 %))
                            (reduce *)))))]
  (prn {:one (reduce fn1 (+ (* 2 my) (* 2 mx)) yxvs)
        :two (reduce fn2 0 yxvs)}))
;{:one 1785, :two 345168}

👏 1

Late to the party; here's the meat of the solution:

(defn d8-part-2-distance-can-see-in-direction
  (let [[this-h & rst] (direction)]
    (->> rst
         (take-until (fn [h] (>= h this-h)))

(defn d8-viewing-distances [tree-map x y]
  (let [{:heights/keys [north
        (d8-heights-fns tree-map x y)]

    (* (d8-part-2-distance-can-see-in-direction north)
       (d8-part-2-distance-can-see-in-direction south)
       (d8-part-2-distance-can-see-in-direction west)
       (d8-part-2-distance-can-see-in-direction east))))

(defn d8-part-2-solve
  (let [tree-map
        (d8-parse-input input)

        {:keys [height width]}
        (d8-dimensions tree-map)]
    (->> (for [x (range 1 (dec width))
               y (range 1 (dec height))]
           (d8-viewing-distances tree-map x y))


Trying to catch up, here is mine: My approach was basically to loop over a grid, consisting of the input sequence in rows and a copy transposed to columns each mapped to coordinate-indices for easier lookups. The rows and columns are reversed to consume the grid from left, right, top and bottom at the same time in each iteration, while accumulating the visible trees (part 1) or the scenic scores for each tree (part 2) in a map. I’ll have to check what others have done, there are certainly much simpler and more performant solutions.


(four-way or seen)doesn't work


I just went with scanning each row and column both ways while obtaining the columns by transposition, then reducing the 2d result, it's dumb but it works 😄


it works and it's short. i love it.

👍 1

(defn four-way [comb f]
  (let [sides (fn [ts] (map comb (f ts) (reverse (f (reverse ts)))))] 
    (mapcat (partial map comb)
            (map sides data) 
            (apply map list (map sides (apply mapv vector data))))))

(println "1:" (->> (four-way #(or % %2) seen) (filter true?) count))
(println "2:" (->> (four-way * scenic) (reduce max)))
save a few bytes.

👍 1

(let [d (->> (slurp "resources/202208")
             (re-seq #"[^\n]+")
             (map #(->> (re-seq #"\d" %) (map parse-long))))
      seen (fn [row]
             (loop [[h & t] row, top -1, r []]
               (if h
                 (recur t (max top h) (conj r (> h top)))
      scenic (fn [row]
               (loop [[h & t] row, r []]
                 (if h
                   (recur t
                          (let [l (count (take-while #(< % h) t))]
                            (conj r (if (= l (count t)) l (inc l)))))
      four-way (fn [comb f]
                 (let [rowfn (fn [row] (map comb (f row) (reverse (f (reverse row)))))]
                   (mapcat (partial map comb)
                           (map rowfn d)
                           (apply map list (map rowfn (apply map list d))))))]
  [(->> (four-way #(or % %2) seen) (filter true?) count)
   (->> (four-way * scenic) (reduce max))])
minor improvement. count forward in scenic.

👍 2
metal 2

Your solution is not dumb at all. Efficient and elegant!

❤️ 1

thank you! 😊


I went with reducing using coordinates, ugly but still manages to stay fairly concise (and below the 26 line limit : ):

  (:require [clojure.string :refer [split]]))

(let [nd    (mapv #(mapv (comp parse-long str) %) (split (slurp "../") #"\n"))
      td    (apply mapv vector nd)
      [my mx] [(dec (count nd)) (dec (count td))]
      views (fn [y x] [(reverse (take x (nd y))) (drop (inc x) (nd y))
                       (reverse (take y (td x))) (drop (inc y) (td x))])
      yxvs  (for [y (range 1 my) x (range 1 mx)] [y x ((nd y) x)])
      fn1   (fn [a [y x v]]
              (if (some #(every? (partial > v) %) (views y x))
                (inc a) a))
      fn2   (fn [a [y x v]]
              (let [dist #(if (< %2 v) (inc %) (reduced (inc %)))]
                (max a (->> (views y x)
                            (map #(reduce dist 0 %))
                            (reduce *)))))]
  (prn {:one (reduce fn1 (+ (* 2 my) (* 2 mx)) yxvs)
        :two (reduce fn2 0 yxvs)}))
;{:one 1785, :two 345168}


performant it is not


I feel like I get schooled every day by your solutions @UP82LQR9N


challenging myself and trying to find ways to cut a few bytes off your solutions @UP82LQR9N - a possible alternative to the reducing function for day 7 using fnil and reductions:

(defn parser [state [_ cmd path filesize]]
  (if (= "cd" cmd)
    (if (= path "..")
      (update state :path pop)
      (update state :path conj path))
    (let [size (parse-long filesize)]
      (reduce #(update-in %1 [:size %2] (fnil + 0) size)
              (rest (reductions conj [] (:path state)))))))


always wanted to use reductions for something : )


hey hey, this time I schooled @UP82LQR9N, not the other way round, then they schooled me on my solution though ;D


ah that was your solution @UJEK1RGJ0 ? well pardonne moi - I stand corrected, I get schooled by both of you : )

😂 1

@U4VDXB2TU I see you like concise solutions. After day 2 I decided not to go above 26 lines as a challenge to myself. Maybe you'll be interested in my AoC repo:

Apple20:12:55 this is soooooooooooo cool!

🙌 2
☝️ 1

thanks! but use at your own risk 😅 I wouldn't call it remotely mature and the similarity to Clojure is superficial at the moment 😉


Thanks for the repo @UJEK1RGJ0!

👍 1

wow chatgpt is freaky