aoc/year2019/
day13.rs

1//! # Care Package
2//!
3//! Keeps track of the `x` position of both the ball and paddle then uses the [`signum`] function
4//! to provide input to the joystick that tracks the ball.
5//!
6//! Just for fun this solution will play an animated game in the console if
7//! "--features frivolity" is enabled.
8//!
9//! [`signum`]: i64::signum
10use super::intcode::*;
11use crate::util::parse::*;
12
13pub fn parse(input: &str) -> Vec<i64> {
14    input.iter_signed().collect()
15}
16
17pub fn part1(input: &[i64]) -> usize {
18    let mut computer = Computer::new(input);
19    let mut blocks = 0;
20
21    while let [_, _, State::Output(t)] = [computer.run(), computer.run(), computer.run()] {
22        if t == 2 {
23            blocks += 1;
24        }
25    }
26
27    blocks
28}
29
30pub fn part2(input: &[i64]) -> i64 {
31    let mut modified = input.to_vec();
32    modified[0] = 2;
33
34    let mut computer = Computer::new(&modified);
35    let mut tiles = Vec::with_capacity(1_000);
36    let mut stride = 0;
37    let mut score = 0;
38    let mut blocks = score;
39    let mut ball: i64 = 0;
40    let mut paddle: i64 = 0;
41
42    loop {
43        let x = match computer.run() {
44            State::Input => {
45                // Always track the ball
46                let delta = (ball - paddle).signum();
47                computer.input(delta);
48                continue;
49            }
50            State::Output(x) => x,
51            State::Halted => unreachable!(),
52        };
53        let State::Output(y) = computer.run() else {
54            unreachable!();
55        };
56        let State::Output(t) = computer.run() else {
57            unreachable!();
58        };
59
60        if x < 0 {
61            score = t;
62            if blocks == 0 {
63                break score;
64            }
65        } else {
66            if x >= stride {
67                stride = x + 1;
68            }
69            let index = (stride * y + x) as usize;
70            if index >= tiles.len() {
71                tiles.resize(index + 1, 0);
72            }
73
74            match t {
75                0 if tiles[index] == 2 => blocks -= 1,
76                2 if tiles[index] != 2 => blocks += 1,
77                3 => paddle = x,
78                4 => ball = x,
79                _ => (),
80            }
81
82            tiles[index] = t;
83        }
84
85        // Non essential but hilarious. Enable feature then run program in a command line
86        // console to observe an animated game of breakout.
87        #[cfg(feature = "frivolity")]
88        draw(&tiles, stride, score, blocks);
89    }
90}
91
92#[cfg(feature = "frivolity")]
93fn draw(tiles: &[i64], stride: i64, score: i64, blocks: i64) {
94    use crate::util::ansi::*;
95    use std::fmt::Write as _;
96    use std::thread::sleep;
97    use std::time::Duration;
98
99    // Wait until the initial screen is complete
100    let paddle = tiles.iter().rposition(|&t| t == 3).unwrap_or(tiles.len());
101    if tiles[paddle..].iter().filter(|&&t| t == 1).count() < 3 {
102        return;
103    }
104
105    let s = &mut String::new();
106    let _ = writeln!(s, "{WHITE}{BOLD}Blocks: {blocks}\tScore: {score} {RESET}");
107    let mut y = 0;
108
109    while stride * y < tiles.len() as i64 {
110        for x in 0..stride {
111            let index = (stride * y + x) as usize;
112            let _unused = match tiles[index] {
113                0 => write!(s, " "),
114                1 if y == 0 => write!(s, "{GREEN}_{RESET}"),
115                1 => write!(s, "{GREEN}|{RESET}"),
116                2 => write!(s, "{BLUE}#{RESET}"),
117                3 => write!(s, "{WHITE}{BOLD}={RESET}"),
118                4 => write!(s, "{YELLOW}{BOLD}o{RESET}"),
119                _ => unreachable!(),
120            };
121        }
122        s.push('\n');
123        y += 1;
124    }
125
126    println!("{HOME}{CLEAR}{s}");
127    sleep(Duration::from_millis(20));
128}