Switch to a resumption-style implementation for machines.

This commit is contained in:
2019-12-23 15:33:32 -08:00
parent e479b8ef62
commit 9acf32d696
5 changed files with 380 additions and 344 deletions

View File

@@ -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);
}

View File

@@ -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;
}
}
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]);
}

View File

@@ -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);
}
}
}

View File

@@ -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,28 +114,32 @@ 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 {
@@ -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) => {
let response = MoveResult::new(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);
match response {
MoveResult::HitWall => {
self.set(tx, ty, Tile::Wall);
false
}
MoveResult::Done => {
self.set(tx, ty, Tile::Empty);
self.x = tx;
self.y = ty;
true
}
MoveResult::FoundSystem => {
self.set(tx, ty, Tile::Oxygen);
self.x = tx;
self.y = ty;
true
self.computer = next;
match response {
MoveResult::HitWall => {
self.set(tx, ty, Tile::Wall);
return (self, false);
}
MoveResult::Done => {
self.set(tx, ty, Tile::Empty);
self.x = tx;
self.y = ty;
return (self, true);
}
MoveResult::FoundSystem => {
self.set(tx, ty, Tile::Oxygen);
self.x = tx;
self.y = ty;
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());
}

View File

@@ -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() }
self.robot_dir = if rotation == 0 {
self.robot_dir.rotate_right()
} else {
self.robot_dir.rotate_left()
};
self.step();
true
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();
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));
}