use itertools::izip; use std::collections::{HashMap}; use std::env; use std::fs::File; use std::io::{self, BufRead, BufReader}; use std::sync::mpsc::{channel, Receiver, Sender}; use std::time; use std::thread; type BusWidth = i128; type IOType = i128; fn read_program(filename: &str) -> Vec { let f = File::open(filename).expect(filename); let f = BufReader::new(f); f.lines() .filter(|line| match line { Ok(s) => { let b = s.as_bytes(); if b.len() == 0 || b.len() > 0 && b[0] == b'#' { false } else { true } }, _ => true, }) .map(|line| match line { Ok(line) => { let data:Vec = line.split(',').map(|e| { e.trim_start().trim_end() }) .filter(|v| match v.parse::() { Ok(_) => true, _ => { eprintln!("Could not parse: {:?}", v); false }, }) .map(|r| r.parse().unwrap()) .collect(); data }, Err(_) => panic!("Could not parse {}.", filename), }).flatten().collect() } #[derive(Clone, Debug)] struct Memory { mem: Vec, } impl Memory { fn new() -> Memory { Memory { mem: Vec::new(), } } fn init(&mut self, mem:Vec) { self.mem = mem; } fn read(&self, address: usize) -> BusWidth { match self.mem.get(address) { Some(value) => *value, None => 0, } } fn write(&mut self, address: usize, value: BusWidth) { if self.len() < address { self.mem.resize(address*2, 0); } self.mem[address] = value; } fn len(&self) -> usize { self.mem.len() } } #[derive(Debug)] struct Registers { ip: usize, base: usize, } #[derive(Debug)] enum Arithm { Add, Mul, LessThan, Equals, } #[derive(Debug)] enum IO { In, Out, } #[derive(Debug)] enum Jmp { JmpTrue, JmpFalse, } #[derive(Debug)] enum RegOp { BaseMod, } #[derive(Debug, PartialEq)] enum ParameterMode { Position, Immediate, Relative, } #[derive(Debug, PartialEq)] enum DataDirection { Read, Write, } struct Hack { out_counter: usize, outlog: Vec, ball: (IOType, IOType), paddle: IOType, } impl Hack { fn new() -> Hack { Hack { out_counter: 0, outlog: vec![], ball: (0, 0), paddle: 0, } } } struct Intcode { r: Registers, sender: Sender, receiver: Receiver, hack: Hack, } struct Game { canvas: HashMap<(isize, isize), isize>, sender: Sender, receiver: Receiver, score: isize, } impl Game { fn new(sender: Sender, receiver: Receiver) -> Game { Game { canvas: HashMap::new(), sender, receiver, score: 0, } } fn run(&mut self) -> isize { loop { let (x, y, tile_id) = match izip!(self.receiver.iter(), self. receiver.iter(), self. receiver.iter()).next() { Some((x, y, t)) => (x as isize, y as isize, t as isize), _ => break, }; if x ==-1 && y == 0 { if tile_id == 0 && self.score != 0 { break; } self.score = tile_id; } self.canvas.insert((x, y), tile_id); self.draw_tiles(); } self.score } fn draw_tiles(&mut self) { let min_x: isize = match self.canvas.keys().map(|(x, _)| x).min() { Some(v) => *v, _ => 0, }; let max_x: isize = match self.canvas.keys().map(|(x, _)| x).max() { Some(v) => *v, _ => 0, }; let min_y: isize = match self.canvas.keys().map(|(_, y)| y).min() { Some(v) => *v, _ => 0, }; let max_y: isize = match self.canvas.keys().map(|(_, y)| y).max() { Some(v) => *v, _ => 0, }; print!(""); for y in min_y..=max_y { for x in min_x..=max_x { print!("{}", match self.canvas.get(&(x, y)) { Some(1) => "██", Some(2) => "██", Some(3) => "██", Some(4) => "◖◗", _ => " ", }); } println!(""); } print!("[{};0H", max_y+2); println!("Score: {}", self.score); println!(""); } } impl Intcode { fn new(sender: Sender, receiver: Receiver) -> Intcode { Intcode { r: Registers { ip: 0, base: 0, }, sender, receiver, hack: Hack::new(), } } fn param_to_argument(&self, mode: &ParameterMode, mem: &Memory, offset: usize, dir: &DataDirection) -> usize { match mode { ParameterMode::Position => { if mem.read(offset) < 0 { panic!("Attempt to address negative memory at IP: {}"); } mem.read(offset) as usize }, ParameterMode::Immediate => { if *dir == DataDirection::Write { panic!("Immediate parameter mode at IP: {}", self.r.ip); } offset }, ParameterMode::Relative => { if (mem.read(offset) + self.r.base as BusWidth) < 0 { panic!("Attempt to address negative memory at IP: {}"); } (mem.read(offset) + self.r.base as BusWidth) as usize }, } } fn arithm(&self, op: Arithm, modes: Vec, memory: &mut Memory) { let parameter_directions = [DataDirection::Read, DataDirection::Read, DataDirection::Write]; let params:Vec = (0..parameter_directions.len()) .zip(parameter_directions.iter()) .map(|(p, d)| { self.param_to_argument(&modes[p], memory, self.r.ip + 1 + p, d) }).collect(); let left = params[0]; let right = params[1]; let result = params[2]; match op { Arithm::Add => memory.write(result, memory.read(left) + memory.read(right)), Arithm::Mul => memory.write(result, memory.read(left) * memory.read(right)), Arithm::LessThan => memory.write(result, if memory.read(left) < memory.read(right) { 1 } else { 0 }), Arithm::Equals => memory.write(result, if memory.read(left) == memory.read(right) { 1 } else { 0 }), } } fn jmp(&mut self, op: Jmp, modes: Vec, memory: &mut Memory) { let parameter_directions = [DataDirection::Read, DataDirection::Read]; let params:Vec = (0..parameter_directions.len()) .zip(parameter_directions.iter()) .map(|(p, d)| { self.param_to_argument(&modes[p], memory, self.r.ip + 1 + p, d) }).collect(); let value = params[0]; let address = params[1]; self.r.ip = match op { Jmp::JmpTrue => (if memory.read(value) != 0 { memory.read(address) as usize } else { self.r.ip + 3 }), Jmp::JmpFalse => (if memory.read(value) == 0 { memory.read(address) as usize } else { self.r.ip + 3 }), }; } fn regop(&mut self, op: RegOp, modes: Vec, memory: &mut Memory) { let parameter_directions = [DataDirection::Read]; let params:Vec = (0..parameter_directions.len()) .zip(parameter_directions.iter()) .map(|(p, d)| { self.param_to_argument(&modes[p], memory, self.r.ip + 1 + p, d) }).collect(); let value = memory.read(params[0]); match op { RegOp::BaseMod => { if value < 0 { self.r.base -= (-1 * value) as usize; } else { self.r.base += value as usize; } }, }; } fn io_in(&mut self, _op: IO, modes: Vec, memory:&mut Memory) { let parameter_directions = [DataDirection::Write]; let params:Vec = (0..parameter_directions.len()) .zip(parameter_directions.iter()) .map(|(p, d)| { self.param_to_argument(&modes[p], memory, self.r.ip + 1 + p, d) }).collect(); let address = params[0]; let value = self.receiver.recv(); match value { Ok(_) => { let v = if self.hack.paddle < self.hack.ball.0 { 1 } else if self.hack.paddle > self.hack.ball.0 { -1 } else { 0 }; memory.write(address, v) }, _ => panic!("Could not receive input. Remote disappeared."), } } fn io_out(&mut self, _op: IO, modes: Vec, memory:&mut Memory ) { let parameter_directions = [DataDirection::Read]; let params:Vec = (0..parameter_directions.len()) .zip(parameter_directions.iter()) .map(|(p, d)| { self.param_to_argument(&modes[p], memory, self.r.ip + 1 + p, d) }).collect(); let address = params[0]; let o = memory.read(address); self.sender.send(o as IOType).expect("Communication error. Nothing connected to CPU?"); if (self.hack.out_counter % 3) == 0 { self.hack.outlog = Vec::new(); } self.hack.outlog.push(o); self.hack.out_counter += 1; if (self.hack.out_counter % 3) == 0 { match self.hack.outlog[2] { 3 => self.hack.paddle = self.hack.outlog[0], 4 => self.hack.ball = ( self.hack.outlog[0], self.hack.outlog[1] ), _ => {}, }; } } fn intcode(&mut self, mut memory: Memory) { while self.r.ip < memory.len() { let op = memory.read(self.r.ip) % 100; let modes:Vec = (2..=4) .map(|e| memory.read(self.r.ip) / 10i32.pow(e) as BusWidth % 10) .map(|n| match n { 0 => ParameterMode::Position, 1 => ParameterMode::Immediate, 2 => ParameterMode::Relative, p => panic!("Invalid parameter mode {} at IP: {}", p, self.r.ip), }).collect(); match op { 1 => { self.arithm(Arithm::Add, modes, &mut memory); self.r.ip += 4 }, 2 => { self.arithm(Arithm::Mul, modes, &mut memory); self.r.ip += 4 }, 7 => { self.arithm(Arithm::LessThan, modes, &mut memory); self.r.ip += 4 }, 8 => { self.arithm(Arithm::Equals, modes, &mut memory); self.r.ip += 4 }, 3 => { self.io_in( IO::In, modes, &mut memory); self.r.ip += 2 }, 4 => { self.io_out(IO::Out, modes, &mut memory); self.r.ip += 2 }, 5 => { self.jmp( Jmp::JmpTrue, modes, &mut memory); }, 6 => { self.jmp( Jmp::JmpFalse, modes, &mut memory); }, 9 => { self.regop(RegOp::BaseMod, modes, &mut memory); self.r.ip += 2}, 99 => break, op => panic!("Invalid operation {} at IP: {}", op, self.r.ip), } } } } fn play_game(program: &String) -> isize { let mut memory = Memory::new(); memory.init(read_program(&program)); memory.write(0,2); let ( cpu_sender, game_receiver ): (Sender, Receiver) = channel(); let ( main_sender, cpu_receiver ): (Sender, Receiver) = channel(); let ( game_sender, f ): (Sender, Receiver) = channel(); let mut cpu = Intcode::new(cpu_sender, cpu_receiver); let mut game = Game::new(game_sender, game_receiver); eprintln!("Spawning cpu"); let cpu_handler = thread::spawn(move || cpu.intcode(memory.clone())); eprintln!("Spawning game"); let game_handler = thread::spawn(move || { let n = game.run(); n }); eprintln!("Starting loop"); loop { let v = 0; match main_sender.send(v) { Ok(_) => {}, _ => {break;} } } cpu_handler.join().expect("Problem with cpu thread."); match game_handler.join() { Ok(v) => v, _ => -1, } } fn main() { let args: Vec = env::args().collect(); let mut opts = getopts::Options::new(); opts.optflag("e", "use-emergency-panel", "Place white panel at (0,0)"); opts.reqopt("p", "program", "File to load program from", "FILE"); let matches = match opts.parse(&args[1..]) { Ok(m) => { m } Err(f) => { panic!(f.to_string()) } }; let program: String = matches.opt_str("program").unwrap(); let score = play_game(&program); eprintln!("Final score {}", score); }