use shared::intcode::{IntCode, SimulateStep, CELL}; use std::collections::HashSet; #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)] pub enum Tile { Empty, Wall, Block, HorizontalPaddle, Ball, } impl Tile { pub fn new(t: CELL) -> Self { match t { 0 => Self::Empty, 1 => Self::Wall, 2 => Self::Block, 3 => Self::HorizontalPaddle, 4 => Self::Ball, _ => panic!("Unknown tile {}", t), } } } #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)] pub struct Position { pub x: CELL, pub y: CELL, } #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)] pub enum Update { Tile { position: Position, tile: Tile, }, Score { score: CELL, }, } #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)] #[repr(i8)] pub enum Joystick { Left = -1, Neutral = 0, Right = 1, } fn view_game(mut ic: IntCode) -> impl Iterator { std::iter::from_fn(move || { let x = match ic.simulate_step() { SimulateStep::Finished => return None, SimulateStep::WaitForInput => panic!("no input for game"), SimulateStep::Output(n) => n, }; let y = ic.simulate_step().expect_output(); let p = Position { x, y }; let tile = Tile::new(ic.simulate_step().expect_output()); Some((p, tile)) }) } fn find_blocks(tiles: I) -> HashSet where I: Iterator, { let mut blocks = HashSet::new(); for (pos, tile) in tiles { match tile { Tile::Block => { blocks.insert(pos); } _ => { blocks.remove(&pos); } } } blocks } fn get_update(ic: &mut IntCode, joystick: Joystick) -> Option { let joystick = joystick as i8 as CELL; ic.next_input = Some(joystick); let x = match ic.simulate_step() { SimulateStep::Finished => { ic.next_input = None; return None; }, SimulateStep::WaitForInput => panic!("no input for game"), SimulateStep::Output(n) => n, }; let y = ic.simulate_step().expect_output(); if x == -1 && y == 0 { Some(Update::Score { score: ic.simulate_step().expect_output() }) } else { let position = Position { x, y }; let tile = Tile::new(ic.simulate_step().expect_output()); Some(Update::Tile { position, tile }) } } fn break_all_blocks(mut ic: IntCode) -> CELL { ic.data[0] = 2; // "play for free" let mut last_score = None; let mut last_paddle = None; let mut last_ball = None; let mut blocks = HashSet::new(); let mut had_blocks = false; let mut joystick = Joystick::Neutral; while let Some(update) = get_update(&mut ic, joystick) { match update { Update::Score { score } => last_score = Some(score), Update::Tile { position, tile } => { blocks.remove(&position); match tile { Tile::Empty => (), Tile::Wall => (), Tile::Block => { blocks.insert(position); had_blocks = true; }, Tile::HorizontalPaddle => last_paddle = Some(position), Tile::Ball => last_ball = Some(position), } } } if let (Some(paddle), Some(ball)) = (&last_paddle, &last_ball) { let diff_x = ball.x - paddle.x; joystick = if diff_x > 0 { Joystick::Right } else if diff_x < 0 { Joystick::Left } else { Joystick::Neutral }; } } if had_blocks && blocks.is_empty() { println!("Won game, all blocks destroyed"); } else { println!("Lost game"); } last_score.expect("no score written") } fn main() { let ic = include_str!("input.txt").parse::().unwrap(); let blocks = find_blocks(view_game(ic.clone())); println!("Number of blocks: {}", blocks.len()); let score = break_all_blocks(ic); println!("Score: {}", score); }