diff --git a/2019/18/input2 b/2019/18/input2 new file mode 100644 index 0000000..d5d888b --- /dev/null +++ b/2019/18/input2 @@ -0,0 +1,9 @@ +################# +#i.G..c...e..H.p# +########.######## +#j.A..b...f..D.o# +########@######## +#k.E..a...g..B.n# +########.######## +#l.F..d...h..C.m# +################# diff --git a/2019/18/input3 b/2019/18/input3 new file mode 100644 index 0000000..ee6598e --- /dev/null +++ b/2019/18/input3 @@ -0,0 +1,6 @@ +######################## +#@..............ac.GI.b# +###d#e#f################ +###A#B#C################ +###g#h#i################ +######################## diff --git a/2019/18/input4 b/2019/18/input4 new file mode 100644 index 0000000..b650235 --- /dev/null +++ b/2019/18/input4 @@ -0,0 +1,5 @@ +######################## +#...............b.C.D.f# +#.###################### +#.....@.a.B.c.d.A.e.F.g# +######################## diff --git a/2019/18/notes b/2019/18/notes index 2ff8dd0..17faa68 100644 --- a/2019/18/notes +++ b/2019/18/notes @@ -9,3 +9,6 @@ tasks: dead end detection -> also done? //implement pathfinding (maybe dijkstra-like?) + +4816 too high +4266 too high diff --git a/2019/18/src/main.rs b/2019/18/src/main.rs index 887ba72..fe20979 100644 --- a/2019/18/src/main.rs +++ b/2019/18/src/main.rs @@ -1,49 +1,71 @@ use grid::*; use std::char; -use std::collections::{HashMap,HashSet}; +use std::collections::{HashMap, HashSet}; use std::io::{self, BufRead}; +use std::sync::Mutex; +use std::ops::Mul; #[macro_use] extern crate lazy_static; +#[derive(Hash, PartialEq, Eq, Clone, Debug)] +struct Door(char); + +#[derive(Hash, PartialEq, Eq, Clone, Debug)] +struct Key(char); + lazy_static! { - static ref MAP: HashMap = io::stdin() - .lock() - .lines() - .enumerate() - .flat_map(move |(y, l)| l - .unwrap() - .to_owned() - .chars() + static ref MAP: Mutex> = Mutex::new( + io::stdin() + .lock() + .lines() .enumerate() - .map(move |(x, c)| ((x, y).into(), c)) - .collect::>()) - .collect(); + .flat_map(move |(y, l)| l + .unwrap() + .to_owned() + .chars() + .enumerate() + .map(move |(x, c)| ((x, y).into(), c)) + .collect::>()) + .collect() + ); } -fn visit_neighbors(position: Position2D, steps: usize, distances: &mut HashMap, dependencies: &mut HashMap>, mut doors: HashSet) { +fn get(p: &Position2D) -> Option { + MAP.lock().unwrap().get(p).copied() +} + +fn visit_neighbors( + position: Position2D, + steps: usize, + distances: &mut HashMap, + dependencies: &mut HashMap, HashSet)>, + door_dependencies: &mut HashMap>, + mut doors: HashSet, + mut keys: HashSet, +) { + let c = 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 }); + } + if c.is_uppercase() { + 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 MAP.get(p) { - Some(&'#') => (), - Some(n) if n.is_alphanumeric() => { - if n.is_lowercase() { - dependencies.insert(*n, doors.clone()); - } - if n.is_uppercase() { - doors.insert(*n); - } - if distances.get(p).is_none() { - unvisited.push(p.to_owned()); - } - }, + match get(p) { + Some('#') => (), _ => { if distances.get(p).is_none() { unvisited.push(p.to_owned()); } - }, + } } - if MAP.get(p) != Some(&'#') && distances.get(p).is_none() { + if get(p) != Some('#') && distances.get(p).is_none() { unvisited.push(p.to_owned()); } } @@ -53,31 +75,135 @@ fn visit_neighbors(position: Position2D, steps: usize, distances: &mut HashMap

(HashMap, HashMap>) { +fn traverse( + start: Position2D, +) -> ( + HashMap, + HashMap, HashSet)>, + HashMap>, +) { let mut distances = HashMap::new(); let mut dependencies = HashMap::new(); + let mut door_dependencies = HashMap::new(); distances.insert(start, 0usize); - visit_neighbors(start, 0, &mut distances, &mut dependencies, HashSet::new()); - (distances, dependencies) + visit_neighbors( + start, + 0, + &mut distances, + &mut dependencies, + &mut door_dependencies, + HashSet::new(), + HashSet::new(), + ); + (distances, dependencies, door_dependencies) +} + +fn find_keys_at_dead_ends(deps: &HashMap, HashSet)>) -> HashSet { + let keydeps: HashSet<_> = deps.iter().flat_map(|(_, (keys, _))| keys).collect(); + deps.keys() + .collect::>() + .difference(&keydeps) + .into_iter() + .map(|k| k.to_owned().to_owned()) + .collect() +} + +fn find(c: char) -> Option { + let map = MAP.lock().unwrap(); + map.keys().find(|p| map.get(&p) == Some(&c)).copied() +} + +fn clear_field(p: &Position2D) { + MAP.lock().unwrap().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 distance_to_door(k: &Key) -> usize { + let (distances, _, _) = traverse(find(k.0).unwrap()); + let door_pos = find(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()) + .map(|p| distances.get(&p).unwrap_or(&9999999999999)) + .min() + .unwrap_or(&0) } fn main() { - //println!("{:?}", MAP.keys()); - println!("{}", draw_ascii(&MAP, ' ')); - let start = MAP.keys().find(|p| MAP.get(&p) == Some(&'@')).unwrap(); - let v = MAP.keys().find(|p| MAP.get(&p) == Some(&'v')).unwrap(); - println!("Start: {:?}", start); - let (distances, dependencies) = traverse(start.to_owned()); - println!("{:?}", distances); - println!("{:?}", distances.get(&v)); - println!("{:?}", distances.len()); - println!("{:?}", dependencies.get(&'m')); - println!("{}", dependencies.len()); + 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); + } + println!("{}", steps); }