diff --git a/input/day18.txt b/input/day18.txt new file mode 100644 index 0000000..fe635cb --- /dev/null +++ b/input/day18.txt @@ -0,0 +1,81 @@ +################################################################################# +#.#...#.....#...............#.....#v....#.........#p....#...#...................# +#.#.#.#.#.#.#M#############.#.###.#####.#####.###.#.###.#.#.#################.#.# +#..n#...#.#...#.#...........#...#.......#.....#.....#.#...#...#.......#...#...#.# +#########.#####.#.###########.#.#######.#.###########.#######.#C#####.#.#.#.###.# +#.........#.#.....#.........#.#.#.......#...........#...........#...#.#.#...#...# +#.#########.#.#########.#####.#W###.###############.###.#########.###.#.#####.### +#.Y...#...#...#.......#z......#...#.#...#...#.....#..d#.#.#..x..#...#.#.#...#.#.# +#####.#.#.#.###.#.###.#####.#####.#.#.#.#.#.#.###.###.#.#.#.###.#.#.#.#.#.#.#.#.# +#.....#.#...#...#.#.#.....#...#...#.#.#.#.#...#.......#...#.#...#.#.....#.#...#.# +#.#######.#######.#.#####.#####.#####.#.#.#############.###.#.###.#########.###.# +#...#...#.#...#...#.#...#.....#...B...#.#.....#...#.....#...#.#.....#....t#.....# +#.#.#.#.#.#.#.#.###.#.#.#####.#.#######.#.###.#.#.###.###.###.#.#####.###.#####N# +#.#...#.#...#.#.#.....#...#...#.#...#...#...#.#.#...#.#...#...#...#...#.#...#.#.# +#.#####.#####.#.#########.#.###.#.#.#.#.###.#.#.###.###.###.#######.###.###.#.#.# +#.#...#.#.......#...#.....#...#...#.#.#.#.#.#...#.#.....#...#.....#.#...#...#.#.# +#.#.###.#########.#.#.###.###.#####.#.#.#.#.#####.#######.###.###.#.#.###.###.#.# +#.#...#.#...#.....#.#...#...#.#...#.#.#.#.#.#.....#.....#...#.#.#...#...#.#...#.# +#.###.#.#.#.#.#####.#.#.#####.#.###.#.###.#.#.###.###.#.###.#.#.#####.#.#J#E###.# +#.....#.#.#...#...#.#.#.....#.#.....#...#.#.#.#.......#.#...#.#...#...#...#.....# +#######.#.#####.###.#######.#.#########.#.#.#.#########.#.###.#.#.#.#########.### +#.......#.#.....#.I.#.....#.............#...#...#.....#.#...#.#.#.#.........#...# +#.#######.###.###.#.#####.#.###########.#.###.#.###.###.###.#####.#########.###.# +#.......#...#.#...#.....#.#.#.......#...#.#...#.#...#.....#.........#.....#.#...# +#.#####.###.#.#.#######.#.###.#####.#.###.#####.#.###.#######.#####.#.###.#.###.# +#.#...#...#.#.#.......#.#.....#.#...#...#.#...#.#...#u#.....#...#...#.#.#.#...#.# +#.###.#.###.#.#######.#.#######.#.#####.#.#.#.#.#.#.#.#.#.#.###.#####.#.#.###.#.# +#...#.#...#.....#.....#.#.....#...#...#.#...#.#.#.#...#.#.#...#.#.....#.#.#...#.# +###.#.###.#####.#.#####.#.#.#.#.###.#.#.#####.#.#.#####.#.#.###.#.#####.#.#.###.# +#...#...#.......#.#.....#.#.#.#.....#.#.#.....#.#.#.....#.#.#...#.....#.#.#.#..k# +#.###.###########.#.#######.#.#######.#.#.#####.###.#####.###.#.#####.#.#.#.#.### +#...#...........#.#.....#...#.......#.#.#...#.........#...#...#.#.....#.#...#.#.# +###.#.###.###.###.#####.#.#.#######.#.#.###.#.#########.#.#.###.#.#####.#####.#.# +#.#.#...#...#.#...#...#.#.#.#.....#.#.#.#.#.#...#.......#.#.#...#...#.....#...#.# +#.#.###.###.#.#.###.#.#.#.###.###.###.###.#.#####.#######.#.#.#####.#.#.#.#.###.# +#.#.#.....#.#.#.....#g#...#...#.#...#...#.#.......#...#...#.#...#.#.#.#.#.#.....# +#.#.#######.#.#######.###.#.###.###.###.#.#########.###.###.###.#.#.#.#.#######.# +#...#.....#.#.#.....#.#...#.......#.#...#.....#.......#.#.#...#...#.#.#.......#.# +#.###.###.#.#.#.###.#.###########.#.#.###.###.#.#####.#.#.###.#####.#.#######.#.# +#.....#.....#...#...#.A...........#.........#.......#.R.....#.......#.......#...# +#######################################.@.####################################### +#.....#.........#...........#......r#...........#.#...#.............#.......#...# +###.###.#.#######.#.#######.#.#####.###.#.#.###.#.#.#.#.#######.###.#.#####.###.# +#...#...#.........#...#...#.#.....#.....#.#...#...#.#...#...#...#.#.#.....#.....# +#.###.###############.#.###.#.###.#####.#.###.#####.#######.#.###.#.#####.####### +#.....#.......#.#.....#...#.#.#...#.....#.#.#.#.....#.......#.#...#.....#.......# +#.#######.###.#.#.#######.#.###.#.#######.#.#.#.#########.###.#.#######G#.#####.# +#.....#...#.#...#.#.......#...#.#.#.....#...#.#.#.....#..a#...#.#.....#.#i....#.# +#####.#.###.###.#.###.#.#####.#.###.###.#####.#.###.#.#.#.#.###.#.###.#.#######.# +#...#.#.#.....#.#...#.#.#.....#.....#...#...#.......#...#.#.#.....#.#.........#.# +#.#.#.#.#.#####.###.#.###.###########.###.#.#.#############.#.#####.#########.#.# +#.#.#.#.#.......#...#.....#.....#...#...#.#.#.#.............#.......#...#.....O.# +#.#.#.#.#########.#######.#.###.#.#.###.#.#.###.###########.#########.#.#######.# +#.#.#e#.........#.#.....#...#...#.#.....#.#.#...#...........#.......#.#.....#...# +###.#.#########.#.#.###.#####P###.#######.#.#.#########.#####.#####.#.#####.#.### +#...#.......#...#...#.#.#...#...#...#...#.#...#.......#.#w....#f..#...#...#.#...# +#.#.#######.#.#######.#.#.#####.#.#.#.#.#.#.###.#####.###.###.#.#.#####.#.#.##### +#.#.#...#...#.........#.#...#...#.#...#.#.#.#.......#.#...#...#.#...#...#.#.#...# +#.#.#.###.#######.###.#.#.#.#.###.#####.#.###.#######.#.#######.###.#.###.#.#.#.# +#.#...#...#.....#...#.#.#.#...#..o..#...#...#...#...#.#...#...F.#...#...#.#...#.# +#.#####.###.###.###.###.#####.#####.#.#.#.#.#.###.#.#.###.#.#####L###.#.#.#####.# +#.......#...#.#.#...#.#.....#.....#.#.#.#.#...#...#.#.#...#...#...#...#.#.....#.# +#K#######.#.#.#.#.###.#####.#####X###.#.#######.###.#.#.#######.#####.#.#######.# +#....j..#.#.#.....#...#.....#...#...#.#.#.......#...#.#.....#...#.S.#.#.........# +#######.###.#####.#.###.#####.#.###.#.###.#######.###.#####.#.###.#.#.########### +#.....#...#.....#.#...#.......#...#.#...#.....#.#.#.......#.#l....#.#.#...#.....# +#T#.#####.#####.#####.#.#########.#.###.#.###.#.#.#####.#.#.#######.#.#.###.###.# +#.#.....#.#.....#.....#.#.......#.#.#...#c#.#.#.....#...#.#...Z.#...#.#.....#...# +#.###.###.#.#.###.#####.#.#####.#.#.#.#.#.#.#.#####.#.#######.###.###.#.#####.#.# +#...#...#...#.#...#...#...#...#.#.#.#.#.#...#.....#...#.....#.#s..#...#.#.#...#.# +###.###.#.#####.###.###.###.#.###.#.#.###.#######.###.#.#.###.#.###.###Q#.#.##### +#.#.#.#.#.#...#...#...#.#...#.....#.#...#.#.....#.#...#.#.#...#.V.#.#...#.#.....# +#.#.#.#.#.#.#.###.#.#.#.#.#########.###.#.#.###.#.#####.#.#.#####.#.#.###.#####.# +#...#.#...#.#...#.#.#.#.#...#...#...#...#.#...#y..#...#.#.#.......#.#.#.......#.# +#.###.#####.###.#.###.#.###.#.###.###.#.#####.#####.#.#.#.#########.#.#.#.#####.# +#.#.....#...#.#...#...#.#...#.#.D.#.U.#.#.....#...#.#.#.#.......#...#..b#.#.....# +#.#####.#.###.#####.###.#.###.#.###.###.#.#####.#.#.#.#.#######.#.#######.#.###.# +#.H...#.#...#...........#.#...#.#.....#.#m......#...#...#.......#.#...#...#.#...# +#####.#.###.#############.#.#.#.#####.#.#.###############.#######.#.#.#####.#.### +#..q......#...............#.#.........#.#...............#...........#.......#..h# +################################################################################# \ No newline at end of file diff --git a/src/main.rs b/src/main.rs index 62cc75c..4f6627c 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,5 +1,5 @@ mod tasks; fn main() { - tasks::day15::run(); + tasks::day18::run(); } diff --git a/src/tasks/day18.rs b/src/tasks/day18.rs new file mode 100644 index 0000000..bc36a15 --- /dev/null +++ b/src/tasks/day18.rs @@ -0,0 +1,217 @@ +use itertools::Itertools; +use std::cmp::{Ord, Ordering, PartialOrd}; +use std::collections::BinaryHeap; +use std::collections::HashSet; +use std::collections::VecDeque; +use std::rc::Rc; + +pub fn run() { + let input: Map = Map(std::fs::read_to_string("input/day18.txt") + .unwrap() + .lines() + .map(|line| line.chars().collect_vec()) + .collect_vec()); + + task1(input.clone()); +} + +fn task1(map: Map) { + let mut all_keys = map + .0 + .iter() + .flatten() + .filter(|c| c.is_alphabetic() && c.is_lowercase()) + .collect_vec(); + all_keys.sort(); + let all_keys: String = all_keys.into_iter().collect(); + let map = Rc::from(map); + + let mut visited: HashSet = HashSet::new(); + let mut open: BinaryHeap = BinaryHeap::new(); + let mut best_found_for_all = std::usize::MAX; + + open.push(State::new(map.clone())); + while let Some(state) = open.pop() { + let summary = StateSummary::from(&state); + if visited.contains(&summary) || best_found_for_all <= state.steps_taken { + // there could come no better solution + continue; + } + + if summary.1 == all_keys { + if state.steps_taken < best_found_for_all { + best_found_for_all = state.steps_taken; + } + } + + state + .get_available_options() + .into_iter() + .for_each(|s| open.push(s)); + + visited.insert(summary); + } + println!( + "Task 1: best bound to get all keys is {}", + best_found_for_all + ); +} + +#[derive(Eq, PartialEq, Hash, Debug)] +struct StateSummary(Pos, String); +impl StateSummary { + fn from(state: &State) -> Self { + let mut s = state.opened.iter().collect_vec(); + s.sort(); + Self( + state.current, + s.into_iter() + .map(|c| { + let mut x = *c; + x.make_ascii_lowercase(); + x + }) + .collect(), + ) + } +} +#[derive(Eq, PartialEq)] +struct StateSummaryDist(Pos, HashSet, usize); + +#[derive(PartialEq, Eq, Clone)] +struct Map(Vec>); + +impl Map { + fn coordinate_of(&self, symbol: char) -> Pos { + self.0 + .iter() + .enumerate() + .map(|(y, line)| { + line.iter() + .enumerate() + .find(|(_x, c)| **c == symbol) + .map(|(x, _c)| Pos(x, y)) + }) + .find(|op| op.is_some()) + .unwrap() + .unwrap() + } + + fn reachable_keys(&self, start: Pos, open_doors: &HashSet) -> Vec<(char, usize, Pos)> { + let all_keys = 'a'..='z'; + let mut result = vec![]; + + let mut open: VecDeque<(Pos, usize)> = VecDeque::new(); + open.push_back((start, 0)); + let mut visited: HashSet = HashSet::new(); + while let Some((current, walked)) = open.pop_front() { + if visited.contains(¤t) { + continue; + } + let field = self.0[current.1][current.0]; + let field_is_key = all_keys.contains(&field); + // if can move over current type: push neighbors to open + if field == '.' || field == '@' || open_doors.contains(&field) || field_is_key { + current + .neighbors() + .iter() + .for_each(|n| open.push_back((*n, walked + 1))); + } + + // if it is a key: push it to result + if field_is_key { + result.push((field, walked, current)); + } + + // anyways: push to visited + visited.insert(current); + } + + result + } +} + +#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)] +struct Pos(usize, usize); + +impl Pos { + fn neighbors(&self) -> [Pos; 4] { + [ + Pos(self.0 - 1, self.1), + Pos(self.0 + 1, self.1), + Pos(self.0, self.1 - 1), + Pos(self.0, self.1 + 1), + ] + } +} + +#[derive(Eq, PartialEq)] +struct State { + current: Pos, + opened: HashSet, // capital letter + map: Rc, + steps_taken: usize, +} + +impl State { + fn new(map: Rc) -> Self { + Self { + current: map.coordinate_of('@'), + opened: HashSet::new(), + map: map, + steps_taken: 0, + } + } + + fn get_available_options(&self) -> Vec { + // find all keys that are not yet collected + their distance + position + let next_keys = self + .map + .reachable_keys(self.current, &self.opened) + .into_iter() + .filter(|(key, _, _)| { + let mut c = *key; + c.make_ascii_uppercase(); + !self.opened.contains(&c) + }) + .collect_vec(); + + // create new state with one open door added + steps increased + current position updated + next_keys + .into_iter() + .map(|(key, distance, current)| self.advance(key, distance, current)) + .collect_vec() + } + + fn advance(&self, key_added: char, additional_steps: usize, new_pos: Pos) -> Self { + let mut open_doors = self.opened.clone(); + let mut door = key_added; + door.make_ascii_uppercase(); + open_doors.insert(door); + Self { + current: new_pos, + opened: open_doors, + map: self.map.clone(), + steps_taken: self.steps_taken + additional_steps, + } + } +} + +// The priority queue depends on `Ord`. +// Explicitly implement the trait so the queue becomes a min-heap +// instead of a max-heap. +impl Ord for State { + fn cmp(&self, other: &State) -> Ordering { + // Notice that the we flip the ordering on costs. + // In case of a tie we compare positions - this step is necessary + // to make implementations of `PartialEq` and `Ord` consistent. + other.steps_taken.cmp(&self.steps_taken) + } +} + +// `PartialOrd` needs to be implemented as well. +impl PartialOrd for State { + fn partial_cmp(&self, other: &State) -> Option { + Some(self.cmp(other)) + } +} diff --git a/src/tasks/mod.rs b/src/tasks/mod.rs index be4bb81..42da51c 100644 --- a/src/tasks/mod.rs +++ b/src/tasks/mod.rs @@ -13,4 +13,5 @@ pub mod day13; pub mod day14; pub mod day15; pub mod day16; +pub mod day18; pub mod day21;