implement day 9

This commit is contained in:
Jan Christian Grünhage 2021-12-10 08:17:38 +01:00
parent e02dbc3577
commit 0416116a65
4 changed files with 201 additions and 0 deletions

63
src/day_09/mod.rs Normal file
View 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
View 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
View 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());
}
}

View file

@ -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 }