day08 finished
This commit is contained in:
parent
745abc936e
commit
a2f7808f6d
4 changed files with 339 additions and 1 deletions
232
src/days/day08/mod.rs
Normal file
232
src/days/day08/mod.rs
Normal file
|
|
@ -0,0 +1,232 @@
|
|||
use super::template::{DayTrait, ResultType};
|
||||
use itertools::{iproduct, FoldWhile, Itertools};
|
||||
use thiserror::Error;
|
||||
|
||||
const DAY_NUMBER: usize = 8;
|
||||
|
||||
pub struct Day;
|
||||
|
||||
impl DayTrait for Day {
|
||||
fn get_day_number(&self) -> usize {
|
||||
DAY_NUMBER
|
||||
}
|
||||
|
||||
fn part1(&self, lines: &[String]) -> anyhow::Result<ResultType> {
|
||||
let forest = Forest::parse(lines)?;
|
||||
let result = forest.count_visible();
|
||||
Ok(ResultType::Integer(result))
|
||||
}
|
||||
|
||||
fn part2(&self, lines: &[String]) -> anyhow::Result<ResultType> {
|
||||
let forest = Forest::parse(lines)?;
|
||||
let result = forest.best_score();
|
||||
Ok(ResultType::Integer(result))
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
enum ForestError {
|
||||
#[error("Not a legal digit: {0}")]
|
||||
NoLegalDigit(char),
|
||||
|
||||
#[error("The Forst is not a rectangle")]
|
||||
NonRectangleForest,
|
||||
|
||||
#[error("Zero Sized Forest not allowed")]
|
||||
NoZeroSizedForest,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct Forest {
|
||||
trees: Vec<Vec<u32>>,
|
||||
width: usize,
|
||||
height: usize,
|
||||
}
|
||||
|
||||
impl Forest {
|
||||
pub fn create(trees: Vec<Vec<u32>>) -> Result<Self, ForestError> {
|
||||
let height = trees.len();
|
||||
if height == 0 {
|
||||
return Err(ForestError::NoZeroSizedForest);
|
||||
}
|
||||
let width = trees[0].len();
|
||||
if width == 0 {
|
||||
return Err(ForestError::NoZeroSizedForest);
|
||||
}
|
||||
|
||||
for row in trees.iter() {
|
||||
if row.len() != width {
|
||||
return Err(ForestError::NonRectangleForest);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(Forest {
|
||||
trees,
|
||||
width,
|
||||
height,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn parse(lines: &[String]) -> Result<Forest, ForestError> {
|
||||
lines
|
||||
.iter()
|
||||
.map(|line| {
|
||||
line.chars()
|
||||
.map(|height| height.to_digit(10).ok_or(ForestError::NoLegalDigit(height)))
|
||||
.collect::<Result<Vec<_>, _>>()
|
||||
})
|
||||
.collect::<Result<Vec<_>, _>>()
|
||||
.and_then(Forest::create)
|
||||
}
|
||||
|
||||
pub fn count_visible(&self) -> i64 {
|
||||
let mut vis_count = 2 * (self.width + self.height - 2) as i64;
|
||||
let mut visible = vec![vec![false; self.width - 2]; self.height - 2];
|
||||
|
||||
for y in 1..self.height - 1 {
|
||||
let mut talest_seen = self.trees[y][0];
|
||||
for x in 1..self.width - 1 {
|
||||
let tree = self.trees[y][x];
|
||||
if tree > talest_seen {
|
||||
vis_count += 1;
|
||||
visible[y - 1][x - 1] = true;
|
||||
talest_seen = self.trees[y][x];
|
||||
}
|
||||
}
|
||||
|
||||
let mut talest_seen = self.trees[y][self.width - 1];
|
||||
for x in (1..self.width - 1).rev() {
|
||||
let tree = self.trees[y][x];
|
||||
if tree > talest_seen {
|
||||
if !visible[y - 1][x - 1] {
|
||||
vis_count += 1;
|
||||
visible[y - 1][x - 1] = true;
|
||||
}
|
||||
talest_seen = self.trees[y][x];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for x in 1..self.width - 1 {
|
||||
let mut talest_seen = self.trees[0][x];
|
||||
for y in 1..self.height - 1 {
|
||||
let tree = self.trees[y][x];
|
||||
if tree > talest_seen {
|
||||
if !visible[y - 1][x - 1] {
|
||||
vis_count += 1;
|
||||
visible[y - 1][x - 1] = true;
|
||||
}
|
||||
talest_seen = self.trees[y][x];
|
||||
}
|
||||
}
|
||||
|
||||
let mut talest_seen = self.trees[self.height - 1][x];
|
||||
for y in (1..self.height - 1).rev() {
|
||||
let tree = self.trees[y][x];
|
||||
if tree > talest_seen {
|
||||
if !visible[y - 1][x - 1] {
|
||||
vis_count += 1;
|
||||
visible[y - 1][x - 1] = true;
|
||||
}
|
||||
talest_seen = self.trees[y][x];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
vis_count
|
||||
}
|
||||
|
||||
pub fn best_score(&self) -> i64 {
|
||||
iproduct!(1..self.width - 1, 1..self.height - 1)
|
||||
.map(|(x, y)| self.score(x, y))
|
||||
.max()
|
||||
// None can only happen when we have a forrest with no inner trees.
|
||||
// In that case 0 is the correct solution.
|
||||
// We already forbid zero height and zero width forests during create.
|
||||
.unwrap_or(0)
|
||||
}
|
||||
|
||||
fn score(&self, x: usize, y: usize) -> i64 {
|
||||
let tree = self.trees[y][x];
|
||||
let left = (0..x)
|
||||
.rev()
|
||||
.fold_while(0, |acc, x| {
|
||||
if self.trees[y][x] < tree {
|
||||
FoldWhile::Continue(acc + 1)
|
||||
} else {
|
||||
FoldWhile::Done(acc + 1)
|
||||
}
|
||||
})
|
||||
.into_inner();
|
||||
let right = (x + 1..self.width)
|
||||
.fold_while(0, |acc, x| {
|
||||
if self.trees[y][x] < tree {
|
||||
FoldWhile::Continue(acc + 1)
|
||||
} else {
|
||||
FoldWhile::Done(acc + 1)
|
||||
}
|
||||
})
|
||||
.into_inner();
|
||||
let up = (0..y)
|
||||
.rev()
|
||||
.fold_while(0, |acc, y| {
|
||||
if self.trees[y][x] < tree {
|
||||
FoldWhile::Continue(acc + 1)
|
||||
} else {
|
||||
FoldWhile::Done(acc + 1)
|
||||
}
|
||||
})
|
||||
.into_inner();
|
||||
let down = (y + 1..self.height)
|
||||
.fold_while(0, |acc, y| {
|
||||
if self.trees[y][x] < tree {
|
||||
FoldWhile::Continue(acc + 1)
|
||||
} else {
|
||||
FoldWhile::Done(acc + 1)
|
||||
}
|
||||
})
|
||||
.into_inner();
|
||||
|
||||
left * right * up * down
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
use crate::common::file::read_lines;
|
||||
use anyhow::Result;
|
||||
|
||||
#[test]
|
||||
fn test_parse() -> Result<()> {
|
||||
let day = Day {};
|
||||
let lines = read_lines(day.get_day_number(), "example01.txt")?;
|
||||
let forest = Forest::parse(&lines)?;
|
||||
assert_eq!(forest.width, 5);
|
||||
assert_eq!(forest.height, 5);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_part1() -> Result<()> {
|
||||
let day = Day {};
|
||||
let lines = read_lines(day.get_day_number(), "example01.txt")?;
|
||||
let expected = ResultType::Integer(21);
|
||||
let result = day.part1(&lines)?;
|
||||
assert_eq!(result, expected);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_part2() -> Result<()> {
|
||||
let day = Day {};
|
||||
let lines = read_lines(day.get_day_number(), "example01.txt")?;
|
||||
let expected = ResultType::Integer(8);
|
||||
let result = day.part2(&lines)?;
|
||||
assert_eq!(result, expected);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue