Navigable trait
This commit is contained in:
parent
5e6870d092
commit
ae143d4c57
@ -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);
|
||||
}
|
||||
|
||||
|
238
src/day22.rs
238
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<Vec<Option<bool>>>,
|
||||
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<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 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);
|
||||
}
|
||||
|
||||
let grid = Grid {
|
||||
let grid = SparseGrid {
|
||||
grid,
|
||||
height,
|
||||
width,
|
||||
@ -84,7 +222,7 @@ pub fn parse_map_and_path(input: &str) -> (Grid, Vec<Instruction>) {
|
||||
.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<Instruction>) {
|
||||
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,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user