adventofcode

2022-12-08T05:43:43.673079Z

Day 8 - Solutions

pieterbreed 2022-12-10T08:46:05.586629Z

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

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

(defn d8-viewing-distances [tree-map x y]
  (let [{:heights/keys [north
                        south
                        west
                        east]}
        (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
  [input]
  (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))
         (sort)
         (reverse)
         (first))))

peterh 2022-12-14T23:05:45.123149Z

Trying to catch up, here is mine: https://github.clerk.garden/formsandlines/aoc2022-clojure/commit/73f80fe4c2b12eed7e92ba5d3863f27a180c1fef/src/advent_of_clerk/day_08.html 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.

2022-12-08T05:46:28.466539Z

https://gitlab.com/maximoburrito/advent2022/-/blob/main/src/day08/main.clj 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 Oakley 2022-12-08T06:43:17.906499Z

https://github.com/callum-oakley/advent-of-code/blob/main/src/aoc/2022/08.clj breaking out the grid helper functions from previous years ๐Ÿ˜Œ

๐Ÿ’ช 1
Andrew Byala 2022-12-08T06:44:43.852279Z

A little parsing, a couple of transducers... all normal stuff here. โ€ข https://github.com/abyala/advent-2022-clojure/blob/main/docs/day08.md โ€ข https://github.com/abyala/advent-2022-clojure/blob/main/src/advent_2022_clojure/day08.clj

Felipe 2022-12-09T12:25:26.939339Z

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

2022-12-09T13:03:23.286929Z

https://github.com/stuartstein777/clj-advent-of-code/blob/master/src/stuartstein777/2022/day8.clj 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.

mbjarland 2022-12-09T15:09:15.709939Z

(ns aoc.day-08
  (:require [clojure.string :refer [split]]))

