AoC 2022/8 - Messy tree patch

TL;DR

On with Advent of Code puzzle 8 from 2022: my solution is a mess.

This day’s puzzle(s) really expose my weaknesses. My many weaknesses.

Not convinced? Go look at my solution it’s a mess, full of code that’s not needed.

There’s the complexity trap. I spend way too much time thinking about how this approach takes $O(\mathit{whatever})$ and maybe there’s something better. And surely not finding it will make my solution end up slow to a crawl.

Then, after solving the whole thing, I indulge in the Reddit threads and see this visualization, which does the thing visually in reasonable time. So I’m inclined to confirm it: gut feelings bear little to no resemblance to reality.

Then there’s the fear of being made fun of. Not other people, of course, but by somebody inside me that reminds me to refactor mercilessly, so I can’t possibly code different iterations for horizontal and vertical scanning, let alone for left-to-right or right-to-left. Real programmers know better. So it took me ages to figure out and then code generic routines to implement transposing and flipping so that I can properly rotate some text.

OK, now on with the good part. This solution by s3aker is amazing: compact, to the point, and correct. Let’s then take a look at it instead of my mess…

Reading the inputs gets a bidimensional array back. This is something that took me a lot of time to decide.

my @trees = $*IN.lines.map({ .comb».Int.Array }).Array;
my \rows = +@trees;
my \cols = +@trees[0];

Part 1 scans all inner trees, assessing whether they are visible from the perimeter or not. The iteration is done over the cartesian product of the two dimensions, which is cool. The check is done in a very idiomatic and readable way: the or of assessing the visibility from each side of the perimeter. I feel humbled.

put 'part 1: ',
    2 * (rows + cols) - 4 +
    (1..^rows-1 X 1..^cols-1).grep(-> ($r,$c) {
        @trees[$r;$c] > (
            @trees[$r;^$c        ].all |
            @trees[$r;$c^..cols-1].all |
            @trees[^$r;        $c].all |
            @trees[$r^..rows-1;$c].all
    ) });

Part 2 introduces a method. To be honest, I’m not totally getting why a method and not a regular sub to call upon map’s iteration variable, but whatever. There’s the same iteration on the inner trees, this time calculating the score for each of them and taking the maximum. The score is calculated, of course, based on the visibility from the tree towards the four directions.

my method viewing-distance(@A : UInt:D $height --> UInt:D) {
    (@A.first(* ≥ $height, :k) // (+@A - 1)) + 1
}

put 'part 2: ',
    (1..^rows-1 X 1..^cols-1).map(-> ($r,$c) {
        [*] (@trees[$r;$c^...0],
             @trees[$r;$c^..cols-1],
             @trees[$r^...0;$c],
             @trees[$r^..rows-1;$c]
            ).map({ .&viewing-distance(@trees[$r;$c]) })
    }).max;

Again, this code above is not mine but by s3aker, whom I thank!

All in all, I like these puzzles. They challenge me and prod me to be a better (hobbist) programmer and gives us all the opportunity to look at amazing alternative solutions.

Stay safe!


Comments? Octodon, Twitter, GitHub, Reddit, or drop me a line!