From b67ed69f7df2ac9d6b91e30acc569f52769ee485 Mon Sep 17 00:00:00 2001 From: Johannes Date: Sat, 14 Dec 2019 18:02:24 +0100 Subject: [PATCH] day 13 task 2 --- src/tasks/day13.rs | 211 +++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 196 insertions(+), 15 deletions(-) diff --git a/src/tasks/day13.rs b/src/tasks/day13.rs index a4b5dd5..ee6295b 100644 --- a/src/tasks/day13.rs +++ b/src/tasks/day13.rs @@ -1,6 +1,7 @@ use super::day05::{IntCodeComputer, RAM}; use itertools::Itertools; use std::collections::HashMap; +use std::fmt; #[allow(dead_code)] pub fn run() { @@ -11,6 +12,7 @@ pub fn run() { .enumerate() .collect(); task1(program.clone()); + task2_ai(program.clone()); } #[derive(PartialEq)] @@ -22,6 +24,22 @@ enum FieldType { Ball, } +impl fmt::Display for FieldType { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!( + f, + "{}", + match self { + Self::Empty => " ", + Self::Wall => "X", + Self::Block => "B", + Self::HorizontalPaddle => "_", + Self::Ball => "O", + } + ) + } +} + impl FieldType { fn from(input: i128) -> Self { match input { @@ -35,19 +53,182 @@ impl FieldType { } } -fn task1(program: RAM) { - let mut pc = IntCodeComputer::new(vec![], program); - pc.run_until_end(); - let field = pc.get_output().iter().tuples().fold( - HashMap::<(i128, i128), FieldType>::new(), - |mut map, (x, y, t)| { - map.insert((*x, *y), FieldType::from(*t)); - map - }, - ); - let block_count = field - .iter() - .filter(|(_, ftype)| **ftype == FieldType::Block) - .count(); - println!("Task 1: There are {} blocks in the field", block_count); +struct Game { + pc: IntCodeComputer, + field: HashMap<(i128, i128), FieldType>, + score: i128, + show_output: bool, +} + +impl Game { + fn new(coins: Option, mut ram: RAM, show_output: bool) -> Self { + if let Some(c) = coins { + ram.insert(0, c); + } + let pc = IntCodeComputer::new(vec![], ram); + Game { + pc, + field: HashMap::new(), + score: 0, + show_output, + } + } + + /** + * true means the game is over, false means input is awaited + */ + fn run(&mut self) -> bool { + self.pc.clear_output(); + let done = self.pc.run_until_input_empty(); + let output = self.pc.get_output().into_iter().map(|it| *it).collect_vec(); + let mut score = 0; + output + .iter() + .tuples() + .scan(&mut self.field, |map, (x, y, t)| { + if *x == -1 && *y == 0 { + score = *t; + } else { + map.insert((*x, *y), FieldType::from(*t)); + } + Some(()) + }) + .any(|_| false); + self.score = score; + if self.show_output { + self.print(); + } + done + } + + fn set_input(&mut self, input: Input) { + self.pc.set_input(&[input.int()]); + } + + fn print(&self) { + let xmin = *self.field.iter().map(|((x, _), _)| x).min().unwrap(); + let xmax = *self.field.iter().map(|((x, _), _)| x).max().unwrap(); + let ymin = *self.field.iter().map(|((_, y), _)| y).min().unwrap(); + let ymax = *self.field.iter().map(|((_, y), _)| y).max().unwrap(); + for y in ymin..=ymax { + for x in xmin..=xmax { + if let Some(f) = self.field.get(&(x, y)) { + print!("{}", f); + } else { + panic!("There is an unspecified tile in the field"); + } + } + println!(); + } + println!("Score: {}", self.score); + } +} + +#[derive(PartialEq)] +enum Input { + Neutral, + Left, + Right, +} + +impl Input { + fn int(&self) -> i128 { + match self { + Self::Neutral => 0, + Self::Left => -1, + Self::Right => 1, + } + } + + fn from(input: char) -> Self { + match input { + 's' => Self::Neutral, + 'a' => Self::Left, + 'd' => Self::Right, + _ => Self::Neutral, + } + } +} + +fn task1(program: RAM) { + let mut game = Game::new(None, program, true); + if game.run() { + let block_count = game + .field + .iter() + .filter(|(_, ftype)| **ftype == FieldType::Block) + .count(); + println!("Task 1: There are {} blocks in the field", block_count); + } else { + unreachable!("In this task the game should end without any input."); + } +} + +#[allow(dead_code)] +fn task2(program: RAM) { + use std::io::{self, BufRead}; + let mut game = Game::new(Some(2), program, true); + let stdin = io::stdin(); + let mut input_iter = stdin.lock().lines(); + while !game.run() { + // read input + let next = input_iter.next().unwrap().unwrap().chars().next(); + if let Some(s) = next { + game.set_input(Input::from(s)); + } else { + game.set_input(Input::Neutral); + } + } + println!( + "You won against the game! Your final score was {}", + game.score + ); +} + +fn task2_ai(program: RAM) { + let mut game = Game::new(Some(2), program, false); + let mut moves = 0; + while !game.run() { + // always keep the paddle under the ball + let x_ball = game + .field + .iter() + .find_map(|((x, _), t)| { + if *t == FieldType::Ball { + Some(*x) + } else { + None + } + }) + .unwrap(); + let x_paddle = game + .field + .iter() + .find_map(|((x, _), t)| { + if *t == FieldType::HorizontalPaddle { + Some(*x) + } else { + None + } + }) + .unwrap(); + let direction = if x_ball < x_paddle { + Input::Left + } else if x_ball > x_paddle { + Input::Right + } else { + Input::Neutral + }; + game.set_input(direction); + moves += 1; + } + println!("Played for {} moves", moves); + println!( + "Game is over! There are {} blocks left. Your final score is {}", + game.field + .iter() + .filter(|(_, t)| **t == FieldType::Block) + .count(), + game.score + ); }