2019-12-15 16:16:20 +01:00
#[ derive(Clone) ]
2019-12-11 18:59:20 +01:00
pub struct IntComputer {
2019-12-09 19:18:20 +01:00
pub pos : i64 ,
pub tape : Vec < i64 > ,
pub params : Vec < i64 > ,
2019-12-09 20:28:24 +01:00
relative_base : i64 ,
2019-12-13 10:20:29 +01:00
cmd_buffer : Vec < Operation > ,
2019-12-09 19:18:20 +01:00
}
2019-12-11 18:59:20 +01:00
pub enum IntComputerResult {
Halt ,
Output ( i64 ) ,
2019-12-13 10:44:13 +01:00
Continue ,
2019-12-11 18:59:20 +01:00
}
2019-12-12 22:52:13 +01:00
impl IntComputerResult {
pub fn unwrap ( self ) -> i64 {
match self {
IntComputerResult ::Halt = > panic! ( " Attempted to get value of halt operation " ) ,
IntComputerResult ::Output ( o ) = > o ,
2019-12-13 10:44:13 +01:00
IntComputerResult ::Continue = > panic! ( " Attempted to get value of non-output operation " ) ,
2019-12-12 22:52:13 +01:00
}
}
}
2019-12-11 18:59:20 +01:00
impl IntComputer {
2019-12-09 20:28:24 +01:00
pub fn new ( tape : Vec < i64 > , pos : i64 , params : Vec < i64 > ) -> Self {
2019-12-11 18:59:20 +01:00
IntComputer {
2019-12-09 20:28:24 +01:00
pos ,
tape ,
params ,
relative_base : 0 ,
2019-12-13 10:20:29 +01:00
cmd_buffer : Vec ::new ( ) ,
2019-12-09 20:28:24 +01:00
}
}
2019-12-13 10:44:13 +01:00
pub fn step ( & mut self ) -> IntComputerResult {
match self . cmd_buffer . pop ( ) . unwrap_or_else ( | | self . decode_next ( ) ) {
2019-12-13 19:36:47 +01:00
Operation ::Halt { } = > IntComputerResult ::Halt ,
2019-12-13 10:44:13 +01:00
op = > {
2019-12-13 19:36:47 +01:00
if let Some ( o ) = self . execute ( op ) {
IntComputerResult ::Output ( o )
2019-12-13 10:44:13 +01:00
} else {
2019-12-13 19:36:47 +01:00
IntComputerResult ::Continue
2019-12-13 10:44:13 +01:00
}
}
}
}
2019-12-11 18:59:20 +01:00
pub fn run ( & mut self ) -> IntComputerResult {
2019-12-09 19:18:20 +01:00
loop {
2019-12-13 10:44:13 +01:00
match self . step ( ) {
IntComputerResult ::Halt = > return IntComputerResult ::Halt ,
IntComputerResult ::Output ( o ) = > return IntComputerResult ::Output ( o ) ,
IntComputerResult ::Continue = > ( ) ,
2019-12-09 19:18:20 +01:00
}
}
}
2019-12-13 08:24:01 +01:00
pub fn get_all_outputs ( & mut self ) -> Vec < i64 > {
let mut outputs = Vec ::new ( ) ;
while let IntComputerResult ::Output ( o ) = self . run ( ) {
outputs . push ( o ) ;
}
outputs
}
2019-12-13 10:33:16 +01:00
pub fn peek_operation ( & mut self ) -> Operation {
if self . cmd_buffer . is_empty ( ) {
let next = self . decode_next ( ) ;
self . cmd_buffer . push ( next ) ;
}
self . cmd_buffer [ 0 ] . clone ( )
}
2019-12-09 20:28:24 +01:00
#[ rustfmt::skip ]
2019-12-09 19:18:20 +01:00
fn get_next ( & mut self , mode : Mode ) -> i64 {
2019-12-09 23:11:59 +01:00
let value = * self . tape . get ( self . pos as usize ) . unwrap ( ) ;
2019-12-09 19:18:20 +01:00
let next = match mode {
2019-12-09 20:28:24 +01:00
Mode ::Position = > * self . tape . get ( value as usize ) . unwrap_or ( & 0 ) ,
2019-12-09 19:18:20 +01:00
Mode ::Immediate = > value ,
2019-12-09 23:11:59 +01:00
Mode ::Relative = > * self . tape . get ( ( value + self . relative_base ) as usize ) . unwrap_or ( & 0 ) ,
2019-12-09 19:18:20 +01:00
} ;
self . pos + = 1 ;
next
}
2019-12-09 23:11:59 +01:00
#[ rustfmt::skip ]
fn get_next_address ( & mut self , mode : Mode ) -> i64 {
let value = match mode {
Mode ::Position = > * self . tape . get ( self . pos as usize ) . unwrap_or ( & 0 ) ,
Mode ::Immediate = > unreachable! ( ) ,
Mode ::Relative = > * self . tape . get ( self . pos as usize ) . unwrap_or ( & 0 ) + self . relative_base ,
} ;
self . pos + = 1 ;
value
}
2019-12-13 10:20:29 +01:00
fn decode_next ( & mut self ) -> Operation {
2019-12-09 19:18:20 +01:00
let next = self . get_next ( Mode ::Immediate ) ;
2019-12-09 20:28:24 +01:00
let mut full_opcode : Vec < _ > = next . to_string ( ) . chars ( ) . collect ( ) ;
full_opcode . reverse ( ) ;
let raw_opcode = [
full_opcode . get ( 1 ) . unwrap_or ( & '0' ) . to_owned ( ) ,
full_opcode [ 0 ] ,
] ;
match raw_opcode {
2019-12-13 10:20:29 +01:00
[ '0' , '1' ] = > Operation ::Add {
2019-12-09 20:28:24 +01:00
x : self . get_next ( get_mode ( & full_opcode , ParameterPosition ::First ) ) ,
y : self . get_next ( get_mode ( & full_opcode , ParameterPosition ::Second ) ) ,
2019-12-09 23:11:59 +01:00
addr : self . get_next_address ( get_mode ( & full_opcode , ParameterPosition ::Third ) )
as usize ,
2019-12-13 10:20:29 +01:00
} ,
[ '0' , '2' ] = > Operation ::Multiply {
2019-12-09 20:28:24 +01:00
x : self . get_next ( get_mode ( & full_opcode , ParameterPosition ::First ) ) ,
y : self . get_next ( get_mode ( & full_opcode , ParameterPosition ::Second ) ) ,
2019-12-09 23:11:59 +01:00
addr : self . get_next_address ( get_mode ( & full_opcode , ParameterPosition ::Third ) )
as usize ,
2019-12-13 10:20:29 +01:00
} ,
[ '0' , '3' ] = > Operation ::Input {
2019-12-09 19:18:20 +01:00
value : self . params . pop ( ) . unwrap ( ) ,
2019-12-09 23:11:59 +01:00
addr : self . get_next_address ( get_mode ( & full_opcode , ParameterPosition ::First ) )
as usize ,
2019-12-13 10:20:29 +01:00
} ,
[ '0' , '4' ] = > Operation ::Output {
2019-12-09 20:28:24 +01:00
value : self . get_next ( get_mode ( & full_opcode , ParameterPosition ::First ) ) ,
2019-12-13 10:20:29 +01:00
} ,
[ '0' , '5' ] = > Operation ::JumpIfTrue {
2019-12-09 20:28:24 +01:00
value : self . get_next ( get_mode ( & full_opcode , ParameterPosition ::First ) ) ,
addr : self . get_next ( get_mode ( & full_opcode , ParameterPosition ::Second ) ) as usize ,
2019-12-13 10:20:29 +01:00
} ,
[ '0' , '6' ] = > Operation ::JumpIfFalse {
2019-12-09 20:28:24 +01:00
value : self . get_next ( get_mode ( & full_opcode , ParameterPosition ::First ) ) ,
addr : self . get_next ( get_mode ( & full_opcode , ParameterPosition ::Second ) ) as usize ,
2019-12-13 10:20:29 +01:00
} ,
[ '0' , '7' ] = > Operation ::LessThan {
2019-12-09 20:28:24 +01:00
first : self . get_next ( get_mode ( & full_opcode , ParameterPosition ::First ) ) ,
second : self . get_next ( get_mode ( & full_opcode , ParameterPosition ::Second ) ) ,
2019-12-09 23:11:59 +01:00
addr : self . get_next_address ( get_mode ( & full_opcode , ParameterPosition ::Third ) )
as usize ,
2019-12-13 10:20:29 +01:00
} ,
[ '0' , '8' ] = > Operation ::Equals {
2019-12-09 20:28:24 +01:00
first : self . get_next ( get_mode ( & full_opcode , ParameterPosition ::First ) ) ,
second : self . get_next ( get_mode ( & full_opcode , ParameterPosition ::Second ) ) ,
2019-12-09 23:11:59 +01:00
addr : self . get_next_address ( get_mode ( & full_opcode , ParameterPosition ::Third ) )
as usize ,
2019-12-13 10:20:29 +01:00
} ,
[ '0' , '9' ] = > Operation ::AdjustRelativeBase {
2019-12-09 20:28:24 +01:00
value : self . get_next ( get_mode ( & full_opcode , ParameterPosition ::First ) ) ,
2019-12-13 10:20:29 +01:00
} ,
[ '9' , '9' ] = > Operation ::Halt { } ,
2019-12-09 23:11:59 +01:00
_ = > unreachable! ( " Unknown opcode " ) ,
2019-12-09 19:18:20 +01:00
}
}
fn execute ( & mut self , op : Operation ) -> Option < i64 > {
2019-12-09 20:28:24 +01:00
fn safe_write ( tape : & mut Vec < i64 > , index : usize , value : i64 ) {
if tape . len ( ) < index + 1 {
tape . resize ( index + 1 , 0 ) ;
2019-12-09 19:18:20 +01:00
}
2019-12-09 20:28:24 +01:00
tape [ index ] = value ;
}
match op {
Operation ::Add { x , y , addr } = > safe_write ( & mut self . tape , addr , x + y ) ,
Operation ::Multiply { x , y , addr } = > safe_write ( & mut self . tape , addr , x * y ) ,
Operation ::Input { value , addr } = > safe_write ( & mut self . tape , addr , value ) ,
Operation ::AdjustRelativeBase { value } = > self . relative_base + = value ,
2019-12-09 19:18:20 +01:00
Operation ::JumpIfTrue { value , addr } = > {
if value ! = 0 {
2019-12-09 20:28:24 +01:00
self . pos = addr as i64
2019-12-09 19:18:20 +01:00
}
}
Operation ::JumpIfFalse { value , addr } = > {
if value = = 0 {
2019-12-09 20:28:24 +01:00
self . pos = addr as i64
2019-12-09 19:18:20 +01:00
}
}
Operation ::LessThan {
first ,
second ,
addr ,
2019-12-09 20:28:24 +01:00
} = > self . tape [ addr as usize ] = ( first < second ) as i64 ,
2019-12-09 19:18:20 +01:00
Operation ::Equals {
first ,
second ,
addr ,
2019-12-09 20:28:24 +01:00
} = > safe_write ( & mut self . tape , addr , ( first = = second ) as i64 ) ,
Operation ::Output { value } = > {
2019-12-09 23:11:59 +01:00
return Some ( value ) ;
}
2019-12-13 10:20:29 +01:00
Operation ::Halt { } = > unreachable! ( ) ,
2019-12-09 20:28:24 +01:00
} ;
None
2019-12-09 19:18:20 +01:00
}
}
2019-12-09 23:28:45 +01:00
#[ inline ]
2019-12-09 19:18:20 +01:00
fn get_mode ( raw_opcode : & [ char ] , pos : ParameterPosition ) -> Mode {
2019-12-09 23:28:45 +01:00
raw_opcode . get ::< usize > ( pos . into ( ) ) . unwrap_or ( & '0' ) . into ( )
2019-12-09 19:18:20 +01:00
}
2019-12-13 10:20:29 +01:00
#[ derive(Debug, Clone) ]
2019-12-13 10:33:16 +01:00
pub enum Operation {
2019-12-09 23:11:59 +01:00
Add {
x : i64 ,
y : i64 ,
addr : usize ,
} ,
Multiply {
x : i64 ,
y : i64 ,
addr : usize ,
} ,
Input {
value : i64 ,
addr : usize ,
} ,
Output {
value : i64 ,
} ,
JumpIfTrue {
value : i64 ,
addr : usize ,
} ,
JumpIfFalse {
value : i64 ,
addr : usize ,
} ,
LessThan {
first : i64 ,
second : i64 ,
addr : usize ,
} ,
Equals {
first : i64 ,
second : i64 ,
addr : usize ,
} ,
AdjustRelativeBase {
value : i64 ,
} ,
2019-12-13 10:20:29 +01:00
Halt { } ,
2019-12-09 19:18:20 +01:00
}
#[ derive(Debug) ]
enum Mode {
Immediate ,
Position ,
2019-12-09 20:28:24 +01:00
Relative ,
2019-12-09 19:18:20 +01:00
}
enum ParameterPosition {
First ,
Second ,
2019-12-09 23:11:59 +01:00
Third ,
2019-12-09 19:18:20 +01:00
}
impl Into < usize > for ParameterPosition {
fn into ( self ) -> usize {
match self {
ParameterPosition ::First = > 2 ,
ParameterPosition ::Second = > 3 ,
2019-12-09 23:11:59 +01:00
ParameterPosition ::Third = > 4 ,
2019-12-09 19:18:20 +01:00
}
}
}
impl Into < Mode > for & char {
fn into ( self ) -> Mode {
match self {
'0' = > Mode ::Position ,
'1' = > Mode ::Immediate ,
2019-12-09 20:28:24 +01:00
'2' = > Mode ::Relative ,
2019-12-09 19:18:20 +01:00
_ = > unreachable! ( ) ,
}
}
}