use nom::{ branch::alt, bytes::complete::tag, character::complete::{line_ending, u32}, combinator::{map, value}, multi::{many1, separated_list1}, sequence::tuple, IResult, }; use std::fs; fn main() -> Result<(), Box> { let input = fs::read_to_string("inputs/day_22.txt")?; let parsed = Input::parser(&input).unwrap().1; dbg!(State::walk_the_map(&parsed)); dbg!(State::walk_the_map(&parsed).score()); Ok(()) } #[derive(Debug, Clone)] struct Input { map: Map, instructions: Vec, } #[derive(Debug, Clone)] struct Map(Vec>); #[derive(Debug, Clone, Copy, PartialEq, Eq)] enum MapPoint { Wall, Empty, Void, } #[derive(Debug, Clone)] enum Instruction { TurnLeft, TurnRight, Walk(u32), } impl Input { fn parser(input: &str) -> IResult<&str, Self> { map( tuple(( Map::parser, line_ending, line_ending, many1(Instruction::parser), )), |(map, _, _, instructions)| Input { map, instructions }, )(input) } } impl Map { fn parser(input: &str) -> IResult<&str, Self> { map(separated_list1(line_ending, many1(MapPoint::parser)), Map)(input) } } impl MapPoint { fn parser(input: &str) -> IResult<&str, Self> { alt(( value(MapPoint::Wall, tag("#")), value(MapPoint::Empty, tag(".")), value(MapPoint::Void, tag(" ")), ))(input) } } impl Instruction { fn parser(input: &str) -> IResult<&str, Self> { alt(( value(Instruction::TurnLeft, tag("L")), value(Instruction::TurnRight, tag("R")), map(u32, Instruction::Walk), ))(input) } } #[derive(Debug, Clone)] struct State { position: Point, facing: Direction, } #[derive(Debug, Clone)] struct Point { x: usize, y: usize, } #[derive(Debug, Clone)] enum Direction { Right, Down, Left, Up, } impl State { fn walk_the_map(input: &Input) -> State { let mut state = State::spawn(&input.map); for instruction in &input.instructions { state.process_instruction(&input.map, &instruction); dbg!((&instruction, &state)); } state } fn spawn(map: &Map) -> State { let y = 0; let x = map.0[y].iter().position(|p| p == &MapPoint::Empty).unwrap(); State { position: Point { x, y }, facing: Direction::Right, } } fn process_instruction(&mut self, map: &Map, instruction: &Instruction) { match instruction { Instruction::TurnLeft => { self.facing = self.facing.spin_left(); } Instruction::TurnRight => { self.facing = self.facing.spin_right(); } Instruction::Walk(amount) => { for _ in 0..*amount { let mut next_point = self.position.clone(); let mut made_a_step = false; let mut hit_a_wall = false; while !made_a_step && !hit_a_wall { let peek = match self.facing { Direction::Right => Point { x: if next_point.x < map.0[next_point.y].len() - 1 { next_point.x + 1 } else { 0 }, ..next_point }, Direction::Left => Point { x: if next_point.x > 0 { next_point.x - 1 } else { map.0[next_point.y].len() - 1 }, ..next_point }, Direction::Down => Point { y: if next_point.y < map.0.len() - 1 { next_point.y + 1 } else { 0 }, ..next_point }, Direction::Up => Point { y: if next_point.y > 0 { next_point.y - 1 } else { map.0.len() - 1 }, ..next_point }, }; let peek_value = map.0[peek.y].get(peek.x); match peek_value { Some(MapPoint::Empty) => { next_point = peek; made_a_step = true; } Some(MapPoint::Void) | None => { next_point = peek; } Some(MapPoint::Wall) => { hit_a_wall = true; } } } if !hit_a_wall { self.position = next_point; } } } } } fn score(&self) -> usize { (self.position.y + 1) * 1000 + (self.position.x + 1) * 4 + self.facing.score() } } impl Direction { fn spin_left(&self) -> Direction { match self { Direction::Right => Direction::Up, Direction::Down => Direction::Right, Direction::Left => Direction::Down, Direction::Up => Direction::Left, } } fn spin_right(&self) -> Direction { self.spin_left().spin_left().spin_left() } fn score(&self) -> usize { match self { Direction::Right => 0, Direction::Down => 1, Direction::Left => 2, Direction::Up => 3, } } }