123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216 |
- use aoc2023_niels_overkamp::common::{self, AOCResult};
- use std::collections::HashSet;
- const DAY: &str = "day10";
- fn main() -> Result<(), Box<dyn std::error::Error>> {
- common::run(DAY, &run)
- }
- #[derive(Clone, Copy, Debug, PartialEq, Eq)]
- enum Curvature {
- Right = -1, Straight = 0, Left = 1
- }
- impl Curvature {
- fn as_number(&self) -> isize {
- *self as isize
- }
- }
- #[derive(Clone, Copy, Debug, PartialEq, Eq)]
- enum Direction {
- North, East, South, West
- }
- impl Direction {
- fn take_pipe(&self, pipe: &u8) -> Option<(Self, Curvature)> {
- match (pipe, self) {
- (b'|', Self::North) => Some((Self::North, Curvature::Straight)),
- (b'|', Self::South) => Some((Self::South, Curvature::Straight)),
- (b'-', Self::West) => Some((Self::West, Curvature::Straight)),
- (b'-', Self::East) => Some((Self::East, Curvature::Straight)),
- (b'L', Self::South) => Some((Self::East, Curvature::Left)),
- (b'L', Self::West) => Some((Self::North, Curvature::Right)),
- (b'J', Self::South) => Some((Self::West, Curvature::Right)),
- (b'J', Self::East) => Some((Self::North, Curvature::Left)),
- (b'7', Self::East) => Some((Self::South, Curvature::Right)),
- (b'7', Self::North) => Some((Self::West, Curvature::Left)),
- (b'F', Self::West) => Some((Self::South, Curvature::Left)),
- (b'F', Self::North) => Some((Self::East, Curvature::Right)),
- _ => None
- }
- }
-
- fn curvature(&self, other: &Direction) -> Curvature {
- if *self == *other {
- Curvature::Straight
- } else if self.left() == *other {
- Curvature::Left
- } else {
- Curvature::Right
- }
- }
-
- fn do_move(&self, (x, y): (usize, usize)) -> Option<(usize, usize)> {
- match self {
- Self::North => y.checked_sub(1).map(|y| (x, y)),
- Self::East => Some((x + 1, y)),
- Self::South => Some((x, y + 1)),
- Self::West => x.checked_sub(1).map(|x| (x, y))
- }
- }
-
- fn left(&self) -> Self {
- match self {
- Self::North => Self::West,
- Self::East => Self::North,
- Self::South => Self::East,
- Self::West => Self::South
- }
- }
-
- fn right(&self) -> Self {
- match self {
- Self::West => Self::North,
- Self::North => Self::East,
- Self::East => Self::South,
- Self::South => Self::West
- }
- }
- }
- const DIRECTIONS: [Direction;4] = [Direction::North, Direction::East, Direction::South, Direction::West];
- pub fn run(input: &Vec<String>) -> AOCResult {
- let mut start = None;
- 'outer: for (y, line) in input.iter().enumerate() {
- for (x, b) in line.as_bytes().iter().enumerate() {
- if *b == b'S' {
- start = Some((x,y));
- break 'outer;
- }
- }
- }
-
- let input: Vec<&[u8]> = input.into_iter().map(|s| s.as_bytes()).collect();
- let (mut x, mut y) = start.ok_or::<Box<dyn std::error::Error>>("No S found in input".into())?;
- let mut start_direction = None;
-
- if let Some(_) = Direction::East.take_pipe(&input[y][x + 1]) {
- start_direction = Some(Direction::East);
- } else if let Some(_) = Direction::South.take_pipe(&input[y + 1][x]) {
- start_direction = Some(Direction::South);
- } else if x > 0 {
- if let Some(_) = Direction::West.take_pipe(&input[y][x - 1]) {
- start_direction = Some(Direction::West);
- }
- } else if y > 0 {
- if let Some(_) = Direction::North.take_pipe(&input[y][x - 1]) {
- start_direction = Some(Direction::North);
- }
- }
-
- let mut direction = start_direction.ok_or::<Box<dyn std::error::Error>>("No valid connection to S found".into())?;
- let start_direction = direction;
- let mut loop_size = 0;
- let mut curvature = 0;
-
- let mut left_outline = HashSet::new();
- let mut line = HashSet::new();
- let mut right_outline = HashSet::new();
-
-
- loop {
- loop_size += 1;
- (x, y) = direction.do_move((x, y)).ok_or_else(|| -> Box<dyn std::error::Error> { format!("Invalid move at {}, {} {:?}", x, y, direction).into() })?;
- line.insert((x,y));
- let pipe = input[y][x];
- let (out_direction, pipe_curvature) = if pipe == b'S' {
- (start_direction, direction.curvature(&start_direction))
- } else {
- direction
- .take_pipe(&pipe)
- .ok_or_else(|| -> Box<dyn std::error::Error> { format!("No valid connection at {}, {} {:?}", x, y, direction).into() })?
- };
- // println!("{}, {}, {:?}-{:?}->{:?}", x, y, direction, pipe_curvature, out_direction);
- match pipe_curvature {
- Curvature::Left => {
- if let Some(coord) = out_direction.right().do_move((x,y)) {
- // println!("{:?} right", coord);
- right_outline.insert(coord);
- }
- if let Some(coord) = direction.right().do_move((x,y)) {
- // println!("{:?} right", coord);
- right_outline.insert(coord);
- }
- }
- Curvature::Right => {
- if let Some(coord) = out_direction.left().do_move((x,y)) {
- // println!("{:?} left", coord);
- left_outline.insert(coord);
- }
- if let Some(coord) = direction.left().do_move((x,y)) {
- // println!("{:?} left", coord);
- left_outline.insert(coord);
- }
- }
- Curvature::Straight => ()
- }
-
- direction = out_direction;
- curvature += pipe_curvature.as_number();
-
- if pipe == b'S' {
- break
- }
- }
-
- // println!("{:?}", left_outline);
- // println!("{:?}", right_outline);
- // println!("{:?}", line);
- // println!("{}", curvature);
-
- let outline = if curvature.signum() == (Curvature::Left as isize) {
- left_outline
- } else {
- right_outline
- };
-
- let mut visited = HashSet::new();
- let mut inner_count = 0;
-
- let mut frontier = vec![];
- for coord in outline.into_iter() {
- if visited.contains(&coord) || line.contains(&coord) {
- continue
- }
- frontier.push(coord);
- while let Some(coord) = frontier.pop() {
- if visited.contains(&coord) || line.contains(&coord) {
- continue
- }
- if !line.contains(&coord) {
- println!("{:?} {:?}", coord, visited);
- visited.insert(coord);
- inner_count += 1;
- }
- DIRECTIONS.iter()
- .for_each(|direction| {
- direction
- .do_move(coord)
- .filter(|coord| !visited.contains(coord) && !line.contains(coord))
- .map(|coord| frontier.push(coord));
- });
- }
- }
- Ok([Some((loop_size / 2).to_string()), Some(inner_count.to_string())])
- }
- #[test]
- pub fn test_day10() {
- assert!(common::run_test(DAY, &run))
- }
|