refactor day 11, large speedup

This commit is contained in:
timeshifter
2026-07-04 21:39:09 +02:00
parent 62d8f3227b
commit 12c01293da
+50 -118
View File
@@ -1,7 +1,4 @@
use std::{ use std::collections::{HashSet, VecDeque};
cmp::Reverse,
collections::{BTreeSet, BinaryHeap, HashSet, VecDeque},
};
use itertools::Itertools; use itertools::Itertools;
@@ -24,37 +21,43 @@ fn part_1() {
#[derive(Clone, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] #[derive(Clone, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
struct State { struct State {
floors: [BTreeSet<Item>; 4], sorted_vec_of_pairs: Vec<(usize, usize)>,
elevator: usize, elevator: usize,
} }
#[derive(Clone, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
enum Item {
Generator(String),
Chip(String),
}
impl State { impl State {
fn is_valid(&self) -> bool { fn is_valid(&self) -> bool {
self.floors.iter().all(floor_is_valid) self.sorted_vec_of_pairs
.iter()
.all(|(c, g)| c == g || self.sorted_vec_of_pairs.iter().all(|(_, g2)| c != g2))
} }
fn is_goal_state(&self) -> bool { fn is_goal_state(&self) -> bool {
self.elevator == 3 // fourth floor, off by one self.elevator == 3 // fourth floor, off by one
&& self.floors[0..=2].iter().all(|floor| floor.is_empty()) && self.sorted_vec_of_pairs.iter().all(|pair| *pair == (3, 3))
} }
fn move_candidates(&self) -> &BTreeSet<Item> { fn move_candidates(&self) -> Vec<(usize, bool)> {
&self.floors[self.elevator] let mut result = vec![];
for (idx, (c, g)) in self.sorted_vec_of_pairs.iter().enumerate() {
if *c == self.elevator {
result.push((idx, true))
};
if *g == self.elevator {
result.push((idx, false))
};
}
result
} }
fn possible_moves(&self) -> impl Iterator<Item = Vec<&Item>> { fn possible_moves(&self) -> impl Iterator<Item = Vec<(usize, bool)>> {
let candidates = self.move_candidates(); let candidates = self.move_candidates();
candidates candidates
.iter() .clone()
.into_iter()
.combinations(1) .combinations(1)
.chain(candidates.iter().combinations(2)) .chain(candidates.into_iter().combinations(2))
} }
fn possible_move_to_floors(&self) -> Vec<usize> { fn possible_move_to_floors(&self) -> Vec<usize> {
@@ -65,19 +68,21 @@ impl State {
} }
} }
fn apply_move(&self, movers: &[&Item], destination: usize) -> State { fn apply_move(&self, movers: &[(usize, bool)], destination: usize) -> State {
let mut result = self.clone(); let mut result = self.clone();
for mover in movers { for (idx, is_chip) in movers {
result.floors[result.elevator].remove(*mover); if *is_chip {
result.sorted_vec_of_pairs[*idx].0 = destination;
} else {
result.sorted_vec_of_pairs[*idx].1 = destination;
}
} }
result.sorted_vec_of_pairs.sort();
result.elevator = destination; result.elevator = destination;
for mover in movers {
result.floors[result.elevator].insert((*mover).clone());
}
result result
} }
@@ -91,16 +96,14 @@ impl State {
#[cfg(debug_assertions)] #[cfg(debug_assertions)]
fn generate_start_state_1() -> Self { fn generate_start_state_1() -> Self {
let mut result = Self { let mut result = Self {
floors: [const { BTreeSet::new() }; 4], sorted_vec_of_pairs: vec![],
elevator: 0, elevator: 0,
}; };
result.floors[0].insert(Item::Chip("hydrogen".to_string())); result.sorted_vec_of_pairs.push((0, 1));
result.floors[0].insert(Item::Chip("lithium".to_string())); result.sorted_vec_of_pairs.push((0, 2));
result.floors[1].insert(Item::Generator("hydrogen".to_string())); result.sorted_vec_of_pairs.sort();
result.floors[2].insert(Item::Generator("lithium".to_string()));
result result
} }
@@ -108,78 +111,41 @@ impl State {
#[cfg(not(debug_assertions))] #[cfg(not(debug_assertions))]
fn generate_start_state_1() -> Self { fn generate_start_state_1() -> Self {
let mut result = Self { let mut result = Self {
floors: [const { BTreeSet::new() }; 4], sorted_vec_of_pairs: vec![],
elevator: 0, elevator: 0,
}; };
result.floors[0].insert(Item::Generator("promethium".to_string())); result.sorted_vec_of_pairs.push((0, 0));
result.floors[0].insert(Item::Chip("promethium".to_string()));
result.floors[1].insert(Item::Generator("cobalt".to_string())); result.sorted_vec_of_pairs.push((2, 1));
result.floors[1].insert(Item::Generator("curium".to_string())); result.sorted_vec_of_pairs.push((2, 1));
result.floors[1].insert(Item::Generator("ruthenium".to_string())); result.sorted_vec_of_pairs.push((2, 1));
result.floors[1].insert(Item::Generator("plutonium".to_string())); result.sorted_vec_of_pairs.push((2, 1));
result.floors[2].insert(Item::Chip("cobalt".to_string())); result.sorted_vec_of_pairs.sort();
result.floors[2].insert(Item::Chip("curium".to_string()));
result.floors[2].insert(Item::Chip("ruthenium".to_string()));
result.floors[2].insert(Item::Chip("plutonium".to_string()));
result result
} }
fn generate_start_state_2() -> State { fn generate_start_state_2() -> State {
let mut result = Self { let mut result = Self {
floors: [const { BTreeSet::new() }; 4], sorted_vec_of_pairs: vec![],
elevator: 0, elevator: 0,
}; };
result.floors[0].insert(Item::Generator("promethium".to_string())); result.sorted_vec_of_pairs.push((0, 0));
result.floors[0].insert(Item::Chip("promethium".to_string())); result.sorted_vec_of_pairs.push((0, 0));
result.floors[0].insert(Item::Generator("elerium".to_string())); result.sorted_vec_of_pairs.push((0, 0));
result.floors[0].insert(Item::Chip("elerium".to_string()));
result.floors[0].insert(Item::Generator("dilithium".to_string()));
result.floors[0].insert(Item::Chip("dilithium".to_string()));
result.floors[1].insert(Item::Generator("cobalt".to_string())); result.sorted_vec_of_pairs.push((2, 1));
result.floors[1].insert(Item::Generator("curium".to_string())); result.sorted_vec_of_pairs.push((2, 1));
result.floors[1].insert(Item::Generator("ruthenium".to_string())); result.sorted_vec_of_pairs.push((2, 1));
result.floors[1].insert(Item::Generator("plutonium".to_string())); result.sorted_vec_of_pairs.push((2, 1));
result.floors[2].insert(Item::Chip("cobalt".to_string())); result.sorted_vec_of_pairs.sort();
result.floors[2].insert(Item::Chip("curium".to_string()));
result.floors[2].insert(Item::Chip("ruthenium".to_string()));
result.floors[2].insert(Item::Chip("plutonium".to_string()));
result result
} }
fn heuristic(&self) -> usize {
self.floors
.iter()
.take(3)
.rev()
.zip(1..=3)
.map(|(set, distance)| set.len() * distance)
.sum::<usize>()
/ 2
}
}
fn floor_is_valid(floor: &BTreeSet<Item>) -> bool {
let mut generators = HashSet::new();
let mut chips = HashSet::new();
for item in floor {
match item {
Item::Generator(name) => generators.insert(name.as_str()),
Item::Chip(name) => chips.insert(name.as_str()),
};
}
chips
.into_iter()
.all(|chip| generators.contains(chip) || generators.is_empty())
} }
fn bfs(start: State) -> usize { fn bfs(start: State) -> usize {
@@ -204,37 +170,3 @@ fn bfs(start: State) -> usize {
} }
panic!("goal state unreachable"); panic!("goal state unreachable");
} }
#[allow(dead_code)]
fn a_star(start: State) -> usize {
let mut heap: BinaryHeap<_> = BinaryHeap::new();
let mut visited = HashSet::new();
heap.push(Reverse((start.heuristic(), 0_usize, start)));
while let Some(Reverse((_, distance, current_state))) = heap.pop() {
if visited.contains(&current_state) {
continue;
} else {
visited.insert(current_state.clone());
}
if current_state.is_goal_state() {
return distance;
}
for state in current_state.neighbors() {
if visited.contains(&state) {
continue;
}
let heuristic = state.heuristic();
let new_distance = distance + 1;
// f_score = g_score + h_score
// = distance + heuristic;
heap.push(Reverse((distance + heuristic, new_distance, state)));
}
}
panic!("goal state unreachable");
}