diff --git a/samples/day10_1.txt b/samples/day10_1.txt new file mode 100644 index 0000000..3aea4dd --- /dev/null +++ b/samples/day10_1.txt @@ -0,0 +1,5 @@ +7-F7- +.FJ|7 +SJLL7 +|F--J +LJ.LJ diff --git a/samples/day10_2.txt b/samples/day10_2.txt new file mode 100644 index 0000000..8f950ae --- /dev/null +++ b/samples/day10_2.txt @@ -0,0 +1,10 @@ +FF7FSF7F7F7F7F7F---7 +L|LJ||||||||||||F--J +FL-7LJLJ||||||LJL-77 +F--JF--7||LJLJ7F7FJ- +L---JF-JLJ.||-FJLJJ7 +|F|F-JF---7F7-L7L|7| +|FFJF7L7F-JF7|JL---7 +7-L-JL7||F7|L7F-7F7| +L.L7LFJ|||||FJL7||LJ +L7JLJL-JLJLJL--JLJ.L diff --git a/src/bin/day10.rs b/src/bin/day10.rs new file mode 100644 index 0000000..5f1b0fa --- /dev/null +++ b/src/bin/day10.rs @@ -0,0 +1,226 @@ +use std::{collections::VecDeque, fmt::Display, fmt::Write}; + +use aoc_2023::*; + +type Output1 = i32; +type Output2 = Output1; + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +enum Tile { + Vertical, + Horizontal, + NorthEast, + NorthWest, + SouthWest, + SouthEast, + Ground, + Start, +} + +impl Tile { + fn connections(&self) -> Vec> { + match self { + Tile::Vertical => vec![Vector2D::new(0, -1), Vector2D::new(0, 1)], + Tile::Horizontal => vec![Vector2D::new(-1, 0), Vector2D::new(1, 0)], + Tile::NorthEast => vec![Vector2D::new(0, -1), Vector2D::new(1, 0)], + Tile::NorthWest => vec![Vector2D::new(0, -1), Vector2D::new(-1, 0)], + Tile::SouthWest => vec![Vector2D::new(-1, 0), Vector2D::new(0, 1)], + Tile::SouthEast => vec![Vector2D::new(0, 1), Vector2D::new(1, 0)], + Tile::Ground => vec![], + Tile::Start => vec![ + Vector2D::new(0, -1), + Vector2D::new(0, 1), + Vector2D::new(-1, 0), + Vector2D::new(1, 0), + ], + } + } + + fn next(&self, position: Vector2D) -> Vec> { + self.connections() + .iter() + .map(|dv| *dv + position) + .collect_vec() + } +} + +impl From for Tile { + fn from(value: char) -> Self { + match value { + '|' => Self::Vertical, + '-' => Self::Horizontal, + 'L' => Self::NorthEast, + 'J' => Self::NorthWest, + '7' => Self::SouthWest, + 'F' => Self::SouthEast, + '.' => Self::Ground, + 'S' => Self::Start, + _ => unreachable!("{} is an invalid tile", value), + } + } +} + +impl Display for Tile { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.write_char(match self { + Tile::Vertical => '║', + Tile::Horizontal => '═', + Tile::NorthEast => '╚', + Tile::NorthWest => '╝', + Tile::SouthWest => '╗', + Tile::SouthEast => '╔', + Tile::Ground => ' ', + Tile::Start => 'S', + }) + } +} + +impl From for char { + fn from(value: Tile) -> Self { + match value { + Tile::Vertical => '|', + Tile::Horizontal => '-', + Tile::NorthEast => 'L', + Tile::NorthWest => 'J', + Tile::SouthWest => '7', + Tile::SouthEast => 'F', + Tile::Ground => '.', + Tile::Start => 'S', + } + } +} + +struct TileMap<'a>(&'a [Vec]); +impl Display for TileMap<'_> { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + for row in self.0 { + for tile in row { + f.write_fmt(format_args!("{}", tile))?; + } + f.write_char('\n')?; + } + + Ok(()) + } +} + +struct Day10 { + pipes: Vec>, + distances: Vec>, +} + +impl Day10 { + fn find_start(&self) -> Vector2D { + for y in 0..self.pipes.len() { + for x in 0..self.pipes[y].len() { + if self.pipes[y][x] == Tile::Start { + return Vector2D::new(x.try_into().unwrap(), y.try_into().unwrap()); + } + } + } + + unreachable!("there's always start") + } + + fn bfs(&mut self) { + let mut distances = vec![vec![-1; self.pipes[0].len()]; self.pipes.len()]; + + // find the start and set the initial distance to zero + let start = self.find_start(); + distances[start] = 0; + + let mut q = VecDeque::from([start]); + while let Some(p) = q.pop_front() { + for neighbour in self.pipes[p].next(p) { + if in_range(&distances, &neighbour) + && distances[neighbour] == -1 + && self.pipes[neighbour].next(neighbour).contains(&p) + { + distances[neighbour] = distances[p] + 1; + q.push_back(neighbour); + } + } + } + + self.distances = distances; + + // debug!("{:?}", distances); + } +} + +impl Solution for Day10 { + fn new>(pathname: P) -> Self { + Self { + pipes: file_to_string(pathname) + .lines() + .map(|s| s.chars().map(|c| c.into()).collect_vec()) + .collect_vec(), + distances: vec![vec![]], + } + } + + fn part_1(&mut self) -> Output1 { + self.bfs(); + + self.distances + .iter() + .map(|row| *row.iter().max().unwrap()) + .max() + .unwrap() + } + + fn part_2(&mut self) -> Output2 { + self.bfs(); + + let mut counter = 0; + + let mut horizontally_enclosed_west = vec![false; self.pipes[0].len()]; + let mut horizontally_enclosed_east = vec![false; self.pipes[0].len()]; + + for (y, row) in self.pipes.iter().enumerate() { + let mut vertically_enclosed_north = false; + let mut vertically_enclosed_south = false; + for (x, &t) in row.iter().enumerate() { + if self.distances[y][x] != -1 { + if matches!( + t, + Tile::Vertical | Tile::NorthEast | Tile::NorthWest | Tile::Start + ) { + vertically_enclosed_north = !vertically_enclosed_north; + } + if matches!( + t, + Tile::Vertical | Tile::SouthEast | Tile::SouthWest | Tile::Start + ) { + vertically_enclosed_south = !vertically_enclosed_south; + } + + if matches!( + t, + Tile::Horizontal | Tile::NorthWest | Tile::SouthWest | Tile::Start + ) { + horizontally_enclosed_west[x] = !horizontally_enclosed_west[x]; + } + if matches!( + t, + Tile::Horizontal | Tile::NorthEast | Tile::SouthEast | Tile::Start + ) { + horizontally_enclosed_east[x] = !horizontally_enclosed_east[x]; + } + } else if (vertically_enclosed_north || vertically_enclosed_south) + && (horizontally_enclosed_east[x] || horizontally_enclosed_west[x]) + { + counter += 1; + } + } + } + + counter + } +} + +fn main() -> Result<()> { + Day10::main() +} + +test_sample!(day_10, Day10, 8, 10);