day 18 task 2

This commit is contained in:
Johannes
2019-12-25 16:12:33 +01:00
parent 7b51854bb2
commit c726b9090f

View File

@@ -5,6 +5,7 @@ use std::collections::HashSet;
use std::collections::VecDeque; use std::collections::VecDeque;
use std::rc::Rc; use std::rc::Rc;
#[allow(dead_code)]
pub fn run() { pub fn run() {
let input: Map = Map(std::fs::read_to_string("input/day18.txt") let input: Map = Map(std::fs::read_to_string("input/day18.txt")
.unwrap() .unwrap()
@@ -12,10 +13,13 @@ pub fn run() {
.map(|line| line.chars().collect_vec()) .map(|line| line.chars().collect_vec())
.collect_vec()); .collect_vec());
task1(input.clone()); 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 task1(map: Map) { fn task(map: Map) -> usize {
let mut all_keys = map let mut all_keys = map
.0 .0
.iter() .iter()
@@ -51,20 +55,18 @@ fn task1(map: Map) {
visited.insert(summary); visited.insert(summary);
} }
println!(
"Task 1: best bound to get all keys is {}", best_found_for_all
best_found_for_all
);
} }
#[derive(Eq, PartialEq, Hash, Debug)] #[derive(Eq, PartialEq, Hash, Debug)]
struct StateSummary(Pos, String); struct StateSummary(Vec<Pos>, String);
impl StateSummary { impl StateSummary {
fn from(state: &State) -> Self { fn from(state: &State) -> Self {
let mut s = state.opened.iter().collect_vec(); let mut s = state.opened.iter().collect_vec();
s.sort(); s.sort();
Self( Self(
state.current, state.currents.clone(),
s.into_iter() s.into_iter()
.map(|c| { .map(|c| {
let mut x = *c; let mut x = *c;
@@ -82,52 +84,96 @@ struct StateSummaryDist(Pos, HashSet<char>, usize);
struct Map(Vec<Vec<char>>); struct Map(Vec<Vec<char>>);
impl Map { impl Map {
fn coordinate_of(&self, symbol: char) -> Pos { fn coordinate_of(&self, symbol: char) -> Vec<Pos> {
self.0 self.0
.iter() .iter()
.enumerate() .enumerate()
.map(|(y, line)| { .fold(Vec::new(), |vec, (y, line)| {
line.iter() line.iter()
.enumerate() .enumerate()
.find(|(_x, c)| **c == symbol) .filter(|(_x, c)| **c == symbol)
.map(|(x, _c)| Pos(x, y)) .fold(vec, |mut vec, (x, _c)| {
vec.push(Pos(x, y));
vec
})
}) })
.find(|op| op.is_some())
.unwrap()
.unwrap()
} }
fn reachable_keys(&self, start: Pos, open_doors: &HashSet<char>) -> Vec<(char, usize, Pos)> { /**
* 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 all_keys = 'a'..='z';
let mut result = vec![];
let mut open: VecDeque<(Pos, usize)> = VecDeque::new(); let bfs = |start: Pos, open_doors: &HashSet<char>| {
open.push_back((start, 0)); // key, distance, new_pos, number of steps of distance until last_open door is met
let mut visited: HashSet<Pos> = HashSet::new(); let mut result: Vec<(char, usize, Pos, usize)> = vec![];
while let Some((current, walked)) = open.pop_front() { let mut open: VecDeque<(Pos, usize, usize)> = VecDeque::new(); //position, distance form start, distance until last door opened is met
if visited.contains(&current) { open.push_back((start, 0, 0));
continue; let mut visited: HashSet<Pos> = HashSet::new();
} while let Some((current, walked, ldod)) = open.pop_front() {
let field = self.0[current.1][current.0]; if visited.contains(&current) {
let field_is_key = all_keys.contains(&field); continue;
// if can move over current type: push neighbors to open }
if field == '.' || field == '@' || open_doors.contains(&field) || field_is_key { let field = self.0[current.1][current.0];
current let field_is_key = all_keys.contains(&field);
.neighbors() // if can move over current type: push neighbors to open
.iter() if field == '.' || field == '@' || open_doors.contains(&field) || field_is_key {
.for_each(|n| open.push_back((*n, walked + 1))); 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);
} }
// if it is a key: push it to result result
if field_is_key { };
result.push((field, walked, current));
}
// anyways: push to visited start_points
visited.insert(current); .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()
}
result 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
} }
} }
@@ -147,19 +193,25 @@ impl Pos {
#[derive(Eq, PartialEq)] #[derive(Eq, PartialEq)]
struct State { struct State {
current: Pos, currents: Vec<Pos>,
underrun: Vec<usize>, // the number of steps a robot is behind the leading robots steps
opened: HashSet<char>, // capital letter opened: HashSet<char>, // capital letter
map: Rc<Map>, map: Rc<Map>,
steps_taken: usize, steps_taken: usize,
last_door_opened: Option<char>,
} }
impl State { impl State {
fn new(map: Rc<Map>) -> Self { fn new(map: Rc<Map>) -> Self {
let currents = map.coordinate_of('@');
let no_robots = currents.len();
Self { Self {
current: map.coordinate_of('@'), currents: currents,
underrun: std::iter::repeat(0).take(no_robots).collect_vec(),
opened: HashSet::new(), opened: HashSet::new(),
map: map, map: map,
steps_taken: 0, steps_taken: 0,
last_door_opened: None,
} }
} }
@@ -167,9 +219,9 @@ impl State {
// find all keys that are not yet collected + their distance + position // find all keys that are not yet collected + their distance + position
let next_keys = self let next_keys = self
.map .map
.reachable_keys(self.current, &self.opened) .reachable_keys(self.currents.clone(), &self.opened, self.last_door_opened)
.into_iter() .into_iter()
.filter(|(key, _, _)| { .filter(|(key, _, _, _, _)| {
let mut c = *key; let mut c = *key;
c.make_ascii_uppercase(); c.make_ascii_uppercase();
!self.opened.contains(&c) !self.opened.contains(&c)
@@ -179,20 +231,52 @@ impl State {
// create new state with one open door added + steps increased + current position updated // create new state with one open door added + steps increased + current position updated
next_keys next_keys
.into_iter() .into_iter()
.map(|(key, distance, current)| self.advance(key, distance, current)) .map(|(key, robot_i, distance, current, ldod)| {
self.advance(key, robot_i, distance, current, ldod)
})
.collect_vec() .collect_vec()
} }
fn advance(&self, key_added: char, additional_steps: usize, new_pos: Pos) -> Self { 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 open_doors = self.opened.clone();
let mut door = key_added; let mut door = key_added;
door.make_ascii_uppercase(); door.make_ascii_uppercase();
open_doors.insert(door); 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 { Self {
current: new_pos, currents: positions,
underrun: underrun,
opened: open_doors, opened: open_doors,
map: self.map.clone(), map: self.map.clone(),
steps_taken: self.steps_taken + additional_steps, steps_taken: self.steps_taken + steps_diff as usize,
last_door_opened: Some(key_added),
} }
} }
} }