148 lines
3.5 KiB
Rust
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);
|
|
}
|