11//! Assembunny interpreter.
22//!
3- //! See [`Day12`](crate::Day12) and [`Day23`](crate::Day23).
3+ //! See [`Day12`](crate::Day12), [`Day23`](crate::Day23) and [`Day25`](crate::Day25 ).
44
5- use std:: marker :: PhantomData ;
5+ use std:: ops :: ControlFlow ;
66use utils:: prelude:: * ;
77
8- pub ( crate ) trait InterpreterConfig {
9- const SUPPORTS_TOGGLE : bool = false ;
10- }
11-
128#[ derive( Clone , Debug ) ]
13- pub ( crate ) struct Interpreter < C : InterpreterConfig > {
9+ pub ( crate ) struct Interpreter < const TGL : bool , const OUT : bool > {
1410 instructions : Vec < Instruction > ,
15- phantom : PhantomData < C > ,
1611}
1712
1813#[ derive( Copy , Clone , PartialEq , Eq , Debug ) ]
@@ -36,10 +31,11 @@ enum Instruction {
3631 Decrement ( Register ) ,
3732 JumpIfNotZero ( Value , Value ) ,
3833 Toggle ( Register ) ,
34+ Out ( Register ) ,
3935 Invalid2 ( Value , Value ) ,
4036}
4137
42- impl < C : InterpreterConfig > Interpreter < C > {
38+ impl < const TGL : bool , const OUT : bool > Interpreter < TGL , OUT > {
4339 pub fn new ( input : & str ) -> Result < Self , InputError > {
4440 let register = parser:: one_of ( (
4541 b'a' . map ( |_| Register :: A ) ,
@@ -64,117 +60,172 @@ impl<C: InterpreterConfig> Interpreter<C> {
6460 . then ( value. with_prefix ( " " ) )
6561 . map ( |( v, o) | Instruction :: JumpIfNotZero ( v, o) ) ,
6662 register. with_prefix ( "tgl " ) . map_res ( |r| {
67- if C :: SUPPORTS_TOGGLE {
63+ if TGL {
6864 Ok ( Instruction :: Toggle ( r) )
6965 } else {
70- Err ( "toggle instruction not supported" )
66+ Err ( "tgl instruction not supported" )
67+ }
68+ } ) ,
69+ register. with_prefix ( "out " ) . map_res ( |r| {
70+ if OUT {
71+ Ok ( Instruction :: Out ( r) )
72+ } else {
73+ Err ( "out instruction not supported" )
7174 }
7275 } ) ,
7376 ) )
7477 . parse_lines ( input) ?,
75- phantom : PhantomData ,
7678 } )
7779 }
80+ }
81+
82+ impl < const TGL : bool > Interpreter < TGL , false > {
83+ pub fn execute ( & self , reg : [ i32 ; 4 ] ) -> i32 {
84+ execute ( & self . instructions , reg, |_, _| unreachable ! ( ) )
85+ }
86+ }
87+
88+ impl < const TGL : bool > Interpreter < TGL , true > {
89+ pub fn execute (
90+ & self ,
91+ reg : [ i32 ; 4 ] ,
92+ out_fn : impl FnMut ( i32 , ( usize , [ i32 ; 4 ] ) ) -> ControlFlow < ( ) > ,
93+ ) -> i32 {
94+ execute ( & self . instructions , reg, out_fn)
95+ }
96+ }
97+
98+ #[ inline]
99+ fn execute (
100+ instructions : & [ Instruction ] ,
101+ mut reg : [ i32 ; 4 ] ,
102+ mut out_fn : impl FnMut ( i32 , ( usize , [ i32 ; 4 ] ) ) -> ControlFlow < ( ) > ,
103+ ) -> i32 {
104+ let mut pc = 0 ;
105+
106+ let mut instructions = instructions. to_vec ( ) ;
107+ while pc < instructions. len ( ) {
108+ #[ rustfmt:: skip] // Rustfmt wants each pattern to be on a single really long line
109+ match instructions[ pc..] {
110+ // Recognize the following pattern of instructions which can be simplified to addition
111+ // inc $x
112+ // dec $y
113+ // jnz $y -2
114+ // This is the key optimization for Day 12
115+ [
116+ Instruction :: Increment ( x) ,
117+ Instruction :: Decrement ( y) ,
118+ Instruction :: JumpIfNotZero ( Value :: Register ( y2) , Value :: Number ( -2 ) ) ,
119+ ..
120+ ] if y == y2 => {
121+ reg[ x as usize ] += reg[ y as usize ] ;
122+ reg[ y as usize ] = 0 ;
123+ pc += 3 ;
124+ continue ;
125+ }
126+ // Recognize the following pattern of instructions which can be simplified to multiplication
127+ // cpy $w $x
128+ // inc $y
129+ // dec $x
130+ // jnz $x -2
131+ // dec $z
132+ // jnz $z -5
133+ // This is the key optimisation for Day 23
134+ [
135+ Instruction :: Copy ( Value :: Register ( w) , x) ,
136+ Instruction :: Increment ( y) ,
137+ Instruction :: Decrement ( x2) ,
138+ Instruction :: JumpIfNotZero ( Value :: Register ( x3) , Value :: Number ( -2 ) ) ,
139+ Instruction :: Decrement ( z) ,
140+ Instruction :: JumpIfNotZero ( Value :: Register ( z2) , Value :: Number ( -5 ) ) ,
141+ ..
142+ ] if x == x2 && x == x3 && z == z2 => {
143+ reg[ y as usize ] = reg[ w as usize ] * reg[ z as usize ] ;
144+ reg[ x as usize ] = 0 ;
145+ reg[ z as usize ] = 0 ;
146+ pc += 6 ;
147+ continue ;
148+ }
149+ // Recognize the following pattern of instructions which can be simplified to division
150+ // cpy $N $x
151+ // jnz $y 2
152+ // jnz 1 6
153+ // dec $y
154+ // dec $x
155+ // jnz $x -4
156+ // inc $z
157+ // jnz 1 -7
158+ // This is the key optimisation for Day 25
159+ [
160+ Instruction :: Copy ( Value :: Number ( n) , x) ,
161+ Instruction :: JumpIfNotZero ( Value :: Register ( y) , Value :: Number ( 2 ) ) ,
162+ Instruction :: JumpIfNotZero ( Value :: Number ( 1 ) , Value :: Number ( 6 ) ) ,
163+ Instruction :: Decrement ( y2) ,
164+ Instruction :: Decrement ( x2) ,
165+ Instruction :: JumpIfNotZero ( Value :: Register ( x3) , Value :: Number ( -4 ) ) ,
166+ Instruction :: Increment ( z) ,
167+ Instruction :: JumpIfNotZero ( Value :: Number ( 1 ) , Value :: Number ( -7 ) ) ,
168+ ..
169+ ] if x == x2 && x == x3 && y == y2 => {
170+ reg[ z as usize ] += reg[ y as usize ] / n;
171+ reg[ x as usize ] = n - ( reg[ y as usize ] % n) ;
172+ reg[ y as usize ] = 0 ;
173+ pc += 8 ;
174+ continue ;
175+ }
176+ _ => { }
177+ } ;
78178
79- pub fn execute ( & self , mut reg : [ i32 ; 4 ] ) -> i32 {
80- let mut pc = 0 ;
81-
82- let mut instructions = self . instructions . clone ( ) ;
83- while pc < instructions. len ( ) {
84- #[ rustfmt:: skip] // Rustfmt wants each pattern to be on a single really long line
85- match instructions[ pc..] {
86- // Recognize the following pattern of instructions which can be simplified to addition
87- // inc $x
88- // dec $y
89- // jnz $y -2
90- // This is the key optimization for Day 12
91- [
92- Instruction :: Increment ( x) ,
93- Instruction :: Decrement ( y) ,
94- Instruction :: JumpIfNotZero ( Value :: Register ( y2) , Value :: Number ( -2 ) ) ,
95- ..
96- ] if y == y2 => {
97- reg[ x as usize ] += reg[ y as usize ] ;
98- reg[ y as usize ] = 0 ;
99- pc += 3 ;
179+ match instructions[ pc] {
180+ Instruction :: Copy ( v, dst) => reg[ dst as usize ] = v. get ( & reg) ,
181+ Instruction :: Increment ( dst) => reg[ dst as usize ] += 1 ,
182+ Instruction :: Decrement ( dst) => reg[ dst as usize ] -= 1 ,
183+ Instruction :: JumpIfNotZero ( v, offset) => {
184+ if v. get ( & reg) != 0 {
185+ let offset = offset. get ( & reg) ;
186+ let Some ( new_pc) = pc. checked_add_signed ( offset as isize ) else {
187+ break ;
188+ } ;
189+ pc = new_pc;
100190 continue ;
101191 }
102- // Recognize the following pattern of instructions which can be simplified to multiplication
103- // cpy $w $x
104- // inc $y
105- // dec $x
106- // jnz $x -2
107- // dec $z
108- // jnz $z -5
109- // This is the key optimisation for Day 23
110- [
111- Instruction :: Copy ( Value :: Register ( w) , x) ,
112- Instruction :: Increment ( y) ,
113- Instruction :: Decrement ( x2) ,
114- Instruction :: JumpIfNotZero ( Value :: Register ( x3) , Value :: Number ( -2 ) ) ,
115- Instruction :: Decrement ( z) ,
116- Instruction :: JumpIfNotZero ( Value :: Register ( z2) , Value :: Number ( -5 ) ) ,
117- ..
118- ] if x == x2 && x == x3 && z == z2 => {
119- reg[ y as usize ] = reg[ w as usize ] * reg[ z as usize ] ;
120- reg[ x as usize ] = 0 ;
121- reg[ z as usize ] = 0 ;
122- pc += 6 ;
123- continue ;
192+ }
193+ Instruction :: Toggle ( r) => ' toggle: {
194+ let Some ( index) = pc. checked_add_signed ( reg[ r as usize ] as isize ) else {
195+ break ' toggle;
196+ } ;
197+ if index >= instructions. len ( ) {
198+ break ' toggle;
124199 }
125- _ => { }
126- } ;
127-
128- match instructions[ pc] {
129- Instruction :: Copy ( v, dst) => reg[ dst as usize ] = v. get ( & reg) ,
130- Instruction :: Increment ( dst) => reg[ dst as usize ] += 1 ,
131- Instruction :: Decrement ( dst) => reg[ dst as usize ] -= 1 ,
132- Instruction :: JumpIfNotZero ( v, offset) => {
133- if v. get ( & reg) != 0 {
134- let offset = offset. get ( & reg) ;
135- let Some ( new_pc) = pc. checked_add_signed ( offset as isize ) else {
136- break ;
137- } ;
138- pc = new_pc;
139- continue ;
200+
201+ instructions[ index] = match instructions[ index] {
202+ Instruction :: Increment ( r) => Instruction :: Decrement ( r) ,
203+ Instruction :: Decrement ( r) => Instruction :: Increment ( r) ,
204+ Instruction :: Toggle ( r) => Instruction :: Increment ( r) ,
205+ Instruction :: Out ( r) => Instruction :: Increment ( r) ,
206+ Instruction :: JumpIfNotZero ( v, Value :: Register ( r) ) => Instruction :: Copy ( v, r) ,
207+ Instruction :: JumpIfNotZero ( v, o @ Value :: Number ( _) ) => {
208+ Instruction :: Invalid2 ( v, o)
140209 }
141- }
142- Instruction :: Toggle ( r) => ' toggle: {
143- let Some ( index) = pc. checked_add_signed ( reg[ r as usize ] as isize ) else {
144- break ' toggle;
145- } ;
146- if index >= instructions. len ( ) {
147- break ' toggle;
210+ Instruction :: Copy ( v, r) => Instruction :: JumpIfNotZero ( v, Value :: Register ( r) ) ,
211+ Instruction :: Invalid2 ( v1, v2) => {
212+ // Effectively an invalid copy instruction
213+ Instruction :: JumpIfNotZero ( v1, v2)
148214 }
149-
150- instructions[ index] = match instructions[ index] {
151- Instruction :: Increment ( r) => Instruction :: Decrement ( r) ,
152- Instruction :: Decrement ( r) | Instruction :: Toggle ( r) => {
153- Instruction :: Increment ( r)
154- }
155- Instruction :: JumpIfNotZero ( v, Value :: Register ( r) ) => {
156- Instruction :: Copy ( v, r)
157- }
158- Instruction :: JumpIfNotZero ( v, o @ Value :: Number ( _) ) => {
159- Instruction :: Invalid2 ( v, o)
160- }
161- Instruction :: Copy ( v, r) => {
162- Instruction :: JumpIfNotZero ( v, Value :: Register ( r) )
163- }
164- Instruction :: Invalid2 ( v1, v2) => {
165- // Effectively an invalid copy instruction
166- Instruction :: JumpIfNotZero ( v1, v2)
167- }
168- } ;
215+ } ;
216+ }
217+ Instruction :: Out ( r) => {
218+ if out_fn ( reg[ r as usize ] , ( pc, reg) ) . is_break ( ) {
219+ break ;
169220 }
170- Instruction :: Invalid2 ( _, _) => { }
171221 }
172-
173- pc += 1 ;
222+ Instruction :: Invalid2 ( _, _) => { }
174223 }
175224
176- reg [ 0 ]
225+ pc += 1 ;
177226 }
227+
228+ reg[ 0 ]
178229}
179230
180231impl Value {
0 commit comments