use nom::{ branch::alt, character::complete::{char as nom_char, line_ending}, combinator::{map, value}, multi::{many1, separated_list1}, IResult, }; use std::fs; fn main() -> Result<(), Box> { let input = fs::read_to_string("inputs/day_10.txt")?; let parsed = Maze::parser(&input).unwrap().1; dbg!(&parsed.find_furthest_point_in_loop()); Ok(()) } #[derive(Debug)] struct Maze(Vec>); #[derive(Debug, Clone, Copy, PartialEq, Eq)] enum Pipe { Start, Nothing, DownLeft, DownRight, DownUp, LeftRight, UpLeft, UpRight, } #[derive(Debug, Clone, Copy, PartialEq, Eq)] enum Direction { Up, Down, Left, Right, } #[derive(Debug, Clone, Copy, PartialEq, Eq)] struct Point { x: usize, y: usize, } impl Maze { fn parser(input: &str) -> IResult<&str, Self> { map(separated_list1(line_ending, many1(Pipe::parser)), Maze)(input) } fn find_furthest_point_in_loop(&self) -> usize { self.measure_loop_size() / 2 } fn measure_loop_size(&self) -> usize { let mut position = self.find_start(); let mut facing = if position.y > 0 && self .at(position.up()) .connections() .contains(&Direction::Down) { position = position.up(); Direction::Up } else if position.y < self.0.len() - 1 && self .at(position.down()) .connections() .contains(&Direction::Up) { position = position.down(); Direction::Down } else if position.x > 0 && self .at(position.left()) .connections() .contains(&Direction::Right) { position = position.left(); Direction::Left } else if position.x < self.0[position.y].len() - 1 && self .at(position.right()) .connections() .contains(&Direction::Left) { position = position.right(); Direction::Right } else { panic!() }; let mut distance = 1; while self.at(position) != Pipe::Start { let current_pipe = self.at(position); facing = current_pipe.exit_facing(facing).unwrap(); position = match facing { Direction::Up => position.up(), Direction::Down => position.down(), Direction::Left => position.left(), Direction::Right => position.right(), }; distance += 1; } distance } fn at(&self, p: Point) -> Pipe { self.0[p.y][p.x] } fn find_start(&self) -> Point { for (y, row) in self.0.iter().enumerate() { for (x, pipe) in row.iter().enumerate() { if *pipe == Pipe::Start { return Point { x, y }; } } } panic!("No Start!"); } } impl Pipe { fn parser(input: &str) -> IResult<&str, Self> { use Pipe::*; alt(( value(Start, nom_char('S')), value(Nothing, nom_char('.')), value(DownLeft, nom_char('7')), value(DownRight, nom_char('F')), value(DownUp, nom_char('|')), value(LeftRight, nom_char('-')), value(UpLeft, nom_char('J')), value(UpRight, nom_char('L')), ))(input) } fn connections(&self) -> Vec { use Direction::*; match self { Pipe::Start => vec![], Pipe::Nothing => vec![], Pipe::DownLeft => vec![Down, Left], Pipe::DownRight => vec![Down, Right], Pipe::DownUp => vec![Down, Up], Pipe::LeftRight => vec![Left, Right], Pipe::UpLeft => vec![Up, Left], Pipe::UpRight => vec![Up, Right], } } fn exit_facing(&self, facing: Direction) -> Option { use Direction::*; match self { Pipe::Start => None, Pipe::Nothing => None, Pipe::DownLeft => match facing { Up => Some(Left), Right => Some(Down), _ => None, }, Pipe::DownRight => match facing { Up => Some(Right), Left => Some(Down), _ => None, }, Pipe::DownUp => match facing { Up => Some(Up), Down => Some(Down), _ => None, }, Pipe::LeftRight => match facing { Left => Some(Left), Right => Some(Right), _ => None, }, Pipe::UpLeft => match facing { Down => Some(Left), Right => Some(Up), _ => None, }, Pipe::UpRight => match facing { Down => Some(Right), Left => Some(Up), _ => None, }, } } } impl Point { fn up(&self) -> Point { Point { x: self.x, y: self.y - 1, } } fn down(&self) -> Point { Point { x: self.x, y: self.y + 1, } } fn left(&self) -> Point { Point { x: self.x - 1, y: self.y, } } fn right(&self) -> Point { Point { x: self.x + 1, y: self.y, } } } // 6756 is too low