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