Skip to content

Instantly share code, notes, and snippets.

@svantelidman
Created December 11, 2019 18:44
Show Gist options
  • Select an option

  • Save svantelidman/95ee7d3a8f9c07fa7c8f4a49506ab4a5 to your computer and use it in GitHub Desktop.

Select an option

Save svantelidman/95ee7d3a8f9c07fa7c8f4a49506ab4a5 to your computer and use it in GitHub Desktop.
use std::io::{BufReader, BufRead};
use std::fs::File;
use std::collections::HashMap;
struct Machine {
prog_ptr: i64,
relative_base: i64,
program: Vec<i64>,
input: Vec<i64>,
output: Vec<i64>,
state: MachineState
}
#[derive(PartialEq)]
enum MachineState {
ReadyToRun,
WaitingForInput,
OutputAvailable,
Terminated
}
enum InstructionResult {
Continue,
WaitForInput,
OutputDelivered,
Invalid,
Exit
}
enum ParameterMode {
Position = 0,
Direct = 1,
Relative= 2
}
impl Machine {
fn resume(&mut self) {
loop {
let a = self.run_one_instruction();
match a {
InstructionResult::Continue => { self.state = MachineState::ReadyToRun },
InstructionResult::WaitForInput => { self.state = MachineState::WaitingForInput; break; },
InstructionResult::OutputDelivered => { self.state = MachineState::OutputAvailable; break; },
InstructionResult::Exit => { self.state = MachineState::Terminated; break; },
_ => panic!("Unexpected action."),
}
}
}
fn grow_storage_if_required(&mut self, location: i64) {
if location >= self.program.len() as i64 {
self.program.resize((location + 1) as usize, 0)
}
}
fn get(&mut self, location: i64) -> i64 {
self.grow_storage_if_required(location);
self.program[location as usize]
}
fn put(&mut self, location: i64, value: i64) {
self.grow_storage_if_required(location);
self.program[location as usize] = value;
}
fn next_cell(&mut self) -> i64 {
let ind = self.prog_ptr;
self.prog_ptr += 1;
self.get(ind)
}
fn get_parameter_value(&mut self, mode: i64) -> i64 {
let value = self.next_cell();
if mode == ParameterMode::Position as i64 {
return self.get(value)
} else if mode == ParameterMode::Direct as i64 {
return value
} else if mode == ParameterMode::Relative as i64 {
return self.get(value + self.relative_base)
} else {
panic!("Unknown parameter mode: {}", mode)
}
}
fn get_storage_location(&mut self, mode: i64) -> i64 {
if mode == ParameterMode::Relative as i64 {
self.next_cell() + self.relative_base
} else {
self.next_cell()
}
}
const ADD: i64 = 1;
const MULT: i64 = 2;
const INPUT: i64 = 3;
const OUTPUT: i64 = 4;
const JUMP_TRUE: i64 = 5;
const JUMP_FALSE: i64 = 6;
const LESS_THAN: i64 = 7;
const EQUALS: i64 = 8;
const RELATIVE_BASE_OFFSET: i64 = 9;
const EXIT: i64 = 99;
fn run_one_instruction(&mut self) -> InstructionResult {
let saved_prog_ptr = self.prog_ptr;
let next_instruction_code = self.next_cell();
let next_op_code = next_instruction_code % 100;
let mode_par_1 = (next_instruction_code / 100) % 10;
let mode_par_2 = (next_instruction_code / 1000) % 10;
let mode_par_3 = (next_instruction_code / 10000) % 10;
match next_op_code {
Machine::ADD => {
let t1 = self.get_parameter_value(mode_par_1);
let t2 = self.get_parameter_value(mode_par_2);
let store_at = self.get_storage_location(mode_par_3);
self.put(store_at, t1 + t2);
InstructionResult::Continue
},
Machine::MULT => {
let f1 = self.get_parameter_value(mode_par_1);
let f2 = self.get_parameter_value(mode_par_2);
let store_at = self.get_storage_location(mode_par_3);
self.put(store_at, f1 * f2);
InstructionResult::Continue
},
Machine::INPUT => {
if !self.input.is_empty() {
let store_at = self.get_storage_location(mode_par_1);
let c = self.input.remove(0);
self.put(store_at, c);
InstructionResult::Continue
} else {
self.prog_ptr = saved_prog_ptr;
InstructionResult::WaitForInput
}
},
Machine::JUMP_TRUE => {
let value = self.get_parameter_value(mode_par_1);
let jump_target = self.get_parameter_value(mode_par_2);
if value != 0 {
self.prog_ptr = jump_target
}
InstructionResult::Continue
},
Machine::JUMP_FALSE => {
let value = self.get_parameter_value(mode_par_1);
let jump_target = self.get_parameter_value(mode_par_2);
if value == 0 {
self.prog_ptr = jump_target
}
InstructionResult::Continue
},
Machine::LESS_THAN => {
let p1 = self.get_parameter_value(mode_par_1);
let p2 = self.get_parameter_value(mode_par_2);
let store_at = self.get_storage_location(mode_par_3);
let result = if p1 < p2 { 1 } else { 0 };
self.put(store_at, result);
InstructionResult::Continue
},
Machine::EQUALS => {
let p1 = self.get_parameter_value(mode_par_1);
let p2 = self.get_parameter_value(mode_par_2);
let store_at = self.get_storage_location(mode_par_3);
let result = if p1 == p2 { 1 } else { 0 };
self.put(store_at, result);
InstructionResult::Continue
},
Machine::OUTPUT => {
let v = self.get_parameter_value(mode_par_1);
self.output.push(v);
InstructionResult::OutputDelivered
},
Machine::RELATIVE_BASE_OFFSET => {
let v = self.get_parameter_value(mode_par_1);
self.relative_base += v;
InstructionResult::Continue
},
Machine::EXIT => InstructionResult::Exit,
_ => InstructionResult::Invalid
}
}
}
fn load_program(program_file: &String) -> Vec<i64> {
let mut prog: Vec<i64> = vec![];
let file = File::open(program_file).expect("Could not open program file!");
let reader = BufReader::new(file);
let line = reader.lines().next().expect("Could not read from file");
let line = line.expect("No string there.");
for s in line.split(",") {
let c = i64::from_str_radix(&s, 10).expect("Could not parse int");
prog.push(c);
}
prog
}
fn main() {
let program_file = "prog".to_string();
let program: Vec<i64> = load_program(&program_file);
let mut machine = Machine {
prog_ptr: 0,
relative_base: 0,
program: program.clone(),
input: vec!(),
output: vec!(),
state: MachineState::ReadyToRun
};
let mut robot = PaintingRobot {
position: (0,0),
state: RobotState::ReadyToPaint,
painted_boards: HashMap::new(),
direction: Direction::Up,
};
robot.paint_at_current_position(Color::White);
loop {
match machine.state {
MachineState::Terminated => break,
MachineState::OutputAvailable => {
match robot.state {
RobotState::ReadyToPaint => {
let color_value = machine.output.remove(0);
let color = match color_value {
0 => Color::Black,
1 => Color::White,
_ => panic!("Unexpected color code {}", color_value)
};
robot.paint_at_current_position(color as Color);
robot.state = RobotState::ReadyToTurn;
},
RobotState::ReadyToTurn => {
let turn_value = machine.output.remove(0);
let turn = match turn_value {
0 => TurnDirection::Left,
1 => TurnDirection::Right,
_ => panic!("Unexcpected turn code {}", turn_value)
};
robot.turn_and_move(turn);
robot.state = RobotState::ReadyToPaint;
}
}
},
MachineState::WaitingForInput => {
machine.input.push(robot.read_color_at_current_position() as i64);
},
MachineState::ReadyToRun => {},
}
machine.resume()
}
let n_painted = robot.painted_boards.iter().count();
println!("Number of painted boards: {}", n_painted);
draw_call_sign(&robot.painted_boards);
}
fn draw_call_sign(painted_board: &HashMap<(i64, i64), Color>) {
let min_x = painted_board.keys().fold(std::i64::MAX, |acc, k| std::cmp::min(acc, k.0));
let max_x = painted_board.keys().fold(std::i64::MIN, |acc, k| std::cmp::max(acc, k.0));
let min_y = painted_board.keys().fold(std::i64::MAX, |acc, k| std::cmp::min(acc, k.1));
let max_y = painted_board.keys().fold(std::i64::MIN, |acc, k| std::cmp::max(acc, k.1));
for y in min_y..=max_y {
for x in min_x..=max_x {
let color_char = match painted_board.get(&(x, y)) {
Some(color) => char_for_color(color),
None => char_for_color(&Color::Black)
};
print!("{}", color_char)
}
println!();
}
}
fn char_for_color(color: &Color) -> char {
match color {
Color::White => '#',
Color::Black => ' '
}
}
enum Direction {
Up = 0,
Down = 1,
Left = 2,
Right = 3
}
enum TurnDirection {
Left = 0,
Right = 1
}
#[derive(Copy, Clone)]
enum Color {
Black = 0,
White = 1
}
enum RobotState {
ReadyToPaint,
ReadyToTurn
}
struct PaintingRobot {
position: (i64, i64),
state: RobotState,
painted_boards: HashMap<(i64, i64), Color>,
direction: Direction
}
impl PaintingRobot {
fn read_color_at_current_position(&self) -> Color {
match self.painted_boards.get(&self.position) {
Some(color) => *color,
None => Color::Black
}
}
fn paint_at_current_position(&mut self, color: Color) {
self.painted_boards.insert(self.position, color);
}
fn turn_and_move(&mut self, turn: TurnDirection) {
self.direction = match self.direction {
Direction::Up => {
match turn {
TurnDirection::Left => Direction::Left,
TurnDirection::Right => Direction::Right
}
},
Direction::Down => {
match turn {
TurnDirection::Left => Direction::Right,
TurnDirection::Right => Direction::Left
}
},
Direction::Left => {
match turn {
TurnDirection::Left => Direction::Down,
TurnDirection::Right => Direction::Up
}
},
Direction::Right => {
match turn {
TurnDirection::Left => Direction::Up,
TurnDirection::Right => Direction::Down
}
}
};
let (x, y) = self.position;
self.position = match self.direction {
Direction::Up => (x, y - 1),
Direction::Down => (x, y + 1),
Direction::Left => (x - 1, y),
Direction::Right => (x + 1, y)
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment