diff --git a/2019/18/Cargo.toml b/2019/18/Cargo.toml index 96923f8..02180db 100644 --- a/2019/18/Cargo.toml +++ b/2019/18/Cargo.toml @@ -9,3 +9,4 @@ edition = "2018" [dependencies] grid = { path = "../grid" } lazy_static = "1.3.0" +rayon = "1.2.1" diff --git a/2019/18/src/main.rs b/2019/18/src/main.rs index fe20979..70ac452 100644 --- a/2019/18/src/main.rs +++ b/2019/18/src/main.rs @@ -1,9 +1,10 @@ use grid::*; +use rayon::prelude::*; use std::char; use std::collections::{HashMap, HashSet}; use std::io::{self, BufRead}; -use std::sync::Mutex; use std::ops::Mul; +use std::sync::Mutex; #[macro_use] extern crate lazy_static; @@ -14,27 +15,24 @@ struct Door(char); struct Key(char); lazy_static! { - static ref MAP: Mutex> = Mutex::new( - 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::>()) - .collect() - ); + static ref BEST: Mutex = Mutex::new(999999); } -fn get(p: &Position2D) -> Option { - MAP.lock().unwrap().get(p).copied() +fn too_slow(i: usize) -> bool { + 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( + map: &HashMap, position: Position2D, steps: usize, distances: &mut HashMap, @@ -43,21 +41,21 @@ fn visit_neighbors( mut doors: HashSet, mut keys: HashSet, ) { - let c = get(&position).unwrap(); + let c = map.get(&position).unwrap(); if c.is_alphabetic() { if c.is_lowercase() { - dependencies.insert(Key { 0: c }, (keys.clone(), doors.clone())); - keys.insert(Key { 0: c }); + dependencies.insert(Key { 0: *c }, (keys.clone(), doors.clone())); + keys.insert(Key { 0: *c }); } if c.is_uppercase() { - door_dependencies.insert(Door { 0: c }, doors.clone()); - doors.insert(Door { 0: c }); + door_dependencies.insert(Door { 0: *c }, doors.clone()); + doors.insert(Door { 0: *c }); } } let mut unvisited = Vec::new(); // TODO: fix ownership and make this a single filter().inspect().for_each() for (_, p) in &position.neighbors() { - match get(p) { + match map.get(p) { Some('#') => (), _ => { 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()); } } @@ -79,6 +77,7 @@ fn visit_neighbors( } for p in &unvisited { visit_neighbors( + map, p.to_owned(), steps + 1, distances, @@ -91,6 +90,7 @@ fn visit_neighbors( } fn traverse( + map: &HashMap, start: Position2D, ) -> ( HashMap, @@ -102,6 +102,7 @@ fn traverse( let mut door_dependencies = HashMap::new(); distances.insert(start, 0usize); visit_neighbors( + map, start, 0, &mut distances, @@ -123,87 +124,140 @@ fn find_keys_at_dead_ends(deps: &HashMap, HashSet)>) -> .collect() } -fn find(c: char) -> Option { - let map = MAP.lock().unwrap(); +fn find(map: &HashMap, c: char) -> Option { map.keys().find(|p| map.get(&p) == Some(&c)).copied() } -fn clear_field(p: &Position2D) { - MAP.lock().unwrap().insert(*p, '.'); +fn clear_field(map: &mut HashMap, p: &Position2D) { + map.insert(*p, '.'); } -fn remove(k: Key) { - let key_pos = find(k.0).unwrap(); - clear_field(&key_pos); - if let Some(door_pos) = find(k.0.to_uppercase().next().unwrap()) { - clear_field(&door_pos); +fn remove(map: &mut HashMap, k: Key) { + let key_pos = find(map, k.0).unwrap(); + clear_field(map, &key_pos); + if let Some(door_pos) = find(map, k.0.to_uppercase().next().unwrap()) { + clear_field(map, &door_pos); } } -fn distance_to_door(k: &Key) -> usize { - let (distances, _, _) = traverse(find(k.0).unwrap()); - let door_pos = find(k.0.to_uppercase().next().unwrap()); +fn distance_to_door(map: &HashMap, k: &Key) -> usize { + let (distances, _, _) = traverse(map, find(map, k.0).unwrap()); + let door_pos = find(map, k.0.to_uppercase().next().unwrap()); *door_pos.map(|p| distances.get(&p).unwrap()).unwrap_or(&10) } -fn distance_to_locked_door(k: &Key) -> usize { - let (distances, _, doors) = traverse(find(k.0).unwrap()); - *doors.keys() - .map(|d| find(d.0).unwrap()) +fn distance_to_locked_door(map: &HashMap, k: &Key) -> usize { + let (distances, _, doors) = traverse(map, find(map, k.0).unwrap()); + *doors + .keys() + .map(|d| find(map, d.0).unwrap()) .map(|p| distances.get(&p).unwrap_or(&9999999999999)) .min() .unwrap_or(&0) } -fn main() { - let mut pos = find('@').unwrap(); - let mut steps = 0; - let (_, dependencies, _) = traverse(pos.to_owned()); - let mut i = 0; - while i < dependencies.len() { - println!("{}", i); - let (distances, dependencies, door_dependencies) = traverse(pos.to_owned()); - //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); +fn next_key( + map: &mut HashMap, + pos: &Position2D, + steps: usize, +) -> usize { + if too_slow(steps) { + // println!("Aborting after {} steps", steps); + return 999999; } - 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 = 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::>() + }) + .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()); }