day 15 task 1

This commit is contained in:
Johannes
2019-12-21 23:08:59 +01:00
parent 73e4d50c51
commit 73b612d25b
5 changed files with 257 additions and 1 deletions

View File

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

253
src/tasks/day15.rs Normal file
View File

@@ -0,0 +1,253 @@
use super::day05::*;
use std::collections::HashMap;
use std::collections::VecDeque;
use Direction::*;
#[allow(dead_code)]
pub fn run() {
let ram = load_ram("input/day15.txt");
task1(ram.clone());
}
#[allow(dead_code)]
fn task1(ram: RAM) {
let (map, goal) = explore_map(ram);
let shortest_path = map.get_shortest_path_to(Pos(0, 0), goal);
/*
map.print(
Map::positions_on_path(Pos(0, 0), &shortest_path)
.iter()
.map(|p| (*p, 'x'))
.collect(),
);
*/
println!(
"Task 1: shortest path to goal ({:?}) has length {}",
goal,
shortest_path.len()
);
}
fn explore_map(ram: RAM) -> (Map, Pos) {
let mut map = Map {
positions: HashMap::new(),
};
let mut current = Pos(0, 0);
map.positions.insert(current, '.');
let mut target: Option<Pos> = None;
let mut pc = IntCodeComputer::new(vec![], ram);
while let Some(non_explored_neighbor) = map.get_unexplored() {
// move to next field with unexplored neighbors
// *find path
// *follow directions
let round_starts_from = current;
let directions = map.get_shortest_path_to(round_starts_from, non_explored_neighbor);
for d in directions {
let res = pc.run_single(d.to_robot_cmd());
if res == 0 {
map.print(HashMap::new());
panic!("Ran against a wall stepping {:?} from {:?}.", d, current);
}
current = current.neighbor(d);
}
// explore all 4 neighbor positions
// i.e. for every direction move - if result is 0 mark as wall,
// if result is 1 or 2 move back and mark as empty. If result is 2 store
// target position
let ns = vec![N, S, W, E];
for d in ns {
let r = pc.run_single(d.to_robot_cmd());
match r {
0 => {
map.positions.insert(current.neighbor(d), '#');
}
1 => {
map.positions.insert(current.neighbor(d), '.');
pc.run_single(d.opposite().to_robot_cmd());
}
2 => {
map.positions.insert(current.neighbor(d), '.');
pc.run_single(d.opposite().to_robot_cmd());
target = Some(current.neighbor(d));
}
_ => panic!("unknown field type {}", r),
};
}
}
(map, target.unwrap())
}
#[derive(PartialEq, Eq, Hash, Copy, Clone, Debug)]
struct Pos(i32, i32);
impl Pos {
fn add(&self, other: Self) -> Self {
Pos(self.0 + other.0, self.1 + other.1)
}
fn direction_to(&self, from: Pos) -> Direction {
let m: HashMap<Pos, Direction> = vec![
(Pos(0, 1), N),
(Pos(0, -1), S),
(Pos(1, 0), W),
(Pos(-1, 0), E),
]
.into_iter()
.collect();
m.get(&Pos(self.0 - from.0, self.1 - from.1))
.map(|p| *p)
.unwrap()
}
fn neighbor(&self, direction: Direction) -> Pos {
match direction {
N => self.add(Pos(0, -1)),
S => self.add(Pos(0, 1)),
W => self.add(Pos(-1, 0)),
E => self.add(Pos(1, 0)),
}
}
}
#[derive(Debug)]
struct Map {
positions: HashMap<Pos, char>,
}
impl Map {
fn get_unexplored(&self) -> Option<Pos> {
let neighbors = vec![Pos(-1, 0), Pos(1, 0), Pos(0, -1), Pos(0, 1)];
let r = self
.positions
.iter()
.filter(|(_, c)| **c == '.')
.map(|(p, _)| p)
.find(|pos| {
neighbors
.iter()
.any(|delta| !self.positions.contains_key(&pos.add(*delta)))
});
r.map(|p| *p)
}
fn get_shortest_path_to(&self, start: Pos, goal: Pos) -> Vec<Direction> {
let neighbors = vec![Pos(-1, 0), Pos(1, 0), Pos(0, -1), Pos(0, 1)];
let mut open: VecDeque<Pos> = VecDeque::new();
open.push_back(start);
let mut predecessors: HashMap<Pos, Option<Pos>> = HashMap::new();
predecessors.insert(start, None);
while let Some(current) = open.pop_front() {
let field = self.positions.get(&current);
if let Some(v) = field {
if *v != '.' {
continue;
}
} else {
continue;
}
neighbors.iter().for_each(|d| {
let n = current.add(*d);
if !predecessors.contains_key(&n) {
predecessors.insert(n, Some(current));
open.push_back(n);
}
});
if current == goal {
break;
}
}
let mut directions = Vec::new();
let mut current = goal;
while let Some(Some(pred)) = predecessors.get(&current) {
directions.push(current.direction_to(*pred).opposite());
current = *pred;
}
directions.reverse();
directions
}
#[allow(dead_code)]
fn positions_on_path(start: Pos, directions: &Vec<Direction>) -> Vec<Pos> {
let mut result = vec![start];
let mut current = start;
for d in directions {
current = current.neighbor(*d);
result.push(current);
}
result
}
fn print(&self, special: HashMap<Pos, char>) {
let xmin = (self
.positions
.iter()
.min_by_key(|(Pos(x, _), _)| x)
.unwrap()
.0)
.0;
let xmax = (self
.positions
.iter()
.max_by_key(|(Pos(x, _), _)| x)
.unwrap()
.0)
.0;
let ymin = (self
.positions
.iter()
.min_by_key(|(Pos(_, y), _)| y)
.unwrap()
.0)
.1;
let ymax = (self
.positions
.iter()
.max_by_key(|(Pos(_, y), _)| y)
.unwrap()
.0)
.1;
for y in ymin..=ymax {
for x in xmin..=xmax {
let c = self.positions.get(&Pos(x, y)).unwrap_or(&' ');
let c = if let Some(s) = special.get(&Pos(x, y)) {
s
} else {
c
};
print!("{}", c);
}
println!("");
}
}
}
#[derive(Hash, PartialEq, Copy, Clone, Debug)]
enum Direction {
N,
S,
W,
E,
}
impl Direction {
fn opposite(&self) -> Self {
match self {
N => S,
S => N,
W => E,
E => W,
}
}
fn to_robot_cmd(&self) -> i128 {
match self {
N => 1,
S => 2,
W => 3,
E => 4,
}
}
}

View File

@@ -8,6 +8,7 @@ pub fn run() {
.enumerate()
.map(|(i, s)| (i, s.parse::<i128>().unwrap()))
.collect();
task1(ram.clone());
task2(ram.clone());
}

View File

@@ -11,5 +11,6 @@ pub mod day11;
pub mod day12;
pub mod day13;
pub mod day14;
pub mod day15;
pub mod day16;
pub mod day21;