day 18 finsihed
This commit is contained in:
parent
75c7b08449
commit
7f5b6e03f9
4 changed files with 3161 additions and 1 deletions
13
data/day18/example01.txt
Normal file
13
data/day18/example01.txt
Normal file
|
|
@ -0,0 +1,13 @@
|
||||||
|
2,2,2
|
||||||
|
1,2,2
|
||||||
|
3,2,2
|
||||||
|
2,1,2
|
||||||
|
2,3,2
|
||||||
|
2,2,1
|
||||||
|
2,2,3
|
||||||
|
2,2,4
|
||||||
|
2,2,6
|
||||||
|
1,2,5
|
||||||
|
3,2,5
|
||||||
|
2,1,5
|
||||||
|
2,3,5
|
||||||
2888
data/day18/input.txt
Normal file
2888
data/day18/input.txt
Normal file
File diff suppressed because it is too large
Load diff
257
src/days/day18/mod.rs
Normal file
257
src/days/day18/mod.rs
Normal file
|
|
@ -0,0 +1,257 @@
|
||||||
|
use itertools::Itertools;
|
||||||
|
use std::{collections::HashSet, num::ParseIntError};
|
||||||
|
use thiserror::Error;
|
||||||
|
|
||||||
|
use super::template::{DayTrait, ResultType};
|
||||||
|
|
||||||
|
const DAY_NUMBER: usize = 18;
|
||||||
|
|
||||||
|
pub struct Day;
|
||||||
|
|
||||||
|
impl DayTrait for Day {
|
||||||
|
fn get_day_number(&self) -> usize {
|
||||||
|
DAY_NUMBER
|
||||||
|
}
|
||||||
|
|
||||||
|
fn part1(&self, lines: &[String]) -> anyhow::Result<ResultType> {
|
||||||
|
let blob = Blob::parse(lines)?;
|
||||||
|
Ok(ResultType::Integer(blob.count_sides()))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn part2(&self, lines: &[String]) -> anyhow::Result<ResultType> {
|
||||||
|
let blob = Blob::parse(lines)?;
|
||||||
|
Ok(ResultType::Integer(blob.count_outside()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Error)]
|
||||||
|
enum DropletError {
|
||||||
|
#[error("Illegal Droplet: {0}")]
|
||||||
|
IllegalDroplet(String),
|
||||||
|
|
||||||
|
#[error("no Integer")]
|
||||||
|
NoInteger(#[from] ParseIntError),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
|
||||||
|
struct Droplet(i64, i64, i64);
|
||||||
|
|
||||||
|
impl Droplet {
|
||||||
|
fn parse(line: &str) -> Result<Droplet, DropletError> {
|
||||||
|
let split = line.split(",").collect_vec();
|
||||||
|
if split.len() != 3 {
|
||||||
|
return Err(DropletError::IllegalDroplet(line.to_owned()));
|
||||||
|
}
|
||||||
|
let (x, y, z) = (split[0].parse()?, split[1].parse()?, split[2].parse()?);
|
||||||
|
Ok(Droplet(x, y, z))
|
||||||
|
}
|
||||||
|
pub fn neighbors(&self) -> Neighbors<'_> {
|
||||||
|
Neighbors::new(self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Neighbors<'a>(&'a Droplet, usize);
|
||||||
|
|
||||||
|
impl<'a> Neighbors<'a> {
|
||||||
|
pub fn new(droplet: &'a Droplet) -> Neighbors<'a> {
|
||||||
|
Neighbors(droplet, 0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> Iterator for Neighbors<'a> {
|
||||||
|
type Item = Droplet;
|
||||||
|
|
||||||
|
fn next(&mut self) -> Option<Self::Item> {
|
||||||
|
self.1 += 1;
|
||||||
|
match self.1 {
|
||||||
|
1 => Some(Droplet(self.0 .0 - 1, self.0 .1, self.0 .2)),
|
||||||
|
2 => Some(Droplet(self.0 .0 + 1, self.0 .1, self.0 .2)),
|
||||||
|
3 => Some(Droplet(self.0 .0, self.0 .1 - 1, self.0 .2)),
|
||||||
|
4 => Some(Droplet(self.0 .0, self.0 .1 + 1, self.0 .2)),
|
||||||
|
5 => Some(Droplet(self.0 .0, self.0 .1, self.0 .2 - 1)),
|
||||||
|
6 => Some(Droplet(self.0 .0, self.0 .1, self.0 .2 + 1)),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Ranges {
|
||||||
|
x: (i64, i64),
|
||||||
|
y: (i64, i64),
|
||||||
|
z: (i64, i64),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Ranges {
|
||||||
|
pub fn new(droplet: &Droplet) -> Self {
|
||||||
|
Ranges {
|
||||||
|
x: (droplet.0, droplet.0),
|
||||||
|
y: (droplet.1, droplet.1),
|
||||||
|
z: (droplet.2, droplet.2),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn contains(&self, droplet: &Droplet) -> bool {
|
||||||
|
self.x.0 <= droplet.0
|
||||||
|
&& droplet.0 <= self.x.1
|
||||||
|
&& self.y.0 <= droplet.1
|
||||||
|
&& droplet.1 <= self.y.1
|
||||||
|
&& self.z.0 <= droplet.2
|
||||||
|
&& droplet.2 <= self.z.1
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn grow(&mut self) {
|
||||||
|
self.x.0 -= 1;
|
||||||
|
self.x.1 += 1;
|
||||||
|
self.y.0 -= 1;
|
||||||
|
self.y.1 += 1;
|
||||||
|
self.z.0 -= 1;
|
||||||
|
self.z.1 += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn first(&self) -> Droplet {
|
||||||
|
Droplet(self.x.0, self.y.0, self.z.0)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn extend(&mut self, droplet: &Droplet) {
|
||||||
|
self.x.0 = self.x.0.min(droplet.0);
|
||||||
|
self.x.1 = self.x.1.max(droplet.0);
|
||||||
|
self.y.0 = self.y.0.min(droplet.1);
|
||||||
|
self.y.1 = self.y.1.max(droplet.1);
|
||||||
|
self.z.0 = self.z.0.min(droplet.2);
|
||||||
|
self.z.1 = self.z.1.max(droplet.2);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
|
pub fn volume(&self) -> i64 {
|
||||||
|
(self.x.1 - self.x.0 + 1) * (self.y.1 - self.y.0 + 1) * (self.z.1 - self.z.0 + 1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Blob {
|
||||||
|
droplets: HashSet<Droplet>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Blob {
|
||||||
|
fn parse(lines: &[String]) -> Result<Self, DropletError> {
|
||||||
|
let droplets = lines
|
||||||
|
.iter()
|
||||||
|
.map(|line| Droplet::parse(line))
|
||||||
|
.try_collect()?;
|
||||||
|
Ok(Blob { droplets })
|
||||||
|
}
|
||||||
|
|
||||||
|
fn extent(&self) -> Option<Ranges> {
|
||||||
|
if self.droplets.is_empty() {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut range = Ranges::new(&self.droplets.iter().next().unwrap());
|
||||||
|
for droplet in self.droplets.iter().skip(1) {
|
||||||
|
range.extend(droplet);
|
||||||
|
}
|
||||||
|
Some(range)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn count_sides(&self) -> i64 {
|
||||||
|
let mut sides = 0;
|
||||||
|
let mut known = HashSet::new();
|
||||||
|
for droplet in &self.droplets {
|
||||||
|
sides += 6;
|
||||||
|
for neighbor in droplet.neighbors() {
|
||||||
|
if known.contains(&neighbor) {
|
||||||
|
sides -= 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
known.insert(droplet);
|
||||||
|
}
|
||||||
|
sides
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn count_outside(&self) -> i64 {
|
||||||
|
let Some(mut extent)= self.extent() else {
|
||||||
|
return 0;
|
||||||
|
};
|
||||||
|
let mut sides = 0;
|
||||||
|
|
||||||
|
extent.grow();
|
||||||
|
|
||||||
|
let mut outer = HashSet::new();
|
||||||
|
let mut queue = Vec::new();
|
||||||
|
queue.push(extent.first());
|
||||||
|
while let Some(next) = queue.pop() {
|
||||||
|
for neighbor in next.neighbors() {
|
||||||
|
if extent.contains(&neighbor) && !outer.contains(&neighbor) {
|
||||||
|
if self.droplets.contains(&neighbor) {
|
||||||
|
sides += 1;
|
||||||
|
} else if !queue.contains(&neighbor) {
|
||||||
|
queue.push(neighbor);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
outer.insert(next);
|
||||||
|
}
|
||||||
|
|
||||||
|
sides
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod test {
|
||||||
|
use super::*;
|
||||||
|
use crate::common::file::read_lines;
|
||||||
|
use anyhow::Result;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_part1() -> Result<()> {
|
||||||
|
let day = Day {};
|
||||||
|
let lines = read_lines(day.get_day_number(), "example01.txt")?;
|
||||||
|
let expected = ResultType::Integer(64);
|
||||||
|
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(58);
|
||||||
|
let result = day.part2(&lines)?;
|
||||||
|
assert_eq!(result, expected);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn parse() -> Result<()> {
|
||||||
|
let input = "1,2,3";
|
||||||
|
let expected = Droplet(1, 2, 3);
|
||||||
|
let result = Droplet::parse(input)?;
|
||||||
|
assert_eq!(result, expected);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn neighbors() {
|
||||||
|
let droplet = Droplet(1, 1, 1);
|
||||||
|
let expected = vec![
|
||||||
|
Droplet(0, 1, 1),
|
||||||
|
Droplet(2, 1, 1),
|
||||||
|
Droplet(1, 0, 1),
|
||||||
|
Droplet(1, 2, 1),
|
||||||
|
Droplet(1, 1, 0),
|
||||||
|
Droplet(1, 1, 2),
|
||||||
|
];
|
||||||
|
assert_eq!(droplet.neighbors().collect_vec(), expected);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn simple() {
|
||||||
|
let blob = Blob {
|
||||||
|
droplets: HashSet::from_iter(vec![Droplet(1, 1, 1), Droplet(1, 1, 2)]),
|
||||||
|
};
|
||||||
|
assert_eq!(blob.count_sides(), 10);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -15,6 +15,7 @@ mod day14;
|
||||||
mod day15;
|
mod day15;
|
||||||
mod day16;
|
mod day16;
|
||||||
mod day17;
|
mod day17;
|
||||||
|
mod day18;
|
||||||
mod template;
|
mod template;
|
||||||
|
|
||||||
pub use template::DayTrait;
|
pub use template::DayTrait;
|
||||||
|
|
@ -24,7 +25,7 @@ pub mod day_provider {
|
||||||
use super::*;
|
use super::*;
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
|
|
||||||
const MAX_DAY: usize = 17;
|
const MAX_DAY: usize = 18;
|
||||||
|
|
||||||
pub fn get_day(day_num: usize) -> Result<Box<dyn DayTrait>, ProviderError> {
|
pub fn get_day(day_num: usize) -> Result<Box<dyn DayTrait>, ProviderError> {
|
||||||
match day_num {
|
match day_num {
|
||||||
|
|
@ -45,6 +46,7 @@ pub mod day_provider {
|
||||||
15 => Ok(Box::new(day15::Day)),
|
15 => Ok(Box::new(day15::Day)),
|
||||||
16 => Ok(Box::new(day16::Day)),
|
16 => Ok(Box::new(day16::Day)),
|
||||||
17 => Ok(Box::new(day17::Day)),
|
17 => Ok(Box::new(day17::Day)),
|
||||||
|
18 => Ok(Box::new(day18::Day)),
|
||||||
_ => Err(ProviderError::InvalidNumber(day_num)),
|
_ => Err(ProviderError::InvalidNumber(day_num)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue