refactor day 11, large speedup
This commit is contained in:
+50
-118
@@ -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(¤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