day15 fast

This commit is contained in:
foosinn 2020-06-03 23:17:01 +02:00
parent 244e308200
commit f2ad6c7ad8
3 changed files with 209 additions and 0 deletions

204
src/day15/main.rs Normal file
View file

@ -0,0 +1,204 @@
use std::collections::HashMap;
use std::error::Error;
use std::fs::File;
use aoc2019::intcode;
fn main() -> Result<(), Box<dyn Error>> {
let f = File::open("input15")?;
let computer = intcode::Computer::from(f);
let mut map = Map::new();
let steps = map.walk(&XY { x: 0, y: 0 }, &computer, 0).unwrap();
for y in map.ymin..=map.ymax {
for x in map.xmin..=map.xmax {
if x == 0 && y == 0 {
print!("0");
continue;
}
let status = map.m.get(&XY { x, y }).unwrap_or(&Status::Empty);
let c = match status {
Status::HitWall => "#",
Status::MoveComplete => ".",
Status::Finished => "X",
Status::Empty => " ",
Status::Oxigenated => "o",
};
print!("{}", c);
}
println!("");
}
println!("i walked {} steps", steps);
let steps = map.fill();
println!("filled with oxygen after {} minutes", steps);
Ok(())
}
#[derive(Default, Debug)]
struct Map {
m: HashMap<XY, Status>,
xmin: isize,
xmax: isize,
ymin: isize,
ymax: isize,
}
impl Map {
fn new() -> Self {
Map::default()
}
fn walk(&mut self, start: &XY, computer: &intcode::Computer, steps: usize) -> Option<usize> {
let steps = steps + 1;
for dir in 1..=4 {
let direction = Direction::from(dir);
let xy = start.add((&direction).into());
if self.m.contains_key(&xy) {
continue;
}
self.update_limits(&xy);
let mut computer = computer.clone();
if let Some(status) = computer.run_io(dir) {
let status = Status::from(status);
self.m.insert(xy.clone(), status.clone());
match status {
Status::HitWall => (),
Status::MoveComplete => match self.walk(&xy, &computer, steps) {
Some(i) => return Some(i + 1),
None => (),
},
Status::Finished => return Some(1),
_ => unreachable!("unknown status"),
}
} else {
unreachable!("computer unexpectedly halted");
}
}
None
}
fn fill(&mut self) -> usize {
let mut src: Vec<XY> = self
.m
.iter()
.filter_map(|(xy, status)| match status {
Status::Finished => Some(xy),
_ => None,
})
.cloned()
.collect();
let mut steps = 0;
while src.len() > 0 {
steps += 1;
src.iter()
.for_each(|xy| *self.m.get_mut(xy).unwrap() = Status::Oxigenated);
src = src
.into_iter()
.map(|xy| {
(1..=4)
.map(Direction::from)
.map(move |d| xy.add((&d).into()))
})
.flatten()
.filter(|xy| match self.m.get(xy) {
Some(Status::MoveComplete) => true,
_ => false,
})
.collect();
}
assert_eq!(
self.m
.values()
.filter(|status| match status {
Status::MoveComplete => true,
_ => false,
})
.count(),
0
);
steps - 1
}
fn update_limits(&mut self, xy: &XY) {
if self.xmin > xy.x {
self.xmin = xy.x
}
if self.xmax < xy.x {
self.xmax = xy.x
}
if self.ymin > xy.y {
self.ymin = xy.y
}
if self.ymax < xy.y {
self.ymax = xy.y
}
}
}
#[derive(Debug)]
enum Direction {
North = 1,
South = 2,
West = 3,
East = 4,
}
impl From<isize> for Direction {
fn from(i: isize) -> Self {
match i {
1 => Direction::North,
2 => Direction::South,
3 => Direction::West,
4 => Direction::East,
_ => unreachable!("no status for computer output"),
}
}
}
impl Into<(isize, isize)> for &Direction {
fn into(self) -> (isize, isize) {
match self {
Direction::North => (0, 1),
Direction::South => (0, -1),
Direction::West => (-1, 0),
Direction::East => (1, 0),
}
}
}
#[derive(Clone, Debug)]
enum Status {
HitWall = 0,
MoveComplete = 1,
Finished = 2,
Empty = 3,
Oxigenated = 4,
}
impl From<isize> for Status {
fn from(i: isize) -> Self {
match i {
0 => Status::HitWall,
1 => Status::MoveComplete,
2 => Status::Finished,
_ => unreachable!("no status for computer output"),
}
}
}
#[derive(PartialEq, Eq, Hash, Clone, Debug)]
struct XY {
x: isize,
y: isize,
}
impl XY {
fn add(&self, delta: (isize, isize)) -> XY {
XY {
x: self.x + delta.0,
y: self.y + delta.1,
}
}
}