Skip to content

Commit

Permalink
Optimization
Browse files Browse the repository at this point in the history
  • Loading branch information
komu committed Nov 16, 2023
1 parent 88ad8e7 commit 2f145ef
Show file tree
Hide file tree
Showing 4 changed files with 87 additions and 29 deletions.
5 changes: 3 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,10 +33,11 @@ a total budget of 100 milliseconds.
| [21](https://adventofcode.com/2022/day/21) | [21.rs](src/bin/21.rs) | 325.30µs | 235.40µs | - |
| [22](https://adventofcode.com/2022/day/22) | [22.rs](src/bin/22.rs) | 139.31µs | 127.73µs | - |
| [23](https://adventofcode.com/2022/day/23) | [23.rs](src/bin/23.rs) | 15.51ms | 831.17ms | 😔 |
| [24](https://adventofcode.com/2022/day/24) | [24.rs](src/bin/24.rs) | 9.62s | 92.24s | 😔 |
| [24](https://adventofcode.com/2022/day/24) | [24.rs](src/bin/24.rs) | 4.21s | 39.31s | 😔 |
| [25](https://adventofcode.com/2022/day/25) | [25.rs](src/bin/25.rs) | 18.00ns | - | - |

(Totally unscientific numbers from a single run, will improve these in the future.)
In the end, days 15, 19, 23 and 24 blew the 100 ms budget by themselves, but ignoring those the total time for the rest
of the 21 days is 114 ms, which is pretty decent.

## Previous years

Expand Down
63 changes: 36 additions & 27 deletions src/bin/24.rs
Original file line number Diff line number Diff line change
@@ -1,16 +1,13 @@
use std::cmp::Reverse;
use std::iter::once;

use priority_queue::PriorityQueue;

use aoc::point::CardinalDirection;
use aoc::shortest_path::{Graph, shortest_path_len};

pub fn part_one(input: &str) -> Option<u32> {
Basin::parse(input).shortest_path_len(false)
Basin::parse(input, false).shortest_path()
}

pub fn part_two(input: &str) -> Option<u32> {
Basin::parse(input).shortest_path_len(true)
Basin::parse(input, true).shortest_path()
}

type Minutes = u32;
Expand Down Expand Up @@ -67,10 +64,11 @@ struct Basin {
width: Coordinate,
height: Coordinate,
blizzards: Vec<Blizzard>,
go_back_to_start: bool,
}

impl Basin {
fn parse(s: &str) -> Self {
fn parse(s: &str, go_back_to_start: bool) -> Self {
let lines: Vec<_> = s.lines().filter(|l| l.starts_with('#')).collect();

let mut blizzards = Vec::<Blizzard>::new();
Expand All @@ -96,6 +94,7 @@ impl Basin {
width,
height,
blizzards,
go_back_to_start,
}
}

Expand All @@ -109,33 +108,27 @@ impl Basin {
}
}

fn shortest_path_len(&self, go_back_to_start: bool) -> Option<u32> {
fn shortest_path(&self) -> Option<u32> {
let start = SearchState {
pos: self.start,
minutes: 0,
state: TripState::Initial,
};
shortest_path_len(self, start).map(|x|x.1)
}
}

let mut queue = PriorityQueue::<SearchState, Reverse<Minutes>>::new();
queue.push(start, Reverse(start.minutes));

while let Some((current, _)) = queue.pop() {
if current.pos == self.end && (!go_back_to_start || current.state == TripState::VisitedStartAfterEnd) {
return Some(current.minutes);
}

for neighbor in self.neighbors(&current) {
queue.push(neighbor, Reverse(neighbor.minutes));
}
}
impl Graph for Basin {
type Node = SearchState;

None
fn is_solution(&self, node: &Self::Node) -> bool {
node.pos == self.end && (!self.go_back_to_start || node.state == TripState::VisitedStartAfterEnd)
}

fn neighbors(&self, current: &SearchState) -> impl Iterator<Item=SearchState> + '_ {
let current_pos = current.pos;
let current_minutes = current.minutes;
let current_state = current.state;
fn collect_neighbors(&self, node: &Self::Node, neighbors: &mut Vec<(Self::Node, u32)>) {
let current_pos = node.pos;
let current_minutes = node.minutes;
let current_state = node.state;

let wait_state = SearchState {
pos: current_pos,
Expand All @@ -155,12 +148,28 @@ impl Basin {
Initial => if pos == self.end { VisitedEnd } else { Initial }
VisitedEnd => if pos == self.start { VisitedStartAfterEnd } else { VisitedEnd }
VisitedStartAfterEnd => VisitedStartAfterEnd
}
},
}
});

move_states.chain(once(wait_state))
let cs = move_states.chain(once(wait_state))
.filter(|s| self.is_empty(s.pos, s.minutes))
.map(|n| (n, 1));

neighbors.extend(cs);
}

fn heuristic_distance(&self, node: &Self::Node) -> u32 {
if self.go_back_to_start {
let start_to_end = self.start.manhattan_distance(&self.end);
(match node.state {
TripState::Initial => 2 * start_to_end + node.pos.manhattan_distance(&self.end),
TripState::VisitedEnd => start_to_end + node.pos.manhattan_distance(&self.start),
TripState::VisitedStartAfterEnd => node.pos.manhattan_distance(&self.end),
}) as u32
} else {
node.pos.manhattan_distance(&self.end) as u32
}
}
}

Expand Down
1 change: 1 addition & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ use std::fs;

pub mod helpers;
pub mod point;
pub mod shortest_path;

pub const ANSI_ITALIC: &str = "\x1b[3m";
pub const ANSI_BOLD: &str = "\x1b[1m";
Expand Down
47 changes: 47 additions & 0 deletions src/shortest_path.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
use std::cmp::Reverse;
use std::hash::Hash;

use hashbrown::HashMap;
use priority_queue::PriorityQueue;

pub trait Graph {
type Node: Eq + Hash + Clone;

fn is_solution(&self, node: &Self::Node) -> bool;
fn collect_neighbors(&self, node: &Self::Node, neighbors: &mut Vec<(Self::Node, u32)>);
fn heuristic_distance(&self, node: &Self::Node) -> u32;
}

pub fn shortest_path_len<G>(g: &G, start: G::Node) -> Option<(G::Node, u32)>
where
G: Graph,
{
let mut g_score = HashMap::<G::Node, u32>::new();
let mut open_set = PriorityQueue::<G::Node, Reverse<u32>>::new();
let mut neighbors = Vec::new();

g_score.insert(start.clone(), 0);
let start_distance = g.heuristic_distance(&start);
open_set.push(start, Reverse(start_distance));

while let Some((current, _)) = open_set.pop() {
let current_gscore = *g_score.get(&current).unwrap();

if g.is_solution(&current) {
return Some((current, current_gscore));
}

g.collect_neighbors(&current, &mut neighbors);
for (neighbor, cost) in neighbors.drain(..) {
let tentative_gscore = current_gscore + cost;
if tentative_gscore < g_score.get(&neighbor).copied().unwrap_or(u32::max_value()) {
g_score.insert(neighbor.clone(), tentative_gscore);

let neighbor_score = tentative_gscore + g.heuristic_distance(&neighbor);
open_set.push(neighbor, Reverse(neighbor_score));
}
}
}

None
}

0 comments on commit 2f145ef

Please sign in to comment.