adventofcode 2022-12-08

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))))

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.

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

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

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

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.

(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

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

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

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

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

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

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

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.

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

๐Ÿ˜ 2

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

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

๐Ÿ‘ 1

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

๐Ÿ‘ 1

nope ๐Ÿ˜ฎ

all the cool stuff is on Mastodon it seems

๐Ÿ˜‚ 1

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

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

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.

oh it looks like thats what you did โ€ฆ

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

it's a map-ness monster

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! ๐Ÿ˜

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 @felipecortezfi :))

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

nobody saw me editing it, right ๐Ÿ˜

Depends :D

๐Ÿ˜‚ 1

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

๐Ÿ‘€ 2
๐Ÿ˜„ 7

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

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

๐Ÿ‘ 1

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

(four-way or seen)doesn't work

Thanks for the repo @xnooga!

๐Ÿ‘ 1

wow chatgpt is freaky

thank you! ๐Ÿ˜Š

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}

performant it is not

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

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)))))))

always wanted to use reductions for something : )

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

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

๐Ÿ˜‚ 1

@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

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

โ˜๏ธ 1
๐Ÿ™Œ 2

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

I'm not writing Clojure though

my or is a function

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

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

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

nope. got an ugly one instead.

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

๐Ÿ‘ 1

(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

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'

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

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

the functional solutions are more concise.

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)))
                 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

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

โค๏ธ 1