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 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> = Mutex::new(HashMap::new()); static ref OXYGEN_BOT: Mutex> = Mutex::new(Vec::with_capacity(1)); } #[inline] fn get(p: &Position2D) -> Option { 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)); }