aoc/year2019/
day13.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
//! # Care Package
//!
//! Keeps track of the `x` position of both the ball and paddle then uses the [`signum`] function
//! to provide input to the joystick that tracks the ball.
//!
//! Just for fun this solution will play an animated game in the console if
//! "--features frivolity" is enabled.
//!
//! [`signum`]: i64::signum
use super::intcode::*;
use crate::util::parse::*;

pub fn parse(input: &str) -> Vec<i64> {
    input.iter_signed().collect()
}

pub fn part1(input: &[i64]) -> usize {
    let mut computer = Computer::new(input);
    let mut blocks = 0;

    loop {
        let State::Output(_) = computer.run() else {
            break;
        };
        let State::Output(_) = computer.run() else {
            break;
        };
        let State::Output(t) = computer.run() else {
            break;
        };
        if t == 2 {
            blocks += 1;
        }
    }

    blocks
}

pub fn part2(input: &[i64]) -> i64 {
    let mut modified = input.to_vec();
    modified[0] = 2;

    let mut computer = Computer::new(&modified);
    let mut tiles = Vec::with_capacity(1_000);
    let mut stride = 0;
    let mut score = 0;
    let mut blocks = score;
    let mut ball: i64 = 0;
    let mut paddle: i64 = 0;

    loop {
        let x = match computer.run() {
            State::Input => {
                // Always track the ball
                let delta = (ball - paddle).signum();
                computer.input(delta);
                continue;
            }
            State::Output(x) => x,
            State::Halted => unreachable!(),
        };
        let State::Output(y) = computer.run() else {
            unreachable!();
        };
        let State::Output(t) = computer.run() else {
            unreachable!();
        };

        if x < 0 {
            score = t;
            if blocks == 0 {
                break score;
            }
        } else {
            if x >= stride {
                stride = x + 1;
            }
            let index = (stride * y + x) as usize;
            if index >= tiles.len() {
                tiles.resize(index + 1, 0);
            }

            match t {
                0 if tiles[index] == 2 => blocks -= 1,
                2 if tiles[index] != 2 => blocks += 1,
                3 => paddle = x,
                4 => ball = x,
                _ => (),
            }

            tiles[index] = t;
        }

        // Non essential but hilarious. Enable feature then run program in a command line
        // conosle to observe an animated game of breakout.
        #[cfg(feature = "frivolity")]
        draw(&tiles, stride, score, blocks);
    }
}

#[cfg(feature = "frivolity")]
fn draw(tiles: &[i64], stride: i64, score: i64, blocks: i64) {
    use crate::util::ansi::*;
    use std::fmt::Write as _;
    use std::thread::sleep;
    use std::time::Duration;

    // Wait until the initial screen is complete
    let paddle = tiles.iter().rposition(|&t| t == 3).unwrap_or(tiles.len());
    if tiles[paddle..].iter().filter(|&&t| t == 1).count() < 3 {
        return;
    }

    let s = &mut String::new();
    let _ = writeln!(s, "{WHITE}{BOLD}Blocks: {blocks}\tScore: {score} {RESET}");
    let mut y = 0;

    while stride * y < tiles.len() as i64 {
        for x in 0..stride {
            let index = (stride * y + x) as usize;
            let _unused = match tiles[index] {
                0 => write!(s, " "),
                1 if y == 0 => write!(s, "{GREEN}_{RESET}"),
                1 => write!(s, "{GREEN}|{RESET}"),
                2 => write!(s, "{BLUE}#{RESET}"),
                3 => write!(s, "{WHITE}{BOLD}={RESET}"),
                4 => write!(s, "{YELLOW}{BOLD}o{RESET}"),
                _ => unreachable!(),
            };
        }
        s.push('\n');
        y += 1;
    }

    println!("{HOME}{CLEAR}{s}");
    sleep(Duration::from_millis(20));
}