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
//! # 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 tiles = [0; 44 * 20];

    loop {
        let State::Output(x) = computer.run() else {
            break;
        };
        let State::Output(y) = computer.run() else {
            break;
        };
        let State::Output(t) = computer.run() else {
            break;
        };
        tiles[(44 * y + x) as usize] = t;
    }

    tiles.iter().filter(|&&t| t == 2).count()
}

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

    let mut computer = Computer::new(&modified);
    let mut tiles = [0; 44 * 20];
    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 {
            let index = (44 * y + x) as usize;

            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, score, blocks);
    }
}

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

    // Wait until the initial screen is complete
    if tiles[879] != 1 {
        return;
    }

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

    for y in 0..20 {
        for x in 0..44 {
            let _unused = match tiles[44 * y + x] {
                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');
    }

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