aoc2019/day13/src/main.rs

148 lines
3.5 KiB
Rust

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<Item=(Position, Tile)> {
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<I>(tiles: I) -> HashSet<Position>
where
I: Iterator<Item=(Position, Tile)>,
{
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<Update> {
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::<IntCode>().unwrap();
let blocks = find_blocks(view_game(ic.clone()));
println!("Number of blocks: {}", blocks.len());
let score = break_all_blocks(ic);
println!("Score: {}", score);
}