Compare commits

...

2 Commits

Author SHA1 Message Date
Johannes
c726b9090f day 18 task 2 2019-12-25 16:12:33 +01:00
Johannes
7b51854bb2 day 18 task 1 2019-12-22 14:11:23 +01:00
4 changed files with 384 additions and 1 deletions

81
input/day18.txt Normal file
View File

@@ -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#
#################################################################################

View File

@@ -1,5 +1,5 @@
mod tasks; mod tasks;
fn main() { fn main() {
tasks::day15::run(); tasks::day18::run();
} }

301
src/tasks/day18.rs Normal file
View File

@@ -0,0 +1,301 @@
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;
#[allow(dead_code)]
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());
let t1 = task(input.clone());
println!("Task 1: best bound to get all keys is {}", t1);
let t2 = task(input.clone().split_robot());
println!("Task 2: best bound to get all keys is {}", t2);
}
fn task(map: Map) -> usize {
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<StateSummary> = HashSet::new();
let mut open: BinaryHeap<State> = 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);
}
best_found_for_all
}
#[derive(Eq, PartialEq, Hash, Debug)]
struct StateSummary(Vec<Pos>, String);
impl StateSummary {
fn from(state: &State) -> Self {
let mut s = state.opened.iter().collect_vec();
s.sort();
Self(
state.currents.clone(),
s.into_iter()
.map(|c| {
let mut x = *c;
x.make_ascii_lowercase();
x
})
.collect(),
)
}
}
#[derive(Eq, PartialEq)]
struct StateSummaryDist(Pos, HashSet<char>, usize);
#[derive(PartialEq, Eq, Clone)]
struct Map(Vec<Vec<char>>);
impl Map {
fn coordinate_of(&self, symbol: char) -> Vec<Pos> {
self.0
.iter()
.enumerate()
.fold(Vec::new(), |vec, (y, line)| {
line.iter()
.enumerate()
.filter(|(_x, c)| **c == symbol)
.fold(vec, |mut vec, (x, _c)| {
vec.push(Pos(x, y));
vec
})
})
}
/**
* return: (char: found key, usize1: index of moved robot,
* usize2: distance that robot moved, Pos: new position of robot[index]),
* usize3: number of empty points on path to last_door_opened
*/
fn reachable_keys(
&self,
start_points: Vec<Pos>,
open_doors: &HashSet<char>,
last_door_opened: Option<char>,
) -> Vec<(char, usize, usize, Pos, usize)> {
let all_keys = 'a'..='z';
let bfs = |start: Pos, open_doors: &HashSet<char>| {
// key, distance, new_pos, number of steps of distance until last_open door is met
let mut result: Vec<(char, usize, Pos, usize)> = vec![];
let mut open: VecDeque<(Pos, usize, usize)> = VecDeque::new(); //position, distance form start, distance until last door opened is met
open.push_back((start, 0, 0));
let mut visited: HashSet<Pos> = HashSet::new();
while let Some((current, walked, ldod)) = open.pop_front() {
if visited.contains(&current) {
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 {
let mut ldod = 0;
if let Some(ldo) = last_door_opened {
if ldo == field {
ldod = walked;
}
};
current
.neighbors()
.iter()
.for_each(|n| open.push_back((*n, walked + 1, ldod)));
}
// if it is a key: push it to result
if field_is_key {
result.push((field, walked, current, ldod));
}
// anyways: push to visited
visited.insert(current);
}
result
};
start_points
.iter()
.enumerate()
.flat_map(|(robot_i, robot_pos)| {
bfs(*robot_pos, open_doors)
.into_iter()
.map(|(key, dist, final_pos, ldod)| (key, robot_i, dist, final_pos, ldod))
.collect_vec()
})
.collect_vec()
}
fn split_robot(mut self) -> Self {
let Pos(x, y) = self.coordinate_of('@')[0];
self.0[x][y] = '#';
self.0[x - 1][y - 1] = '@';
self.0[x - 1][y] = '#';
self.0[x - 1][y + 1] = '@';
self.0[x + 1][y] = '#';
self.0[x + 1][y + 1] = '@';
self.0[x][y - 1] = '#';
self.0[x + 1][y - 1] = '@';
self.0[x][y + 1] = '#';
self
}
}
#[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 {
currents: Vec<Pos>,
underrun: Vec<usize>, // the number of steps a robot is behind the leading robots steps
opened: HashSet<char>, // capital letter
map: Rc<Map>,
steps_taken: usize,
last_door_opened: Option<char>,
}
impl State {
fn new(map: Rc<Map>) -> Self {
let currents = map.coordinate_of('@');
let no_robots = currents.len();
Self {
currents: currents,
underrun: std::iter::repeat(0).take(no_robots).collect_vec(),
opened: HashSet::new(),
map: map,
steps_taken: 0,
last_door_opened: None,
}
}
fn get_available_options(&self) -> Vec<State> {
// find all keys that are not yet collected + their distance + position
let next_keys = self
.map
.reachable_keys(self.currents.clone(), &self.opened, self.last_door_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, robot_i, distance, current, ldod)| {
self.advance(key, robot_i, distance, current, ldod)
})
.collect_vec()
}
fn advance(
&self,
key_added: char,
robot_index: usize,
additional_steps: usize,
new_pos: Pos,
ldod: usize,
) -> Self {
let mut open_doors = self.opened.clone();
let mut door = key_added;
door.make_ascii_uppercase();
open_doors.insert(door);
let mut positions = self.currents.clone();
positions[robot_index] = new_pos;
let underrun = self
.underrun
.iter()
.enumerate()
.map(|(i, u)| {
if i != robot_index {
*u + additional_steps
} else {
0
}
})
.collect();
let usable_underrun = if self.underrun[robot_index] >= ldod {
ldod
} else {
self.underrun[robot_index]
};
let steps_diff = additional_steps - usable_underrun;
Self {
currents: positions,
underrun: underrun,
opened: open_doors,
map: self.map.clone(),
steps_taken: self.steps_taken + steps_diff as usize,
last_door_opened: Some(key_added),
}
}
}
// 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<Ordering> {
Some(self.cmp(other))
}
}

View File

@@ -13,4 +13,5 @@ pub mod day13;
pub mod day14; pub mod day14;
pub mod day15; pub mod day15;
pub mod day16; pub mod day16;
pub mod day18;
pub mod day21; pub mod day21;