Switch to a resumption-style implementation for machines.
This commit is contained in:
168
src/arcade.rs
168
src/arcade.rs
@@ -1,35 +1,18 @@
|
||||
use crate::endchannel::{Receiver, Sender, channel};
|
||||
use crate::machine::Computer;
|
||||
use crate::machine::{Computer, RunResult};
|
||||
use terminal_graphics::{Colour, Display};
|
||||
use std::collections::VecDeque;
|
||||
use std::fmt;
|
||||
use std::thread;
|
||||
|
||||
pub struct Arcade {
|
||||
screen: Vec<Tile>,
|
||||
width: usize,
|
||||
height: usize,
|
||||
pub score: usize,
|
||||
joystick_port: Sender<i64>,
|
||||
update_port: Receiver<Update>,
|
||||
logic: Computer,
|
||||
ball: (usize, usize),
|
||||
paddle: (usize, usize),
|
||||
}
|
||||
|
||||
pub fn auto_move(arcade: &Arcade) -> Move {
|
||||
let (ball_x, _) = arcade.ball;
|
||||
let (paddle_x, _) = arcade.paddle;
|
||||
|
||||
if paddle_x < ball_x {
|
||||
return Move::Right;
|
||||
}
|
||||
|
||||
if paddle_x > ball_x {
|
||||
return Move::Left;
|
||||
}
|
||||
|
||||
Move::Neutral
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq)]
|
||||
enum Tile {
|
||||
Empty,
|
||||
@@ -39,13 +22,6 @@ enum Tile {
|
||||
Ball,
|
||||
}
|
||||
|
||||
enum Update {
|
||||
Score(usize),
|
||||
Ball(usize, usize),
|
||||
Paddle(usize, usize),
|
||||
Draw(usize, usize, Tile),
|
||||
}
|
||||
|
||||
impl fmt::Display for Tile {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
match self {
|
||||
@@ -84,51 +60,72 @@ impl Tile {
|
||||
impl Arcade {
|
||||
pub fn new(width: usize, height: usize, cheat: bool, logic_file: &str) -> Arcade {
|
||||
let mut logic = Computer::load(logic_file);
|
||||
let ( mysend, mut corecv) = channel();
|
||||
let (mut cosend, mut myrecv) = channel();
|
||||
let (mut upsend, uprecv) = channel();
|
||||
|
||||
if cheat { logic.write(0, 2); }
|
||||
thread::spawn(move || logic.run(&mut corecv, &mut cosend));
|
||||
|
||||
let mut screen = Vec::with_capacity(width * height);
|
||||
screen.resize(width * height, Tile::Empty);
|
||||
|
||||
thread::spawn(move || {
|
||||
while let Some(first) = myrecv.recv() {
|
||||
let second = myrecv.recv().expect("Didn't get second?!");
|
||||
let third = myrecv.recv().expect("Didn't get third?!");
|
||||
|
||||
if first == -1 && second == 0 {
|
||||
upsend.send_ignore_error(Update::Score(third as usize));
|
||||
} else {
|
||||
let x = first as usize;
|
||||
let y = second as usize;
|
||||
let t = Tile::new(third);
|
||||
upsend.send_ignore_error(Update::Draw(x, y, t));
|
||||
if t == Tile::Ball {
|
||||
upsend.send_ignore_error(Update::Ball(x, y));
|
||||
}
|
||||
if t == Tile::HorizontalPaddle {
|
||||
upsend.send_ignore_error(Update::Paddle(x, y));
|
||||
}
|
||||
}
|
||||
}
|
||||
upsend.conclude();
|
||||
});
|
||||
|
||||
Arcade {
|
||||
screen,
|
||||
width,
|
||||
height,
|
||||
joystick_port: mysend,
|
||||
update_port: uprecv,
|
||||
logic,
|
||||
score: 0,
|
||||
ball: (0, 0),
|
||||
paddle: (0, 0),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn run<F: FnMut(&Arcade)>(mut self, mut redraw: F) -> Self {
|
||||
let mut output_buffer = vec![];
|
||||
let mut input_buffer = VecDeque::new();
|
||||
|
||||
loop {
|
||||
match self.logic.run() {
|
||||
RunResult::Continue(next) =>
|
||||
self.logic = next,
|
||||
RunResult::Halted(next) => {
|
||||
self.logic = next;
|
||||
return self;
|
||||
}
|
||||
RunResult::Output(x, next) => {
|
||||
self.logic = next;
|
||||
output_buffer.push(x);
|
||||
if output_buffer.len() == 3 {
|
||||
if output_buffer[0] == -1 && output_buffer[1] == 0 {
|
||||
self.score = output_buffer[2] as usize;
|
||||
} else {
|
||||
let x = output_buffer[0] as usize;
|
||||
let y = output_buffer[1] as usize;
|
||||
let t = Tile::new(output_buffer[2]);
|
||||
self.screen[ (y * self.width) + x ] = t;
|
||||
if t == Tile::Ball {
|
||||
self.ball = (x, y);
|
||||
let (paddle_x, _) = self.paddle;
|
||||
|
||||
if paddle_x < x {
|
||||
input_buffer.push_back(Move::Right);
|
||||
} else if paddle_x > x {
|
||||
input_buffer.push_back(Move::Left);
|
||||
} else {
|
||||
input_buffer.push_back(Move::Neutral);
|
||||
}
|
||||
}
|
||||
if t == Tile::HorizontalPaddle {
|
||||
self.paddle = (x, y);
|
||||
}
|
||||
}
|
||||
output_buffer = vec![];
|
||||
}
|
||||
}
|
||||
RunResult::Input(c) => {
|
||||
self.logic = c(input_buffer.pop_front().unwrap().encode());
|
||||
redraw(&self);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
fn count_blocks(&self) -> usize {
|
||||
let mut count = 0;
|
||||
|
||||
@@ -141,39 +138,6 @@ impl Arcade {
|
||||
count
|
||||
}
|
||||
|
||||
pub fn process_update<F>(&mut self, next_move: F) -> bool
|
||||
where F: Fn(&Arcade) -> Move
|
||||
{
|
||||
let next = self.update_port.recv();
|
||||
|
||||
if let Some(ref update) = next {
|
||||
match update {
|
||||
&Update::Score(s) => self.score = s,
|
||||
&Update::Draw(x, y, t) => self.screen[ (y * self.width) + x ] = t,
|
||||
&Update::Ball(x, y) => {
|
||||
self.ball = (x, y);
|
||||
let next = next_move(&self);
|
||||
self.paddle_move(next);
|
||||
}
|
||||
&Update::Paddle(x, y) => {
|
||||
self.paddle = (x, y);
|
||||
// let next = next_move(&self);
|
||||
// self.paddle_move(next);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
next.is_some()
|
||||
}
|
||||
|
||||
pub fn paddle_move(&mut self, motion: Move) {
|
||||
match motion {
|
||||
Move::Left => self.joystick_port.send_ignore_error(-1),
|
||||
Move::Neutral => self.joystick_port.send_ignore_error(0),
|
||||
Move::Right => self.joystick_port.send_ignore_error(1),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn draw(&self, display: &mut Display) {
|
||||
write_to_screen(display, 0, &format!("Score: {}", self.score));
|
||||
for row in 0..self.height {
|
||||
@@ -202,13 +166,23 @@ pub enum Move {
|
||||
Right,
|
||||
}
|
||||
|
||||
impl Move {
|
||||
fn encode(&self) -> i64 {
|
||||
match self {
|
||||
Move::Left => -1,
|
||||
Move::Neutral => 0,
|
||||
Move::Right => 1,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn day13() {
|
||||
let mut arcade1 = Arcade::new(38, 21, false, "inputs/day13");
|
||||
while arcade1.process_update(auto_move) { }
|
||||
assert_eq!(301, arcade1.count_blocks());
|
||||
let arcade1 = Arcade::new(38, 21, false, "inputs/day13");
|
||||
let result1 = arcade1.run(|_| {});
|
||||
assert_eq!(301, result1.count_blocks());
|
||||
|
||||
let mut arcade2 = Arcade::new(38, 21, true, "inputs/day13");
|
||||
while arcade2.process_update(auto_move) { }
|
||||
assert_eq!(14096, arcade2.score);
|
||||
let arcade2 = Arcade::new(38, 21, true, "inputs/day13");
|
||||
let result2 = arcade2.run(|_| {});
|
||||
assert_eq!(14096, result2.score);
|
||||
}
|
||||
|
||||
294
src/machine.rs
294
src/machine.rs
@@ -1,9 +1,8 @@
|
||||
use crate::endchannel::{Sender, Receiver, channel};
|
||||
use std::collections::VecDeque;
|
||||
use std::fs;
|
||||
use std::iter::FromIterator;
|
||||
use std::ops::Range;
|
||||
use std::str;
|
||||
use std::thread;
|
||||
|
||||
const ADD: i64 = 1;
|
||||
const MULTIPLY: i64 = 2;
|
||||
@@ -42,6 +41,13 @@ pub struct Computer {
|
||||
done: bool
|
||||
}
|
||||
|
||||
pub enum RunResult {
|
||||
Input(Box<dyn FnOnce(i64) -> Computer>),
|
||||
Output(i64, Computer),
|
||||
Continue(Computer),
|
||||
Halted(Computer),
|
||||
}
|
||||
|
||||
impl Computer {
|
||||
pub fn load(path: &str) -> Computer {
|
||||
let byte_buffer = fs::read(path).unwrap();
|
||||
@@ -108,7 +114,7 @@ impl Computer {
|
||||
self.memory[idx] = val;
|
||||
}
|
||||
|
||||
fn step(&mut self, input: &mut Receiver<i64>, output: &mut Sender<i64>) {
|
||||
fn step(mut self) -> RunResult {
|
||||
let next_instruction = self.read(self.position);
|
||||
let opcode = next_instruction % 100;
|
||||
let arg1mode = Mode::from((next_instruction / 100) % 10);
|
||||
@@ -123,6 +129,7 @@ impl Computer {
|
||||
|
||||
self.write(dest, arg1 + arg2);
|
||||
self.position += 4;
|
||||
RunResult::Continue(self)
|
||||
}
|
||||
MULTIPLY => {
|
||||
let arg1 = self.read_arg(arg1mode, self.position + 1);
|
||||
@@ -131,26 +138,20 @@ impl Computer {
|
||||
|
||||
self.write(dest, arg1 * arg2);
|
||||
self.position += 4;
|
||||
RunResult::Continue(self)
|
||||
}
|
||||
INPUT => {
|
||||
let dest = self.read_dest(arg1mode, self.position + 1) as usize;
|
||||
|
||||
match input.recv() {
|
||||
None => {
|
||||
output.conclude();
|
||||
self.done = true;
|
||||
}
|
||||
Some(val) => {
|
||||
self.write(dest, val);
|
||||
self.position += 2;
|
||||
}
|
||||
}
|
||||
RunResult::Input(Box::new(move |x| {
|
||||
self.write(dest, x);
|
||||
self
|
||||
}))
|
||||
}
|
||||
OUTPUT => {
|
||||
let arg1 = self.read_arg(arg1mode, self.position + 1);
|
||||
|
||||
output.send(arg1);
|
||||
self.position += 2;
|
||||
RunResult::Output(arg1, self)
|
||||
}
|
||||
JMPIF => {
|
||||
let arg1 = self.read_arg(arg1mode, self.position + 1);
|
||||
@@ -161,6 +162,7 @@ impl Computer {
|
||||
} else {
|
||||
self.position += 3;
|
||||
}
|
||||
RunResult::Continue(self)
|
||||
}
|
||||
JMPNIF => {
|
||||
let arg1 = self.read_arg(arg1mode, self.position + 1);
|
||||
@@ -171,6 +173,7 @@ impl Computer {
|
||||
} else {
|
||||
self.position += 3;
|
||||
}
|
||||
RunResult::Continue(self)
|
||||
}
|
||||
LESS_THAN => {
|
||||
let arg1 = self.read_arg(arg1mode, self.position + 1);
|
||||
@@ -179,6 +182,7 @@ impl Computer {
|
||||
|
||||
self.write(dest, if arg1 < arg2 { 1 } else { 0 });
|
||||
self.position += 4;
|
||||
RunResult::Continue(self)
|
||||
}
|
||||
EQUALS => {
|
||||
let arg1 = self.read_arg(arg1mode, self.position + 1);
|
||||
@@ -187,16 +191,18 @@ impl Computer {
|
||||
|
||||
self.write(dest, if arg1 == arg2 { 1 } else { 0 });
|
||||
self.position += 4;
|
||||
RunResult::Continue(self)
|
||||
}
|
||||
ADJUST_BASE => {
|
||||
let arg1 = self.read_arg(arg1mode, self.position + 1);
|
||||
|
||||
self.relative_base += arg1;
|
||||
self.position += 2;
|
||||
RunResult::Continue(self)
|
||||
}
|
||||
HALT => {
|
||||
output.conclude();
|
||||
self.done = true;
|
||||
RunResult::Halted(self)
|
||||
}
|
||||
/* */
|
||||
unknown_pos =>
|
||||
@@ -204,29 +210,69 @@ impl Computer {
|
||||
}
|
||||
}
|
||||
|
||||
fn halted(&self) -> bool {
|
||||
self.done
|
||||
pub fn run(mut self) -> RunResult {
|
||||
loop {
|
||||
match self.step() {
|
||||
RunResult::Continue(next) =>
|
||||
self = next,
|
||||
result =>
|
||||
return result,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn run(&mut self, input: &mut Receiver<i64>, output: &mut Sender<i64>) {
|
||||
while !self.halted() {
|
||||
self.step(input, output);
|
||||
pub fn standard_run(mut self, inputs: &[i64]) -> Vec<i64> {
|
||||
let mut idx = 0;
|
||||
let mut res = vec![];
|
||||
|
||||
loop {
|
||||
match self.step() {
|
||||
RunResult::Continue(next) =>
|
||||
self = next,
|
||||
RunResult::Halted(_) =>
|
||||
return res,
|
||||
RunResult::Input(c) if idx < inputs.len() => {
|
||||
self = c(inputs[idx]);
|
||||
idx += 1;
|
||||
}
|
||||
RunResult::Input(_) =>
|
||||
panic!("Ran out of inputs in standard run."),
|
||||
RunResult::Output(x, next) => {
|
||||
res.push(x);
|
||||
self = next;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn serialize(&self, inputs: Vec<i64>) -> i64 {
|
||||
let mut previous_output = vec![0];
|
||||
let mut previous_output = VecDeque::new();
|
||||
|
||||
previous_output.push_back(0);
|
||||
for input in inputs.iter() {
|
||||
let mut my_machine = self.clone();
|
||||
let ( mysend, mut corecv) = channel();
|
||||
let (mut cosend, myrecv) = channel();
|
||||
mysend.send(*input);
|
||||
for i in previous_output.iter() {
|
||||
mysend.send(*i);
|
||||
let mut my_machine = self.clone().prime(*input);
|
||||
let mut output = VecDeque::new();
|
||||
|
||||
loop {
|
||||
match my_machine.run() {
|
||||
RunResult::Continue(next) =>
|
||||
my_machine = next,
|
||||
RunResult::Halted(_) =>
|
||||
break,
|
||||
RunResult::Input(c) =>
|
||||
match previous_output.pop_front() {
|
||||
None =>
|
||||
panic!("Serialized machine wanted input I didn't have!"),
|
||||
Some(next_input) =>
|
||||
my_machine = c(next_input),
|
||||
},
|
||||
RunResult::Output(o, next) => {
|
||||
output.push_back(o);
|
||||
my_machine = next;
|
||||
}
|
||||
my_machine.run(&mut corecv, &mut cosend);
|
||||
previous_output = myrecv.collect();
|
||||
}
|
||||
}
|
||||
previous_output = output;
|
||||
}
|
||||
|
||||
assert_eq!(previous_output.len(), 1);
|
||||
@@ -263,127 +309,143 @@ impl Computer {
|
||||
(best_score, best_result)
|
||||
}
|
||||
|
||||
pub fn prime(self, input: i64) -> Self {
|
||||
match self.run() {
|
||||
RunResult::Input(c) => c(input),
|
||||
_ =>
|
||||
panic!("Priming failure: machine didn't ask for input first.")
|
||||
}
|
||||
}
|
||||
|
||||
pub fn amplifier(&self, settings: Vec<i64>) -> i64 {
|
||||
assert_eq!(settings.len(), 5);
|
||||
|
||||
let mut machine_a = self.clone();
|
||||
let mut machine_b = self.clone();
|
||||
let mut machine_c = self.clone();
|
||||
let mut machine_d = self.clone();
|
||||
let mut machine_e = self.clone();
|
||||
|
||||
let ( send_ha, mut recv_ha) = channel(); send_ha.send(settings[0]);
|
||||
let (mut send_ab, mut recv_ab) = channel(); send_ab.send(settings[1]);
|
||||
let (mut send_bc, mut recv_bc) = channel(); send_bc.send(settings[2]);
|
||||
let (mut send_cd, mut recv_cd) = channel(); send_cd.send(settings[3]);
|
||||
let (mut send_de, mut recv_de) = channel(); send_de.send(settings[4]);
|
||||
let (mut send_eh, recv_eh) = channel();
|
||||
|
||||
thread::spawn(move || { machine_a.run(&mut recv_ha, &mut send_ab) });
|
||||
thread::spawn(move || { machine_b.run(&mut recv_ab, &mut send_bc) });
|
||||
thread::spawn(move || { machine_c.run(&mut recv_bc, &mut send_cd) });
|
||||
thread::spawn(move || { machine_d.run(&mut recv_cd, &mut send_de) });
|
||||
thread::spawn(move || { machine_e.run(&mut recv_de, &mut send_eh) });
|
||||
|
||||
send_ha.send(0); // kick it off
|
||||
|
||||
let mut machine_a = self.clone().prime(settings[0]);
|
||||
let mut machine_b = self.clone().prime(settings[1]);
|
||||
let mut machine_c = self.clone().prime(settings[2]);
|
||||
let mut machine_d = self.clone().prime(settings[3]);
|
||||
let mut machine_e = self.clone().prime(settings[4]);
|
||||
let mut last_output = 0;
|
||||
|
||||
for output in recv_eh {
|
||||
last_output = output;
|
||||
send_ha.send_ignore_error(output);
|
||||
loop {
|
||||
let aout = loop { match machine_a.run() {
|
||||
RunResult::Halted(_) => return last_output,
|
||||
RunResult::Output(o, next) => { machine_a = next; break o; }
|
||||
RunResult::Input(c) => machine_a = c(last_output),
|
||||
_ => panic!("Unexpted aout"),
|
||||
} };
|
||||
let bout = loop { match machine_b.run() {
|
||||
RunResult::Halted(_) => return last_output,
|
||||
RunResult::Output(o, next) => { machine_b = next; break o; }
|
||||
RunResult::Input(c) => machine_b = c(aout),
|
||||
_ => panic!("Unexpted aout"),
|
||||
} };
|
||||
let cout = loop { match machine_c.run() {
|
||||
RunResult::Halted(_) => return last_output,
|
||||
RunResult::Output(o, next) => { machine_c = next; break o; }
|
||||
RunResult::Input(c) => machine_c = c(bout),
|
||||
_ => panic!("Unexpted aout"),
|
||||
} };
|
||||
let dout = loop { match machine_d.run() {
|
||||
RunResult::Halted(_) => return last_output,
|
||||
RunResult::Output(o, next) => { machine_d = next; break o; }
|
||||
RunResult::Input(c) => machine_d = c(cout),
|
||||
_ => panic!("Unexpted aout"),
|
||||
} };
|
||||
let eout = loop { match machine_e.run() {
|
||||
RunResult::Halted(_) => return last_output,
|
||||
RunResult::Output(o, next) => { machine_e = next; break o; }
|
||||
RunResult::Input(c) => machine_e = c(dout),
|
||||
_ => panic!("Unexpted aout"),
|
||||
} };
|
||||
last_output = eout;
|
||||
}
|
||||
|
||||
last_output
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
fn run_example(computer: Vec<i64>, inputs: Vec<i64>, targets: Vec<i64>) {
|
||||
fn run_example(computer: Vec<i64>, inputs: &[i64], targets: &[i64]) {
|
||||
let day5a = Computer{ memory: computer, position: 0, relative_base: 0, done: false };
|
||||
run_computer(day5a, inputs, targets);
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
fn run_computer(mut computer: Computer, inputs: Vec<i64>, targets: Vec<i64>) {
|
||||
let ( mysend, mut corecv) = channel();
|
||||
let (mut cosend, myrecv) = channel();
|
||||
for i in inputs.iter() {
|
||||
mysend.send(*i);
|
||||
}
|
||||
computer.run(&mut corecv, &mut cosend);
|
||||
let outputs: Vec<i64> = myrecv.collect();
|
||||
assert_eq!(targets, outputs);
|
||||
fn run_computer(computer: Computer, inputs: &[i64], targets: &[i64]) {
|
||||
let outputs = computer.standard_run(inputs);
|
||||
assert_eq!(&outputs, &targets);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_examples() {
|
||||
let (mut deadsend, mut deadrecv) = channel();
|
||||
|
||||
let mut example1 = Computer::from_string("1,0,0,0,99");
|
||||
let example1 = Computer::from_string("1,0,0,0,99");
|
||||
let answer1 = Computer{ memory: vec![2,0,0,0,99], position: 4, relative_base: 0, done: false };
|
||||
example1.step(&mut deadrecv, &mut deadsend);
|
||||
assert_eq!(example1, answer1);
|
||||
match example1.step() {
|
||||
RunResult::Continue(result) => assert_eq!(answer1, result),
|
||||
_ => assert!(false),
|
||||
}
|
||||
|
||||
let mut example2 = Computer::from_string("2,3,0,3,99");
|
||||
let example2 = Computer::from_string("2,3,0,3,99");
|
||||
let answer2 = Computer{ memory: vec![2,3,0,6,99], position: 4, relative_base: 0, done: false };
|
||||
example2.step(&mut deadrecv, &mut deadsend);
|
||||
assert_eq!(example2, answer2);
|
||||
match example2.step() {
|
||||
RunResult::Continue(result) => assert_eq!(answer2, result),
|
||||
_ => assert!(false),
|
||||
}
|
||||
|
||||
let mut example3 = Computer::from_string("2,4,4,5,99,0");
|
||||
let example3 = Computer::from_string("2,4,4,5,99,0");
|
||||
let answer3 = Computer{ memory: vec![2,4,4,5,99,9801], position: 4, relative_base: 0, done: false };
|
||||
example3.step(&mut deadrecv, &mut deadsend);
|
||||
assert_eq!(example3, answer3);
|
||||
match example3.step() {
|
||||
RunResult::Continue(result) => assert_eq!(answer3, result),
|
||||
_ => assert!(false),
|
||||
}
|
||||
|
||||
let mut example4 = Computer::from_string("1,1,1,4,99,5,6,0,99");
|
||||
let example4 = Computer::from_string("1,1,1,4,99,5,6,0,99");
|
||||
let answer4 = Computer{ memory: vec![30,1,1,4,2,5,6,0,99], position: 8, relative_base: 0, done: true };
|
||||
example4.run(&mut deadrecv, &mut deadsend);
|
||||
assert_eq!(example4, answer4);
|
||||
assert!(example4.halted());
|
||||
match example4.run() {
|
||||
RunResult::Halted(result) => assert_eq!(answer4, result),
|
||||
_ => assert!(false),
|
||||
}
|
||||
|
||||
let mut example5 = Computer::from_string("1002,4,3,4,33");
|
||||
let example5 = Computer::from_string("1002,4,3,4,33");
|
||||
let answer5 = Computer{ memory: vec![1002,4,3,4,99], position: 4, relative_base: 0, done: true };
|
||||
example5.run(&mut deadrecv, &mut deadsend);
|
||||
assert_eq!(example5, answer5);
|
||||
assert!(example5.halted());
|
||||
match example5.run() {
|
||||
RunResult::Halted(result) => assert_eq!(answer5, result),
|
||||
_ => assert!(false),
|
||||
}
|
||||
|
||||
let mut example6 = Computer::from_string("1101,100,-1,4,0");
|
||||
let example6 = Computer::from_string("1101,100,-1,4,0");
|
||||
let answer6 = Computer{ memory: vec![1101,100,-1,4,99], position: 4, relative_base: 0, done: true };
|
||||
example6.run(&mut deadrecv, &mut deadsend);
|
||||
assert_eq!(example6, answer6);
|
||||
assert!(example6.halted());
|
||||
match example6.run() {
|
||||
RunResult::Halted(result) => assert_eq!(answer6, result),
|
||||
_ => assert!(false),
|
||||
}
|
||||
|
||||
let mut day5a = Computer::load("inputs/day5");
|
||||
let day5a = Computer::load("inputs/day5");
|
||||
let target = vec![0,0,0,0,0,0,0,0,0,7_259_358];
|
||||
let ( mysend, mut corecv) = channel();
|
||||
let (mut cosend, myrecv) = channel();
|
||||
mysend.send(1);
|
||||
day5a.run(&mut corecv, &mut cosend);
|
||||
let outputs: Vec<i64> = myrecv.collect();
|
||||
let outputs = day5a.standard_run(&[1]);
|
||||
assert_eq!(target, outputs);
|
||||
|
||||
run_example(vec![3,9,8,9,10,9,4,9,99,-1,8], vec![8], vec![1]);
|
||||
run_example(vec![3,9,8,9,10,9,4,9,99,-1,8], vec![9], vec![0]);
|
||||
run_example(vec![3,9,7,9,10,9,4,9,99,-1,8], vec![4], vec![1]);
|
||||
run_example(vec![3,9,7,9,10,9,4,9,99,-1,8], vec![8], vec![0]);
|
||||
run_example(vec![3,9,7,9,10,9,4,9,99,-1,8], vec![9], vec![0]);
|
||||
run_example(vec![3,3,1108,-1,8,3,4,3,99], vec![8], vec![1]);
|
||||
run_example(vec![3,3,1108,-1,8,3,4,3,99], vec![9], vec![0]);
|
||||
run_example(vec![3,3,1107,-1,8,3,4,3,99], vec![4], vec![1]);
|
||||
run_example(vec![3,3,1107,-1,8,3,4,3,99], vec![8], vec![0]);
|
||||
run_example(vec![3,3,1107,-1,8,3,4,3,99], vec![9], vec![0]);
|
||||
run_example(vec![3,9,8,9,10,9,4,9,99,-1,8], &[8], &[1]);
|
||||
run_example(vec![3,9,8,9,10,9,4,9,99,-1,8], &[9], &[0]);
|
||||
run_example(vec![3,9,7,9,10,9,4,9,99,-1,8], &[4], &[1]);
|
||||
run_example(vec![3,9,7,9,10,9,4,9,99,-1,8], &[8], &[0]);
|
||||
run_example(vec![3,9,7,9,10,9,4,9,99,-1,8], &[9], &[0]);
|
||||
run_example(vec![3,3,1108,-1,8,3,4,3,99], &[8], &[1]);
|
||||
run_example(vec![3,3,1108,-1,8,3,4,3,99], &[9], &[0]);
|
||||
run_example(vec![3,3,1107,-1,8,3,4,3,99], &[4], &[1]);
|
||||
run_example(vec![3,3,1107,-1,8,3,4,3,99], &[8], &[0]);
|
||||
run_example(vec![3,3,1107,-1,8,3,4,3,99], &[9], &[0]);
|
||||
run_example(vec![3,21,1008,21,8,20,1005,20,22,107,8,21,20,1006,20,31,
|
||||
1106,0,36,98,0,0,1002,21,125,20,4,20,1105,1,46,104,
|
||||
999,1105,1,46,1101,1000,1,20,4,20,1105,1,46,98,99],
|
||||
vec![3], vec![999]);
|
||||
&[3], &[999]);
|
||||
run_example(vec![3,21,1008,21,8,20,1005,20,22,107,8,21,20,1006,20,31,
|
||||
1106,0,36,98,0,0,1002,21,125,20,4,20,1105,1,46,104,
|
||||
999,1105,1,46,1101,1000,1,20,4,20,1105,1,46,98,99],
|
||||
vec![8], vec![1000]);
|
||||
&[8], &[1000]);
|
||||
run_example(vec![3,21,1008,21,8,20,1005,20,22,107,8,21,20,1006,20,31,
|
||||
1106,0,36,98,0,0,1002,21,125,20,4,20,1105,1,46,104,
|
||||
999,1105,1,46,1101,1000,1,20,4,20,1105,1,46,98,99],
|
||||
vec![192], vec![1001]);
|
||||
&[192], &[1001]);
|
||||
|
||||
let example7a = Computer::from_string("3,15,3,16,1002,16,10,16,1,16,15,15,4,15,99,0,0");
|
||||
let result7a = example7a.serialize(vec![4,3,2,1,0]);
|
||||
@@ -416,15 +478,15 @@ fn test_examples() {
|
||||
assert_eq!(vec![9,7,8,5,6], example7ft);
|
||||
|
||||
run_example(vec![109,1,204,-1,1001,100,1,100,1008,100,16,101,1006,101,0,99],
|
||||
vec![],
|
||||
vec![109,1,204,-1,1001,100,1,100,1008,100,16,101,1006,101,0,99]);
|
||||
&[],
|
||||
&[109,1,204,-1,1001,100,1,100,1008,100,16,101,1006,101,0,99]);
|
||||
run_example(vec![1102,34915192,34915192,7,4,7,99,0],
|
||||
vec![],
|
||||
vec![1219070632396864]);
|
||||
&[],
|
||||
&[1219070632396864]);
|
||||
run_example(vec![104,1125899906842624,99],
|
||||
vec![],
|
||||
vec![1125899906842624]);
|
||||
&[],
|
||||
&[1125899906842624]);
|
||||
|
||||
run_computer(Computer::load("inputs/day9"), vec![1], vec![3063082071]);
|
||||
run_computer(Computer::load("inputs/day9"), vec![2], vec![81348]);
|
||||
run_computer(Computer::load("inputs/day9"), &[1], &[3063082071]);
|
||||
run_computer(Computer::load("inputs/day9"), &[2], &[81348]);
|
||||
}
|
||||
|
||||
24
src/main.rs
24
src/main.rs
@@ -1,17 +1,16 @@
|
||||
mod arcade;
|
||||
mod args;
|
||||
mod endchannel;
|
||||
mod fuel;
|
||||
mod image;
|
||||
mod machine;
|
||||
mod orbits;
|
||||
#[cfg(test)]
|
||||
mod repair;
|
||||
#[cfg(test)]
|
||||
mod robot;
|
||||
mod wiremap;
|
||||
|
||||
use crate::arcade::{auto_move, Move};
|
||||
use crate::args::Command;
|
||||
use crate::endchannel::channel;
|
||||
use crate::fuel::calculate_fuel;
|
||||
use crate::orbits::Object;
|
||||
use crate::wiremap::WireMap;
|
||||
@@ -32,15 +31,12 @@ fn main() {
|
||||
println!("TOTAL FUEL: {}", total);
|
||||
}
|
||||
|
||||
Command::RunComputer(mut initial) => {
|
||||
let (my_sender, mut their_receiver) = channel();
|
||||
let (mut their_sender, my_receiver) = channel();
|
||||
Command::RunComputer(initial) => {
|
||||
println!("Initial Computer:");
|
||||
initial.show();
|
||||
println!("Running, with input 5.");
|
||||
my_sender.send(5);
|
||||
initial.run(&mut their_receiver, &mut their_sender);
|
||||
for val in my_receiver {
|
||||
let results = initial.standard_run(&[5]);
|
||||
for val in results.iter() {
|
||||
println!("Received value: {}", val);
|
||||
}
|
||||
}
|
||||
@@ -162,15 +158,15 @@ fn main() {
|
||||
image.draw();
|
||||
}
|
||||
|
||||
Command::Arcade(mut arcade) => {
|
||||
Command::Arcade(arcade) => {
|
||||
let mut screen = Display::new(40, 40);
|
||||
|
||||
screen.clear();
|
||||
while arcade.process_update(auto_move) {
|
||||
arcade.draw(&mut screen);
|
||||
let result = arcade.run(move |a| {
|
||||
a.draw(&mut screen);
|
||||
screen.print();
|
||||
}
|
||||
println!("Final score: {}", arcade.score);
|
||||
});
|
||||
println!("Final score: {}", result.score);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
121
src/repair.rs
121
src/repair.rs
@@ -1,7 +1,5 @@
|
||||
use crate::endchannel::{Receiver, Sender, channel};
|
||||
use crate::machine::Computer;
|
||||
use crate::machine::{Computer, RunResult};
|
||||
use std::collections::VecDeque;
|
||||
use std::thread;
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq)]
|
||||
enum Direction {
|
||||
@@ -20,16 +18,6 @@ impl Direction {
|
||||
Direction::West => (x - 1, y),
|
||||
}
|
||||
}
|
||||
|
||||
fn random() -> Direction {
|
||||
match rand::random::<u8>() & 0x3 {
|
||||
0 => Direction::North,
|
||||
1 => Direction::East,
|
||||
2 => Direction::South,
|
||||
3 => Direction::West,
|
||||
_ => panic!("The world broke")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const ALL_DIRECTIONS: [Direction; 4] = [Direction::North,
|
||||
@@ -38,15 +26,6 @@ const ALL_DIRECTIONS: [Direction; 4] = [Direction::North,
|
||||
Direction::West];
|
||||
|
||||
impl Direction {
|
||||
fn reverse(&self) -> Direction {
|
||||
match self {
|
||||
Direction::North => Direction::South,
|
||||
Direction::East => Direction::West,
|
||||
Direction::South => Direction::North,
|
||||
Direction::West => Direction::East,
|
||||
}
|
||||
}
|
||||
|
||||
fn encode(&self) -> i64 {
|
||||
match self {
|
||||
Direction::North => 1,
|
||||
@@ -67,16 +46,6 @@ impl Path {
|
||||
Path{ steps: vec![] }
|
||||
}
|
||||
|
||||
fn reverse(&self) -> Path {
|
||||
let mut steps = Vec::with_capacity(self.steps.len());
|
||||
|
||||
for step in self.steps.iter().rev() {
|
||||
steps.push(step.reverse());
|
||||
}
|
||||
|
||||
Path{ steps }
|
||||
}
|
||||
|
||||
fn extend(&self) -> Vec<Path> {
|
||||
let mut res = Vec::with_capacity(4);
|
||||
|
||||
@@ -145,29 +114,33 @@ impl RepairSearch {
|
||||
}
|
||||
|
||||
fn try_path(&mut self, path: &Path) -> MoveResult {
|
||||
let (mut mysend, mut corecv) = channel();
|
||||
let (mut cosend, mut myrecv) = channel();
|
||||
let my_computer = self.computer.clone();
|
||||
let mut my_computer = self.computer.clone();
|
||||
let mut last_response = MoveResult::Done;
|
||||
let mut idx = 0;
|
||||
|
||||
thread::spawn(move || my_computer.clone().run(&mut corecv, &mut cosend));
|
||||
for step in path.steps.iter() {
|
||||
mysend.send(step.encode());
|
||||
match myrecv.recv() {
|
||||
None =>
|
||||
loop {
|
||||
match my_computer.run() {
|
||||
RunResult::Continue(next) =>
|
||||
my_computer = next,
|
||||
RunResult::Halted(_) =>
|
||||
return last_response,
|
||||
Some(response) => {
|
||||
last_response = MoveResult::new(response);
|
||||
RunResult::Output(x, next) => {
|
||||
my_computer = next;
|
||||
last_response = MoveResult::new(x);
|
||||
if last_response == MoveResult::HitWall {
|
||||
break
|
||||
return last_response;
|
||||
}
|
||||
}
|
||||
RunResult::Input(c) => {
|
||||
if idx >= path.steps.len() {
|
||||
return last_response;
|
||||
}
|
||||
my_computer = c(path.steps[idx].encode());
|
||||
idx += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
mysend.conclude();
|
||||
|
||||
last_response
|
||||
}
|
||||
|
||||
fn run_search(&mut self) -> usize {
|
||||
let mut horizon = Path::new().extend();
|
||||
@@ -201,8 +174,7 @@ enum Tile {
|
||||
|
||||
struct Room {
|
||||
layout: Vec<Tile>,
|
||||
computer_input: Sender<i64>,
|
||||
computer_output: Receiver<i64>,
|
||||
computer: Computer,
|
||||
width: usize,
|
||||
height: usize,
|
||||
x: usize,
|
||||
@@ -211,17 +183,13 @@ struct Room {
|
||||
|
||||
impl Room {
|
||||
fn new(width: usize, height: usize, f: &str) -> Room {
|
||||
let (mut mysend, mut corecv) = channel();
|
||||
let (mut cosend, mut myrecv) = channel();
|
||||
let mut my_computer = Computer::load(f);
|
||||
let computer = Computer::load(f);
|
||||
let mut layout = Vec::with_capacity(width * height);
|
||||
|
||||
layout.resize(width * height, Tile::Unknown);
|
||||
thread::spawn(move || my_computer.run(&mut corecv, &mut cosend));
|
||||
Room{
|
||||
layout,
|
||||
computer_input: mysend,
|
||||
computer_output: myrecv,
|
||||
computer,
|
||||
width, height,
|
||||
x: width / 2,
|
||||
y: height / 2,
|
||||
@@ -248,6 +216,7 @@ impl Room {
|
||||
res
|
||||
}
|
||||
|
||||
/*
|
||||
fn print(&self) {
|
||||
println!("\n\n\n\n\n\n");
|
||||
for y in 0..self.height {
|
||||
@@ -266,6 +235,7 @@ impl Room {
|
||||
println!();
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
fn next_unknown(&self) -> Option<Direction> {
|
||||
let mut visited = vec![];
|
||||
@@ -292,51 +262,61 @@ impl Room {
|
||||
None
|
||||
}
|
||||
|
||||
fn step(&mut self, direction: Direction) -> bool {
|
||||
fn step(mut self, direction: Direction) -> (Self, bool) {
|
||||
let (tx, ty) = direction.apply(self.x, self.y);
|
||||
|
||||
if self.get(tx, ty) == Tile::Wall {
|
||||
return false;
|
||||
return (self, false);
|
||||
}
|
||||
|
||||
self.computer_input.send(direction.encode());
|
||||
match self.computer_output.recv() {
|
||||
None => false,
|
||||
Some(resp) => {
|
||||
loop {
|
||||
match self.computer.run() {
|
||||
RunResult::Continue(next) =>
|
||||
self.computer = next,
|
||||
RunResult::Halted(next) => {
|
||||
self.computer = next;
|
||||
return (self, false);
|
||||
}
|
||||
RunResult::Input(c) =>
|
||||
self.computer = c(direction.encode()),
|
||||
RunResult::Output(resp, next) => {
|
||||
let response = MoveResult::new(resp);
|
||||
|
||||
self.computer = next;
|
||||
match response {
|
||||
MoveResult::HitWall => {
|
||||
self.set(tx, ty, Tile::Wall);
|
||||
false
|
||||
return (self, false);
|
||||
}
|
||||
MoveResult::Done => {
|
||||
self.set(tx, ty, Tile::Empty);
|
||||
self.x = tx;
|
||||
self.y = ty;
|
||||
true
|
||||
return (self, true);
|
||||
}
|
||||
MoveResult::FoundSystem => {
|
||||
self.set(tx, ty, Tile::Oxygen);
|
||||
self.x = tx;
|
||||
self.y = ty;
|
||||
true
|
||||
return (self, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn map_room(&mut self) -> usize {
|
||||
fn map_room(mut self) -> (Self, usize) {
|
||||
let mut steps = 0;
|
||||
|
||||
while let Some(next_step) = self.next_unknown() {
|
||||
//println!("Steps taken: {} [x {}, y {}, next {:?}]", steps, self.x, self.y, next_step);
|
||||
self.step(next_step);
|
||||
let (next, _) = self.step(next_step);
|
||||
self = next;
|
||||
steps += 1;
|
||||
}
|
||||
|
||||
steps
|
||||
(self, steps)
|
||||
}
|
||||
|
||||
fn has_empty_space(&self) -> bool {
|
||||
@@ -381,8 +361,9 @@ fn day15a() {
|
||||
|
||||
#[test]
|
||||
fn day15b() {
|
||||
let mut day15b = Room::new(50, 50, "inputs/day15");
|
||||
let day15b = Room::new(50, 50, "inputs/day15");
|
||||
assert!(day15b.next_unknown().is_some());
|
||||
assert_eq!(2452, day15b.map_room());
|
||||
assert_eq!(346, day15b.spread());
|
||||
let (mut mapped, size) = day15b.map_room();
|
||||
assert_eq!(2452, size);
|
||||
assert_eq!(346, mapped.spread());
|
||||
}
|
||||
|
||||
77
src/robot.rs
77
src/robot.rs
@@ -1,33 +1,26 @@
|
||||
use crate::endchannel::{Sender, Receiver, channel};
|
||||
use crate::machine::Computer;
|
||||
use crate::machine::{Computer, RunResult};
|
||||
use image::{ImageBuffer, Rgb};
|
||||
use std::thread;
|
||||
|
||||
struct HullGrid {
|
||||
data: ImageBuffer<Rgb<u8>, Vec<u8>>,
|
||||
computer: Computer,
|
||||
robot_x: u32,
|
||||
robot_y: u32,
|
||||
robot_dir: Direction,
|
||||
sender: Sender<i64>,
|
||||
receiver: Receiver<i64>,
|
||||
}
|
||||
|
||||
impl HullGrid {
|
||||
fn new(width: u32, height: u32, computer_path: &str) -> HullGrid {
|
||||
let mut init_computer = Computer::load(computer_path);
|
||||
let ( mysend, mut corecv) = channel();
|
||||
let (mut cosend, myrecv) = channel();
|
||||
let computer = Computer::load(computer_path);
|
||||
|
||||
assert!(width & 1 == 1);
|
||||
assert!(height & 1 == 1);
|
||||
thread::spawn(move || init_computer.run(&mut corecv, &mut cosend));
|
||||
HullGrid {
|
||||
data: ImageBuffer::new(width, height),
|
||||
computer,
|
||||
robot_x: width / 2,
|
||||
robot_y: height / 2,
|
||||
robot_dir: Direction::Up,
|
||||
sender: mysend,
|
||||
receiver: myrecv,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -53,29 +46,55 @@ impl HullGrid {
|
||||
}
|
||||
}
|
||||
|
||||
fn paint_next(&mut self) -> bool {
|
||||
self.sender.send_ignore_error(if self.is_white() { 1 } else { 0 });
|
||||
match self.receiver.recv() {
|
||||
None =>
|
||||
false,
|
||||
Some(new_color) => {
|
||||
let rotation = self.receiver.recv().expect("Didn't get rotation back");
|
||||
if new_color == 0 { self.set_black() } else { self.set_white() }
|
||||
fn paint_next(mut self, output: Option<&str>) -> Option<Self> {
|
||||
let mut new_color: Option<i64> = None;
|
||||
|
||||
loop {
|
||||
let color = if self.is_white() { 1 } else { 0 };
|
||||
|
||||
match self.computer.run() {
|
||||
RunResult::Continue(next) =>
|
||||
self.computer = next,
|
||||
RunResult::Halted(next) => {
|
||||
self.computer = next;
|
||||
if let Some(fname) = output {
|
||||
self.render(fname);
|
||||
}
|
||||
return None;
|
||||
}
|
||||
RunResult::Input(c) =>
|
||||
self.computer = c(color),
|
||||
RunResult::Output(o, next) if new_color.is_none() => {
|
||||
new_color = Some(o);
|
||||
self.computer = next;
|
||||
}
|
||||
RunResult::Output(rotation, next) => {
|
||||
self.computer = next;
|
||||
|
||||
if new_color.unwrap() == 0 {
|
||||
self.set_black()
|
||||
} else {
|
||||
self.set_white()
|
||||
}
|
||||
|
||||
self.robot_dir = if rotation == 0 {
|
||||
self.robot_dir.rotate_right()
|
||||
} else {
|
||||
self.robot_dir.rotate_left()
|
||||
};
|
||||
|
||||
self.step();
|
||||
true
|
||||
return Some(self);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn paint_hull(&mut self) -> usize {
|
||||
fn paint_hull(mut self, output: Option<&str>) -> usize {
|
||||
let mut points = vec![];
|
||||
|
||||
while self.paint_next() {
|
||||
while let Some(next) = self.paint_next(output) {
|
||||
self = next;
|
||||
let cur = (self.robot_x, self.robot_y);
|
||||
if !points.contains(&cur) {
|
||||
points.push(cur);
|
||||
@@ -86,7 +105,12 @@ impl HullGrid {
|
||||
}
|
||||
|
||||
fn render(&self, file: &str) {
|
||||
self.data.save(file);
|
||||
match self.data.save(file) {
|
||||
Err(e) =>
|
||||
println!("Error saving file: {}", e),
|
||||
Ok(_) =>
|
||||
{}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -143,10 +167,9 @@ fn rotations_work() {
|
||||
|
||||
#[test]
|
||||
fn day11() {
|
||||
let mut day1a = HullGrid::new(1001, 1001, "inputs/day11");
|
||||
assert_eq!(2373, day1a.paint_hull());
|
||||
let day1a = HullGrid::new(1001, 1001, "inputs/day11");
|
||||
assert_eq!(2373, day1a.paint_hull(None));
|
||||
let mut day1b = HullGrid::new(1001, 1001, "inputs/day11");
|
||||
day1b.set_white();
|
||||
assert_eq!(249, day1b.paint_hull());
|
||||
day1b.render("day1.png");
|
||||
assert_eq!(249, day1b.paint_hull(None));
|
||||
}
|
||||
Reference in New Issue
Block a user