refactor day 11, large speedup
This commit is contained in:
+50
-118
@@ -1,7 +1,4 @@
|
||||
use std::{
|
||||
cmp::Reverse,
|
||||
collections::{BTreeSet, BinaryHeap, HashSet, VecDeque},
|
||||
};
|
||||
use std::collections::{HashSet, VecDeque};
|
||||
|
||||
use itertools::Itertools;
|
||||
|
||||
@@ -24,37 +21,43 @@ fn part_1() {
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
|
||||
struct State {
|
||||
floors: [BTreeSet<Item>; 4],
|
||||
sorted_vec_of_pairs: Vec<(usize, usize)>,
|
||||
elevator: usize,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
|
||||
enum Item {
|
||||
Generator(String),
|
||||
Chip(String),
|
||||
}
|
||||
|
||||
impl State {
|
||||
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 {
|
||||
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> {
|
||||
&self.floors[self.elevator]
|
||||
fn move_candidates(&self) -> Vec<(usize, bool)> {
|
||||
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();
|
||||
|
||||
candidates
|
||||
.iter()
|
||||
.clone()
|
||||
.into_iter()
|
||||
.combinations(1)
|
||||
.chain(candidates.iter().combinations(2))
|
||||
.chain(candidates.into_iter().combinations(2))
|
||||
}
|
||||
|
||||
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();
|
||||
|
||||
for mover in movers {
|
||||
result.floors[result.elevator].remove(*mover);
|
||||
for (idx, is_chip) in movers {
|
||||
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;
|
||||
|
||||
for mover in movers {
|
||||
result.floors[result.elevator].insert((*mover).clone());
|
||||
}
|
||||
|
||||
result
|
||||
}
|
||||
|
||||
@@ -91,16 +96,14 @@ impl State {
|
||||
#[cfg(debug_assertions)]
|
||||
fn generate_start_state_1() -> Self {
|
||||
let mut result = Self {
|
||||
floors: [const { BTreeSet::new() }; 4],
|
||||
sorted_vec_of_pairs: vec![],
|
||||
elevator: 0,
|
||||
};
|
||||
|
||||
result.floors[0].insert(Item::Chip("hydrogen".to_string()));
|
||||
result.floors[0].insert(Item::Chip("lithium".to_string()));
|
||||
result.sorted_vec_of_pairs.push((0, 1));
|
||||
result.sorted_vec_of_pairs.push((0, 2));
|
||||
|
||||
result.floors[1].insert(Item::Generator("hydrogen".to_string()));
|
||||
|
||||
result.floors[2].insert(Item::Generator("lithium".to_string()));
|
||||
result.sorted_vec_of_pairs.sort();
|
||||
|
||||
result
|
||||
}
|
||||
@@ -108,78 +111,41 @@ impl State {
|
||||
#[cfg(not(debug_assertions))]
|
||||
fn generate_start_state_1() -> Self {
|
||||
let mut result = Self {
|
||||
floors: [const { BTreeSet::new() }; 4],
|
||||
sorted_vec_of_pairs: vec![],
|
||||
elevator: 0,
|
||||
};
|
||||
|
||||
result.floors[0].insert(Item::Generator("promethium".to_string()));
|
||||
result.floors[0].insert(Item::Chip("promethium".to_string()));
|
||||
result.sorted_vec_of_pairs.push((0, 0));
|
||||
|
||||
result.floors[1].insert(Item::Generator("cobalt".to_string()));
|
||||
result.floors[1].insert(Item::Generator("curium".to_string()));
|
||||
result.floors[1].insert(Item::Generator("ruthenium".to_string()));
|
||||
result.floors[1].insert(Item::Generator("plutonium".to_string()));
|
||||
result.sorted_vec_of_pairs.push((2, 1));
|
||||
result.sorted_vec_of_pairs.push((2, 1));
|
||||
result.sorted_vec_of_pairs.push((2, 1));
|
||||
result.sorted_vec_of_pairs.push((2, 1));
|
||||
|
||||
result.floors[2].insert(Item::Chip("cobalt".to_string()));
|
||||
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.sorted_vec_of_pairs.sort();
|
||||
|
||||
result
|
||||
}
|
||||
|
||||
fn generate_start_state_2() -> State {
|
||||
let mut result = Self {
|
||||
floors: [const { BTreeSet::new() }; 4],
|
||||
sorted_vec_of_pairs: vec![],
|
||||
elevator: 0,
|
||||
};
|
||||
|
||||
result.floors[0].insert(Item::Generator("promethium".to_string()));
|
||||
result.floors[0].insert(Item::Chip("promethium".to_string()));
|
||||
result.floors[0].insert(Item::Generator("elerium".to_string()));
|
||||
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.sorted_vec_of_pairs.push((0, 0));
|
||||
result.sorted_vec_of_pairs.push((0, 0));
|
||||
result.sorted_vec_of_pairs.push((0, 0));
|
||||
|
||||
result.floors[1].insert(Item::Generator("cobalt".to_string()));
|
||||
result.floors[1].insert(Item::Generator("curium".to_string()));
|
||||
result.floors[1].insert(Item::Generator("ruthenium".to_string()));
|
||||
result.floors[1].insert(Item::Generator("plutonium".to_string()));
|
||||
result.sorted_vec_of_pairs.push((2, 1));
|
||||
result.sorted_vec_of_pairs.push((2, 1));
|
||||
result.sorted_vec_of_pairs.push((2, 1));
|
||||
result.sorted_vec_of_pairs.push((2, 1));
|
||||
|
||||
result.floors[2].insert(Item::Chip("cobalt".to_string()));
|
||||
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.sorted_vec_of_pairs.sort();
|
||||
|
||||
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 {
|
||||
@@ -204,37 +170,3 @@ fn bfs(start: State) -> usize {
|
||||
}
|
||||
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(¤t_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");
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user