diff --git a/2021/src/day9.rs b/2021/src/day9.rs index b6f5182..025e74a 100644 --- a/2021/src/day9.rs +++ b/2021/src/day9.rs @@ -1,7 +1,9 @@ +use std::collections::HashSet; + use color_eyre::eyre; use eyre::eyre; -use crate::lib::Vec2; +use crate::lib::{IterExt, Vec2}; #[derive(Debug)] @@ -10,6 +12,42 @@ struct Cell { min: bool, } +struct NeighborsIter { + x: usize, + y: usize, + rows: usize, + cols: usize, + state: usize, +} + +impl Iterator for NeighborsIter { + type Item = (usize, usize); + fn next(&mut self) -> Option { + let neighbor = match self.state { + 0 if self.x > 0 => Some((self.y, self.x - 1)), + 1 if self.y > 0 => Some((self.y - 1, self.x)), + 2 if self.x < self.cols - 1 => Some((self.y, self.x + 1)), + 3 if self.y < self.rows - 1 => Some((self.y + 1, self.x)), + _ => None, + }; + self.state += 1; + + if self.state > 4 { + None + } + else { + neighbor.or_else(|| self.next()) + } + } +} + + +// struct CellWindow { +// before: Option, +// cell: Cell, +// above: Option, +// } + fn load(data: &str) -> eyre::Result> { let num_columns = data.lines().next().unwrap().len(); @@ -18,7 +56,7 @@ fn load(data: &str) -> eyre::Result> { let mut row = Vec::new(); for c in line.chars() { match c.to_digit(10) { - Some(d) => row.push(Cell {height: d, min: true}), + Some(d) => row.push(Cell {height: d, min: false}), None => return Err(eyre!("Invalid character (not a digit): {}", c)), } } @@ -29,62 +67,84 @@ fn load(data: &str) -> eyre::Result> { fn scan(map: &mut Vec2) { - for r in 0..map.row_count() { + for row in 0..map.row_count() { for col in 0..map.col_count() { + let mut lt_left = true; if col > 0 { - if map[r][col].height <= map[r][col - 1].height { - map[r][col - 1].min = false; - if map[r][col].height == map[r][col - 1].height { - map[r][col].min = false; - } + if map[row][col].height < map[row][col - 1].height { + map[row][col - 1].min = false; + } + else { + lt_left = false; } } - if r > 0 { - if map[r][col].height <= map[r - 1][col].height { - map[r - 1][col].min = false; - if map[r][col].height == map[r - 1][col].height { - map[r][col].min = false; - } + let mut lt_top = true; + if row > 0 { + if map[row][col].height < map[row - 1][col].height { + map[row - 1][col].min = false; } + else { + lt_top = false; + } + } + + if lt_left && lt_top { + map[row][col].min = true; } } } - - // for (r, row) in map.rows().enumerate() { - // for (col, cell) in row.iter().enumerate() { - // if col > 0 && cell.height < row[col - 1].height { - // map[r][col - 1].min = false; - // } - - // if r > 0 && cell.height < map[r - 1][col].height { - // map[r - 1][col].min = false; - // } - // } - // } } -fn part1(map: &Vec2) -> u32 { - map.values() - .filter(|c| c.min) - .fold(0, |acc, c| acc + c.height + 1) +fn low_points(map: &Vec2) -> impl Iterator { + map.values().enumerate().filter(|(_, c)| c.min) +} + + +fn neighbors(map: &Vec2, coords: (usize, usize)) -> NeighborsIter { + NeighborsIter { + x: coords.1, + y: coords.0, + rows: map.row_count(), + cols: map.col_count(), + state: 0, + } +} + + +fn basin_size (map: &Vec2, start: (usize, usize), visited: &mut HashSet<(usize, usize)>) -> u32 { + let mut total = 1; + for coords in neighbors(map, start) { + if map[coords.0][coords.1].height < 9 && !visited.contains(&coords) { + visited.insert(coords); + total += basin_size(map, coords, visited); + } + } + total +} + + +fn basins(map: &Vec2) -> u32 { + low_points(map) + .map(|(i, _)| { + let start = map.coords(i); + basin_size(map, start, &mut HashSet::from([start])) + }) + .max_n(3) + .unwrap() + .iter() + .product() } pub fn run(data: &str) -> eyre::Result<(u32, u32)> { - let sample = "2199943210\n3987894921\n9856789892\n8767896789\n9899965678"; - let mut map = load(sample)?; + // let sample = "2199943210\n3987894921\n9856789892\n8767896789\n9899965678"; + let mut map = load(data)?; scan(&mut map); - dbg!(&map[1], &map[2]); + let one = low_points(&map).fold(0, |acc, (_, c)| acc + c.height + 1); + let two = basins(&map); - for (i, cell) in map.values().enumerate() { - if cell.min { - println!("{}, {} ({})", i / map.row_count(), i % map.row_count(), cell.height) - } - } - - let one = part1(&map); - Ok((one, 0)) + Ok((one, two)) } diff --git a/2021/src/lib.rs b/2021/src/lib.rs index 341c2c2..5deb0fc 100644 --- a/2021/src/lib.rs +++ b/2021/src/lib.rs @@ -1,4 +1,5 @@ use std::io::Read; +use std::collections::VecDeque; use std::str::FromStr; use std::fs::File; use std::ops::{Index, IndexMut}; @@ -67,6 +68,34 @@ pub trait IterExt: Iterator { }; Ok((a, b)) } + + fn max_n(&mut self, n: usize) -> eyre::Result> + where Self: Sized, Self::Item: Ord, + { + let mut items = VecDeque::with_capacity(n); + while let Some(item) = self.next() { + for i in 0..=items.len() { + if i < items.len() && item > items[i] { + if items.len() == n { + items.pop_back(); + } + items.insert(i, item); + break; + } + else if i == items.len() && i < n { + items.push_back(item); + break; + } + } + } + + if items.len() < n { + Err(eyre!("Not enough values to take the maximum {}", n)) + } + else { + Ok(items) + } + } } impl IterExt for I {} @@ -96,6 +125,11 @@ impl Vec2 { self.columns } + pub fn coords(&self, i: usize) -> (usize, usize) { + // convert underlying index into 2d coordinates + (i / self.columns, i % self.columns) + } + pub fn rows(&self) -> impl Iterator { self.data.chunks_exact(self.columns) }