day6: further performance improvement, ~55ms total runtime
We only need to run the guard forward from the step before the obstacle we placed, instead of starting at the original starting position. The code starts to look ugly...
This commit is contained in:
parent
b1918bbebf
commit
fee37aebd0
@ -117,6 +117,7 @@ struct Map {
|
|||||||
visited_from: grid::Grid<DirectionSet>,
|
visited_from: grid::Grid<DirectionSet>,
|
||||||
guard_facing: FacingDirection,
|
guard_facing: FacingDirection,
|
||||||
guard_pos: (i64, i64),
|
guard_pos: (i64, i64),
|
||||||
|
path: Vec<((i64, i64), FacingDirection)>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: BufRead> From<Lines<T>> for Map {
|
impl<T: BufRead> From<Lines<T>> for Map {
|
||||||
@ -131,6 +132,7 @@ impl<T: BufRead> From<Lines<T>> for Map {
|
|||||||
guard_pos,
|
guard_pos,
|
||||||
guard_facing,
|
guard_facing,
|
||||||
visited_from,
|
visited_from,
|
||||||
|
path: Vec::new(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -145,7 +147,7 @@ impl Map {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
/// Move one step in the facing direction, return if we are still inside the bounds
|
/// Move one step in the facing direction, return if we are still inside the bounds
|
||||||
fn step_guard(&mut self) -> StepOutcome {
|
fn step_guard<const RECORD_PATH: bool>(&mut self) -> StepOutcome {
|
||||||
let new_pos = self.guard_facing.pos_ofs(self.guard_pos);
|
let new_pos = self.guard_facing.pos_ofs(self.guard_pos);
|
||||||
if self
|
if self
|
||||||
.visited_from
|
.visited_from
|
||||||
@ -155,6 +157,9 @@ impl Map {
|
|||||||
return StepOutcome::LoopFound;
|
return StepOutcome::LoopFound;
|
||||||
}
|
}
|
||||||
if self.grid.set(new_pos.0, new_pos.1, b'X') {
|
if self.grid.set(new_pos.0, new_pos.1, b'X') {
|
||||||
|
if RECORD_PATH {
|
||||||
|
self.path.push((new_pos, self.guard_facing));
|
||||||
|
}
|
||||||
self.visited_from.set(
|
self.visited_from.set(
|
||||||
new_pos.0,
|
new_pos.0,
|
||||||
new_pos.1,
|
new_pos.1,
|
||||||
@ -166,34 +171,14 @@ impl Map {
|
|||||||
StepOutcome::LeftMap
|
StepOutcome::LeftMap
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fn run_guard(&mut self) -> RunOutcome {
|
fn run_guard<const RECORD_PATH: bool>(&mut self) -> RunOutcome {
|
||||||
// if the guard is surrounded by obstacles, bail out
|
|
||||||
if self
|
|
||||||
.grid
|
|
||||||
.get(self.guard_pos.0 - 1, self.guard_pos.1)
|
|
||||||
.is_some_and(|v| v == b'#')
|
|
||||||
&& self
|
|
||||||
.grid
|
|
||||||
.get(self.guard_pos.0 + 1, self.guard_pos.1)
|
|
||||||
.is_some_and(|v| v == b'#')
|
|
||||||
&& self
|
|
||||||
.grid
|
|
||||||
.get(self.guard_pos.0, self.guard_pos.1 - 1)
|
|
||||||
.is_some_and(|v| v == b'#')
|
|
||||||
&& self
|
|
||||||
.grid
|
|
||||||
.get(self.guard_pos.0, self.guard_pos.1 + 1)
|
|
||||||
.is_some_and(|v| v == b'#')
|
|
||||||
{
|
|
||||||
return RunOutcome::Stuck;
|
|
||||||
}
|
|
||||||
while let Some(val) = self.look(&self.guard_facing) {
|
while let Some(val) = self.look(&self.guard_facing) {
|
||||||
match val {
|
match val {
|
||||||
b'#' => {
|
b'#' => {
|
||||||
// obstacle, turn right
|
// obstacle, turn right
|
||||||
self.guard_facing = self.guard_facing.next();
|
self.guard_facing = self.guard_facing.next();
|
||||||
}
|
}
|
||||||
_ => match self.step_guard() {
|
_ => match self.step_guard::<RECORD_PATH>() {
|
||||||
StepOutcome::LeftMap => return RunOutcome::LeftMap,
|
StepOutcome::LeftMap => return RunOutcome::LeftMap,
|
||||||
StepOutcome::LoopFound => return RunOutcome::LoopFound,
|
StepOutcome::LoopFound => return RunOutcome::LoopFound,
|
||||||
StepOutcome::Continue => {}
|
StepOutcome::Continue => {}
|
||||||
@ -208,7 +193,7 @@ impl Map {
|
|||||||
|
|
||||||
fn problem1<T: BufRead>(input: Lines<T>) -> u64 {
|
fn problem1<T: BufRead>(input: Lines<T>) -> u64 {
|
||||||
let mut map = Map::from(input);
|
let mut map = Map::from(input);
|
||||||
map.run_guard();
|
map.run_guard::<false>();
|
||||||
|
|
||||||
(map.grid.count(b'X') + map.grid.count(b'-') + map.grid.count(b'|') + map.grid.count(b'^')) as u64
|
(map.grid.count(b'X') + map.grid.count(b'-') + map.grid.count(b'|') + map.grid.count(b'^')) as u64
|
||||||
}
|
}
|
||||||
@ -217,21 +202,27 @@ fn problem1<T: BufRead>(input: Lines<T>) -> u64 {
|
|||||||
fn problem2<T: BufRead>(input: Lines<T>) -> u64 {
|
fn problem2<T: BufRead>(input: Lines<T>) -> u64 {
|
||||||
let input_map = Map::from(input);
|
let input_map = Map::from(input);
|
||||||
// Use the solution from problem 1 to reduce the number of positions where obstacle placement will change the path
|
// Use the solution from problem 1 to reduce the number of positions where obstacle placement will change the path
|
||||||
let mut part1 = input_map.clone();
|
let mut path_map = input_map.clone();
|
||||||
part1.run_guard();
|
path_map.run_guard::<true>();
|
||||||
|
|
||||||
|
let mut tested_position: grid::Grid<bool> = grid::Grid::new(path_map.grid.width() as i64);
|
||||||
|
tested_position.data.resize(path_map.grid.data.len(), false);
|
||||||
|
|
||||||
let mut loop_count = 0u64;
|
let mut loop_count = 0u64;
|
||||||
for pos in part1
|
let mut last_posdir = (input_map.guard_pos, input_map.guard_facing);
|
||||||
.grid
|
|
||||||
.data
|
for ((x, y), dir) in path_map.path.iter() {
|
||||||
.iter()
|
if !tested_position.get(*x, *y).unwrap() {
|
||||||
.enumerate()
|
tested_position.set(*x, *y, true);
|
||||||
.filter_map(|(pos, c)| if *c == b'X' { Some(pos) } else { None })
|
let mut test_map = input_map.clone();
|
||||||
{
|
test_map.grid.set(*x, *y, b'#');
|
||||||
let mut test_map = input_map.clone();
|
test_map.guard_pos = last_posdir.0;
|
||||||
test_map.grid.data[pos] = b'#';
|
test_map.guard_facing = last_posdir.1;
|
||||||
if let RunOutcome::LoopFound = test_map.run_guard() {
|
|
||||||
loop_count += 1
|
if let RunOutcome::LoopFound = test_map.run_guard::<false>() {
|
||||||
|
loop_count += 1
|
||||||
|
}
|
||||||
|
last_posdir = ((*x, *y), *dir);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user