diff options
-rw-r--r-- | 2019/rust/Cargo.toml | 1 | ||||
-rw-r--r-- | 2019/rust/day13/Cargo.toml | 9 | ||||
-rwxr-xr-x | 2019/rust/day13/both_parts.sh | 2 | ||||
-rwxr-xr-x | 2019/rust/day13/part_one.sh | 3 | ||||
-rwxr-xr-x | 2019/rust/day13/part_two.sh | 1 | ||||
-rw-r--r-- | 2019/rust/day13/src/main.rs | 442 |
6 files changed, 458 insertions, 0 deletions
diff --git a/2019/rust/Cargo.toml b/2019/rust/Cargo.toml index 93d1ea6..e49e67b 100644 --- a/2019/rust/Cargo.toml +++ b/2019/rust/Cargo.toml @@ -12,4 +12,5 @@ members = [ "day10", "day11", "day12", + "day13", ] diff --git a/2019/rust/day13/Cargo.toml b/2019/rust/day13/Cargo.toml new file mode 100644 index 0000000..5ba0dc9 --- /dev/null +++ b/2019/rust/day13/Cargo.toml @@ -0,0 +1,9 @@ +[package] +name = "day13" +version = "0.1.0" +authors = ["cos <cos>"] +edition = "2018" + +[dependencies] +getopts = "0.2" +itertools = "0" diff --git a/2019/rust/day13/both_parts.sh b/2019/rust/day13/both_parts.sh new file mode 100755 index 0000000..7a91b46 --- /dev/null +++ b/2019/rust/day13/both_parts.sh @@ -0,0 +1,2 @@ +./part_one.sh +./part_two.sh diff --git a/2019/rust/day13/part_one.sh b/2019/rust/day13/part_one.sh new file mode 100755 index 0000000..9564e61 --- /dev/null +++ b/2019/rust/day13/part_one.sh @@ -0,0 +1,3 @@ +echo $0 >&2 +echo Part one currently missing >&2 +exit 1 diff --git a/2019/rust/day13/part_two.sh b/2019/rust/day13/part_two.sh new file mode 100755 index 0000000..7daa48b --- /dev/null +++ b/2019/rust/day13/part_two.sh @@ -0,0 +1 @@ +$VG ../target/release/day13 --program input diff --git a/2019/rust/day13/src/main.rs b/2019/rust/day13/src/main.rs new file mode 100644 index 0000000..63c6fe0 --- /dev/null +++ b/2019/rust/day13/src/main.rs @@ -0,0 +1,442 @@ +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<BusWidth> { + 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<BusWidth> = line.split(',').map(|e| { e.trim_start().trim_end() }) + .filter(|v| match v.parse::<BusWidth>() { 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<BusWidth>, +} + +impl Memory { + fn new() -> Memory { + Memory { mem: Vec::new(), } + } + + fn init(&mut self, mem:Vec<BusWidth>) { + 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<IOType>, + 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<IOType>, + receiver: Receiver<IOType>, + hack: Hack, +} + +struct Game { + canvas: HashMap<(isize, isize), isize>, + sender: Sender<IOType>, + receiver: Receiver<IOType>, + score: isize, +} + +impl Game { + fn new(sender: Sender<IOType>, receiver: Receiver<IOType>) -> 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!("[H"); + 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!("[H"); + } +} + +impl Intcode { + fn new(sender: Sender<IOType>, receiver: Receiver<IOType>) -> 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<ParameterMode>, memory: &mut Memory) { + let parameter_directions = + [DataDirection::Read, DataDirection::Read, DataDirection::Write]; + let params:Vec<usize> = (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<ParameterMode>, memory: &mut Memory) + { + let parameter_directions = [DataDirection::Read, DataDirection::Read]; + let params:Vec<usize> = (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<ParameterMode>, memory: &mut Memory) + { + let parameter_directions = [DataDirection::Read]; + let params:Vec<usize> = (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<ParameterMode>, memory:&mut Memory) + { + let parameter_directions = [DataDirection::Write]; + let params:Vec<usize> = (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<ParameterMode>, memory:&mut Memory ) + { + let parameter_directions = [DataDirection::Read]; + let params:Vec<usize> = (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<ParameterMode> = (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<IOType>, Receiver<IOType>) = channel(); + let ( main_sender, cpu_receiver ): (Sender<IOType>, Receiver<IOType>) = channel(); + let ( game_sender, f ): (Sender<IOType>, Receiver<IOType>) = 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<String> = 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); +} |