2017/18a.hs

71 lines
2.5 KiB
Haskell

import Text.Read (readMaybe)
import Data.Sequence (Seq, fromList, index)
import Data.Vector.Unboxed (Vector, (!), (//))
import qualified Data.Vector.Unboxed as V (replicate)
type Registers = Vector Int
type State = (Registers, Int, Int, Int)
type Instruction = State -> State
data Value = Register Char | Number Int
getIndex :: Value -> Int
getIndex (Register c) = case c of
'a' -> 0
'b' -> 1
'f' -> 2
'i' -> 3
'p' -> 4
getValue :: Value -> Registers -> Int
getValue value registers = case value of
Number i -> i
c -> registers ! (getIndex c)
son :: Value -> State -> State
son freq (reg, pos, _, rec) =
(reg, pos + 1, getValue freq reg, rec)
rcv :: Value -> State -> State
rcv v (reg, pos, freq, rec) =
(reg, pos + 1, freq, if getValue v reg == 0 then rec else freq)
app :: (Int -> Int -> Int) -> Value -> Value -> State -> State
app f i v (reg, pos, freq, rec) =
let ind = getIndex i
val = getValue v reg
in (reg // [(ind, reg ! ind `f` val)], pos + 1, freq, rec)
jgz :: Value -> Value -> State -> State
jgz condition offset (reg, pos, freq, rec) =
(reg, pos + if getValue condition reg > 0 then getValue offset reg else 1, freq, rec)
parseLine :: String -> State -> State
parseLine str =
let op : vs = words str
in case op of
"snd" -> son $ parseValue $ head vs
"set" -> app (flip const) (parseValue $ head vs) (parseValue $ last vs)
"add" -> app (+) (parseValue $ head vs) (parseValue $ last vs)
"mul" -> app (*) (parseValue $ head vs) (parseValue $ last vs)
"mod" -> app mod (parseValue $ head vs) (parseValue $ last vs)
"rcv" -> rcv $ parseValue $ head vs
"jgz" -> jgz (parseValue $ head vs) (parseValue $ last vs)
where parseValue s = case readMaybe s of
Just i -> Number i
Nothing -> Register $ head s
-- precondition: pos < length instructions
executeNextInstruction :: Seq Instruction -> State -> State
executeNextInstruction instructions (reg, pos, freq, rec) =
instructions `index` pos $ (reg, pos, freq, rec)
recover :: Seq Instruction -> State -> Int
recover instructions (reg, pos, freq, rec) =
if rec /= 0 then rec else
recover instructions $ executeNextInstruction instructions (reg, pos, freq, rec)
main :: IO ()
main = do
instructions <- fromList . map parseLine . lines <$> readFile "18.txt"
let initialState = (V.replicate 5 0, 0, 0, 0)
print $ recover instructions initialState