finish day 9

This commit is contained in:
Joseph Montanaro 2021-12-15 13:20:33 -08:00
parent 8991100b59
commit bf72a26b2b
2 changed files with 135 additions and 41 deletions

View File

@ -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<Self::Item> {
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: Cell,
// above: Option<Cell>,
// }
fn load(data: &str) -> eyre::Result<Vec2<Cell>> {
let num_columns = data.lines().next().unwrap().len();
@ -18,7 +56,7 @@ fn load(data: &str) -> eyre::Result<Vec2<Cell>> {
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<Vec2<Cell>> {
fn scan(map: &mut Vec2<Cell>) {
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<Cell>) -> u32 {
map.values()
.filter(|c| c.min)
.fold(0, |acc, c| acc + c.height + 1)
fn low_points(map: &Vec2<Cell>) -> impl Iterator<Item=(usize, &Cell)> {
map.values().enumerate().filter(|(_, c)| c.min)
}
fn neighbors(map: &Vec2<Cell>, 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<Cell>, 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<Cell>) -> 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))
}

View File

@ -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<VecDeque<Self::Item>>
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<I: Iterator> IterExt for I {}
@ -96,6 +125,11 @@ impl<T> Vec2<T> {
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<Item=&[T]> {
self.data.chunks_exact(self.columns)
}