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() {
|
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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
238
src/day22.rs
238
src/day22.rs
@ -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,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user