(let [nd    (mapv #(mapv (comp parse-long str) %) (split (slurp "../day_08.data") #"\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
motform 2022-12-08T08:44:54.008489Z

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). https://github.com/motform/advent-of-clojure/blob/master/src/advent_of_clojure/2022/08.clj

2022-12-08T09:10:48.794849Z

Using clojure.core.matrix: https://github.com/tylerw/advent-of-code-2022/blob/master/src/aoc2022/day08.cljc

๐Ÿ‘ 3
genmeblog 2022-12-08T10:28:11.869969Z

with split-at and split-with today. https://github.com/genmeblog/advent-of-code/blob/master/src/advent_of_code_2022/day08.clj

๐Ÿ†’ 3
nbardiuk 2022-12-08T11:32:12.866919Z

I've over engineered again https://github.com/nbardiuk/adventofcode/blob/master/2022/src/day08.clj

R.A. Porter 2022-12-08T13:38:02.728869Z

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: https://coyotesqrl.github.io/advent-of-code/2022/src/coyotesqrl/2022/day08.html

Mark Wardle 2022-12-08T13:58:42.100979Z

Oooo using clerk is nice @coyotesqrl very pretty. Here is my effort, which seems overlong compared to the problem at hand. https://github.com/wardle/aoc2022/blob/main/src/day08.clj

Casey 2022-12-08T14:49:28.688189Z

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) https://github.com/Ramblurr/advent-of-code-2022/blob/2bee95d7f45a4fb1572004882fb17d11b0aa82e7/src/aoc/day08.clj#L25-L59 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 Ducharm 2022-12-08T14:49:52.472179Z

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 https://github.com/mducharm/advent_of_code_2022/blob/main/src/advent_of_code/day_08.clj

๐Ÿ‘ 4
๐Ÿ‘๐Ÿป 1
wevrem 2022-12-08T16:11:50.798109Z

Couldnโ€™t get to this last night (puzzles open up at 10pm for me) but I solved it this morning. I see @ramblurr mentioned a take-upto which sounds interesting. I contemplated creating something along those lines (and that is a great name for it) but solved another way. Need to look into that. https://github.com/wevre/advent-of-code/blob/master/src/advent_of_code/2022/day_08_trees.clj

๐Ÿ‘ 1
bhauman 2022-12-08T16:21:06.918609Z

Not satisfied with my answer. I should have done the store in a map with indexes being addresses solution where you use operators on the addresses to move around. Thatโ€™s what I did no previous years. All in all happy that things are getting a little more interesting.

bhauman 2022-12-08T16:21:30.597909Z

Iโ€™m loving all of the short concise solutions that everyone is posting.

๐Ÿ˜ 2
nooga 2022-12-08T17:06:53.461629Z

this was uh, dense

(ns day8)

(def data (->> "input/day8.txt" slurp lines (mapv #(mapv (comp parse-long str) %)))) 

(defn seen [ts]
  (loop [[t & r] ts top -1 c []]
    (if t (recur r (max top t) (conj c (> t top))) c)))

(defn scenic [ts]
  (loop [[t & r] ts prev () c []]
    (if t (recur r (cons t prev)
                 (let [l (count (take-while #(< % t) prev))]
                   (conj c (if (= l (count prev)) l (inc l)))))
        c)))

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

(println "1:" (reduce + (map #(count (filter true? %)) (four-way or seen))))
(println "2:" (reduce max (apply concat (four-way * scenic))))

2
๐Ÿคฏ 5
bhauman 2022-12-08T17:09:51.160029Z

@nooga sust checking if youโ€™ve seen this https://weinholt.se/articles/loko-scheme-2022-q4/

๐Ÿ‘ 1
bhauman 2022-12-08T17:10:40.640539Z

its a scheme thatโ€™s the author has bootstrapped to a bare metal compiler into an OS of course.

๐Ÿ‘ 1
nooga 2022-12-08T17:10:41.376229Z

nope ๐Ÿ˜ฎ

bhauman 2022-12-08T17:12:12.279799Z

all the cool stuff is on Mastodon it seems

๐Ÿ˜‚ 1
nooga 2022-12-08T17:13:13.903909Z

huh, given the staggering amount of care for my solution to be well optimized, it takes a hot 200ms on my machine to compute both parts

nooga 2022-12-08T17:13:49.282569Z

I mean, including compilation of the core lib and the program itself

bhauman 2022-12-08T17:58:13.718619Z

my completely unoptimized solution for part 2 is 360ms in the repl. But this is close to O^2 for the naive approach right? You could march across the rows using info from the previous row to get solutions in linear time I expect.

bhauman 2022-12-08T17:59:03.212509Z

oh it looks like thats what you did โ€ฆ

nooga 2022-12-08T18:04:06.564319Z

neh, I look at each row and column 2 times, so there's 4 scans over the entire thing and then I look at everything again to combine the results

nooga 2022-12-08T18:04:18.928779Z

it's a map-ness monster

tschady 2022-12-08T22:42:43.577969Z

core.matrix, medley, recycled helpers https://github.com/tschady/advent-of-code/blob/main/src/aoc/2022/d08.clj

๐Ÿ‘ 2
carnundotcom 2022-12-09T00:12:54.419759Z

definitely late to the game today... with a rather ugly entry: https://github.com/CarnunMP/Advent-of-Code/blob/master/src/y2022/d8.clj now to read your solutions and realise what trick I missed! ๐Ÿ˜

carnundotcom 2022-12-09T00:31:23.777719Z

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. ๐Ÿ™ƒ

carnundotcom 2022-12-09T00:33:55.828799Z

nice https://github.com/callum-oakley/advent-of-code/blob/main/src/aoc/grid.clj @c.oakley108!

๐Ÿ˜ 1
carnundotcom 2022-12-09T00:44:16.976199Z

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

nooga 2022-12-08T18:58:21.922609Z

omg I just realized I have this in my solution: (if (> t top) (conj c true) (conj c false))

nooga 2022-12-08T18:58:28.556229Z

how dumb

nooga 2022-12-08T18:58:45.805359Z

๐Ÿคฆ

nooga 2022-12-08T19:02:06.886049Z

nobody saw me editing it, right ๐Ÿ˜

genmeblog 2022-12-08T19:03:14.057419Z

Depends :D

๐Ÿ˜‚ 1
borkdude 2022-12-08T19:04:01.203709Z

As long as you don't use flatten, you'll be alright

๐Ÿ‘€ 2
๐Ÿ˜„ 7
nooga 2022-12-08T19:06:55.296839Z

btw. is there a more idiomatic way than (count (filter identity ...)) to count trues in coll of bools?

Miฤทelis Vindavs 2022-12-11T16:18:50.303259Z

Itโ€™s not more idiomatic, but I use a helper function which is much faster than filter + count because it does not allocate as much

(defn count-where [pred coll]
  (reduce (fn [c x] (if (pred x) (inc c) c))
          0
          coll))

๐Ÿ‘ 1
genmeblog 2022-12-08T19:11:44.367129Z

Sometimes I encode true as 1 and false as zero, then just reduce.

๐Ÿ‘ 1
tschady 2022-12-08T19:32:08.227349Z

generally i find thereโ€™s a way to go back a step, use keep and not need the bools. but with that line maybe filter true? is more readable

๐Ÿ‘ 4
Apple 2022-12-08T21:02:30.294679Z

(four-way or seen)doesn't work

mbjarland 2022-12-10T12:49:02.905149Z

Thanks for the repo @xnooga!

๐Ÿ‘ 1
mbjarland 2022-12-10T14:05:56.058939Z

wow chatgpt is freaky

nooga 2022-12-09T08:49:08.360019Z

thank you! ๐Ÿ˜Š

mbjarland 2022-12-09T15:25:48.266729Z

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

(ns aoc.day-08
  (:require [clojure.string :refer [split]]))

(let [nd    (mapv #(mapv (comp parse-long str) %) (split (slurp "../day_08.data") #"\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}

mbjarland 2022-12-09T15:27:24.264279Z

performant it is not

mbjarland 2022-12-09T15:30:38.084289Z

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

mbjarland 2022-12-09T15:33:10.563479Z

challenging myself and trying to find ways to cut a few bytes off your solutions @zengxh - 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)
              state
              (rest (reductions conj [] (:path state)))))))

mbjarland 2022-12-09T15:34:17.231119Z

always wanted to use reductions for something : )

nooga 2022-12-09T15:35:18.465019Z

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

mbjarland 2022-12-09T15:38:38.604619Z

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

๐Ÿ˜‚ 1
Apple 2022-12-09T15:42:38.662679Z

lol

nooga 2022-12-09T19:32:41.744649Z

@mbjarland 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: https://github.com/nooga/aoc2022

Apple 2022-12-09T20:02:55.049189Z

https://github.com/nooga/let-go this is soooooooooooo cool!

โ˜๏ธ 1
๐Ÿ™Œ 2
nooga 2022-12-09T20:04:08.028369Z

thanks! but use at your own risk ๐Ÿ˜… I wouldn't call it remotely mature and the similarity to Clojure is superficial at the moment ๐Ÿ˜‰

nooga 2022-12-08T21:04:41.384199Z

I'm not writing Clojure though

nooga 2022-12-08T21:04:51.099809Z

my or is a function

nooga 2022-12-08T21:05:34.495229Z

I just made use of that here, though it reminded me to implement a proper or

nooga 2022-12-08T21:05:52.453309Z

you could use #(or %1 %2) instead

Apple 2022-12-08T21:06:49.377359Z

ok thx!

nooga 2022-12-08T21:08:51.802809Z

Did you manage to get a shorter solution than mine today @zengxh?

Apple 2022-12-08T21:09:22.746839Z

nope. got an ugly one instead.

nooga 2022-12-08T21:09:52.986039Z

mine is atrocious but I've made a commitment to not go above 26 LoC

๐Ÿ‘ 1
nooga 2022-12-08T21:10:18.485419Z

๐Ÿ˜„

nooga 2022-12-08T22:42:17.825069Z

(ns day8)

#?(:bb (require '[clojure.string :as string]))
#?(:bb (defn lines [s] (string/split s #"\n")))

(def data (->> "input/day8.txt" slurp lines (mapv #(mapv (comp parse-long str) %)))) 

(defn seen [ts]
  (loop [[t & r] ts top -1 c []]
    (if t (recur r (max top t) (conj c (> t top))) c)))

(defn scenic [ts]
  (loop [[t & r] ts prev () c []]
    (if t (recur r (cons t prev)
                 (let [l (count (take-while #(< % t) prev))]
                   (conj c (if (= l (count prev)) l (inc l)))))
        c)))

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

(println "1:" (reduce + (map #(count (filter true? %)) (four-way #?(:bb #(or %1 %2) :lg or) seen))))
(println "2:" (reduce max (apply concat (four-way * scenic))))
@zengxh here's a version that runs both under Babashka and let-go

nooga 2022-12-08T22:42:44.482279Z

and here's a benchmark:

โžœ  aoc2022 git:(master) โœ— hyperfine './lg day8.lg' 'bb day8.lg'
Benchmark 1: ./lg day8.lg
  Time (mean ยฑ ฯƒ):     147.1 ms ยฑ   1.6 ms    [User: 180.3 ms, System: 12.6 ms]
  Range (min โ€ฆ max):   144.3 ms โ€ฆ 151.0 ms    20 runs
 
Benchmark 2: bb day8.lg
  Time (mean ยฑ ฯƒ):      68.4 ms ยฑ   1.6 ms    [User: 55.4 ms, System: 10.5 ms]
  Range (min โ€ฆ max):    65.7 ms โ€ฆ  73.1 ms    40 runs
 
Summary
  'bb day8.lg' ran
    2.15 ยฑ 0.05 times faster than './lg day8.lg'

Apple 2022-12-08T23:48:45.532899Z

https://news.ycombinator.com/item?id=33908058

Apple 2022-12-08T23:48:57.178699Z

https://www.youtube.com/watch?v=X4fD-GhW0e4

Apple 2022-12-08T23:51:44.027829Z

find-visible-x (fn [row]
                        (->> row
                             (reduce (fn [{:keys [mark i r]} n]
                                       {:mark (max mark n)
                                        :i (inc i)
                                        :r (if (< mark n) (conj r i) r)})
                                     {:mark -1, :i 0, :r []})
                             :r))
i see some similarity to your seen

๐Ÿ‘ 1
Apple 2022-12-08T23:53:39.199039Z

my strategy is to scan line by line, get the x/y coordinates then use set to eliminate duplicates.

Apple 2022-12-08T23:55:33.328109Z

the functional solutions are more concise.

nooga 2022-12-09T00:01:04.154559Z

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 ๐Ÿ˜„

Apple 2022-12-09T00:02:30.448669Z

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

๐Ÿ‘ 1
Apple 2022-12-09T01:37:55.629339Z

(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
Apple 2022-12-09T02:10:59.129869Z

(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)))
                 r)))
      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)))))
                   r)))
      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
2
Apple 2022-12-09T02:30:52.578289Z

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

โค๏ธ 1