You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 

107 lines
3.0 KiB

import { readFileSync } from 'node:fs';
const input = readFileSync('input', 'utf-8');
// Parse grid
const grid = input
.split('\n')
.filter(line => line)
.map(line => line
.split('')
.filter(c => c)
.map(height => parseInt(height))
);
const w = grid[0]?.length || 0;
const h = grid.length;
// Confirm grid is complete
for (const row of grid) {
if (row.length !== w) {
throw new Error('Invalid grid row');
}
}
// Return an array with the remaining integers after start and up to end
function rest(start, end) {
const steps = Math.abs(start - end);
const mult = start < end ? 1 : -1;
return new Array(steps)
.fill()
.map((_, i) => start + (i + 1) * mult)
}
// Calculate the maximum value returned by getHeight when called with a range
// of values from start (non-inclusive) to end
function maxHeight(start, end, getHeight) {
return rest(start, end).reduce(
(max, pos) => Math.max(max, getHeight(pos)),
0
);
}
// Create a map where each item represents the smallest of the next tallest
// tree in all four directions
const maxMap = grid.map((row, y) => row.map((height, x) => {
const u = maxHeight(y, 0, (pos) => grid[pos][x]);
const d = maxHeight(y, h - 1, (pos) => grid[pos][x]);
const l = maxHeight(x, 0, (pos) => grid[y][pos]);
const r = maxHeight(x, w - 1, (pos) => grid[y][pos]);
// Return the smallest tree of the tallest trees in each direction
return Math.min(u, r, d, l);
}));
// Create a map of visible trees
const visibleTrees = grid.map((row, y) => row.map((height, x) => {
// Edge case
if (x === 0 || y === 0 || x === w - 1 || y === h - 1) {
return true;
}
const obscuredByHeight = maxMap[y][x];
return height > obscuredByHeight;
}));
// Sum visible trees
const totalVisibleTrees = visibleTrees
.flat()
.reduce((total, visible) => total + visible, 0);
console.log(`The total number of trees visible is: ${totalVisibleTrees}`);
// Calculate how many trees are visible based on heights returned by getHeight
// for a range of values from start (non-inclusive) to end
function countVisibleTrees(start, end, maxHeight, getHeight) {
let count = 0;
for (const pos of rest(start, end)) {
count++;
if (getHeight(pos) >= maxHeight) {
break;
}
}
return count;
}
// Calculate the scenic score for a tree
function scenicScore(x, y) {
const maxHeight = grid[y][x];
const u = countVisibleTrees(y, 0, maxHeight, (pos) => grid[pos][x]);
const d = countVisibleTrees(y, h - 1, maxHeight, (pos) => grid[pos][x]);
const l = countVisibleTrees(x, 0, maxHeight, (pos) => grid[y][pos]);
const r = countVisibleTrees(x, w - 1, maxHeight, (pos) => grid[y][pos]);
// Return the smallest tree of the tallest trees in each direction
return u * r * d * l;
}
// Create a map of scenic scores for all trees
const scenicMap = grid.map((row, y) => row.map((height, x) =>
scenicScore(x, y)
));
const highestScenicScore = Math.max(...scenicMap.flat());
console.log(`The highest possible scenic score is: ${highestScenicScore}`);