DEV Community

Discussion on: Advent of Code 2019 Solution Megathread - Day 11: Space Police

Collapse
 
rpalo profile image
Ryan Palo

Not too bad. Follow the steps: Input -> Process -> Output -> Repeat and keep track of the touched squares in a hashmap. Could have gone full-hog and made structs out of points and headings, but this makes sorting easier. Here's my Rust solution:

/// Day 11: Space Police
/// 
/// Repaint your ship to comply with the fuzz

use std::collections::HashMap;
use std::fs;

use crate::intcode::IntcodeInterpreter;

#[derive(Eq, PartialEq, PartialOrd, Ord, Debug)]
enum Color {
    White,
    Black,
}

/// A Position is a ordered pair (x, y) in 2D space.
type Position = (isize, isize);

/// A Heading is a direction vector (x, y) with y positive being up.
type Heading = (isize, isize);

/// Parses the input.  Expects a single line of integers separated by
/// commas
fn parse_input() -> Vec<isize> {
    let text: String = fs::read_to_string("data/day11.txt").unwrap();
    let cleaned = text.trim();
    cleaned.split(",").map( |c| c.parse().unwrap()).collect()
}

/// Turns right or left, returns the new heading
/// For direction, 1 means turn right, 0 means turn left
/// No need for a "clever" turning function I think.
fn turn(heading: Heading, direction: isize) -> Heading {
    if direction == 1 {
        match heading {
            (0, 1) => (1, 0),
            (1, 0) => (0, -1),
            (0, -1) => (-1, 0),
            (-1, 0) => (0, 1),
            _ => panic!("Unrecognized heading!")
        }
    } else {
        match heading {
            (0, 1) => (-1, 0),
            (-1, 0) => (0, -1),
            (0, -1) => (1, 0),
            (1, 0) => (0, 1),
            _ => panic!("Unrecognized heading!")
        }
    }
}

/// Paints a spacecraft hull using a painter bot that reads the current
/// tile color to decide what to do next
fn paint_hull(bot: &mut IntcodeInterpreter) -> HashMap<Position, Color> {
    let mut tiles: HashMap<Position, Color> = HashMap::new();
    let mut position = (0, 0);
    tiles.insert(position, Color::White);
    let mut heading = (0, 1);

    loop {
        // Read the tile color and pass it to the bot
        let input = match tiles.entry(position).or_insert(Color::Black) {
            Color::Black => 0,
            Color::White => 1,
        };
        bot.push_input(input);

        // Process input
        bot.run();

        // Read the color to paint and direction to turn from the bot
        let output_color = match bot.shift_output() {
            1 => Color::White,
            0 => Color::Black,
            _ => panic!("Bad bot color output")
        };
        let output_turn = bot.shift_output();

        tiles.insert(position, output_color);
        heading = turn(heading, output_turn);
        position = (position.0 + heading.0, position.1 + heading.1);

        // Return if the bot has halted completely
        if bot.current_instruction() == 99 {
            break;
        }
    }

    tiles
}

/// I cheated and printed the tiles map out to see the ranges of x and y
/// Prints black tiles as plain terminal and white as #
fn display_tiles(mut tiles: HashMap<Position, Color>) {
    for row in (-5..=0).rev() {
        for col in 0..42 {
            let out = match tiles.entry((col, row)).or_insert(Color::Black) {
                Color::White => "#",
                Color::Black => " ",
            };
            print!("{}", out);
        }
        println!("");
    }
}

pub fn run() {
    let mut bot = IntcodeInterpreter::new(parse_input());
    let tiles = paint_hull(&mut bot);
    println!("Touched {} squares", tiles.len());
    display_tiles(tiles);
}