Moved Intcode interpreter into a separate module, ensured all previous days still...
[advent-of-code-19.git] / intcode / src / Intcode.hs
1 module Intcode where
2
3 import Debug.Trace
4
5 import Data.Text (Text)
6 import qualified Data.Text.IO as TIO
7
8 import Data.Void (Void)
9
10 import Text.Megaparsec hiding (State)
11 import Text.Megaparsec.Char
12 import qualified Text.Megaparsec.Char.Lexer as L
13 import qualified Control.Applicative as CA
14
15 -- import Control.Monad (unless)
16 import Control.Monad.State.Strict
17 import Control.Monad.Reader
18 import Control.Monad.Writer
19 import Control.Monad.RWS.Strict
20
21
22 import qualified Data.Map.Strict as M
23 import Data.Map.Strict ((!))
24 import Data.List
25
26 type Memory = M.Map Integer Integer
27
28 data Machine = Machine { _memory :: Memory
29 , _ip :: Integer
30 , _inputIndex :: Int
31 , _rb :: Integer
32 }
33 deriving (Show, Eq)
34
35 type ProgrammedMachine = RWS [Integer] [Integer] Machine
36
37 data ExecutionState = Runnable | Blocked | Terminated deriving (Ord, Eq, Show)
38
39 data ParameterMode = Position | Immediate | Relative deriving (Ord, Eq, Show)
40
41
42 -- returns (returnValue, finalMachine, outputs)
43 runProgram :: [Integer] -> [Integer] -> (ExecutionState, Machine, [Integer])
44 runProgram inputs program = runMachine inputs (makeMachine program)
45
46 runMachine :: [Integer] -> Machine -> (ExecutionState, Machine, [Integer])
47 runMachine inputs machine = runRWS runAll inputs machine
48
49
50 makeMachine :: [Integer] -> Machine
51 makeMachine memory = Machine {_ip = 0, _inputIndex = 0, _rb = 0
52 , _memory = M.fromList $ zip [0..] memory
53 }
54
55
56 runAll :: ProgrammedMachine ExecutionState
57 runAll = do mem <- gets _memory
58 ip <- gets _ip
59 input <- ask
60 iIndex <- gets _inputIndex
61 let acutalInputLength = length input
62 let requiredInputLength = iIndex + 1
63 if (mem!ip == 99)
64 then return Terminated
65 else if (mem!ip == 3 && requiredInputLength > acutalInputLength)
66 then return Blocked
67 else do runStep
68 runAll
69
70 runStep :: ProgrammedMachine ()
71 runStep =
72 do mem <- gets _memory
73 ip <- gets _ip
74 rb <- gets _rb
75 let opcode = (mem!ip) `mod` 100
76 let modes = parameterModes ((mem!ip) `div` 100)
77 fetchInput opcode modes
78 putOutput opcode modes
79 mem' <- gets _memory
80 let (mem'', ip', rb') = perform opcode ip modes rb mem'
81 modify (\m -> m {_ip = ip', _memory = mem'', _rb = rb'})
82
83 fetchInput :: Integer -> [ParameterMode] -> ProgrammedMachine ()
84 -- fetchInput opcode _modes | trace ("Input with opcode " ++ show opcode) False = undefined
85 fetchInput 3 modes =
86 do mem <- gets _memory
87 ip <- gets _ip
88 rb <- gets _rb
89 inputIndex <- gets _inputIndex
90 inputs <- ask
91 let mem' = iInsert (ip + 1) (modes!!0) rb (inputs!!inputIndex) mem
92 modify (\m -> m {_inputIndex = inputIndex + 1, _memory = mem'})
93 fetchInput _ _ = return ()
94
95 putOutput :: Integer -> [ParameterMode] -> ProgrammedMachine ()
96 -- putOutput opcode _modes | trace ("Output with opcode " ++ show opcode) False = undefined
97 putOutput 4 modes =
98 do mem <- gets _memory
99 ip <- gets _ip
100 rb <- gets _rb
101 let v = getMemoryValue (ip + 1) (modes!!0) rb mem
102 tell [v]
103 putOutput _ _ = return ()
104
105
106 perform :: Integer -> Integer -> [ParameterMode] -> Integer -> Memory -> (Memory, Integer, Integer)
107 -- perform instr ip modes rb mem | trace ("Perform ip " ++ show ip ++ " opcode " ++ show instr ++ " modes " ++ (show (take 3 modes)) ++ " rb " ++ (show rb) ++ " args " ++ (intercalate ", " (map show [(mem!(ip+1)), (mem!(ip+2)), (mem!(ip+3))]))) False = undefined
108 perform 1 ip modes rb mem = (iInsert (ip + 3) (modes!!2) rb (a + b) mem, ip + 4, rb)
109 where a = getMemoryValue (ip + 1) (modes!!0) rb mem
110 b = getMemoryValue (ip + 2) (modes!!1) rb mem
111 perform 2 ip modes rb mem = (iInsert (ip + 3) (modes!!2) rb (a * b) mem, ip + 4, rb)
112 where a = getMemoryValue (ip + 1) (modes!!0) rb mem
113 b = getMemoryValue (ip + 2) (modes!!1) rb mem
114 perform 3 ip _ rb mem = (mem, ip + 2, rb)
115 perform 4 ip _ rb mem = (mem, ip + 2, rb)
116 perform 5 ip modes rb mem = (mem, ip', rb)
117 where a = getMemoryValue (ip + 1) (modes!!0) rb mem
118 b = getMemoryValue (ip + 2) (modes!!1) rb mem
119 ip' = if a /= 0 then b else ip + 3
120 perform 6 ip modes rb mem = (mem, ip', rb)
121 where a = getMemoryValue (ip + 1) (modes!!0) rb mem
122 b = getMemoryValue (ip + 2) (modes!!1) rb mem
123 ip' = if a == 0 then b else ip + 3
124 perform 7 ip modes rb mem = (iInsert (ip + 3) (modes!!2) rb res mem, ip + 4, rb)
125 where a = getMemoryValue (ip + 1) (modes!!0) rb mem
126 b = getMemoryValue (ip + 2) (modes!!1) rb mem
127 res = if a < b then 1 else 0
128 perform 8 ip modes rb mem = (iInsert (ip + 3) (modes!!2) rb res mem, ip + 4, rb)
129 where a = getMemoryValue (ip + 1) (modes!!0) rb mem
130 b = getMemoryValue (ip + 2) (modes!!1) rb mem
131 res = if a == b then 1 else 0
132 perform 9 ip modes rb mem = (mem, ip + 2, rb + a)
133 where a = getMemoryValue (ip + 1) (modes!!0) rb mem
134 perform _ ip _ rb mem = (mem, ip, rb)
135
136
137 getMemoryValue :: Integer -> ParameterMode -> Integer -> Memory -> Integer
138 getMemoryValue loc Position rb mem = getMemoryValue loc' Immediate rb mem
139 where loc' = M.findWithDefault 0 loc mem
140 getMemoryValue loc Immediate _ mem = M.findWithDefault 0 loc mem
141 getMemoryValue loc Relative rb mem = getMemoryValue loc' Immediate 0 mem
142 where loc' = rb + M.findWithDefault 0 loc mem
143
144 -- indirect insert
145 iInsert :: Integer -> ParameterMode -> Integer -> Integer -> Memory -> Memory
146 iInsert loc Position _rb value mem = M.insert loc' value mem
147 where loc' = M.findWithDefault 0 loc mem
148 iInsert loc Immediate _rb value mem = M.insert loc value mem
149 iInsert loc Relative rb value mem = M.insert loc' value mem
150 where loc' = rb + M.findWithDefault 0 loc mem
151
152 parameterModes :: Integer -> [ParameterMode]
153 parameterModes modeCode = unfoldr generateMode modeCode
154
155 generateMode :: Integer -> Maybe (ParameterMode, Integer)
156 generateMode modeCode = Just (mode, modeCode `div` 10)
157 where mode = case (modeCode `mod` 10) of
158 0 -> Position
159 1 -> Immediate
160 2 -> Relative
161
162
163 -- Parse the input file
164 type Parser = Parsec Void Text
165
166 sc :: Parser ()
167 sc = L.space (skipSome spaceChar) CA.empty CA.empty
168 -- sc = L.space (skipSome (char ' ')) CA.empty CA.empty
169
170 lexeme = L.lexeme sc
171 integer = lexeme L.decimal
172 signedInteger = L.signed sc integer
173 symb = L.symbol sc
174 comma = symb ","
175
176 memoryP = signedInteger `sepBy` comma
177
178
179 parseMachineMemory :: Text -> [Integer]
180 parseMachineMemory input =
181 case parse memoryP "input" input of
182 Left _err -> [] -- TIO.putStr $ T.pack $ parseErrorPretty err
183 Right memory -> memory