implement day 9
This commit is contained in:
parent
e02dbc3577
commit
0416116a65
63
src/day_09/mod.rs
Normal file
63
src/day_09/mod.rs
Normal file
|
@ -0,0 +1,63 @@
|
|||
mod model;
|
||||
mod parsing;
|
||||
|
||||
use aoc_runner_derive::{aoc, aoc_generator};
|
||||
use itertools::Itertools;
|
||||
pub use model::HeightMap;
|
||||
pub use parsing::parse_map;
|
||||
use yap::IntoTokens;
|
||||
|
||||
#[aoc_generator(day9)]
|
||||
pub fn parse_input(input: &str) -> HeightMap {
|
||||
parse_map(&mut input.into_tokens())
|
||||
}
|
||||
|
||||
#[aoc(day9, part1)]
|
||||
pub fn part1(input: &HeightMap) -> usize {
|
||||
input
|
||||
.get_low_points()
|
||||
.iter()
|
||||
.map(|point| input.get_risk_level(*point))
|
||||
.flatten()
|
||||
.sum()
|
||||
}
|
||||
|
||||
#[aoc(day9, part2)]
|
||||
pub fn part2(input: &HeightMap) -> usize {
|
||||
input
|
||||
.get_basin_sizes()
|
||||
.iter()
|
||||
.sorted()
|
||||
.rev()
|
||||
.take(3)
|
||||
.product()
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
const EXAMPLE_INPUT: &str = "2199943210
|
||||
3987894921
|
||||
9856789892
|
||||
8767896789
|
||||
9899965678";
|
||||
const RESULT_PART_1: usize = 15;
|
||||
const RESULT_PART_2: usize = 1134;
|
||||
|
||||
#[test]
|
||||
fn part1_example() {
|
||||
let result = super::part1(&super::parse_input(EXAMPLE_INPUT));
|
||||
assert_eq!(result, RESULT_PART_1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn part2_example() {
|
||||
let result = super::part2(&super::parse_input(EXAMPLE_INPUT));
|
||||
assert_eq!(result, RESULT_PART_2);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_example() {
|
||||
let _example = super::parse_input(EXAMPLE_INPUT);
|
||||
//unimplemented!();
|
||||
}
|
||||
}
|
100
src/day_09/model.rs
Normal file
100
src/day_09/model.rs
Normal file
|
@ -0,0 +1,100 @@
|
|||
use itertools::Itertools;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct HeightMap {
|
||||
pub map: Vec<Vec<usize>>,
|
||||
pub basin: Vec<Vec<Option<usize>>>,
|
||||
}
|
||||
|
||||
impl HeightMap {
|
||||
pub fn new(map: Vec<Vec<usize>>) -> Self {
|
||||
let mut basin = Vec::new();
|
||||
for line in &map {
|
||||
let mut basin_line = Vec::new();
|
||||
for _point in line {
|
||||
basin_line.push(None)
|
||||
}
|
||||
basin.push(basin_line);
|
||||
}
|
||||
let mut new_self = Self { map, basin };
|
||||
let mut basin_counter = 0;
|
||||
for y in 0..new_self.map.len() {
|
||||
for x in 0..new_self.map[y].len() {
|
||||
if new_self.map[y][x] < 9 && new_self.basin[y][x].is_none() {
|
||||
new_self.flood_basin((x, y), basin_counter);
|
||||
basin_counter += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
new_self
|
||||
}
|
||||
|
||||
pub fn get_low_points(&self) -> Vec<(isize, isize)> {
|
||||
let mut low_points = Vec::new();
|
||||
for y in 0..self.map.len() {
|
||||
for x in 0..self.map[y].len() {
|
||||
if self.is_low_point((x as isize, y as isize)) {
|
||||
low_points.push((x as isize, y as isize))
|
||||
}
|
||||
}
|
||||
}
|
||||
low_points
|
||||
}
|
||||
|
||||
pub fn is_low_point(&self, position: (isize, isize)) -> bool {
|
||||
let val = match self.get(position) {
|
||||
Some(pos) => pos,
|
||||
None => return false,
|
||||
};
|
||||
let (x, y) = position;
|
||||
for (x_off, y_off) in [(1isize, 0isize), (0, 1), (-1, 0), (0, -1)].iter() {
|
||||
if let Some(neighbour) = self.get((x + x_off, y + y_off)) {
|
||||
if neighbour <= val {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
true
|
||||
}
|
||||
|
||||
pub fn flood_basin(&mut self, (x, y): (usize, usize), number: usize) {
|
||||
if let Some(basin) = self.basin[y][x] {
|
||||
assert_eq!(basin, number);
|
||||
return;
|
||||
} else {
|
||||
self.basin[y][x] = Some(number)
|
||||
}
|
||||
for (x_off, y_off) in [(1isize, 0isize), (0, 1), (-1, 0), (0, -1)].iter() {
|
||||
if let Some(neighbour) = self.get((x as isize + x_off, y as isize + y_off)) {
|
||||
if neighbour < 9 {
|
||||
self.flood_basin(
|
||||
((x as isize + x_off) as usize, (y as isize + y_off) as usize),
|
||||
number,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get(&self, (x, y): (isize, isize)) -> Option<usize> {
|
||||
let (x, y): (usize, usize) = match (x.try_into(), y.try_into()) {
|
||||
(Ok(x), Ok(y)) => (x, y),
|
||||
_ => return None,
|
||||
};
|
||||
self.map.get(y).map(|val| val.get(x)).flatten().copied()
|
||||
}
|
||||
|
||||
pub fn get_risk_level(&self, position: (isize, isize)) -> Option<usize> {
|
||||
self.get(position).map(|x| x + 1)
|
||||
}
|
||||
|
||||
pub fn get_basin_sizes(&self) -> Vec<usize> {
|
||||
self.basin
|
||||
.iter()
|
||||
.flat_map(|line| line.iter())
|
||||
.filter_map(|val| *val)
|
||||
.counts()
|
||||
.into_values()
|
||||
.collect()
|
||||
}
|
||||
}
|
37
src/day_09/parsing.rs
Normal file
37
src/day_09/parsing.rs
Normal file
|
@ -0,0 +1,37 @@
|
|||
use yap::Tokens;
|
||||
|
||||
use crate::parsing::{newline, parse_digit};
|
||||
|
||||
use super::HeightMap;
|
||||
|
||||
pub fn parse_map(tokens: &mut impl Tokens<Item = char>) -> HeightMap {
|
||||
let map: Vec<Vec<usize>> = tokens.sep_by(|t| parse_line(t), |t| newline(t)).collect();
|
||||
HeightMap::new(map)
|
||||
}
|
||||
|
||||
pub fn parse_line(tokens: &mut impl Tokens<Item = char>) -> Option<Vec<usize>> {
|
||||
let digits: Vec<usize> = tokens.many(|t| parse_digit(t)).collect();
|
||||
if digits.is_empty() {
|
||||
None
|
||||
} else {
|
||||
Some(digits)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use yap::IntoTokens;
|
||||
|
||||
#[test]
|
||||
fn parse_line() {
|
||||
assert_eq!(
|
||||
dbg!(super::parse_line(&mut "2199943210".into_tokens()).unwrap()).len(),
|
||||
10
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_map() {
|
||||
super::parse_map(&mut "01\n11".into_tokens());
|
||||
}
|
||||
}
|
|
@ -10,5 +10,6 @@ pub mod day_05;
|
|||
pub mod day_06;
|
||||
pub mod day_07;
|
||||
pub mod day_08;
|
||||
pub mod day_09;
|
||||
|
||||
aoc_lib! { year = 2021 }
|
||||
|
|
Loading…
Reference in a new issue