Skip to main content
fixed link and cleaned up link
Source Link
Linny
  • 10.4k
  • 5
  • 37
  • 99

I've tried to solve AoC day 2 challenge in Haskell (https://adventofcode.com/2019/day/2/here - don't worry, it's not a competition so sharing a solution here is OK).

I've tried to solve AoC day 2 challenge in Haskell (https://adventofcode.com/2019/day/2/ - don't worry, it's not a competition so sharing a solution here is OK).

I've tried to solve AoC day 2 challenge in Haskell (here - don't worry, it's not a competition so sharing a solution here is OK).

Source Link
msm
  • 51
  • 3

Advent of code #2 Haskell solution

I've tried to solve AoC day 2 challenge in Haskell (https://adventofcode.com/2019/day/2/ - don't worry, it's not a competition so sharing a solution here is OK).

The goal is to implement a very simple VM with opcodes 1 (add), 2 (mult) and 99 (exit).

I feel like my solution is incredibly verbose. That maybe be because I rely heavily on the state monad (my background is imperative programming, so there's that). Is there anything I could improve without rewriting the whole solution?

Here's my code, thanks for all suggestions:

import Data.Sequence
import Control.Monad.State
import Data.List.Split

data Machine = Machine {
    mState :: Seq Int,
    mPos :: Int,
    isDone :: Bool
}

opReadHead :: State Machine Int
opReadHead = do
    machine <- get
    return $ index (mState machine) (mPos machine)

opReadAt :: Int -> State Machine Int
opReadAt target = do
    machine <- get
    return $ index (mState machine) target

opForward :: State Machine ()
opForward = do
    machine <- get
    put $ machine { mPos = mPos machine + 1 }

opWrite :: Int -> Int -> State Machine ()
opWrite target what = do
    machine <- get
    put $ machine { mState = update target what (mState machine) }

opReadNext :: State Machine Int
opReadNext = do
    a <- opReadHead
    opForward
    return a
    
opAdd :: State Machine ()
opAdd = do
    aPtr <- opReadNext
    a <- opReadAt aPtr
    bPtr <- opReadNext
    b <- opReadAt bPtr
    target <- opReadNext
    opWrite target (a + b)

opMult :: State Machine ()
opMult = do
    aPtr <- opReadNext
    a <- opReadAt aPtr
    bPtr <- opReadNext
    b <- opReadAt bPtr
    target <- opReadNext
    opWrite target (a * b)

opExit :: State Machine ()
opExit = do
    current <- get
    put $ current { isDone = True }

isMachineDone :: State Machine Bool
isMachineDone = do
    get >>= (return . isDone)

opcode :: Int -> State Machine ()
opcode 1 = opAdd
opcode 2 = opMult
opcode 99 = opExit

opExecuteNext :: State Machine ()
opExecuteNext = do
    opValue <- opReadNext
    opcode opValue

runCode :: State Machine ()
runCode = do
    done <- isMachineDone
    if done
    then return ()
    else opExecuteNext >> runCode 

evalWith :: Machine -> Int -> Int -> Int
evalWith machine noun verb = do
    fst $ runState (do
        opWrite 1 noun
        opWrite 2 verb
        runCode
        opReadAt 0
        ) machine

main :: IO()
main = do
    fileData <- readFile "input"
    let memory = map read $ splitOn "," fileData
    let machine = Machine {
        mState = fromList memory,
        mPos = 0,
        isDone = False
    }
    let outputs = [(evalWith machine x y, (x, y)) | x <- [0..99], y <- [0..99]]
    print $ snd $ head $ Prelude.filter ((== 19690720) . fst) outputs
```