summaryrefslogtreecommitdiff
path: root/2019
diff options
context:
space:
mode:
Diffstat (limited to '2019')
-rw-r--r--2019/rust/Cargo.toml1
-rw-r--r--2019/rust/day13/Cargo.toml9
-rwxr-xr-x2019/rust/day13/both_parts.sh2
-rwxr-xr-x2019/rust/day13/part_one.sh3
-rwxr-xr-x2019/rust/day13/part_two.sh1
-rw-r--r--2019/rust/day13/src/main.rs442
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!("");
+ 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<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);
+}