From 172c89a6eb08d28b5bc01797f9d1230fceae51c2 Mon Sep 17 00:00:00 2001 From: Bauke Date: Thu, 9 Dec 2021 13:12:49 +0100 Subject: [PATCH] Solve day 9! --- source/day_09/mod.rs | 158 +++++++++++++++++++++++++++++++++++++++++++ source/main.rs | 2 + 2 files changed, 160 insertions(+) create mode 100644 source/day_09/mod.rs diff --git a/source/day_09/mod.rs b/source/day_09/mod.rs new file mode 100644 index 0000000..ed8454a --- /dev/null +++ b/source/day_09/mod.rs @@ -0,0 +1,158 @@ +use std::collections::HashMap; + +use color_eyre::{eyre::eyre, Result}; +use itertools::Itertools; + +pub fn solve() -> Result<()> { + let input_data = include_str!("../../data/day_09.txt").trim(); + println!("Day 09 Part 1: {}", part_1(input_data)?); + println!("Day 09 Part 2: {}", part_2(input_data)?); + Ok(()) +} + +type Coordinate = (isize, isize); + +type HeightMap = HashMap; + +const ADJACENT_OFFSETS: &[Coordinate] = &[ + (-1, 00), // Left + (01, 00), // Right + (00, -1), // Up + (00, 01), // Down +]; + +fn parse_heightmap(input: &str) -> Result { + let mut height_map = HeightMap::new(); + + for (y, line) in input.lines().enumerate() { + for (x, height) in line.char_indices() { + let height = height + .to_digit(10) + .ok_or(eyre!("Invalid input: {}", line))? + .try_into()?; + + height_map.insert((x.try_into()?, y.try_into()?), height); + } + } + + Ok(height_map) +} + +fn parse_map_bounds(input: &str) -> Result { + Ok(( + input + .lines() + .next() + .map(|line| line.chars().count()) + .ok_or(eyre!("Invalid input: {}", input))? + .try_into()?, + input.lines().count().try_into()?, + )) +} + +fn discover_basins( + coordinate: Coordinate, + discovered_coordinates: &mut Vec, + height_map: &HeightMap, +) -> Vec { + let mut coordinates = vec![]; + + for (x_offset, y_offset) in ADJACENT_OFFSETS { + let neighbor = (x_offset + coordinate.0, y_offset + coordinate.1); + if discovered_coordinates.contains(&neighbor) { + continue; + } + + discovered_coordinates.push(neighbor); + match height_map.get(&neighbor) { + Some(9) | None => continue, + Some(_) => { + coordinates.push(neighbor); + coordinates.append(&mut discover_basins( + neighbor, + discovered_coordinates, + height_map, + )); + } + } + } + + coordinates +} + +fn part_1(input: &str) -> Result { + let height_map = parse_heightmap(input)?; + let map_bounds = parse_map_bounds(input)?; + let mut risk_level = 0; + + for y in 0..map_bounds.1 { + 'x_loop: for x in 0..map_bounds.0 { + let coordinate = (x, y); + let height = height_map + .get(&coordinate) + .ok_or(eyre!("Coordinate {:?} not found", coordinate))?; + + for (x_offset, y_offset) in ADJACENT_OFFSETS { + let neighbor = (x_offset + x, y_offset + y); + + if let Some(neighbor_height) = height_map.get(&neighbor) { + if neighbor_height <= height { + continue 'x_loop; + } + } + } + + risk_level += 1 + height; + } + } + + Ok(risk_level) +} + +fn part_2(input: &str) -> Result { + let height_map = parse_heightmap(input)?; + let map_bounds = parse_map_bounds(input)?; + let mut basins = vec![]; + let mut discovered_coordinates = vec![]; + for y in 0..map_bounds.1 { + 'x_loop: for x in 0..map_bounds.0 { + let coordinate = (x, y); + if discovered_coordinates.contains(&coordinate) { + continue 'x_loop; + } + + let height = height_map + .get(&coordinate) + .ok_or(eyre!("Coordinate {:?} not found", coordinate))?; + if height == &9 { + continue 'x_loop; + } + + let mut basin = vec![coordinate]; + discovered_coordinates.push(coordinate); + basin.append(&mut discover_basins( + coordinate, + &mut discovered_coordinates, + &height_map, + )); + basins.push(basin.len()); + discovered_coordinates.append(&mut basin); + } + } + + Ok( + basins + .into_iter() + .sorted_by(|a, b| b.cmp(a)) + .take(3) + .product(), + ) +} + +#[test] +fn test_sample() -> Result<()> { + let sample = "2199943210\n3987894921\n9856789892\n8767896789\n9899965678"; + assert_eq!(part_1(sample)?, 15); + assert_eq!(part_2(sample)?, 1134); + Ok(()) +} diff --git a/source/main.rs b/source/main.rs index d44f3b3..fecf887 100644 --- a/source/main.rs +++ b/source/main.rs @@ -10,6 +10,7 @@ mod day_05; mod day_06; mod day_07; mod day_08; +mod day_09; fn main() -> Result<()> { color_eyre::install()?; @@ -25,6 +26,7 @@ fn main() -> Result<()> { day_06::solve, day_07::solve, day_08::solve, + day_09::solve, ]; for day in days {