diff --git a/samples/day08.txt b/samples/day08.txt new file mode 100644 index 0000000..6557024 --- /dev/null +++ b/samples/day08.txt @@ -0,0 +1,5 @@ +30373 +25512 +65332 +33549 +35390 \ No newline at end of file diff --git a/src/bin/day08.rs b/src/bin/day08.rs new file mode 100644 index 0000000..a1fe4dc --- /dev/null +++ b/src/bin/day08.rs @@ -0,0 +1,144 @@ +use std::{cmp::max, collections::BTreeSet}; + +use aoc_2022::*; + +use color_eyre::eyre::Result; +use itertools::Itertools; +use tracing::*; +use tracing_subscriber::EnvFilter; + +type Input = Vec>; +type Output = usize; + +fn count_in_rows(trees: &Input, counted: &mut BTreeSet<(usize, usize)>) { + trees.iter().enumerate().for_each(|(y, row)| { + row.iter().enumerate().fold(-1, |tallest, (x, tree)| { + if *tree > tallest { + counted.insert((x, y)); + } + + max(*tree, tallest) + }); + + row.iter().enumerate().rfold(-1, |tallest, (x, tree)| { + if *tree > tallest { + counted.insert((x, y)); + } + + max(*tree, tallest) + }); + }); +} + +fn count_in_columns(trees: &Input, counted: &mut BTreeSet<(usize, usize)>) { + (0..trees[0].len()).for_each(|x| { + (0..trees.len()).fold(-1, |tallest, y| { + if trees[y][x] > tallest { + counted.insert((x, y)); + } + + max(tallest, trees[y][x]) + }); + + (0..trees.len()).rev().fold(-1, |tallest, y| { + if trees[y][x] > tallest { + counted.insert((x, y)); + } + + max(tallest, trees[y][x]) + }); + }); +} + +fn part_1(input: &Input) -> Output { + let mut counted: BTreeSet<(usize, usize)> = BTreeSet::new(); + + count_in_rows(input, &mut counted); + count_in_columns(input, &mut counted); + + counted.len() +} + +fn in_range(trees: &Input, x: i32, y: i32) -> bool { + y >= 0 && y < trees.len() as i32 && x >= 0 && x < trees[y as usize].len() as i32 +} + +fn compute_scenic_score(trees: &Input, x: i32, y: i32) -> usize { + vec![(0, 1), (1, 0), (0, -1), (-1, 0)] + .iter() + .map(|&(dx, dy)| { + let max_height = trees[y as usize][x as usize]; + let mut d = 1; + + let mut visible = 0; + + while in_range(trees, x + d * dx, y + d * dy) { + visible += 1; + + if trees[(y + d * dy) as usize][(x + d * dx) as usize] >= max_height { + break; + } + + d += 1; + } + + visible as usize + }) + .product() +} + +fn part_2(input: &Input) -> Output { + (0..input.len()) + .flat_map(|y| { + (0..input[y].len()).map(move |x| compute_scenic_score(input, x as i32, y as i32)) + }) + .max() + .unwrap() +} + +fn parse_input(pathname: &str) -> Input { + file_to_string(pathname) + .lines() + .map(|line| { + line.chars() + .map(|c| c.to_digit(10).unwrap() as i8) + .collect_vec() + }) + .collect_vec() +} + +fn main() -> Result<()> { + tracing_subscriber::fmt() + .with_env_filter(EnvFilter::from_default_env()) + .with_target(false) + .with_file(true) + .with_line_number(true) + .without_time() + .compact() + .init(); + color_eyre::install()?; + + let input = parse_input("inputs/day08.txt"); + + info!("Part 1: {}", part_1(&input)); + info!("Part 2: {}", part_2(&input)); + + Ok(()) +} + +#[cfg(test)] +mod day_08 { + use super::*; + + #[test] + fn test_part_1() { + let sample = parse_input("samples/day08.txt"); + assert_eq!(part_1(&sample), 21); + } + + #[test] + fn test_part_2() { + let sample = parse_input("samples/day08.txt"); + assert_eq!(part_2(&sample), 8); + } +}