-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathrun.hs
174 lines (140 loc) · 5.49 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
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
-- module Main (main) where
import Data.Bool (bool)
import Control.Monad.Writer (MonadWriter, Writer, execWriter, tell)
import Control.Monad.Identity (Identity(..))
import Control.Monad.State (StateT(..), gets, modify, lift, get, MonadState, MonadTrans, State, runState)
import Data.Char (isDigit)
import Data.Maybe (maybe)
import Data.HashMap.Strict (HashMap)
import qualified Data.HashMap.Strict as HashMap
type Program = HashMap Integer Integer
type ExecInfo = (Integer, Program)
class Monad m => Interpreter m where
readInput :: m Integer
writeOutput :: Integer -> m ()
type Address = Integer
data Instruction = Add Param Param Address
| Mul Param Param Address
| Input Address
| Output Param
| JumpNonZero Param Param
| JumpZero Param Param
| LessThan Param Param Address
| Equals Param Param Address
| Exit
deriving Show
instance Interpreter IO where
readInput = do
putStr "Input: "
read . filter (/= '\n') <$> getLine
writeOutput x = putStrLn $ "Output: " ++ show x
type ProgT = StateT ExecInfo
runProgT :: ProgT m a -> ExecInfo -> m (a, ExecInfo)
runProgT = runStateT
data Param = Position Address | Immediate Integer
deriving Show
rawValAt :: MonadState ExecInfo m => Address -> m Integer
rawValAt addr = maybe 0 id . HashMap.lookup addr <$> gets memory
pc :: ExecInfo -> Integer
pc = fst
modifyPc :: MonadState ExecInfo m => (Integer -> Integer) -> m ()
modifyPc f = modify (\(pc, prog) -> (f pc, prog))
memory :: ExecInfo -> Program
memory = snd
paramValue (Immediate x) = pure x
paramValue (Position x) = rawValAt x
writeMemory addr val =
modify (\(pc, prog) -> (pc, HashMap.insert addr val prog))
parseAll :: String -> Program
parseAll input = HashMap.fromList $ zip [0..] . read $ '[' : input ++ "]"
consumeInstruction :: Interpreter m => ProgT m Instruction
consumeInstruction = do
rawInstruction <- rawValAt =<< gets pc
modifyPc (+ 1)
case parseFullOpCode rawInstruction of
(1, [pmx, pmy, 0]) -> Add <$> consumeParam pmx
<*> consumeParam pmy
<*> consumeAddress
(2, [pmx, pmy, 0]) -> Mul <$> consumeParam pmx
<*> consumeParam pmy
<*> consumeAddress
(3, 0:_) -> Input <$> consumeAddress
(4, pmx:_) -> Output <$> consumeParam pmx
(5, pmx:pmy:_) -> JumpNonZero <$> consumeParam pmx <*> consumeParam pmy
(6, pmx:pmy:_) -> JumpZero <$> consumeParam pmx <*> consumeParam pmy
(7, [pmx, pmy, 0]) -> LessThan <$> consumeParam pmx
<*> consumeParam pmy
<*> consumeAddress
(8, [pmx, pmy, 0]) -> Equals <$> consumeParam pmx
<*> consumeParam pmy
<*> consumeAddress
(99, _) -> pure Exit
_ -> do
state <- get
error $ "Invalid opcode (instr: "
++ show rawInstruction ++ "), state is: "
++ show state
parseFullOpCode :: Integer -> (Integer, [Integer])
parseFullOpCode x =
let opCode = x `mod` 100
paramModes = map (\d -> (x `div` d) `mod` 10) [100, 1000, 10000]
in
(opCode, paramModes)
exec :: Interpreter m => Program -> m Program
exec prog = snd . snd <$> runProgT exec' (0, prog)
exec' :: Interpreter m => ProgT m ()
exec' = do
instruction <- consumeInstruction
case instruction of
Add px py outAddr -> binOp (+) px py >>= writeMemory outAddr >> exec'
Mul px py outAddr -> binOp (*) px py >>= writeMemory outAddr >> exec'
Input outAddr -> lift readInput >>= writeMemory outAddr >> exec'
Output px -> paramValue px >>= lift . writeOutput >> exec'
JumpNonZero px pjump -> do
jumpAddr <- paramValue pjump
(/= 0) <$> paramValue px >>= branch jumpAddr >> exec'
JumpZero px pjump ->do
jumpAddr <- paramValue pjump
(== 0) <$> paramValue px >>= branch jumpAddr >> exec'
LessThan px py outAddr ->
comparison (<) px py >>= writeMemory outAddr >> exec'
Equals px py outAddr ->
comparison (==) px py >>= writeMemory outAddr >> exec'
Exit -> pure ()
branch jumpAddr = bool (pure ()) (modifyPc (const jumpAddr))
comparison comp = binOp (\x y -> bool 0 1 (comp x y))
binOp :: MonadState ExecInfo m
=> (Integer -> Integer -> a)
-> Param
-> Param
-> m a
binOp op px py = op <$> paramValue px <*> paramValue py
consumeValue :: MonadState ExecInfo m => m Integer
consumeValue = (gets pc >>= rawValAt) <* modifyPc (+ 1)
consumeAddress :: MonadState ExecInfo m => m Address
consumeAddress = consumeValue
consumeParam :: MonadState ExecInfo m => Integer -> m Param
consumeParam 0 = Position <$> consumeValue
consumeParam 1 = Immediate <$> consumeAddress
newtype Diagnostics a = Diagnostics (State (Integer, [Integer]) a)
deriving ( Monad
, Functor
, Applicative
, MonadState (Integer, [Integer]) )
runDiagnostics :: Integer -> Diagnostics a -> [Integer]
runDiagnostics mode (Diagnostics x) =
snd . snd $ runState x (mode, [])
instance Interpreter Diagnostics where
readInput = gets fst
writeOutput x = modify (\(c, outputs) -> (c, x:outputs))
part1 :: Program -> Integer
part1 = head . runDiagnostics 1 . exec
part2 :: Program -> Integer
part2 = head . runDiagnostics 5 . exec
main :: IO ()
main = do
input <- parseAll <$> readFile "input.txt"
print (part1 input)
print (part2 input)