day 18 task 2
This commit is contained in:
@@ -5,6 +5,7 @@ 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()
|
||||
@@ -12,10 +13,13 @@ pub fn run() {
|
||||
.map(|line| line.chars().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
|
||||
.0
|
||||
.iter()
|
||||
@@ -51,20 +55,18 @@ fn task1(map: Map) {
|
||||
|
||||
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);
|
||||
struct StateSummary(Vec<Pos>, String);
|
||||
impl StateSummary {
|
||||
fn from(state: &State) -> Self {
|
||||
let mut s = state.opened.iter().collect_vec();
|
||||
s.sort();
|
||||
Self(
|
||||
state.current,
|
||||
state.currents.clone(),
|
||||
s.into_iter()
|
||||
.map(|c| {
|
||||
let mut x = *c;
|
||||
@@ -82,29 +84,41 @@ struct StateSummaryDist(Pos, HashSet<char>, usize);
|
||||
struct Map(Vec<Vec<char>>);
|
||||
|
||||
impl Map {
|
||||
fn coordinate_of(&self, symbol: char) -> Pos {
|
||||
fn coordinate_of(&self, symbol: char) -> Vec<Pos> {
|
||||
self.0
|
||||
.iter()
|
||||
.enumerate()
|
||||
.map(|(y, line)| {
|
||||
.fold(Vec::new(), |vec, (y, line)| {
|
||||
line.iter()
|
||||
.enumerate()
|
||||
.find(|(_x, c)| **c == symbol)
|
||||
.map(|(x, _c)| Pos(x, y))
|
||||
.filter(|(_x, c)| **c == symbol)
|
||||
.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 mut result = vec![];
|
||||
|
||||
let mut open: VecDeque<(Pos, usize)> = VecDeque::new();
|
||||
open.push_back((start, 0));
|
||||
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)) = open.pop_front() {
|
||||
while let Some((current, walked, ldod)) = open.pop_front() {
|
||||
if visited.contains(¤t) {
|
||||
continue;
|
||||
}
|
||||
@@ -112,15 +126,21 @@ impl Map {
|
||||
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)));
|
||||
.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));
|
||||
result.push((field, walked, current, ldod));
|
||||
}
|
||||
|
||||
// anyways: push to visited
|
||||
@@ -128,6 +148,32 @@ impl Map {
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -147,19 +193,25 @@ impl Pos {
|
||||
|
||||
#[derive(Eq, PartialEq)]
|
||||
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
|
||||
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 {
|
||||
current: map.coordinate_of('@'),
|
||||
currents: currents,
|
||||
underrun: std::iter::repeat(0).take(no_robots).collect_vec(),
|
||||
opened: HashSet::new(),
|
||||
map: map,
|
||||
steps_taken: 0,
|
||||
last_door_opened: None,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -167,9 +219,9 @@ impl State {
|
||||
// find all keys that are not yet collected + their distance + position
|
||||
let next_keys = self
|
||||
.map
|
||||
.reachable_keys(self.current, &self.opened)
|
||||
.reachable_keys(self.currents.clone(), &self.opened, self.last_door_opened)
|
||||
.into_iter()
|
||||
.filter(|(key, _, _)| {
|
||||
.filter(|(key, _, _, _, _)| {
|
||||
let mut c = *key;
|
||||
c.make_ascii_uppercase();
|
||||
!self.opened.contains(&c)
|
||||
@@ -179,20 +231,52 @@ impl State {
|
||||
// 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))
|
||||
.map(|(key, robot_i, distance, current, ldod)| {
|
||||
self.advance(key, robot_i, distance, current, ldod)
|
||||
})
|
||||
.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 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 {
|
||||
current: new_pos,
|
||||
currents: positions,
|
||||
underrun: underrun,
|
||||
opened: open_doors,
|
||||
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),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user