122 lines
3.4 KiB
Smalltalk
122 lines
3.4 KiB
Smalltalk
Object subclass: VM [
|
|
| A B C ip optable code |
|
|
|
|
copyFrom: vm [ code := vm code. ]
|
|
|
|
A:n[A:=n] B:n[B:=n] C:n[C:=n]
|
|
code: n [code := n]
|
|
code [^code]
|
|
|
|
parse: str
|
|
[ str scanf: ' Register A: %d Register B: %d Register C: %d Program: %s'
|
|
with: [
|
|
:a :b :c :prog |
|
|
A := a. B := b. C := c.
|
|
code := (prog tokenize: ',') collect: [:it | it asNumber].
|
|
] ]
|
|
|
|
printOn: st
|
|
[ st << ('A: %1, B: %2, C: %3, ip: %4' % {A. B. C. ip}) ]
|
|
|
|
initialize
|
|
[ ip := 1.
|
|
optable :=
|
|
{[:x|self adv: (self combo: x)].
|
|
[:x|self bxl: x].
|
|
[:x|self bst: (self combo: x)].
|
|
[:x|self jnz: x + 1].
|
|
[:x|self bxc: x].
|
|
[:x|self out: (self combo: x)].
|
|
[:x|self bdv: (self combo: x)].
|
|
[:x|self cdv: (self combo: x)]}. ]
|
|
|
|
combo: v
|
|
[ v <= 3 ifTrue: [^v].
|
|
v <= 5 ifTrue: [v = 4 ifTrue: [^A] ifFalse: [^B]]
|
|
ifFalse: [v = 6 ifTrue: [^C] ifFalse: [self error: 'Reserved']] ]
|
|
|
|
step
|
|
[ | insn arg |
|
|
insn := code at: ip.
|
|
arg := code at: ip + 1.
|
|
ip := ip + 2.
|
|
^(optable at: insn + 1) value: arg ]
|
|
|
|
run
|
|
[ ip := 1. [ ip < code size ] whileTrue: [ self step ] ]
|
|
|
|
"adv bxl bst jnz bxc out bdv cdv"
|
|
]
|
|
|
|
VM subclass: DisassVM [
|
|
| os |
|
|
initialize
|
|
[ super initialize.
|
|
A := 'A'. B := 'B'. C := 'C'.
|
|
os := stdout ]
|
|
output [nil]
|
|
adv:x [os<<('A <- A >> %1'%{x});nl]
|
|
bxl:x [os<<('B <- B ^ %1'%{x});nl]
|
|
bst:x [os<<('B <- %1 & 7'%{x});nl]
|
|
jnz:x [os<<('A != 0 ? goto %1'%{x});nl]
|
|
bxc:x [os<<('B <- B ^ C');nl]
|
|
out:x [os<<('output %1 & 7'%{x});nl]
|
|
bdv:x [os<<('B <- A >> %1'%{x});nl]
|
|
cdv:x [os<<('C <- A >> %1'%{x});nl]
|
|
]
|
|
|
|
VM subclass: InterpVM [
|
|
| out |
|
|
initialize [super initialize. out := OrderedCollection new.]
|
|
output [^out]
|
|
adv:x [A := A bitShift: x negated]
|
|
bxl:x [B := B bitXor: x]
|
|
bst:x [B := x bitAnd: 7]
|
|
jnz:x [A ~= 0 ifTrue: [ip := x]]
|
|
bxc:x [B := B bitXor: C]
|
|
out:x [out add: (x bitAnd: 7)]
|
|
bdv:x [B := A bitShift: x negated]
|
|
cdv:x [C := A bitShift: x negated]
|
|
]
|
|
|
|
InterpVM subclass: ForcingVM [
|
|
| output |
|
|
|
|
out: x [ output notNil ifTrue: [self error: 'Double output'].
|
|
output := x bitAnd: 7. ]
|
|
jnz: x [ ip > code size & x = 1 ifFalse: [self error: 'Bad jump'] ]
|
|
|
|
runWithA: a
|
|
[ A := a. output := nil. self run ]
|
|
|
|
forceOutputAt: idx withA: a
|
|
[ | requiredOut |
|
|
"The code looks something like this:
|
|
|
|
B <- A & 7
|
|
... mixing B with A and C
|
|
A <- A >> 3
|
|
output B & 7
|
|
A != 0 ? goto 1
|
|
|
|
So in each iteration, A is shifted by 3 bits, and one value is output.
|
|
Thus we simply go backwards starting with 0 trying each bit triple."
|
|
|
|
idx = 0 ifTrue: [^a].
|
|
requiredOut := code at: idx.
|
|
0 to: 7 do: [
|
|
:x | | testA |
|
|
testA := (a bitShift: 3) + x.
|
|
self runWithA: testA.
|
|
A ~= a ifTrue: [self error: 'Bad program'].
|
|
(output = requiredOut) ifTrue: [
|
|
(self forceOutputAt: idx - 1 withA: testA) ifNotNil:
|
|
[:res | ^res]]].
|
|
^nil ]
|
|
]
|
|
|
|
AOC input: [ InterpVM new parse: stdin contents ];
|
|
part1: [ :vm | vm run output chain collect: [:it | it displayString]; join: ',' ];
|
|
part2: [ :vm | ForcingVM new copyFrom: vm; forceOutputAt: vm code size withA: 0 ];
|
|
result: [ :vm :part | part value: vm ];
|
|
finish.
|