Day 2: 1202 Program Alarm

https://adventofcode.com/2019/day/2


In [1]:
inputLine = head . lines <$> readFile "input/day02.txt"

Convert a list of numbers to a program (represented by an array)


In [2]:
import Data.Array

type Program = Array Int Int

listToProgram :: [Int] -> Program
listToProgram numbers = listArray (0, length numbers - 1) numbers

Convert a string to a program


In [3]:
import Data.List.Split

parseProgram :: String -> Program
parseProgram line = listToProgram . map read . splitOn "," $ line

Modify a program by storing a new value in a given memory cell


In [4]:
import Control.Lens

writeTo :: Int -> Int -> Program -> Program
writeTo index newValue program = set (element index) newValue program

Execute a program, and return the state after executing opcode 99


In [5]:
executeIndex :: Int -> Program -> Program
executeIndex index program =
    case opcode of
        99 -> program
        1 -> calculate (+)
        2 -> calculate (*)
    where
        numberAtOffset offset = program ! (index + offset)
        opcode = numberAtOffset 0
        operand number = program ! numberAtOffset number
        destinationIndex = numberAtOffset 3

        calculate operator = executeIndex (index + 4) modifiedProgram
            where
                result = operator (operand 1) (operand 2)
                modifiedProgram = writeTo destinationIndex result program

executeProgram :: Program -> Program
executeProgram program = executeIndex 0 program

Verify the given results


In [6]:
check = elems . executeProgram . listToProgram

In [7]:
check [1,9,10,3,2,3,11,0,99,30,40,50]


[3500,9,10,70,2,3,11,0,99,30,40,50]

In [8]:
check [1,0,0,0,99]


[2,0,0,0,99]

In [9]:
check [2,3,0,3,99]


[2,3,0,6,99]

In [10]:
check [2,4,4,5,99,0]


[2,4,4,5,99,9801]

In [11]:
check [1,1,1,4,99,5,6,0,99]


[30,1,1,4,2,5,6,0,99]

Solution for part 1


In [12]:
program = parseProgram <$> inputLine

In [13]:
modifiedProgram = writeTo 1 12 . writeTo 2 2 <$> program

In [14]:
(!0) . executeProgram <$> modifiedProgram


7594646

Add some convenience functions and reproduce the solution for part 1


In [15]:
programWithInput :: Int -> Int -> Program -> Program
programWithInput input1 input2 = writeTo 1 input1 . writeTo 2 input2

In [16]:
outputForInput :: Int -> Int -> Program -> Int
outputForInput input1 input2 = (!0) . executeProgram . programWithInput input1 input2

In [17]:
outputForInput 12 2 <$> program


7594646

Find pairs of inputs which yield a given output


In [18]:
findInputsForOutput :: Int -> Program -> [(Int, Int)]
findInputsForOutput output program = filter yieldsExpectedOutput [(a, b) | a <- [0..99], b <- [0..99]]
    where
        yieldsExpectedOutput = (==output) . flip (uncurry outputForInput) program

Verify that the result is consistent with part 1


In [19]:
findInputsForOutput 7594646 <$> program


[(12,2)]

Solution for part 2: multiply first input by 100 and add the second


In [20]:
findInputsForOutput 19690720 <$> program


[(33,76)]

In [21]:
solutionPart2 :: Int -> Program -> Int
solutionPart2 output = uncurry ((+) . (*100)) . head . findInputsForOutput output

solutionPart2 19690720 <$> program


3376