From 6d72191e2ce5d423ca03c894d2dad1d3061bd4f3 Mon Sep 17 00:00:00 2001 From: Justin Wernick Date: Tue, 19 Apr 2022 20:27:29 +0200 Subject: Refile for merging repos --- 2021/src/bin/day_9.rs | 186 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 186 insertions(+) create mode 100644 2021/src/bin/day_9.rs (limited to '2021/src/bin/day_9.rs') diff --git a/2021/src/bin/day_9.rs b/2021/src/bin/day_9.rs new file mode 100644 index 0000000..5ef0dae --- /dev/null +++ b/2021/src/bin/day_9.rs @@ -0,0 +1,186 @@ +use nom::{ + character::complete::{line_ending, one_of}, + combinator::{map, map_res}, + multi::{many1, separated_list1}, + IResult, +}; +use std::fs; + +fn main() -> Result<(), Box> { + let input = fs::read_to_string("inputs/day_9.txt")?; + let height_map = parse_height_map(&input).unwrap().1; + let risk_level_sum: RiskLevel = height_map.risk_levels().into_iter().sum(); + dbg!(risk_level_sum); + + let mut basin_sizes: Vec = height_map.basins.iter().map(|basin| basin.size).collect(); + basin_sizes.sort_unstable_by(|a, b| b.cmp(a)); + dbg!(basin_sizes.iter().take(3).product::()); + + Ok(()) +} + +#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Copy)] +struct Height(u8); +#[derive( + Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Copy, derive_more::Add, derive_more::Sum, +)] +struct RiskLevel(u32); + +impl From for RiskLevel { + fn from(h: Height) -> Self { + RiskLevel(h.0 as u32 + 1) + } +} + +#[derive(Debug, Default)] +struct Basin { + size: u32, +} + +#[derive(Debug)] +struct HeightMap { + heights: Vec>, + low_points: Vec<(usize, usize)>, + basins: Vec, + basin_map: Vec>>, +} + +impl HeightMap { + fn new(heights: Vec>) -> HeightMap { + let mut height_map = HeightMap { + heights, + low_points: Vec::new(), + basins: Vec::new(), + basin_map: Vec::new(), + }; + height_map.init_low_points(); + height_map.init_basins(); + height_map + } +} + +impl HeightMap { + fn init_low_points(&mut self) { + self.low_points = Vec::new(); + for y in 0..self.heights.len() { + for x in 0..self.heights[y].len() { + let current = self.heights[y][x]; + let mut is_low_point = true; + let edges = [ + (x as isize - 1, y as isize), + (x as isize + 1, y as isize), + (x as isize, y as isize - 1), + (x as isize, y as isize + 1), + ]; + for edge in edges { + if self + .get_height(edge.0, edge.1) + .map_or(false, |other| other <= current) + { + is_low_point = false; + } + } + + if is_low_point { + self.low_points.push((x, y)); + } + } + } + } + + fn init_basins(&mut self) { + for low_point in self.low_points.clone() { + if self + .get_basin(low_point.0 as isize, low_point.1 as isize) + .is_some() + { + continue; + } + + let mut basin = Basin::default(); + let basin_index = self.basins.len(); + self.set_basin(basin_index, low_point.0, low_point.1); + basin.size += 1; + let mut boundary = vec![low_point]; + while boundary.len() > 0 { + let (x, y) = boundary.pop().unwrap(); + let edges = [ + (x as isize - 1, y as isize), + (x as isize + 1, y as isize), + (x as isize, y as isize - 1), + (x as isize, y as isize + 1), + ]; + for edge in edges { + if self + .get_height(edge.0, edge.1) + .map_or(false, |other| other != Height(9)) + && self.get_basin(edge.0, edge.1).is_none() + { + let x = edge.0 as usize; + let y = edge.1 as usize; + self.set_basin(basin_index, x, y); + basin.size += 1; + boundary.push((x, y)); + } + } + } + self.basins.push(basin); + } + } + + fn get_height(&self, x: isize, y: isize) -> Option { + if x < 0 || y < 0 { + None + } else { + let x = x as usize; + let y = y as usize; + self.heights.get(y).and_then(|row| row.get(x)).cloned() + } + } + + fn get_basin(&self, x: isize, y: isize) -> Option { + if x < 0 || y < 0 { + None + } else { + let x = x as usize; + let y = y as usize; + self.basin_map + .get(y) + .and_then(|row| row.get(x)) + .cloned() + .flatten() + } + } + + fn set_basin(&mut self, basin: usize, x: usize, y: usize) { + while self.basin_map.len() <= y { + self.basin_map.push(Vec::new()); + } + while self.basin_map[y].len() <= x { + self.basin_map[y].push(None); + } + self.basin_map[y][x] = Some(basin); + } + + fn risk_levels(&self) -> Vec { + self.low_points + .iter() + .copied() + .map(|(x, y)| self.heights[y][x].clone().into()) + .collect() + } +} + +fn parse_height_map(input: &str) -> IResult<&str, HeightMap> { + map(separated_list1(line_ending, parse_row), HeightMap::new)(input) +} + +fn parse_row(input: &str) -> IResult<&str, Vec> { + many1(parse_height)(input) +} + +fn parse_height(input: &str) -> IResult<&str, Height> { + map_res(one_of("0123456789"), |digit| { + digit.to_string().parse().map(Height) + })(input) +} -- cgit v1.2.3