Added day 10.

This commit is contained in:
Dennis Brentjes
2023-12-13 20:02:33 +01:00
parent 6f097df1cd
commit 60c9122fa4
4 changed files with 465 additions and 1 deletions

322
src/days/day10.rs Normal file
View File

@@ -0,0 +1,322 @@
use std::{path::Path, str::FromStr, ops::Div};
use itertools::Itertools;
use num::Integer;
use crate::{error::AdventError, input::read_into};
#[derive(Clone, Debug, PartialEq)]
enum Pipe {
None,
NS,
WE,
NE,
NW,
SW,
SE,
Start(Box<Pipe>)
}
#[derive(Clone, Debug)]
enum Direction {
North,
East,
South,
West,
}
impl Pipe {
fn is_start(&self) -> bool {
match self {
Pipe::Start(_) => true,
_ => false
}
}
fn has_connection(&self, direction: Direction) -> bool {
match direction {
Direction::North => match self {
Pipe::NS => true,
Pipe::NE => true,
Pipe::NW => true,
Pipe::Start(s) => s.has_connection(direction),
_ => false
},
Direction::East => match self {
Pipe::WE => true,
Pipe::NE => true,
Pipe::SE => true,
Pipe::Start(s) => s.has_connection(direction),
_ => false
},
Direction::South => match self {
Pipe::NS => true,
Pipe::SW => true,
Pipe::SE => true,
Pipe::Start(s) => s.has_connection(direction),
_ => false
},
Direction::West => match self {
Pipe::WE => true,
Pipe::NW => true,
Pipe::SW => true,
Pipe::Start(s) => s.has_connection(direction),
_ => false
}
}
}
fn other_connection(&self, dir: &Direction) -> Direction {
match (self, dir) {
(Pipe::NE, Direction::South) => Direction::East,
(Pipe::NE, Direction::West) => Direction::North,
(Pipe::NS, Direction::South) => Direction::South,
(Pipe::NS, Direction::North) => Direction::North,
(Pipe::NW, Direction::South) => Direction::West,
(Pipe::NW, Direction::East) => Direction::North,
(Pipe::SE, Direction::North) => Direction::East,
(Pipe::SE, Direction::West) => Direction::South,
(Pipe::SW, Direction::North) => Direction::West,
(Pipe::SW, Direction::East) => Direction::South,
(Pipe::WE, Direction::West) => Direction::West,
(Pipe::WE, Direction::East) => Direction::East,
(Pipe::Start(x), d) => x.other_connection(d),
(_, _) => panic!("This shouldn't happen")
}
}
}
#[derive(Debug)]
struct Network {
stride: usize,
pub pipes: Vec<Pipe>,
pub start_index: Option<usize>,
}
impl FromStr for Network {
type Err = AdventError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let stride = s.lines().next().ok_or(AdventError("No first element in array".into()))?.len();
let mut pipes = vec![];
for line in s.lines() {
for c in line.chars() {
pipes.push(match c {
'|' => Pipe::NS,
'-' => Pipe::WE,
'L' => Pipe::NE,
'J' => Pipe::NW,
'7' => Pipe::SW,
'F' => Pipe::SE,
'.' => Pipe::None,
'S' => Pipe::Start(Box::new(Pipe::None)),
_ => Err(AdventError("Invalid input character".into()))?
});
}
}
let mut network = Network { stride, pipes, start_index: None};
network.determine_start_piece()?;
Ok(network)
}
}
impl Network {
fn find_furthest(&self) -> Result<u64, AdventError> {
if let Pipe::Start(pipe) = &self.pipes[self.start_index.unwrap()] {
let (mut dir1, mut dir2): (Direction, Direction) = vec![Direction::North, Direction::East, Direction::South, Direction::West]
.iter()
.filter(|d| {
pipe.has_connection((*d).clone())
})
.map(|x| (*x).clone()).collect_tuple().unwrap();
let mut indices: Vec<(usize, usize)> = vec![(self.start_index.unwrap(), self.start_index.unwrap())];
for i in 1..u64::MAX {
let index1 = self.get_index(indices.last().unwrap().0, &dir1).unwrap();
let index2 = self.get_index(indices.last().unwrap().1, &dir2).unwrap();
if index1 == index2 {
return Ok(i);
}
indices.push((index1, index2));
dir1 = self.get_next_direction(index1, &dir1);
dir2 = self.get_next_direction(index2, &dir2);
}
panic!("this should happen");
} else {
Err(AdventError("No start pipe at start location".into()))
}
}
fn determine_start_piece(&mut self) -> Result<(), AdventError> {
let (index, _) = self.pipes.iter().find_position(|p| p.is_start()).unwrap();
self.pipes[index] = Pipe::Start(Box::new(match (
self.get_item(index, &Direction::North).unwrap_or(&Pipe::None).has_connection(Direction::South),
self.get_item(index, &Direction::East).unwrap_or(&Pipe::None).has_connection(Direction::West),
self.get_item(index, &Direction::South).unwrap_or(&Pipe::None).has_connection(Direction::South),
self.get_item(index, &Direction::West).unwrap_or(&Pipe::None).has_connection(Direction::East)
) {
(true, true, false, false) => Pipe::NE,
(true, false, true, false) => Pipe::NS,
(true, false, false, true) => Pipe::NW,
(false, true, true, false) => Pipe::SE,
(false, true, false, true) => Pipe::WE,
(false, false, true, true) => Pipe::SW,
(_, _, _, _) => Pipe::None
}));
self.start_index = Some(index);
Ok(())
}
fn get_item(&self, index: usize, direction: &Direction) -> Option<&Pipe> {
if let Some(index) = self.get_index(index, direction) {
Some(&self.pipes[index])
} else {
None
}
}
fn get_index(&self, index: usize, direction: &Direction) -> Option<usize> {
match direction {
Direction::North => {
if index < self.stride {
None
} else {
Some(index - self.stride)
}
},
Direction::East => {
if (index + 1) % self.stride == 0 {
None
} else {
Some(index + 1)
}
}
Direction::South => {
if index + self.stride >= self.pipes.len() {
None
} else {
Some(index + self.stride)
}
}
Direction::West => {
if index % self.stride == 0 {
None
} else {
Some(index - 1)
}
}
}
}
fn get_next_direction(&self, index: usize, dir: &Direction) -> Direction {
self.pipes[index].other_connection(dir)
}
fn get_loop_points(&self) -> Vec<usize> {
let mut loop_points:Vec<usize> = vec![self.start_index.unwrap()];
let mut index = loop_points[0];
let mut direction = vec![Direction::North, Direction::East, Direction::South, Direction::West]
.iter()
.filter(|d| {
self.pipes[index].has_connection((*d).clone())
})
.map(|x| (*x).clone()).next().unwrap();
loop {
index = self.get_index(index, &direction).unwrap();
let pipe = &self.pipes[index];
if let Pipe::Start(_) = pipe {
break;
}
direction = self.pipes[index].other_connection(&direction);
loop_points.push(index);
}
loop_points
}
fn get_relevant_rows(&self, loop_points: &[usize]) -> (usize, usize) {
let mut vec = loop_points.iter().map(|x| {
x.div(self.stride)
}).collect_vec();
vec.sort();
(vec[1], vec[vec.len()-2])
}
fn get_relevant_columns(&self, loop_points: &[usize]) -> (usize, usize) {
let mut vec = loop_points.iter().map(|x| {
x % self.stride
}).collect_vec();
vec.sort();
(vec[1], vec[vec.len()-2])
}
fn number_of_interior_points(&self, min_x: usize, max_x: usize, min_y: usize, max_y: usize, sorted_loop_points: &Vec<usize>) -> u64 {
let mut count = 0;
for x in min_x..=max_x {
for y in min_y..=max_y {
if self.check_interior_point(x, y, sorted_loop_points) {
count = count + 1;
}
}
}
count
}
fn check_interior_point(&self, x: usize, y: usize, sorted_loop_points: &Vec<usize>) -> bool {
let mut coords = vec![];
let mut x = x;
let mut y = y;
loop {
coords.push(y * self.stride + x);
x = x+1;
y = y+1;
if x >= self.stride || y >= self.pipes.len().div(self.stride) {
break;
}
}
if sorted_loop_points.binary_search(&coords[0]).is_ok() {
false
} else {
coords.iter().fold(0, |acc, x| {
if sorted_loop_points.binary_search(x).is_ok() {
acc + crosses(&self.pipes[*x])
} else {
acc
}
}).is_odd()
}
}
}
fn crosses(pipe: &Pipe) -> i32 {
match pipe {
Pipe::None => 0,
Pipe::NS => 1,
Pipe::WE => 1,
Pipe::NE => 0,
Pipe::NW => 1,
Pipe::SW => 0,
Pipe::SE => 1,
Pipe::Start(p) => crosses(p),
}
}
pub fn one() -> Result<u64, AdventError> {
let network = read_into::<Network>(Path::new("resources/input10.txt"))?;
Ok(network.find_furthest()?)
}
pub fn two() -> Result<u64, AdventError> {
let network = read_into::<Network>(Path::new("resources/input10.txt"))?;
let mut loop_points = network.get_loop_points();
let (min_y, max_y) = network.get_relevant_rows(&loop_points);
let (min_x, max_x) = network.get_relevant_columns(&loop_points);
loop_points.sort();
Ok(network.number_of_interior_points(min_x, max_x, min_y, max_y, &loop_points))
}

View File

@@ -6,4 +6,5 @@ pub mod day5;
pub mod day6;
pub mod day7;
pub mod day8;
pub mod day9;
pub mod day9;
pub mod day10;

View File

@@ -20,6 +20,7 @@ fn main() -> Result<(), AdventError> {
(day7::one as fn() -> Result<u64, AdventError>, day7::two as fn() -> Result<u64, AdventError>),
(day8::one as fn() -> Result<u64, AdventError>, day8::two as fn() -> Result<u64, AdventError>),
(day9::one as fn() -> Result<u64, AdventError>, day9::two as fn() -> Result<u64, AdventError>),
(day10::one as fn() -> Result<u64, AdventError>, day10::two as fn() -> Result<u64, AdventError>),
];
if env::args().len() == 1 {