more D18 wip

This commit is contained in:
kageru 2019-12-20 00:51:32 +01:00
parent f4017dd12a
commit 970ad53ce5
Signed by: kageru
GPG Key ID: 8282A2BEA4ADA3D2
2 changed files with 146 additions and 91 deletions

View File

@ -9,3 +9,4 @@ edition = "2018"
[dependencies] [dependencies]
grid = { path = "../grid" } grid = { path = "../grid" }
lazy_static = "1.3.0" lazy_static = "1.3.0"
rayon = "1.2.1"

View File

@ -1,9 +1,10 @@
use grid::*; use grid::*;
use rayon::prelude::*;
use std::char; use std::char;
use std::collections::{HashMap, HashSet}; use std::collections::{HashMap, HashSet};
use std::io::{self, BufRead}; use std::io::{self, BufRead};
use std::sync::Mutex;
use std::ops::Mul; use std::ops::Mul;
use std::sync::Mutex;
#[macro_use] #[macro_use]
extern crate lazy_static; extern crate lazy_static;
@ -14,27 +15,24 @@ struct Door(char);
struct Key(char); struct Key(char);
lazy_static! { lazy_static! {
static ref MAP: Mutex<HashMap<Position2D, char>> = Mutex::new( static ref BEST: Mutex<usize> = Mutex::new(999999);
io::stdin()
.lock()
.lines()
.enumerate()
.flat_map(move |(y, l)| l
.unwrap()
.to_owned()
.chars()
.enumerate()
.map(move |(x, c)| ((x, y).into(), c))
.collect::<Vec<_>>())
.collect()
);
} }
fn get(p: &Position2D) -> Option<char> { fn too_slow(i: usize) -> bool {
MAP.lock().unwrap().get(p).copied() let best = BEST.lock().unwrap();
*best < i
}
fn replace(i: usize) {
let mut best = BEST.lock().unwrap();
if i < *best {
println!("Best so far: {}", i);
*best = i;
}
} }
fn visit_neighbors( fn visit_neighbors(
map: &HashMap<Position2D, char>,
position: Position2D, position: Position2D,
steps: usize, steps: usize,
distances: &mut HashMap<Position2D, usize>, distances: &mut HashMap<Position2D, usize>,
@ -43,21 +41,21 @@ fn visit_neighbors(
mut doors: HashSet<Door>, mut doors: HashSet<Door>,
mut keys: HashSet<Key>, mut keys: HashSet<Key>,
) { ) {
let c = get(&position).unwrap(); let c = map.get(&position).unwrap();
if c.is_alphabetic() { if c.is_alphabetic() {
if c.is_lowercase() { if c.is_lowercase() {
dependencies.insert(Key { 0: c }, (keys.clone(), doors.clone())); dependencies.insert(Key { 0: *c }, (keys.clone(), doors.clone()));
keys.insert(Key { 0: c }); keys.insert(Key { 0: *c });
} }
if c.is_uppercase() { if c.is_uppercase() {
door_dependencies.insert(Door { 0: c }, doors.clone()); door_dependencies.insert(Door { 0: *c }, doors.clone());
doors.insert(Door { 0: c }); doors.insert(Door { 0: *c });
} }
} }
let mut unvisited = Vec::new(); let mut unvisited = Vec::new();
// TODO: fix ownership and make this a single filter().inspect().for_each() // TODO: fix ownership and make this a single filter().inspect().for_each()
for (_, p) in &position.neighbors() { for (_, p) in &position.neighbors() {
match get(p) { match map.get(p) {
Some('#') => (), Some('#') => (),
_ => { _ => {
if distances.get(p).is_none() { if distances.get(p).is_none() {
@ -65,7 +63,7 @@ fn visit_neighbors(
} }
} }
} }
if get(p) != Some('#') && distances.get(p).is_none() { if map.get(p) != Some(&'#') && distances.get(p).is_none() {
unvisited.push(p.to_owned()); unvisited.push(p.to_owned());
} }
} }
@ -79,6 +77,7 @@ fn visit_neighbors(
} }
for p in &unvisited { for p in &unvisited {
visit_neighbors( visit_neighbors(
map,
p.to_owned(), p.to_owned(),
steps + 1, steps + 1,
distances, distances,
@ -91,6 +90,7 @@ fn visit_neighbors(
} }
fn traverse( fn traverse(
map: &HashMap<Position2D, char>,
start: Position2D, start: Position2D,
) -> ( ) -> (
HashMap<Position2D, usize>, HashMap<Position2D, usize>,
@ -102,6 +102,7 @@ fn traverse(
let mut door_dependencies = HashMap::new(); let mut door_dependencies = HashMap::new();
distances.insert(start, 0usize); distances.insert(start, 0usize);
visit_neighbors( visit_neighbors(
map,
start, start,
0, 0,
&mut distances, &mut distances,
@ -123,87 +124,140 @@ fn find_keys_at_dead_ends(deps: &HashMap<Key, (HashSet<Key>, HashSet<Door>)>) ->
.collect() .collect()
} }
fn find(c: char) -> Option<Position2D> { fn find(map: &HashMap<Position2D, char>, c: char) -> Option<Position2D> {
let map = MAP.lock().unwrap();
map.keys().find(|p| map.get(&p) == Some(&c)).copied() map.keys().find(|p| map.get(&p) == Some(&c)).copied()
} }
fn clear_field(p: &Position2D) { fn clear_field(map: &mut HashMap<Position2D, char>, p: &Position2D) {
MAP.lock().unwrap().insert(*p, '.'); map.insert(*p, '.');
} }
fn remove(k: Key) { fn remove(map: &mut HashMap<Position2D, char>, k: Key) {
let key_pos = find(k.0).unwrap(); let key_pos = find(map, k.0).unwrap();
clear_field(&key_pos); clear_field(map, &key_pos);
if let Some(door_pos) = find(k.0.to_uppercase().next().unwrap()) { if let Some(door_pos) = find(map, k.0.to_uppercase().next().unwrap()) {
clear_field(&door_pos); clear_field(map, &door_pos);
} }
} }
fn distance_to_door(k: &Key) -> usize { fn distance_to_door(map: &HashMap<Position2D, char>, k: &Key) -> usize {
let (distances, _, _) = traverse(find(k.0).unwrap()); let (distances, _, _) = traverse(map, find(map, k.0).unwrap());
let door_pos = find(k.0.to_uppercase().next().unwrap()); let door_pos = find(map, k.0.to_uppercase().next().unwrap());
*door_pos.map(|p| distances.get(&p).unwrap()).unwrap_or(&10) *door_pos.map(|p| distances.get(&p).unwrap()).unwrap_or(&10)
} }
fn distance_to_locked_door(k: &Key) -> usize { fn distance_to_locked_door(map: &HashMap<Position2D, char>, k: &Key) -> usize {
let (distances, _, doors) = traverse(find(k.0).unwrap()); let (distances, _, doors) = traverse(map, find(map, k.0).unwrap());
*doors.keys() *doors
.map(|d| find(d.0).unwrap()) .keys()
.map(|d| find(map, d.0).unwrap())
.map(|p| distances.get(&p).unwrap_or(&9999999999999)) .map(|p| distances.get(&p).unwrap_or(&9999999999999))
.min() .min()
.unwrap_or(&0) .unwrap_or(&0)
} }
fn main() { fn next_key(
let mut pos = find('@').unwrap(); map: &mut HashMap<Position2D, char>,
let mut steps = 0; pos: &Position2D,
let (_, dependencies, _) = traverse(pos.to_owned()); steps: usize,
let mut i = 0; ) -> usize {
while i < dependencies.len() { if too_slow(steps) {
println!("{}", i); // println!("Aborting after {} steps", steps);
let (distances, dependencies, door_dependencies) = traverse(pos.to_owned()); return 999999;
//dbg!(distances.get(&find('c').unwrap()));
let reachable_doors: HashSet<_> = door_dependencies
.iter()
.filter_map(|(door, doors)| if doors.is_empty() { Some(door) } else { None })
.collect();
let useful_keys = dependencies
.iter()
.filter(|(_, (_, d))| d.is_empty())
.filter(|(k, _)| {
reachable_doors.contains(&Door {
0: k.0.to_uppercase().next().unwrap(),
})
})
//.min_by_key(|(k, _)| distances.get(&find(k.0).unwrap()))
//.unwrap()
.map(|(k, _)| (k, distances.get(&find(k.0).unwrap()).unwrap().mul(2)));
//.min_by_key(|k| )
let next: Key = find_keys_at_dead_ends(&dependencies)
.iter()
.filter(|k| dependencies.get(k).unwrap().1.is_empty())
.map(|k| (k, distances.get(&find(k.0).unwrap()).unwrap().mul(1)))
//.min_by_key(|k| distances.get(&find(k.0).unwrap()))
.chain(useful_keys)
.min_by_key(|(k, n)| *n)// + distance_to_locked_door(&k))// * 100 + k.0 as usize)
.expect("no candidate found")
.0
.to_owned();
dbg!(&next);
let next_pos = find(next.0).unwrap();
steps += distances.get(&next_pos).unwrap();
pos = next_pos;
for k in &dependencies.get(&next).unwrap().0 {
dbg!(&k);
remove(k.to_owned());
i += 1;
}
remove(next);
i += 1;
dbg!(&i);
dbg!(&steps);
} }
println!("{}", steps); let (distances, dependencies, door_dependencies) = traverse(map, pos.to_owned());
//dbg!(distances.get(&find('c').unwrap()));
if dependencies.len() == 0 {
// println!("End of path after {} steps", steps);
replace(steps);
return steps;
}
let reachable_doors: HashSet<_> = door_dependencies
.iter()
.filter_map(|(door, doors)| if doors.is_empty() { Some(door) } else { None })
.collect();
let mut useful_keys: Vec<_> = dependencies
.iter()
.filter(|(_, (_, d))| d.is_empty())
.filter(|(k, _)| {
reachable_doors.contains(&Door {
0: k.0.to_uppercase().next().unwrap(),
})
})
//.min_by_key(|(k, _)| distances.get(&find(k.0).unwrap()))
//.unwrap()
.map(|(k, _)| {
(
k,
distances
.get(&find(map, k.0).expect("could not find key"))
.expect("No distance to key")
.mul(1),
)
})
.collect();
//.min_by_key(|k| )
let dead_end_keys = find_keys_at_dead_ends(&dependencies);
let mut next: Vec<_> = dead_end_keys
.iter()
.filter(|k| dependencies.get(k).expect("not in deps").1.is_empty())
.map(|k| {
(
k,
distances
.get(&find(map, k.0).expect("could not find key #2"))
.expect("No distance to key #2")
.mul(1),
)
})
//.min_by_key(|k| distances.get(&find(k.0).unwrap()))
//.chain(useful_keys)
.collect();
next.sort_by_key(|(k, n)| *n);//*3 + distance_to_door(map, k));
useful_keys.sort_by_key(|(k, n)| *n);
useful_keys.reverse();
useful_keys.pop().map(|k| next.insert(0, k));
useful_keys.pop().map(|k| next.push(k));
useful_keys.pop().map(|k| next.push(k));
useful_keys.pop().map(|k| next.push(k));
useful_keys.pop().map(|k| next.push(k));
let len = next.len();
next.par_iter()
.take((len.min(5) - 1).max(1))
.map(|(k, _)| {
let mut map2 = map.clone();
let next_pos = find(&map2, k.0).expect("Could not find key #3");
let steps_after_next = steps + distances.get(&next_pos).expect("No distance to key #3");
// collect keys on the way (if any)
for k in &dependencies.get(&k).expect("No dependency found").0 {
remove(&mut map2, k.to_owned());
}
remove(&mut map2, k.to_owned().to_owned());
next_key(&mut map2, &next_pos, steps_after_next)
})
.min()
.unwrap()
}
fn main() {
let map: HashMap<Position2D, char> = io::stdin()
.lock()
.lines()
.enumerate()
.flat_map(move |(y, l)| {
l.unwrap()
.to_owned()
.chars()
.enumerate()
.map(move |(x, c)| ((x, y).into(), c))
.collect::<Vec<_>>()
})
.collect();
let pos = find(&map, '@').unwrap();
let steps = 0;
let (_, dependencies, _) = traverse(&map, pos.to_owned());
let p1 = next_key(&mut map.clone(), &pos, 0);
println!("Part 1: {}", p1);
println!("Part 1: {}", BEST.lock().unwrap());
} }