summaryrefslogtreecommitdiff
path: root/2020/rust/day08/src/intcode/mod.rs
blob: 5ec50e3127e65d0768024d0c9b98aa1d2bf24aa4 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
use std::convert::TryFrom;
use std::error::Error;
use std::fmt;

const DEBUG: bool = false;

#[derive(Clone,Debug)]
pub enum Instruction {
    Acc(Wordsize),
    Jmp(Wordsize),
    Nop(Wordsize),
}

#[derive(Debug)]
pub enum InstructionError {
    InvalidOperation,
    InvalidArgument,
    Parse(std::num::ParseIntError),
}

impl fmt::Display for InstructionError {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "InstructionError")
    }
}

impl Error for InstructionError { }

impl TryFrom<String> for Instruction {
    type Error = InstructionError;

    fn try_from(string: String) -> Result<Self, Self::Error> {
        Instruction::try_from(string.as_str())
    }
}

impl TryFrom<&str> for Instruction {
    type Error = InstructionError;

    fn try_from(string: &str) -> Result<Self, Self::Error> {
        let mut iter = string.split(" ");
        let (op, arg) = (iter.next().ok_or(InstructionError::InvalidOperation)?,
            iter.next().ok_or(InstructionError::InvalidArgument)?);
        let num = arg.parse().map_err(InstructionError::Parse)?;
        match op {
            "nop" => Ok(Instruction::Nop(num)),
            "jmp" => Ok(Instruction::Jmp(num)),
            "acc" => Ok(Instruction::Acc(num)),
            _ => Err(InstructionError::InvalidOperation),
        }
    }
}

pub type Wordsize = i128;

#[derive(Clone,Debug)]
pub struct Registers {
    pub ip: usize,
    pub accumulator: Wordsize,
}

pub struct Intcode {
  program: Vec<Instruction>,
  registers: Registers,
}

impl Intcode {
    pub fn new() -> Intcode {
        Intcode {
            program: vec![],
            registers: Registers {
                accumulator: 0,
                ip: 0,
            }
        }
    }

    fn op_acc(&mut self, arg: &Wordsize) {
        self.registers.accumulator += arg;
    }

    fn op_jmp(&mut self, arg: &Wordsize) {
        if *arg >= 0 {
            self.registers.ip += *arg as usize;
        } else {
            self.registers.ip -= (*arg * -1) as usize;
        }
    }

    pub fn load(&mut self, program: &[Instruction]) {
        if DEBUG {
            eprintln!("{:?}", program);
        }
        self.program = program.into();
    }

    pub fn regdump(&self) -> Registers {
        self.registers.clone()
    }

    pub fn step(&mut self) -> Result<(), IntcodeError>{
        let op = match self.program.get(self.registers.ip) {
            Some(v) => v.clone(),
            None => return Err(IntcodeError::MemoryAccessViolation),
        };
        if DEBUG {
            eprintln!("ip: {}, op: {:?}", self.registers.ip, op);
        }
        match op {
            Instruction::Acc(arg) => self.op_acc(&arg),
            Instruction::Jmp(arg) => self.op_jmp(&arg),
            Instruction::Nop(_) => {},
        }
        match op {
            Instruction::Jmp(_) => {},
            _ => self.registers.ip += 1,
        }

        Ok(())
    }
}

#[derive(Debug)]
pub enum IntcodeError {
    MemoryAccessViolation,
}

impl fmt::Display for IntcodeError {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "IntcodeError")
    }
}

impl Error for IntcodeError { }