day 15 task 1
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
mod tasks;
|
||||
|
||||
fn main() {
|
||||
tasks::day21::run();
|
||||
tasks::day15::run();
|
||||
}
|
||||
|
||||
253
src/tasks/day15.rs
Normal file
253
src/tasks/day15.rs
Normal 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(¤t);
|
||||
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(¤t) {
|
||||
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,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -8,6 +8,7 @@ pub fn run() {
|
||||
.enumerate()
|
||||
.map(|(i, s)| (i, s.parse::<i128>().unwrap()))
|
||||
.collect();
|
||||
task1(ram.clone());
|
||||
task2(ram.clone());
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user