2023 Day 23 Performance Improvements

This commit is contained in:
Burnus 2025-08-18 19:20:28 +02:00
parent 89f2b849ad
commit fef4a68522

View file

@ -1,5 +1,5 @@
use core::fmt::Display; use core::fmt::Display;
use std::{num::ParseIntError, collections::HashSet}; use std::{num::ParseIntError, collections::HashSet, collections::HashMap};
#[derive(Debug, PartialEq, Eq)] #[derive(Debug, PartialEq, Eq)]
pub enum ParseError<'a> { pub enum ParseError<'a> {
@ -32,7 +32,7 @@ impl Display for ParseError<'_> {
} }
} }
#[derive(PartialEq, Eq, Hash, Clone, Copy)] #[derive(PartialEq, Eq, Hash, Clone, Copy, PartialOrd, Ord)]
struct Coordinate(usize, usize); struct Coordinate(usize, usize);
#[derive(PartialEq, Eq, Hash)] #[derive(PartialEq, Eq, Hash)]
@ -51,7 +51,7 @@ impl Direction {
struct Map{ struct Map{
path: HashSet<Coordinate>, path: HashSet<Coordinate>,
slopes: HashSet<(Coordinate, Direction)>, slopes: HashMap<Coordinate, Direction>,
start: Coordinate, start: Coordinate,
dest: Coordinate, dest: Coordinate,
} }
@ -61,17 +61,17 @@ impl<'a> TryFrom<&'a str> for Map {
fn try_from(value: &'a str) -> Result<Self, Self::Error> { fn try_from(value: &'a str) -> Result<Self, Self::Error> {
let mut path = HashSet::new(); let mut path = HashSet::new();
let mut slopes = HashSet::new(); let mut slopes = HashMap::new();
for (y, line) in value.lines().enumerate() { for (y, line) in value.lines().enumerate() {
for (x, c) in line.chars().enumerate() { for (x, c) in line.chars().enumerate() {
match c { match c {
'#' => (), '#' => (),
'.' => _ = path.insert(Coordinate(x, y)), '.' => _ = path.insert(Coordinate(x, y)),
'^' => _ = slopes.insert((Coordinate(x, y), Direction::North)), '^' => _ = slopes.insert(Coordinate(x, y), Direction::North),
'<' => _ = slopes.insert((Coordinate(x, y), Direction::East)), '<' => _ = slopes.insert(Coordinate(x, y), Direction::East),
'>' => _ = slopes.insert((Coordinate(x, y), Direction::West)), '>' => _ = slopes.insert(Coordinate(x, y), Direction::West),
'v' => _ = slopes.insert((Coordinate(x, y), Direction::South)), 'v' => _ = slopes.insert(Coordinate(x, y), Direction::South),
e => return Err(Self::Error::InvalidChar(e)), e => return Err(Self::Error::InvalidChar(e)),
} }
} }
@ -98,21 +98,24 @@ impl<'a> TryFrom<&'a str> for Map {
impl Map { impl Map {
fn distances(&self, steep_slopes: bool) -> Vec<Vec<(usize, usize)>> { fn distances(&self, steep_slopes: bool) -> Vec<Vec<(usize, usize)>> {
let mut res = Vec::new();
let mut nodes = vec![self.start, self.dest]; let mut nodes = vec![self.start, self.dest];
self.path.iter().filter(|n| self.neighbours(**n).len() > 2).for_each(|n| nodes.push(*n)); self.path.iter().filter(|n| self.neighbours(**n).len() > 2).for_each(|n| nodes.push(*n));
self.slopes.iter().for_each(|s| nodes.push(s.0)); self.slopes.keys().for_each(|pos| nodes.push(*pos));
nodes.sort();
let mut res = Vec::with_capacity(nodes.len());
nodes.iter().for_each(|from_node| { nodes.iter().for_each(|from_node| {
let mut this = Vec::new(); let mut this = Vec::new();
self.neighbours(*from_node).iter().for_each(|&n| { self.neighbours(*from_node).iter().for_each(|&n| {
let slope = self.slopes.iter().find(|(pos, _dir)| pos == from_node); if !steep_slopes || {
if !steep_slopes || slope.is_none() || slope.unwrap().1.is_direction(*from_node, n) { let dir = self.slopes.get(from_node);
dir.is_none() || dir.unwrap().is_direction(*from_node, n)
} {
let mut prev = *from_node; let mut prev = *from_node;
let mut curr = n; let mut curr = n;
let mut len = 1; let mut len = 1;
loop { loop {
if let Some(to_idx) = nodes.iter().position(|n| n == &curr) { if let Ok(to_idx) = nodes.binary_search(&curr) {
this.push((to_idx, len)); this.push((to_idx, len));
break; break;
} }
@ -134,17 +137,13 @@ impl Map {
let right_idx = middle[1].0; let right_idx = middle[1].0;
let d = middle[0].1 + middle[1].1; let d = middle[0].1 + middle[1].1;
res[idx] = Vec::new(); res[idx] = Vec::new();
res[left_idx].iter_mut().for_each(|(dest, len)| { res[left_idx].iter_mut().filter(|(dest, _len)| *dest == idx).for_each(|(dest, len)| {
if *dest == idx { *dest = right_idx;
*dest = right_idx; *len = d;
*len = d;
}
}); });
res[right_idx].iter_mut().for_each(|(dest, len)| { res[right_idx].iter_mut().filter(|(dest, _len)| *dest == idx).for_each(|(dest, len)| {
if *dest == idx { *dest = left_idx;
*dest = left_idx; *len = d;
*len = d;
}
}); });
} }
res res
@ -158,10 +157,10 @@ impl Map {
if self.path.contains(&Coordinate(pos.0+1, pos.1)) { res.push(Coordinate(pos.0+1, pos.1)); } if self.path.contains(&Coordinate(pos.0+1, pos.1)) { res.push(Coordinate(pos.0+1, pos.1)); }
if self.path.contains(&Coordinate(pos.0, pos.1+1)) { res.push(Coordinate(pos.0, pos.1+1)); } if self.path.contains(&Coordinate(pos.0, pos.1+1)) { res.push(Coordinate(pos.0, pos.1+1)); }
if pos.0 > 0 && self.slopes.iter().any(|(p, _)| p == &Coordinate(pos.0-1, pos.1)) { res.push(Coordinate(pos.0-1, pos.1)); } if pos.0 > 0 && self.slopes.contains_key(&Coordinate(pos.0-1, pos.1)) { res.push(Coordinate(pos.0-1, pos.1)); }
if pos.1 > 0 && self.slopes.iter().any(|(p, _)| p == &Coordinate(pos.0, pos.1-1)) { res.push(Coordinate(pos.0, pos.1-1)); } if pos.1 > 0 && self.slopes.contains_key(&Coordinate(pos.0, pos.1-1)) { res.push(Coordinate(pos.0, pos.1-1)); }
if self.slopes.iter().any(|(p, _)| p == &Coordinate(pos.0+1, pos.1)) { res.push(Coordinate(pos.0+1, pos.1)); } if self.slopes.contains_key(&Coordinate(pos.0+1, pos.1)) { res.push(Coordinate(pos.0+1, pos.1)); }
if self.slopes.iter().any(|(p, _)| p == &Coordinate(pos.0, pos.1+1)) { res.push(Coordinate(pos.0, pos.1+1)); } if self.slopes.contains_key(&Coordinate(pos.0, pos.1+1)) { res.push(Coordinate(pos.0, pos.1+1)); }
res res
} }
@ -189,9 +188,7 @@ fn longest_route(distances: &[Vec<(usize, usize)>]) -> usize {
continue; continue;
} }
distances[curr].iter().filter(|(n, _)| !path.contains(n)).for_each(|(next, _len)| { distances[curr].iter().filter(|(n, _)| !path.contains(n)).for_each(|(next, _len)| {
let mut new = path.to_vec(); open_set.push(path.iter().cloned().chain(std::iter::once(*next)).collect());
new.push(*next);
open_set.push(new);
}); });
} }
longest_so_far longest_so_far