5.3 KiB
Day 2: Build an Opcode Computer
A program looks like this:
1,5,6,7,
99,
1,2,0
Each line here is an operation. The first number represents a function and the rest of the values are what to do with it. So for example,
First | Second | Third | Fourth |
---|---|---|---|
1 (add) | first address to add | second address to add | address to store output |
2 (multiply) | first address to multiply | second address to multiply | address to store output |
99 (halt) | n/a | n/a | n/a |
So our program above would add the values in position 5 and 6 and write them to position 7, then halt. The result in position 7 should be 3. We can interpret values using this interpreter:
(WARNING: this code sample is no longer valid for only day 2... it still works but it's way more complex that is necessary to solve the problem. It gets extended over the course of the month.)
.> view aoc.intcode.runProgram
aoc.intcode.runProgram : ProgramState -> ProgramState
aoc.intcode.runProgram = runProgramAt 0
.> view aoc.intcode.runProgramAt
aoc.intcode.runProgramAt :
Nat ->{𝕖} ProgramState ->{𝕖} ProgramState
aoc.intcode.runProgramAt at programState =
use List drop
use aoc.intcode runProgramAt
nextInstructions = drop at (memory programState)
opCode =
Optional.map
OpCode.fromInt (intcode.head nextInstructions)
paramValues = drop 1 nextInstructions
case (opCode, paramValues) of
(Some (Add mode1 mode2), [ptr1, ptr2, ptrDest] ++ _) ->
num1 = dereference ptr1 mode1 programState
num2 = dereference ptr2 mode2 programState
runProgramAt
(at Nat.+ 4)
(memory.modify
(replace (truncate0 ptrDest) (num1 Int.+ num2))
programState)
(Some (Multiply mode1 mode2), [ptr1, ptr2, ptrDest] ++ _) ->
use Int *
use Nat +
num1 = dereference ptr1 mode1 programState
num2 = dereference ptr2 mode2 programState
runProgramAt
(at + 4)
(memory.modify
(replace (truncate0 ptrDest) (num1 * num2))
programState)
(Some Input, ptrDest +: _) ->
case input programState of
input +: restOfInputs ->
use Nat +
programState
|> memory.modify (replace (truncate0 ptrDest) input)
|> input.set restOfInputs
|> runProgramAt (at + 2)
[] -> exit.set (Some InsufficientInput) programState
(Some (Output mode), ptr +: _) ->
use Nat +
num = dereference ptr mode programState
runProgramAt
(at + 2)
(output.modify (out -> out :+ num) programState)
(Some (JumpIfTrue mode1 mode2), [ptr, newAt] ++ _) ->
if ne (dereference ptr mode1 programState) +0 then
runProgramAt
(truncate0 (dereference newAt mode2 programState))
programState
else
use Nat +
runProgramAt (at + 3) programState
(Some (JumpIfFalse mode1 mode2), [ptr, newAt] ++ _) ->
if dereference ptr mode1 programState == +0 then
runProgramAt
(truncate0 (dereference newAt mode2 programState))
programState
else
use Nat +
runProgramAt (at + 3) programState
(Some (LessThan mode1 mode2), [ptr1, ptr2, ptrDest] ++ _) ->
use Nat +
num1 = dereference ptr1 mode1 programState
num2 = dereference ptr2 mode2 programState
out = if num1 < num2 then +1 else +0
programState
|> memory.modify (replace (truncate0 ptrDest) out)
|> runProgramAt (at + 4)
(Some (Equals mode1 mode2), [ptr1, ptr2, ptrDest] ++ _) ->
use Nat +
num1 = dereference ptr1 mode1 programState
num2 = dereference ptr2 mode2 programState
out = if num1 == num2 then +1 else +0
programState
|> memory.modify (replace (truncate0 ptrDest) out)
|> runProgramAt (at + 4)
(Some Exit, _) -> exit.set (Some Normal) programState
(Some (Unknown unknown), _) ->
exit.set (Some (UnexpectedOpCode unknown)) programState
(None, _) ->
exit.set (Some UnexpectedEndOfProgram) programState
This works for very long programs, too.
The puzzle today is one!
I'm not going to print it out, though (look at originalGravityAssistProgram
if you like).
Let's patch and solve it!
.> view aoc.day2.answer
(I broke this transcript somehow! Oops!)
And what is patchProgram
, you ask?
Well, just the program we're gonna use to brute force solving the second part of the puzzle...
.> view aoc.day2.patchProgram
aoc.day2.patchProgram : Nat -> Nat -> [Nat] -> [Nat]
aoc.day2.patchProgram first second memory =
memory |> replace 1 first |> replace 2 second
We're looking for what values given to patchProgram
result in the output 19690720
...
.> view aoc.day2.answer2
aoc.day2.answer2 : [(Nat, Nat)]
aoc.day2.answer2 =
use List map
validValues = range 0 99
validValues
|> map
(first -> map (second -> (first, second)) validValues)
|> join
|> List.flatMap
(pair ->
(if
originalGravityAssistProgram
|> patchProgram (at1 pair) (at2 pair)
|> aoc.intcode.runProgram#ul1e5ej0gs
|> List.at 0
|> orDefault 0
== 19690720 then [pair]
else []))