-
Notifications
You must be signed in to change notification settings - Fork 0
/
run.hs
220 lines (187 loc) · 5.64 KB
/
run.hs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
{-# LANGUAGE BlockArguments #-}
{-# LANGUAGE LambdaCase #-}
import AoC.Parse (numP)
import Control.Monad (when)
import Control.Monad.State (State, execState, get, gets, modify', put)
import Data.Void (Void)
import Data.IntMap (IntMap)
import qualified Data.IntMap as IntMap
import Data.Map.Strict (Map)
import qualified Data.Map.Strict as Map
import Data.Sequence ( Seq
, (|>)
, viewl
, ViewL(..) )
import qualified Data.Sequence as Seq
import Text.Megaparsec hiding (State)
import Text.Megaparsec.Char
type Parser = Parsec Void String
unsafeRight :: Show a => Either a b -> b
unsafeRight (Right x) = x
unsafeRight (Left x) = error $ show x
type Reg = Char
data ProcessState = Receiving
| Running
| Sending Integer
deriving Show
data ProgramState = PState { program :: IntMap (Program ())
, pointer :: Int
, programLength :: Int
, regs :: Map Reg Integer
, queue :: Seq Integer
, processState :: ProcessState
, sentCount :: Integer
}
type Program = State ProgramState
setState :: ProcessState -> ProgramState -> ProgramState
setState p s = s { processState = p }
append :: Integer -> ProgramState -> ProgramState
append a s = s { queue = queue s |> a }
dequeue :: Program (Maybe Integer)
dequeue = do
s <- get
case viewl (queue s) of
x :< rest -> do
put s { queue = rest }
pure (Just x)
_ ->
pure Nothing
receive :: Reg -> Program ()
receive r = dequeue >>= \case
Just i -> do
modify' (setReg r i)
modify' (setState Running)
Nothing ->
modify' (setState Receiving)
setReg :: Reg -> Integer -> ProgramState -> ProgramState
setReg r v s = s { regs = Map.insert r v (regs s) }
regLookup :: Reg -> Map Reg Integer -> Integer
regLookup = Map.findWithDefault 0
reg :: Reg -> ProgramState -> Integer
reg r = regLookup r . regs
send :: Integer -> Program ()
send i = modify' (setState $ Sending i)
regValP :: Parser (Program Integer)
regValP = (pure <$> numP) <|> (gets . reg <$> asciiChar)
operation :: (Integer -> Integer -> Integer)
-> Reg
-> Program Integer
-> Program ()
operation op x y = do
vx <- gets (reg x)
vy <- y
modify' $ setReg x (vx `op` vy)
operationP :: (Integer -> Integer -> Integer)
-> String
-> Parser (Program ())
operationP op s = do
_ <- string s
_ <- spaceChar
x <- asciiChar
_ <- spaceChar
y <- regValP
pure $ operation op x y
sndP :: Parser (Program ())
sndP = do
_ <- string "snd "
reg <- regValP
pure $ reg >>= send
rcvP :: Parser (Program ())
rcvP = do
_ <- string "rcv "
x <- asciiChar
pure $ receive x
jgzP :: Parser (Program ())
jgzP = do
_ <- string "jgz "
x <- regValP
_ <- spaceChar
y <- regValP
pure do
vx <- x
when (vx > 0) do
vy <- y
modify' (\s -> s { pointer = pointer s + fromInteger vy - 1 })
setP :: Parser (Program ())
setP = do
_ <- string "set "
x <- asciiChar
_ <- spaceChar
y <- regValP
pure do
vy <- y
modify' (setReg x vy)
addP :: Parser (Program ())
addP = operationP (+) "add"
mulP :: Parser (Program ())
mulP = operationP (*) "mul"
modP :: Parser (Program ())
modP = operationP mod "mod"
parseInstruction :: Parser (Program ())
parseInstruction =
choice [ sndP
, setP
, addP
, mulP
, modP
, rcvP
, jgzP
]
parseAll :: String -> [Program ()]
parseAll =
map unsafeRight
. map (parse parseInstruction "")
. lines
eval :: (ProgramState, ProgramState) -> (Bool, (ProgramState, ProgramState))
eval (p1, p2) =
case (processState p1, processState p2) of
(Receiving, Receiving) -> (False, (p1, p2))
(Running, _) -> (True, (stepProcess p1, p2))
(Sending _, _) -> (True, send' p1 p2)
(_, Sending _) -> let (p2', p1') = send' p2 p1
in
(True, (p1', p2'))
(_, _) -> (True, (p1, stepProcess p2))
send' :: ProgramState -> ProgramState -> (ProgramState, ProgramState)
send' p1 p2 =
case processState p1 of
Sending i -> ( setState Running p1 { sentCount = sentCount p1 + 1 }
, setState Running (append i p2))
_ -> (p1, p2)
stepProcess :: ProgramState -> ProgramState
stepProcess p = flip execState p do
case IntMap.lookup (pointer p) (program p) of
Just instr -> do
instr
p' <- get
case processState p' of
Receiving -> pure ()
_ -> put p' { pointer = pointer p' + 1 }
Nothing -> pure ()
initial :: [Program ()] -> Integer -> ProgramState
initial prog pid = PState { pointer = 0
, program = IntMap.fromList (zip [0..] prog)
, programLength = length prog
, regs = Map.singleton 'p' pid
, queue = Seq.empty
, processState = Running
, sentCount = 0 }
part1 :: [Program ()] -> Integer
part1 instr = go 0 $ initial instr 0
where go freq state =
let state' = stepProcess state
in case processState state' of
Running -> go freq state'
Sending f -> go f state'
Receiving -> freq
part2 :: [Program ()] -> Integer
part2 instr = go (initial instr 0, initial instr 1)
where go ps =
case eval ps of
(False, (_, p2)) -> sentCount p2
(True, ps') -> go ps'
main :: IO ()
main = do
input <- parseAll <$> readFile "input.txt"
print $ part1 input
print $ part2 input