|
|
|
@ -6,14 +6,14 @@ use crate::pathfinding::Edge;
|
|
|
|
|
|
|
|
|
|
#[derive(Clone, Eq, Hash, PartialEq, Ord, PartialOrd)]
|
|
|
|
|
struct FloorObject {
|
|
|
|
|
material: String,
|
|
|
|
|
material: usize,
|
|
|
|
|
generator: bool,
|
|
|
|
|
floor: i8
|
|
|
|
|
floor: u8
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl fmt::Display for FloorObject {
|
|
|
|
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
|
|
|
|
if self.material == "elevator" {
|
|
|
|
|
if self.material == 0 {
|
|
|
|
|
f.write_fmt(format_args!("(elevator, floor {})", self.floor + 1))
|
|
|
|
|
} else if self.generator {
|
|
|
|
|
f.write_fmt(format_args!("({} generator, floor {})", self.material, self.floor + 1))
|
|
|
|
@ -31,10 +31,10 @@ impl fmt::Debug for FloorObject {
|
|
|
|
|
|
|
|
|
|
pub fn part1() {
|
|
|
|
|
println!("Day 11 part 1");
|
|
|
|
|
let floors = load_initial_state();
|
|
|
|
|
let (floors, materials) = load_initial_state();
|
|
|
|
|
let mut goal = Vec::new();
|
|
|
|
|
for item in floors.iter() {
|
|
|
|
|
goal.push(FloorObject{material: item.material.clone(), generator: item.generator, floor: 3})
|
|
|
|
|
goal.push(FloorObject{material: item.material, generator: item.generator, floor: 3})
|
|
|
|
|
}
|
|
|
|
|
match pathfinding::dijkstra_single_goal(floors, goal, neighbours) {
|
|
|
|
|
Some((cost, path)) => {
|
|
|
|
@ -44,12 +44,12 @@ pub fn part1() {
|
|
|
|
|
print!("{} ", floor + 1);
|
|
|
|
|
for item in step.iter() {
|
|
|
|
|
if item.floor == floor {
|
|
|
|
|
if item.material == "elevator" {
|
|
|
|
|
if item.material == 0 {
|
|
|
|
|
print!(" E ");
|
|
|
|
|
} else if item.generator {
|
|
|
|
|
print!("{}{}G ", item.material[0..1].to_uppercase(), &item.material[1..2]);
|
|
|
|
|
print!("{}{}G ", materials[item.material][0..1].to_uppercase(), &materials[item.material][1..2]);
|
|
|
|
|
} else {
|
|
|
|
|
print!("{}{}M ", item.material[0..1].to_uppercase(), &item.material[1..2]);
|
|
|
|
|
print!("{}{}M ", materials[item.material][0..1].to_uppercase(), &materials[item.material][1..2]);
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
print!(" . ");
|
|
|
|
@ -67,16 +67,17 @@ pub fn part1() {
|
|
|
|
|
|
|
|
|
|
pub fn part2() {
|
|
|
|
|
println!("Day 11 part 2");
|
|
|
|
|
let mut floors = load_initial_state();
|
|
|
|
|
floors.push(FloorObject{material: String::from("elerium"), generator: false, floor: 0});
|
|
|
|
|
floors.push(FloorObject{material: String::from("elerium"), generator: true, floor: 0});
|
|
|
|
|
floors.push(FloorObject{material: String::from("dilithium"), generator: false, floor: 0});
|
|
|
|
|
floors.push(FloorObject{material: String::from("dilithium"), generator: true, floor: 0});
|
|
|
|
|
let (mut floors, materials) = load_initial_state();
|
|
|
|
|
let material_count = materials.len() as usize;
|
|
|
|
|
floors.push(FloorObject{material: material_count, generator: false, floor: 0});
|
|
|
|
|
floors.push(FloorObject{material: material_count, generator: true, floor: 0});
|
|
|
|
|
floors.push(FloorObject{material: material_count + 1, generator: false, floor: 0});
|
|
|
|
|
floors.push(FloorObject{material: material_count + 1, generator: true, floor: 0});
|
|
|
|
|
let mut goal = Vec::new();
|
|
|
|
|
for item in floors.iter() {
|
|
|
|
|
goal.push(FloorObject{material: item.material.clone(), generator: item.generator, floor: 3})
|
|
|
|
|
goal.push(FloorObject{material: item.material, generator: item.generator, floor: 3})
|
|
|
|
|
}
|
|
|
|
|
match pathfinding::a_star(floors, goal, neighbours, h) {
|
|
|
|
|
match pathfinding::dijkstra_single_goal(floors, goal, neighbours) {
|
|
|
|
|
Some((cost, _)) => println!("{}", cost),
|
|
|
|
|
None => println!("No solution found")
|
|
|
|
|
}
|
|
|
|
@ -95,23 +96,55 @@ fn neighbours(floor_contents: &Vec<FloorObject>) -> Vec<Edge<Vec<FloorObject>>>
|
|
|
|
|
movable.push(i);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
// Elevator can only move 1 step at a time
|
|
|
|
|
if elevator.floor > 0 {
|
|
|
|
|
result.append(&mut generate_moves(floor_contents, &movable, elevator.floor - 1));
|
|
|
|
|
|
|
|
|
|
// If on the top floor, only consider splitting one chip-generator pair
|
|
|
|
|
if elevator.floor == 3 {
|
|
|
|
|
let mut generators: HashSet<usize> = HashSet::new();
|
|
|
|
|
let mut chips = HashSet::new();
|
|
|
|
|
for i in movable.iter() {
|
|
|
|
|
let item = &floor_contents[*i];
|
|
|
|
|
if item.generator {
|
|
|
|
|
generators.insert(item.material);
|
|
|
|
|
} else {
|
|
|
|
|
chips.insert(item.material);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let pairs: Vec<usize> = generators.intersection(&chips).map(|x| *x).collect();
|
|
|
|
|
if pairs.len() > 1 {
|
|
|
|
|
let (_,ban) = pairs.split_at(1);
|
|
|
|
|
movable.retain(|i| !ban.contains(&floor_contents[*i].material));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Elevator can only move 1 step at a time
|
|
|
|
|
if elevator.floor < 3 {
|
|
|
|
|
result.append(&mut generate_moves(floor_contents, &movable, elevator.floor + 1));
|
|
|
|
|
}
|
|
|
|
|
if elevator.floor > 0 {
|
|
|
|
|
// Don't bother putting anything back on the bottom floor if it's empty
|
|
|
|
|
if elevator.floor != 1 || floor_zero_count(floor_contents) {
|
|
|
|
|
result.append(&mut generate_moves(floor_contents, &movable, elevator.floor - 1));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
result
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn generate_moves(floor_contents: &Vec<FloorObject>, movable: &Vec<usize>, to_floor: i8) -> Vec<Edge<Vec<FloorObject>>> {
|
|
|
|
|
fn floor_zero_count(floor_contents: &Vec<FloorObject>) -> bool {
|
|
|
|
|
for item in floor_contents.iter() {
|
|
|
|
|
if item.floor == 0 {
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
false
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn generate_moves(floor_contents: &Vec<FloorObject>, movable: &Vec<usize>, to_floor: u8) -> Vec<Edge<Vec<FloorObject>>> {
|
|
|
|
|
let mut result = vec![];
|
|
|
|
|
// Elevator must contain exactly 1 or 2 objects
|
|
|
|
|
'outer: for (i, opt) in combinations(movable) {
|
|
|
|
|
let mut contents_after_move = floor_contents.clone();
|
|
|
|
|
contents_after_move[0] =
|
|
|
|
|
FloorObject { material: String::from("elevator"), generator: false, floor: to_floor };
|
|
|
|
|
contents_after_move[0].floor = to_floor;
|
|
|
|
|
move_to_floor(floor_contents, &mut contents_after_move, to_floor, movable[i]);
|
|
|
|
|
if let Some(j) = opt {
|
|
|
|
|
move_to_floor(floor_contents, &mut contents_after_move, to_floor, movable[j]);
|
|
|
|
@ -125,14 +158,14 @@ fn generate_moves(floor_contents: &Vec<FloorObject>, movable: &Vec<usize>, to_fl
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
if item.generator {
|
|
|
|
|
generators.insert(item.material.as_str());
|
|
|
|
|
generators.insert(item.material);
|
|
|
|
|
} else {
|
|
|
|
|
chips.insert(item.material.as_str());
|
|
|
|
|
chips.insert(item.material);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if !generators.is_empty() {
|
|
|
|
|
for chip in chips {
|
|
|
|
|
if !generators.contains(chip) {
|
|
|
|
|
if !generators.contains(&chip) {
|
|
|
|
|
continue 'outer;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
@ -143,7 +176,7 @@ fn generate_moves(floor_contents: &Vec<FloorObject>, movable: &Vec<usize>, to_fl
|
|
|
|
|
result
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn move_to_floor(floor_contents: &Vec<FloorObject>, contents_after_move: &mut Vec<FloorObject>, to_floor: i8, i: usize) {
|
|
|
|
|
fn move_to_floor(floor_contents: &Vec<FloorObject>, contents_after_move: &mut Vec<FloorObject>, to_floor: u8, i: usize) {
|
|
|
|
|
let mut item = floor_contents[i].clone();
|
|
|
|
|
item.floor = to_floor;
|
|
|
|
|
contents_after_move[i] = item;
|
|
|
|
@ -160,19 +193,20 @@ fn combinations(movable: &Vec<usize>) -> Vec<(usize, Option<usize>)> {
|
|
|
|
|
result
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn load_initial_state() -> Vec<FloorObject> {
|
|
|
|
|
fn load_initial_state() -> (Vec<FloorObject>, Vec<String>) {
|
|
|
|
|
let mut floor_contents = vec![];
|
|
|
|
|
let mut materials = vec![String::from("elevator")];
|
|
|
|
|
|
|
|
|
|
for line in read_input_file("day11.input").split_terminator('\n') {
|
|
|
|
|
if let Some(first_space) = line.find(' ') {
|
|
|
|
|
let ord_start = first_space + 1;
|
|
|
|
|
let ord_end = ord_start + line[ord_start..].find(' ').unwrap();
|
|
|
|
|
let floor: i8 = match &line[ord_start .. ord_end] {
|
|
|
|
|
let floor: u8 = match &line[ord_start .. ord_end] {
|
|
|
|
|
"first" => 0,
|
|
|
|
|
"second" => 1,
|
|
|
|
|
"third" => 2,
|
|
|
|
|
"fourth" => 3,
|
|
|
|
|
_ => panic!(format!("Unhandled ordinal \"{}\" on line \"{}\"", &line[ord_start .. ord_end], line))
|
|
|
|
|
_ => panic!("Unhandled ordinal \"{}\" on line \"{}\"", &line[ord_start .. ord_end], line)
|
|
|
|
|
};
|
|
|
|
|
let contents_start = line.find("contains ").unwrap() + "contains ".len();
|
|
|
|
|
for chunk in line[contents_start..line.len()-1].split(", ").flat_map(|chunk| chunk.split(" and ")) {
|
|
|
|
@ -186,16 +220,23 @@ fn load_initial_state() -> Vec<FloorObject> {
|
|
|
|
|
content_string = &chunk["a ".len()..];
|
|
|
|
|
}
|
|
|
|
|
let (description, thing) = content_string.split_at(content_string.len() - 9);
|
|
|
|
|
let material = match thing {
|
|
|
|
|
let material_str = String::from(match thing {
|
|
|
|
|
"generator" => &description[..description.len() - 1],
|
|
|
|
|
"microchip" => &description[..description.len() - 12],
|
|
|
|
|
_ => panic!(format!("I don't know what a {} is", &thing))
|
|
|
|
|
_ => panic!("I don't know what a {} is", &thing)
|
|
|
|
|
});
|
|
|
|
|
let material_idx = match materials.iter().position(|m| *m == material_str) {
|
|
|
|
|
Some(i) => i,
|
|
|
|
|
None => {
|
|
|
|
|
materials.push(material_str);
|
|
|
|
|
materials.len() - 1
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
floor_contents.push(FloorObject{ material: String::from(material), generator: thing == "generator", floor });
|
|
|
|
|
floor_contents.push(FloorObject{ material: material_idx, generator: thing == "generator", floor });
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
floor_contents.sort();
|
|
|
|
|
floor_contents.insert(0, FloorObject {material: String::from("elevator"), generator: false, floor: 0});
|
|
|
|
|
floor_contents
|
|
|
|
|
floor_contents.insert(0, FloorObject {material: 0, generator: false, floor: 0});
|
|
|
|
|
(floor_contents, materials)
|
|
|
|
|
}
|
|
|
|
|