diff options
author | cos <cos> | 2019-12-11 19:40:45 +0100 |
---|---|---|
committer | cos <cos> | 2019-12-11 19:40:45 +0100 |
commit | 9ab2a7320f7d1eeee9a42198c2cb1667612a62d1 (patch) | |
tree | 7957ffd365e6372742a76c59e59804daae08fde0 | |
parent | 945ee368589a255fc24595ffd7bfa6f2f0027871 (diff) | |
download | adventofcode-9ab2a7320f7d1eeee9a42198c2cb1667612a62d1.zip |
Add day11, 2019
-rw-r--r-- | 2019/rust/Cargo.toml | 1 | ||||
-rwxr-xr-x | 2019/rust/day11/both_parts.sh | 3 | ||||
-rw-r--r-- | 2019/rust/day11/src/main.rs | 410 |
3 files changed, 414 insertions, 0 deletions
diff --git a/2019/rust/Cargo.toml b/2019/rust/Cargo.toml index 0f57bd0..20977c7 100644 --- a/2019/rust/Cargo.toml +++ b/2019/rust/Cargo.toml @@ -10,4 +10,5 @@ members = [ "day08", "day09", "day10", + "day11", ] diff --git a/2019/rust/day11/both_parts.sh b/2019/rust/day11/both_parts.sh new file mode 100755 index 0000000..c9ed7ef --- /dev/null +++ b/2019/rust/day11/both_parts.sh @@ -0,0 +1,3 @@ +../target/release/day11 --program input +../target/release/day11 --program input --use-emergency-panel + diff --git a/2019/rust/day11/src/main.rs b/2019/rust/day11/src/main.rs new file mode 100644 index 0000000..6add91a --- /dev/null +++ b/2019/rust/day11/src/main.rs @@ -0,0 +1,410 @@ +use std::env; +use std::io::{BufRead, BufReader}; +use std::fs::File; +use std::sync::mpsc::{channel, Receiver, Sender}; +use std::thread; +use std::collections::{HashMap}; + +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 Intcode { + r: Registers, + sender: Sender<IOType>, + receiver: Receiver<IOType>, +} + +struct Robot { + canvas: HashMap<(isize, isize), u8>, + sender: Sender<IOType>, + receiver: Receiver<IOType>, +} + +enum Direction { + Up, + Down, + Left, + Right, +} + +impl Robot { + fn new(sender: Sender<IOType>, receiver: Receiver<IOType>) -> Robot { + Robot { + canvas: HashMap::new(), + sender, + receiver, + } + } + + fn run(&mut self) -> usize { + let mut x = 0; + let mut y = 0; + let mut direction = Direction::Up; + let mut painted = 0; + + loop { + match self.sender.send(match self.canvas.get(&(x, y)) { Some(1) => 1, _ => 0, }) { + Ok(_) => {}, _ => break, + } + let (brush, turn) = match self.receiver.iter().zip(self. receiver.iter()).next() { + Some((b, t)) => (b as u8, t), _ => break, + }; + + match self.canvas.insert((x, y), brush as u8) { + None => painted += 1, + _ => {}, + } + + self.propulse(turn, &mut x, &mut y, &mut direction); + } + + painted + } + + fn place_emergency_panel(&mut self) { + self.canvas.insert((0, 0), 1); + } + + fn propulse(&self, turn: IOType, x: &mut isize, y: &mut isize, direction: &mut Direction) { + *direction = if turn == 0 { + match direction { + Direction::Up => Direction::Left, + Direction::Left => Direction::Down, + Direction::Down => Direction::Right, + Direction::Right => Direction::Up, + } + } else if turn == 1 { + match direction { + Direction::Up => Direction::Right, + Direction::Right => Direction::Down, + Direction::Down => Direction::Left, + Direction::Left => Direction::Up, + } + } else { + panic!("Invalid turn instruction: {}", turn); + }; + + match direction { + Direction::Up => *y -= 1, + Direction::Right => *x += 1, + Direction::Down => *y += 1, + Direction::Left => *x -= 1, + } + } + + fn show_painted_panels(&self) { + let boundary_msg = &"Could not determine plot boundary"; + + let min_x = *self.canvas.keys().map(|(x, _)| x).min().expect(boundary_msg); + let max_x = *self.canvas.keys().map(|(x, _)| x).max().expect(boundary_msg); + let min_y = *self.canvas.keys().map(|(_, y)| y).min().expect(boundary_msg); + let max_y = *self.canvas.keys().map(|(_, y)| y).max().expect(boundary_msg); + for y in min_y..=max_y { + for x in min_x..=max_x { + print!("{}", match self.canvas.get(&(x, y)) { + Some(1) => "██", + _ => " ", + }); + } + println!(""); + } + println!(""); + } +} + +impl Intcode { + fn new(sender: Sender<IOType>, receiver: Receiver<IOType>) -> Intcode { + Intcode { + r: Registers { ip: 0, base: 0, }, + sender, + receiver, + } + } + + 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(v) => 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); +// println!("{}", o); + self.sender.send(o as IOType).expect("Communication error. Nothing connected to CPU?"); + } + + 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 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 mut memory = Memory::new(); + memory.init(read_program(&program)); + let ( to_cpu, from_robot ): (Sender<IOType>, Receiver<IOType>) = channel(); + let ( to_robot, from_cpu ): (Sender<IOType>, Receiver<IOType>) = channel(); + let mut cpu = Intcode::new(to_cpu, from_cpu); + let mut robot = Robot::new(to_robot, from_robot); + if matches.opt_present("use-emergency-panel") { + robot.place_emergency_panel(); + } + + let cpu_handler = thread::spawn(move || cpu.intcode(memory.clone())); + let robot_handler = thread::spawn(move || { + let n = robot.run(); + robot.show_painted_panels(); + n + }); + + cpu_handler.join().expect("Problem with cpu thread."); + println!("Panes painted: {:?}", robot_handler.join().unwrap()); +} |