advent-of-code/2019/15/src/main.rs

169 lines
3.9 KiB
Rust

use grid::*;
use intcode::*;
use std::collections::HashMap;
use std::fmt::{self, Display, Formatter};
use std::sync::Mutex;
#[macro_use]
extern crate lazy_static;
#[derive(Clone, Copy, PartialEq, Debug)]
enum Tile {
Wall,
Empty,
Oxygen,
O2,
}
#[derive(Clone)]
struct Robot {
pos: Position2D,
dir: Direction,
ic: IntComputer,
steps: usize,
}
impl Robot {
fn step(&mut self, dir: Direction) -> Tile {
self.dir = dir;
self.ic.params.push(int(dir));
match self.ic.run() {
IntComputerResult::Output(0) => {
save(self.pos + self.dir, Tile::Wall);
Tile::Wall
}
IntComputerResult::Output(1) => {
self.pos += self.dir;
save(self.pos, Tile::Empty);
self.steps += 1;
Tile::Empty
}
IntComputerResult::Output(2) => {
self.pos += self.dir;
self.steps += 1;
save(self.pos, Tile::Oxygen);
OXYGEN_BOT.lock().unwrap().push(self.clone());
Tile::Oxygen
}
_ => unreachable!("This roboter shouldn’t halt"),
}
}
}
impl From<i64> for Tile {
fn from(i: i64) -> Self {
match i {
0 => Tile::Wall,
1 => Tile::Empty,
2 => Tile::Oxygen,
_ => unreachable!("Illegal tile"),
}
}
}
impl Display for Tile {
fn fmt(&self, f: &mut Formatter) -> Result<(), fmt::Error> {
let c = match self {
Tile::Wall => '█',
Tile::Empty => ' ',
Tile::Oxygen => 'X',
Tile::O2 => 'O',
};
write!(f, "{}", c)
}
}
fn int(dir: Direction) -> i64 {
match dir {
Direction::Up => 1,
Direction::Down => 2,
Direction::Left => 3,
Direction::Right => 4,
}
}
lazy_static! {
static ref FIELD: Mutex<HashMap<Position2D, Tile>> = Mutex::new(HashMap::new());
static ref OXYGEN_BOT: Mutex<Vec<Robot>> = Mutex::new(Vec::with_capacity(1));
}
#[inline]
fn get(p: &Position2D) -> Option<Tile> {
FIELD.lock().unwrap().get(p).copied()
}
#[inline]
fn save(p: Position2D, t: Tile) {
FIELD.lock().unwrap().insert(p, t);
}
fn print_field() {
if ENABLE_MAP_PRINT {
std::thread::sleep(std::time::Duration::from_millis(16));
println!("\n{}\n", draw_ascii(&FIELD.lock().unwrap(), Tile::Wall));
}
}
#[rustfmt::skip]
fn is_dead_end(pos: &Position2D) -> bool {
pos.neighbors().iter().filter(|(_, p)| get(p).unwrap() == Tile::Empty).count() == 0
}
fn fill(bot: Robot) -> usize {
if is_dead_end(&bot.pos) {
return bot.steps;
}
bot.pos
.neighbors()
.iter()
.map(|(dir, pos)| {
if get(pos).unwrap() == Tile::Empty {
let mut clone = bot.clone();
clone.pos += *dir;
save(clone.pos, Tile::O2);
clone.steps += 1;
print_field();
fill(clone)
} else {
0
}
})
.max()
.unwrap_or(0)
}
fn explore(bot: Robot) {
let nbs = bot.pos.neighbors();
for (dir, pos) in nbs.iter() {
if get(pos).is_none() {
let mut clone = bot.clone();
if clone.step(*dir) == Tile::Empty {
print_field();
explore(clone);
}
}
}
}
#[rustfmt::skip]
fn find_generator() -> Robot {
save((0, 0).into(), Tile::Empty);
let bot = Robot {
pos: (0, 0).into(),
dir: Direction::Up,
ic: IntComputer::new(read_input(), 0, vec![]),
steps: 0,
};
explore(bot);
OXYGEN_BOT.lock().unwrap().pop().expect("No oxygen found in Part 1")
}
// Pretty visuals :wow:
const ENABLE_MAP_PRINT: bool = true;
fn main() {
let mut bot = find_generator();
println!("Part 1: {}", bot.steps);
bot.steps = 0;
println!("Part 2: {}", fill(bot));
}