finish day 9
This commit is contained in:
parent
8991100b59
commit
bf72a26b2b
142
2021/src/day9.rs
142
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<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))
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user