1 module Main(main) where
3 import Text.Parsec hiding (State)
4 import Text.ParserCombinators.Parsec.Number
5 import Control.Monad.State.Lazy
7 -- import Control.Monad.Writer
8 import Control.Monad.Reader
11 data Location = Literal Int | Register Char deriving (Show, Eq)
12 data Instruction = Cpy Location Location
15 | Jnz Location Location
20 data Machine = Machine { a :: Int
25 , instructions :: [Instruction]
30 data AppConfig = AppConfig { cfgMaxRun :: Int } deriving (Show)
33 type App = ReaderT AppConfig (State Machine) String
36 testInstructions1 = "\
47 testInstructions2 = "jnz 1 0"
52 emptyMachine :: Machine
53 emptyMachine = Machine {a=0, b=0, c=0, d=0, pc=0, instructions=[], execCount=0}
57 text <- readFile "data/advent25.txt"
58 -- let text = testInstructions1
59 let instructions = successfulParse $ parseIfile text
63 part1 :: [Instruction] -> IO ()
65 print $ head validInputs
66 where m0 = emptyMachine {instructions=instrs}
68 validInputs = filter (validMachine) inputs
69 validMachine i = valid $ evalMachine m0 i
72 valid :: String -> Bool
73 valid output = all (\p -> fst p == snd p) $ zip target output
75 evalMachine :: Machine -> Int -> String
76 evalMachine machine0 input = evalState (runReaderT (runMachine "") config) m
77 where m = machine0 {a = input}
78 config = AppConfig {cfgMaxRun = 500000}
80 runMachine :: String -> App
84 if (pc m) >= (length $ instructions m) || execCount m > cfgMaxRun cfg
86 else do thisOutput <- executeStep
87 runMachine (output ++ thisOutput)
93 let i = (instructions m)!!(pc m)
94 let output = generateOutput i m
95 put (executeInstructionPeep i m) {execCount = (execCount m) + 1}
96 -- put (executeInstruction i m) {execCount = (execCount m) + 1}
99 generateOutput :: Instruction -> Machine -> String
100 generateOutput (Out a) m = show $ evaluate m a
101 generateOutput _ _ = ""
103 executeInstructionPeep :: Instruction -> Machine -> Machine
104 executeInstructionPeep i m =
105 if sample == sampleTarget
106 -- then trace ("Peeping 1 " ++ (show m) ++ " to " ++ (show m1)) m1
108 else executeInstruction i m
109 where sample = take (length sampleTarget) $ drop (pc m) $ instructions m
110 sampleTarget = [ Inc (Register 'd')
112 , Jnz (Register 'b') (Literal (-2))
114 , Jnz (Register 'c') (Literal (-5)) ]
115 m1 = m {d = d m + c m * b m, c = 0, b = 0, pc = pc m + (length sample)}
118 executeInstruction :: Instruction -> Machine -> Machine
119 executeInstruction (Inc r@(Register _)) m = m' {pc=pc1}
120 where pc1 = (pc m) + 1
122 m' = writeValue m r (v+1)
123 executeInstruction (Inc (Literal _)) m = m {pc=pc1}
124 where pc1 = (pc m) + 1
125 executeInstruction (Dec r@(Register _)) m = m' {pc=pc1}
126 where pc1 = (pc m) + 1
128 m' = writeValue m r (v-1)
129 executeInstruction (Dec (Literal _)) m = m {pc=pc1}
130 where pc1 = (pc m) + 1
131 executeInstruction (Cpy s d@(Register _)) m = m' {pc=pc1}
132 where pc1 = (pc m) + 1
134 m' = writeValue m d v
135 executeInstruction (Cpy s (Literal _)) m = m {pc=pc1}
136 where pc1 = (pc m) + 1
137 executeInstruction (Jnz s d) m
138 | v == 0 = m {pc=pc1}
139 | otherwise = m {pc=pcj}
140 where pc1 = (pc m) + 1
144 executeInstruction (Tgl a) m
145 | v < (length $ instructions m) = m {instructions = (replace (instructions m) i' v),
147 | otherwise = m {pc=pc1}
149 v = evaluate m a + pc m
150 i = (instructions m)!!v
157 replace xs x i = take i xs ++ [x] ++ drop (i+1) xs
158 executeInstruction (Out _) m = m {pc = pc m + 1}
161 evaluate :: Machine -> Location -> Int
162 evaluate _ (Literal i) = i
163 evaluate m (Register r) =
170 writeValue :: Machine -> Location -> Int -> Machine
171 writeValue m (Literal i) _ = m
172 writeValue m (Register r) v =
180 instructionFile = instructionLine `sepEndBy` newline
181 instructionLine = incL <|> decL <|> cpyL <|> jnzL <|> tglL <|> outL
183 incL = Inc <$> (string "inc" *> spaces *> register)
184 decL = Dec <$> (string "dec" *> spaces *> register)
185 cpyL = Cpy <$> (string "cpy" *> spaces *> location) <*> (spaces *> register)
186 jnzL = Jnz <$> (string "jnz" *> spaces *> location) <*> (spaces *> location)
187 tglL = Tgl <$> (string "tgl" *> spaces *> location)
188 outL = Out <$> (string "out" *> spaces *> location)
190 location = (Literal <$> int) <|> register
191 register = Register <$> (oneOf "abcd")
193 parseIfile :: String -> Either ParseError [Instruction]
194 parseIfile input = parse instructionFile "(unknown)" input
196 parseIline :: String -> Either ParseError Instruction
197 parseIline input = parse instructionLine "(unknown)" input
199 successfulParse :: Either ParseError [a] -> [a]
200 successfulParse (Left _) = []
201 successfulParse (Right a) = a