Skip to main content

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