diff --git a/src/bin/d22p1.rs b/src/bin/d22p1.rs index 8075842..b3a16af 100644 --- a/src/bin/d22p1.rs +++ b/src/bin/d22p1.rs @@ -1,4 +1,7 @@ -use aoc22::{day22, util}; +use aoc22::{ + day22::{self, Navigable}, + util, +}; pub fn main() { let (grid, instructions) = day22::parse_map_and_path(&util::parse_input()); @@ -9,7 +12,7 @@ pub fn main() { let mut pose = day22::Pose::new(&grid); // dbg!(&pose); for inst in &instructions { - pose = day22::exec_instruction(&grid, &pose, inst); + pose = grid.exec_instruction(&pose, inst).0; // dbg!(&pose); } diff --git a/src/day22.rs b/src/day22.rs index 653e1c2..e51f701 100644 --- a/src/day22.rs +++ b/src/day22.rs @@ -9,13 +9,64 @@ pub enum TurnDir { } #[derive(Debug)] -pub enum Instruction { - Move(isize), +pub enum PassInstr { + Move(usize), Turn(TurnDir), } +#[derive(Debug, Clone)] +pub struct Pose { + pub pos: (usize, usize), + pub orientation: Dir, +} + +impl Pose { + pub fn new(grid: &SparseGrid) -> Pose { + let col = grid.grid[0] + .iter() + .position(|t| if let Some(occ) = t { !occ } else { false }) + .unwrap(); + Pose { + pos: (0, col), + orientation: Dir::Right, + } + } +} + +pub enum MoveResult { + Blocked, + BlockedByWrapping, + WrappedAround, + Success, +} + +impl MoveResult { + pub fn was_blocked(&self) -> bool { + matches!(self, MoveResult::Blocked | MoveResult::BlockedByWrapping) + } + + pub fn wrapped_around(&self) -> bool { + matches!( + self, + MoveResult::WrappedAround | MoveResult::BlockedByWrapping + ) + } +} + +pub trait Navigable { + type Pose; + type NavigationInstruction; + + fn forward(&self, from: &Self::Pose, steps: usize) -> (Self::Pose, MoveResult); + fn exec_instruction( + &self, + from: &Self::Pose, + inst: &Self::NavigationInstruction, + ) -> (Self::Pose, MoveResult); +} + #[derive(Debug)] -pub struct Grid { +pub struct SparseGrid { pub grid: Vec>>, pub height: usize, pub width: usize, @@ -23,7 +74,94 @@ pub struct Grid { pub col_bounds: Vec<(usize, usize)>, } -pub fn parse_map_and_path(input: &str) -> (Grid, Vec) { +impl Navigable for SparseGrid { + type Pose = Pose; + type NavigationInstruction = PassInstr; + + fn forward(&self, from: &Pose, steps: usize) -> (Pose, MoveResult) { + let mut pose = from.clone(); + let pos = &mut pose.pos; + let mut status = MoveResult::Success; + for _ in 0..steps { + let next_pos = match from.orientation { + Dir::Left => { + let bounds = &self.row_bounds[pos.0]; + let next_col = if pos.1 == 0 || pos.1 == bounds.0 { + status = MoveResult::WrappedAround; + bounds.1 + } else { + pos.1 - 1 + }; + (pos.0, next_col) + } + Dir::Up => { + let bounds = &self.col_bounds[pos.1]; + let next_row = if pos.0 == 0 || pos.0 == bounds.0 { + status = MoveResult::WrappedAround; + bounds.1 + } else { + pos.0 - 1 + }; + (next_row, pos.1) + } + Dir::Right => { + let bounds = &self.row_bounds[pos.0]; + let next_col = if pos.1 == bounds.1 { + status = MoveResult::WrappedAround; + bounds.0 + } else { + pos.1 + 1 + }; + (pos.0, next_col) + } + Dir::Down => { + let bounds = &self.col_bounds[pos.1]; + let next_row = if pos.0 == bounds.1 { + status = MoveResult::WrappedAround; + bounds.0 + } else { + pos.0 + 1 + }; + (next_row, pos.1) + } + }; + + if self.grid[next_pos.0][next_pos.1].unwrap() { + // We're about to hit a wall + if status.wrapped_around() { + return (pose, MoveResult::BlockedByWrapping); + } else { + return (pose, MoveResult::Blocked); + } + } + *pos = next_pos; + } + + (pose, status) + } + + fn exec_instruction( + &self, + from: &Self::Pose, + inst: &Self::NavigationInstruction, + ) -> (Self::Pose, MoveResult) { + match inst { + PassInstr::Move(n) => self.forward(from, *n), + PassInstr::Turn(dir) => ( + Pose { + pos: from.pos, + orientation: match dir { + TurnDir::CW => from.orientation.cw(), + TurnDir::CCW => from.orientation.ccw(), + }, + }, + MoveResult::Success, + ), + } + } +} + +pub fn parse_map_and_path(input: &str) -> (SparseGrid, Vec) { let lines = input.lines().collect_vec(); let mut grid: Vec>> = Vec::new(); @@ -62,7 +200,7 @@ pub fn parse_map_and_path(input: &str) -> (Grid, Vec) { col_bounds[col] = (lower, upper); } - let grid = Grid { + let grid = SparseGrid { grid, height, width, @@ -84,7 +222,7 @@ pub fn parse_map_and_path(input: &str) -> (Grid, Vec) { .unwrap() .parse() .unwrap(); - instructions.push(Instruction::Move(dist)); + instructions.push(PassInstr::Move(dist)); inst_begin = i; continue; } @@ -94,95 +232,9 @@ pub fn parse_map_and_path(input: &str) -> (Grid, Vec) { b'R' => TurnDir::CW, _ => panic!("Unknown turn direction {}", c), }; - instructions.push(Instruction::Turn(dir)); + instructions.push(PassInstr::Turn(dir)); inst_begin += 1; } (grid, instructions) } - -#[derive(Debug)] -pub struct Pose { - pub pos: (usize, usize), - pub orientation: Dir, -} - -impl Pose { - pub fn new(grid: &Grid) -> Pose { - let col = grid.grid[0] - .iter() - .position(|t| if let Some(occ) = t { !occ } else { false }) - .unwrap(); - Pose { - pos: (0, col), - orientation: Dir::Right, - } - } -} - -pub fn exec_instruction(grid: &Grid, pose: &Pose, instr: &Instruction) -> Pose { - match instr { - Instruction::Turn(turn_dir) => Pose { - pos: pose.pos, - orientation: match turn_dir { - TurnDir::CW => pose.orientation.cw(), - TurnDir::CCW => pose.orientation.ccw(), - }, - }, - Instruction::Move(n) => { - let mut pos = pose.pos; - - for _ in 0..*n { - let next_pos = match pose.orientation { - Dir::Left => { - let bounds = &grid.row_bounds[pos.0]; - let next_col = if pos.1 == 0 || pos.1 == bounds.0 { - bounds.1 - } else { - pos.1 - 1 - }; - (pos.0, next_col) - } - Dir::Up => { - let bounds = &grid.col_bounds[pos.1]; - let next_row = if pos.0 == 0 || pos.0 == bounds.0 { - bounds.1 - } else { - pos.0 - 1 - }; - (next_row, pos.1) - } - Dir::Right => { - let bounds = &grid.row_bounds[pos.0]; - let next_col = if pos.1 == bounds.1 { - bounds.0 - } else { - pos.1 + 1 - }; - (pos.0, next_col) - } - Dir::Down => { - let bounds = &grid.col_bounds[pos.1]; - let next_row = if pos.0 == bounds.1 { - bounds.0 - } else { - pos.0 + 1 - }; - (next_row, pos.1) - } - }; - - if grid.grid[next_pos.0][next_pos.1].unwrap() { - // We're about to hit a wall - break; - } - pos = next_pos; - } - - Pose { - pos, - orientation: pose.orientation, - } - } - } -}