3 import Data.Text (Text)
4 import qualified Data.Text.IO as TIO
6 import Data.Void (Void)
8 import Text.Megaparsec hiding (State)
9 import Text.Megaparsec.Char
10 import qualified Text.Megaparsec.Char.Lexer as L
11 import qualified Control.Applicative as CA
13 import Control.Monad (unless)
14 import Control.Monad.State.Strict
15 import Control.Monad.Reader
16 import Control.Monad.Writer
18 import qualified Data.IntMap.Strict as M
19 import Data.IntMap.Strict ((!))
22 type Memory = M.IntMap Int
24 data Machine = Machine { _memory :: Memory
30 type ProgrammedMachine = WriterT [Int] (ReaderT ([Int]) (State Machine)) ()
32 data ParameterMode = Position | Immediate deriving (Ord, Eq, Show)
37 text <- TIO.readFile "data/advent05.txt"
38 let mem = successfulParse text
39 print $ findMachineOutput [1] mem
40 print $ findMachineOutput [5] mem
43 findMachineOutput inputs program = last output
52 ((_retval, output), _machine) = finalStack
54 makeMachine :: [Int] -> Machine
55 makeMachine memory = Machine {_ip = 0, _inputIndex = 0
56 , _memory = M.fromList $ zip [0..] memory
60 runAll :: ProgrammedMachine
61 runAll = do mem <- gets _memory
67 runStep :: ProgrammedMachine
69 do mem <- gets _memory
71 let opcode = (mem!ip) `mod` 100
72 let modes = parameterModes ((mem!ip) `div` 100)
74 putOutput opcode modes
76 let (mem'', ip') = perform opcode ip modes mem'
77 modify (\m -> m {_ip = ip', _memory = mem''})
80 -- fetchInput opcode | trace ("Input with opcode " ++ show opcode) False = undefined
82 do mem <- gets _memory
84 inputIndex <- gets _inputIndex
86 let mem' = iInsert (ip + 1) (inputs!!inputIndex) mem
87 modify (\m -> m {_inputIndex = inputIndex + 1, _memory = mem'})
88 fetchInput _ = return ()
91 -- putOutput opcode _modes | trace ("Output with opcode " ++ show opcode) False = undefined
93 do mem <- gets _memory
95 let v = getMemoryValue (ip + 1) (modes!!0) mem
97 putOutput _ _ = return ()
100 perform :: Int -> Int -> [ParameterMode] -> Memory -> (Memory, Int)
101 -- perform instr ip modes mem | trace ("Perform ip " ++ show ip ++ " opcode " ++ show instr ++ " modes " ++ (show (take 3 modes)) ++ " args " ++ (intercalate ", " (map show [(mem!(ip+1)), (mem!(ip+2)), (mem!(ip+3))]))) False = undefined
102 perform 1 ip modes mem = (iInsert (ip + 3) (a + b) mem, ip + 4)
103 where a = getMemoryValue (ip + 1) (modes!!0) mem
104 b = getMemoryValue (ip + 2) (modes!!1) mem
105 perform 2 ip modes mem = (iInsert (ip + 3) (a * b) mem, ip + 4)
106 where a = getMemoryValue (ip + 1) (modes!!0) mem
107 b = getMemoryValue (ip + 2) (modes!!1) mem
108 perform 3 ip _ mem = (mem, ip + 2)
109 perform 4 ip _ mem = (mem, ip + 2)
110 perform 5 ip modes mem = (mem, ip')
111 where a = getMemoryValue (ip + 1) (modes!!0) mem
112 b = getMemoryValue (ip + 2) (modes!!1) mem
113 ip' = if a /= 0 then b else ip + 3
114 perform 6 ip modes mem = (mem, ip')
115 where a = getMemoryValue (ip + 1) (modes!!0) mem
116 b = getMemoryValue (ip + 2) (modes!!1) mem
117 ip' = if a == 0 then b else ip + 3
118 perform 7 ip modes mem = (iInsert (ip + 3) res mem, ip + 4)
119 where a = getMemoryValue (ip + 1) (modes!!0) mem
120 b = getMemoryValue (ip + 2) (modes!!1) mem
121 res = if a < b then 1 else 0
122 perform 8 ip modes mem = (iInsert (ip + 3) res mem, ip + 4)
123 where a = getMemoryValue (ip + 1) (modes!!0) mem
124 b = getMemoryValue (ip + 2) (modes!!1) mem
125 res = if a == b then 1 else 0
126 perform _ ip _ mem = (mem, ip)
129 getMemoryValue loc Position mem = mem!>loc
130 getMemoryValue loc Immediate mem = mem!loc
133 parameterModes :: Int -> [ParameterMode]
134 parameterModes modeCode = unfoldr generateMode modeCode
136 generateMode :: Int -> Maybe (ParameterMode, Int)
137 generateMode modeCode = Just (mode, modeCode `div` 10)
138 where mode = case (modeCode `mod` 10) of
143 -- Some IntMap utility functions, for syntactic sugar
145 -- prefix version of (!)
152 iInsert k v m = M.insert (m!k) v m
156 -- Parse the input file
157 type Parser = Parsec Void Text
160 sc = L.space (skipSome spaceChar) CA.empty CA.empty
161 -- sc = L.space (skipSome (char ' ')) CA.empty CA.empty
164 integer = lexeme L.decimal
165 signedInteger = L.signed sc integer
169 memoryP = signedInteger `sepBy` comma
171 successfulParse :: Text -> [Int]
172 successfulParse input =
173 case parse memoryP "input" input of
174 Left _err -> [] -- TIO.putStr $ T.pack $ parseErrorPretty err
175 Right memory -> memory