Added day 10.
This commit is contained in:
322
src/days/day10.rs
Normal file
322
src/days/day10.rs
Normal 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))
|
||||
}
|
||||
@@ -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;
|
||||
@@ -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 {
|
||||
|
||||
Reference in New Issue
Block a user