Day 8 - Solutions
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))))https://github.com/leinfink/aoc-2022-clj/blob/main/src/aoc22/day8.clj
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 ๐
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
@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}
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
Using clojure.core.matrix: https://github.com/tylerw/advent-of-code-2022/blob/master/src/aoc2022/day08.cljc
with split-at and split-with today. https://github.com/genmeblog/advent-of-code/blob/master/src/advent_of_code_2022/day08.clj
I've over engineered again https://github.com/nbardiuk/adventofcode/blob/master/2022/src/day08.clj
https://github.com/FelipeCortez/advent-of-code/blob/master/2022/08.clj
https://github.com/jramosg/advent-of-code/blob/master/src/advent_of_code/year_2022/day_08/main.clj
eduction https://github.com/zelark/AoC-2022/blob/master/src/zelark/aoc_2022/day_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
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
Day 8: https://github.com/dogenpunk/advent-of-code/blob/main/src/aoc22/day08.cljc
Day 8 https://github.com/bhauman/advent-of-code-2022/blob/main/src/adv2022/day8/sol.clj
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
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.
https://github.com/benjamin-asdf/advent-of-code/blob/master/src/Y2022/day8.clj
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))))@nooga sust checking if youโve seen this https://weinholt.se/articles/loko-scheme-2022-q4/
its a scheme thatโs the author has bootstrapped to a bare metal compiler into an OS of course.
nope ๐ฎ
all the cool stuff is on Mastodon it seems
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
https://github.com/rap1ds/advent-of-code-2022/blob/main/src/day8.clj
core.matrix, medley, recycled helpers https://github.com/tschady/advent-of-code/blob/main/src/aoc/2022/d08.clj
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. ๐
nice https://github.com/callum-oakley/advent-of-code/blob/main/src/aoc/grid.clj @c.oakley108!
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))
how dumb
๐คฆ
nobody saw me editing it, right ๐
Depends :D
As long as you don't use flatten, you'll be alright
btw. is there a more idiomatic way than (count (filter identity ...)) to count trues in coll of bools?
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))Sometimes I encode true as 1 and false as zero, then just reduce.
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
(four-way or seen)doesn't work
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 : )
lol
@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!
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
ok thx!
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
๐
(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-goand 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 seenmy 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.
(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.(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.Your solution is not dumb at all. Efficient and elegant!