diff options
author | cos <cos> | 2022-12-11 13:34:12 +0000 |
---|---|---|
committer | cos <cos> | 2022-12-11 15:06:21 +0000 |
commit | 24b0a259fa19bf6cba1693d9bca2a7c3859f3568 (patch) | |
tree | 1c1eddf1a147ad1b87dc94d46071feda491e4628 /2022/rust/day08 | |
parent | 4d9e8678663a1308065d8c4769db5a3e49f3c786 (diff) | |
download | adventofcode-24b0a259fa19bf6cba1693d9bca2a7c3859f3568.zip |
Add day08, 2022
Diffstat (limited to '2022/rust/day08')
-rw-r--r-- | 2022/rust/day08/Cargo.toml | 8 | ||||
-rw-r--r-- | 2022/rust/day08/src/main.rs | 211 |
2 files changed, 219 insertions, 0 deletions
diff --git a/2022/rust/day08/Cargo.toml b/2022/rust/day08/Cargo.toml new file mode 100644 index 0000000..02d05cf --- /dev/null +++ b/2022/rust/day08/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "day08" +version = "0.1.0" +edition = "2021" + +[dependencies] +aoc = { path = "../../../common/rust/aoc" } +anyhow = "1.0" diff --git a/2022/rust/day08/src/main.rs b/2022/rust/day08/src/main.rs new file mode 100644 index 0000000..1a17d93 --- /dev/null +++ b/2022/rust/day08/src/main.rs @@ -0,0 +1,211 @@ +use { + anyhow::{ + anyhow, + Context, + Result, + }, + std::{ + env::args, + fs::File, + io::{ + BufRead, + BufReader, + }, + path::Path, + }, +}; + +#[derive(Clone,Copy,Debug)] +enum Direction { + Up, + Down, + Left, + Right, +} + +#[derive(Clone,Debug,PartialEq)] +struct Position { + x: usize, + y: usize, +} + +#[derive(Debug)] +struct Trees { + size: usize, + heights: Vec<u8>, +} + +impl Trees { + fn new_with_size(size: usize, heights: Vec<u8>) -> Result<Self> { + let len = heights.len(); + if size * size == len { + Ok(Self { size, heights, }) + } else { + Err(anyhow!("Could not create Trees: {size}² ≠ {len}.")) + } + } + + fn size(&self) -> usize { + self.size + } + + fn height(&self, position: &Position) -> u8 { + let x = position.x as usize; + let y = position.y as usize; + if let Some(height) = self.heights.get(y * self.size + x) { + *height + } else { + panic!("Invalid attempt to access element x: {x}, y: {y} in Trees with size: {}.", + self.size); + } + } + + fn is_visible(&self, position: &Position, direction: Direction) -> bool { + let target_height = self.height(position); + + match direction { + Direction::Up => { + for distance in 0..position.y { + let cmp_height = self.height(&Position { x: position.x, y: distance }); + if cmp_height >= target_height { + return false; + } + } + }, + Direction::Down => { + for distance in (position.y + 1)..self.size { + let cmp_height = self.height(&Position { x: position.x, y: distance }); + if cmp_height >= target_height { + return false; + } + } + }, + Direction::Left => { + for distance in 0..position.x { + let cmp_height = self.height(&Position { x: distance, y: position.y }); + if cmp_height >= target_height { + return false; + } + } + }, + Direction::Right => { + for distance in (position.x + 1)..self.size { + let cmp_height = self.height(&Position { x: distance, y: position.y }); + if cmp_height >= target_height { + return false; + } + } + }, + } + true + } + + fn distance(&self, start: &Position, direction: &Direction) -> usize { + let target_height = self.height(start); + let position = start.clone(); + let mut distance = 0; + + match direction { + Direction::Up => { + for i in (0..position.y).rev() { + let cmp_height = self.height(&Position { x: position.x, y: i }); + distance += 1; + if cmp_height >= target_height { + break; + } + } + }, + Direction::Down => { + for i in (position.y + 1)..self.size { + let cmp_height = self.height(&Position { x: position.x, y: i }); + distance += 1; + if cmp_height >= target_height { + break; + } + } + }, + Direction::Left => { + for i in (0..position.x).rev() { + let cmp_height = self.height(&Position { x: i, y: position.y }); + distance += 1; + if cmp_height >= target_height { + break; + } + } + }, + Direction::Right => { + for i in (position.x + 1)..self.size { + let cmp_height = self.height(&Position { x: i, y: position.y }); + distance += 1; + if cmp_height >= target_height { + break; + } + } + }, + } + distance + } +} + +fn read_input<T: AsRef<Path>>(filename: T) -> Result<Trees> { + let reader = BufReader::new(File::open(filename)?); + let mut size = 0; + + let heights = reader.lines().map( + |v| { + size += 1; + let s = v?; + s.bytes().map(|height| match height { + b'0'..=b'9' => Ok(height - b'0'), + _ => Err(anyhow!("Invalid digit: '{height}'")), + }).collect::<Result<Vec<_>>>() + } + ).collect::<Result<Vec<_>>>()?.into_iter().flatten().collect(); + + Trees::new_with_size(size, heights) +} + +fn part1(input: &Trees) -> Result<usize> { + let mut positions: Vec<Position> = vec![]; + + for direction in [Direction::Up, Direction::Down, Direction::Left, Direction::Right].iter() { + for y in 0..input.size() { + for x in 0..input.size() { + let position = Position { x, y }; + if input.is_visible(&position, *direction) && !positions.contains(&position) { + positions.push(position); + } + } + } + } + + Ok(positions.len()) +} + +fn part2(input: &Trees) -> Result<usize> { + (0..input.size()).flat_map(|y| + (0..input.size()).map(move |x| { + let position = Position { x, y }; + [Direction::Up, Direction::Down, Direction::Left, Direction::Right].iter() + .map(|direction| { + input.distance(&position, direction) + }).product() + } + )).max().ok_or_else(|| anyhow!("Really, a tree patch with a 1-by-1 grid?")) +} + +fn main() -> Result<()> { + let ( do_part_1, do_part_2 ) = aoc::do_parts(); + + let filename = args().nth(1).ok_or_else(|| anyhow!("Missing input filename"))?; + let input = read_input(filename).context("Could not read input")?; + if do_part_1 { + let solution = part1(&input).context("No solution for part 1")?; + println!("Part1, solution found to be: {}", solution); + } + if do_part_2 { + let solution = part2(&input).context("No solution for part 2")?; + println!("Part2, solution found to be: {}", solution); + } + Ok(()) +} |