Navigable trait

This commit is contained in:
jazzpi 2022-12-22 15:00:15 +01:00
parent 5e6870d092
commit ae143d4c57
2 changed files with 150 additions and 95 deletions

View File

@ -1,4 +1,7 @@
use aoc22::{day22, util}; use aoc22::{
day22::{self, Navigable},
util,
};
pub fn main() { pub fn main() {
let (grid, instructions) = day22::parse_map_and_path(&util::parse_input()); 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); let mut pose = day22::Pose::new(&grid);
// dbg!(&pose); // dbg!(&pose);
for inst in &instructions { for inst in &instructions {
pose = day22::exec_instruction(&grid, &pose, inst); pose = grid.exec_instruction(&pose, inst).0;
// dbg!(&pose); // dbg!(&pose);
} }

View File

@ -9,13 +9,64 @@ pub enum TurnDir {
} }
#[derive(Debug)] #[derive(Debug)]
pub enum Instruction { pub enum PassInstr {
Move(isize), Move(usize),
Turn(TurnDir), 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)] #[derive(Debug)]
pub struct Grid { pub struct SparseGrid {
pub grid: Vec<Vec<Option<bool>>>, pub grid: Vec<Vec<Option<bool>>>,
pub height: usize, pub height: usize,
pub width: usize, pub width: usize,
@ -23,7 +74,94 @@ pub struct Grid {
pub col_bounds: Vec<(usize, usize)>, pub col_bounds: Vec<(usize, usize)>,
} }
pub fn parse_map_and_path(input: &str) -> (Grid, Vec<Instruction>) { 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<PassInstr>) {
let lines = input.lines().collect_vec(); let lines = input.lines().collect_vec();
let mut grid: Vec<Vec<Option<bool>>> = Vec::new(); let mut grid: Vec<Vec<Option<bool>>> = Vec::new();
@ -62,7 +200,7 @@ pub fn parse_map_and_path(input: &str) -> (Grid, Vec<Instruction>) {
col_bounds[col] = (lower, upper); col_bounds[col] = (lower, upper);
} }
let grid = Grid { let grid = SparseGrid {
grid, grid,
height, height,
width, width,
@ -84,7 +222,7 @@ pub fn parse_map_and_path(input: &str) -> (Grid, Vec<Instruction>) {
.unwrap() .unwrap()
.parse() .parse()
.unwrap(); .unwrap();
instructions.push(Instruction::Move(dist)); instructions.push(PassInstr::Move(dist));
inst_begin = i; inst_begin = i;
continue; continue;
} }
@ -94,95 +232,9 @@ pub fn parse_map_and_path(input: &str) -> (Grid, Vec<Instruction>) {
b'R' => TurnDir::CW, b'R' => TurnDir::CW,
_ => panic!("Unknown turn direction {}", c), _ => panic!("Unknown turn direction {}", c),
}; };
instructions.push(Instruction::Turn(dir)); instructions.push(PassInstr::Turn(dir));
inst_begin += 1; inst_begin += 1;
} }
(grid, instructions) (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,
}
}
}
}