1
0
Fork 0
adventofcode/18b.hs

113 lines
4.4 KiB
Haskell
Raw Normal View History

2017-12-18 21:38:37 +00:00
import Text.Read (readMaybe)
import Data.Sequence (Seq, fromList, empty, index, deleteAt, (|>))
import qualified Data.Sequence as S (null)
import Data.Vector.Unboxed (Vector, (!), (//))
import qualified Data.Vector.Unboxed as V (replicate)
-- DEFINITIONS
type Registers = Vector Int
type Program = (Registers, Int, Seq Int)
type Instruction = ProgramId -> State -> State
data State = State Program Program (Bool, Bool) Int
data Value = Register Char | Number Int
data ProgramId = Zero | One
-- HELPERS
getPos :: ProgramId -> State -> Int
getPos Zero (State (_, pos, _) _ _ _) = pos
getPos One (State _ (_, pos, _) _ _) = pos
getStop :: ProgramId -> State -> Bool
getStop Zero (State _ _ (stop, _) _) = stop
getStop One (State _ _ (_, stop) _) = stop
setStop :: ProgramId -> Bool -> State -> State
setStop Zero b (State p0 p1 (_, stop1) count) = State p0 p1 (b, stop1) count
setStop One b (State p0 p1 (stop0, _) count) = State p0 p1 (stop0, b) count
pop :: Seq Int -> (Seq Int, Int)
pop queue = (deleteAt 0 queue, queue `index` 0)
swap :: State -> State
swap (State p0 p1 (stop0, stop1) count) = State p1 p0 (stop1, stop0) count
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)
-- OPERATIONS
sen :: Value -> ProgramId -> State -> State
sen v Zero (State (reg0, pos0, que0) (reg1, pos1, que1) stop count) =
setStop One False $ State (reg0, pos0 + 1, que0) (reg1, pos1, que1 |> getValue v reg0) stop count
sen v One (State (reg0, pos0, que0) (reg1, pos1, que1) stop count) =
setStop Zero False $ State (reg0, pos0, que0 |> getValue v reg1) (reg1, pos1 + 1, que1) stop (count + 1)
rcv :: Value -> ProgramId -> State -> State
rcv i Zero (State (reg0, pos0, que0) p1 stop count) =
if S.null que0 then setStop Zero True $ State (reg0, pos0, que0) p1 stop count else
let ind = getIndex i
(que, val) = pop que0
in State (reg0 // [(ind, val)], pos0 + 1, que) p1 stop count
rcv i One state = swap . rcv i Zero . swap $ state
app :: (Int -> Int -> Int) -> Value -> Value -> ProgramId -> State -> State
app f i v Zero (State (reg0, pos0, que0) p1 stop count) =
let ind = getIndex i
val = getValue v reg0
in State (reg0 // [(ind, reg0 ! ind `f` val)], pos0 + 1, que0) p1 stop count
app f i v One state = swap . app f i v Zero . swap $ state
jgz :: Value -> Value -> ProgramId -> State -> State
jgz condition offset Zero (State (reg0, pos0, que0) p1 stop count) =
State (reg0, pos0 + if getValue condition reg0 > 0 then getValue offset reg0 else 1, que0) p1 stop count
jgz condition offset One state = swap . jgz condition offset Zero . swap $ state
-- PARSE
parseLine :: String -> Instruction
parseLine str =
let op : vs = words str
in case op of
"snd" -> sen $ 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
-- SOLVE
executeNextInstruction :: Seq Instruction -> ProgramId -> State -> State
executeNextInstruction instructions pid state =
let pos = getPos pid state
in if pos >= length instructions then setStop pid True state
else (instructions `index` pos) pid state
getCount :: Seq Instruction -> State -> Int
getCount _ (State _ _ (True, True) count) = count
getCount instructions state =
if not $ getStop Zero state
then getCount instructions $ executeNextInstruction instructions Zero state
else getCount instructions $ executeNextInstruction instructions One state
main :: IO ()
main = do
instructions <- fmap (fromList . map parseLine . lines) $ readFile "18.txt"
let initialState = (State (V.replicate 5 0, 0, empty) (V.replicate 5 0 // [(4, 1)], 0, empty) (False, False) 0) :: State
print $ getCount instructions initialState