Advent of Code 2019 - Day 13

Advent of CodeRust

Another task that needed to be solved using our intcode computer.

The intcode program we were provided was an implementation of Atari Breakout. We had to write an "AI" to beat the game and then find out what's the final score. This was quite an interesting and I hope the few remaining days will be as interesting as this one.

Code

use std::cmp::Ordering;
use std::collections::HashMap;
use utils::intcode::{ExitCode, VM};
use utils::Point;

#[derive(PartialEq, Eq)]
enum Tile {
    Empty,
    Wall,
    Block,
    Paddle,
    Ball,
}

impl Tile {
    pub fn from(value: i128) -> Self {
        match value {
            0 => Tile::Empty,
            1 => Tile::Wall,
            2 => Tile::Block,
            3 => Tile::Paddle,
            4 => Tile::Ball,
            _ => panic!("Invalid tile {}", value),
        }
    }

    pub fn draw(&self) {
        let output = match &self {
            Tile::Empty => ' ',
            Tile::Wall => '|',
            Tile::Block => '#',
            Tile::Paddle => '_',
            Tile::Ball => '0',
        };

        print!("{}", output);
    }
}

fn draw_board(field: &mut HashMap<Point, Tile>) {
    let max_x = field.keys().max_by_key(|x| x.x).unwrap().x;
    let max_y = field.keys().max_by_key(|x| x.y).unwrap().y;
    for y in 0..=max_y {
        for x in 0..=max_x {
            let tile = field.entry(Point { x, y }).or_insert(Tile::Empty);
            tile.draw();
        }

        println!();
    }
}

fn update_field(vm: &mut VM, field: &mut HashMap<Point, Tile>) {
    for tile in vm.get_output_iter().collect::<Vec<i128>>().chunks(3) {
        if tile[0] == -1 {
            println!("Score: {}", tile[2]);
            continue;
        }

        field.insert(
            Point {
                x: tile[0] as i32,
                y: tile[1] as i32,
            },
            Tile::from(tile[2]),
        );
    }
}

pub fn part_one(input: &str) -> i32 {
    let mut vm = VM::from(input);
    vm.run();
    let mut field = HashMap::new();
    update_field(&mut vm, &mut field);
    field.values().filter(|x| **x == Tile::Block).count() as i32
}

pub fn part_two(input: &str) {
    let mut vm = VM::from(input);
    let mut ball_point;
    let mut paddle_point;
    vm.set_memory(0, 2);
    let mut field = HashMap::new();
    loop {
        let exit_code = vm.run();

        update_field(&mut vm, &mut field);
        draw_board(&mut field);

        if exit_code == ExitCode::Stop {
            break;
        }

        ball_point = field.iter().find(|x| *x.1 == Tile::Ball).unwrap().0;
        paddle_point = field.iter().find(|x| *x.1 == Tile::Paddle).unwrap().0;

        if exit_code == ExitCode::NeedInput {
            let joystick = match paddle_point.x.cmp(&ball_point.x) {
                Ordering::Less => 1,
                Ordering::Equal => 0,
                Ordering::Greater => -1,
            };

            vm.add_input(joystick);
        }
    }
}

Benchmarks

2019 - Day 13 - Part 1 time: 6.955315ms
2019 - Day 13 - Part 2 time: 293.849007ms

Feedback or Questions?

I don't have a direct way to leave comments on posts. But if you want to give some feedback or have some questions you can contact in other ways.

©2019–2020 Severin Kaderli