-
Notifications
You must be signed in to change notification settings - Fork 0
/
advent21.hs
228 lines (192 loc) · 8.41 KB
/
advent21.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
221
222
223
224
225
226
227
228
{-# LANGUAGE NegativeLiterals #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE BangPatterns #-}
import Debug.Trace
-- import Prelude hiding ((++))
import Data.Text (Text)
import qualified Data.Text as T
import qualified Data.Text.IO as TIO
import Data.Void (Void)
import Text.Megaparsec hiding (State)
import Text.Megaparsec.Char
import qualified Text.Megaparsec.Char.Lexer as L
import qualified Control.Applicative as CA
import qualified Data.Map.Strict as M
import Data.Map.Strict ((!))
import qualified Data.Set as S
import Data.Bits ((.&.), (.|.))
import Control.Monad (when)
import Control.Monad.State.Strict
import Control.Monad.Reader
import Control.Monad.Writer
type Memory = M.Map Integer Integer
data Location = Literal Integer | Register Integer deriving (Show, Eq)
data Instruction =
Addr Integer Integer Integer
| Addi Integer Integer Integer
| Mulr Integer Integer Integer
| Muli Integer Integer Integer
| Banr Integer Integer Integer
| Bani Integer Integer Integer
| Borr Integer Integer Integer
| Bori Integer Integer Integer
| Setr Integer Integer Integer
| Seti Integer Integer Integer
| Gtir Integer Integer Integer
| Gtri Integer Integer Integer
| Gtrr Integer Integer Integer
| Eqir Integer Integer Integer
| Eqri Integer Integer Integer
| Eqrr Integer Integer Integer
deriving (Eq, Show, Ord)
data Machine = Machine { _registers :: M.Map Integer Integer
, _pc :: Int
, _history :: S.Set Integer
, _previous :: Integer
-- , _pcReg :: Integer
}
deriving (Show, Eq)
type ProgrammedMachine = WriterT [Integer] (ReaderT (Integer, [Instruction]) (State Machine)) ()
emptyMachine = Machine { _registers = M.fromList (zip [0..5] (repeat 0))
, _pc = 0
, _history = S.empty
, _previous = 0
}
main :: IO ()
main = do
text <- TIO.readFile "data/advent21.txt"
let (ip, instrs) = successfulParse text
-- print (ip, instrs)
-- print $ zip [0..] instrs
print $ part1 ip instrs
print $ part2 ip instrs
part1 ip instructions =
runState (
runReaderT (
runWriterT executeInstructions1
)
(ip, instructions)
)
emptyMachine
part2 ip instructions =
runState (
runReaderT (
runWriterT executeInstructions2
)
(ip, instructions)
)
emptyMachine
-- part2 ip instructions = head (dropWhile terminates [11592302..]) - 1
-- part2 ip instructions = terminates 11592301
-- where emptyRegisters = _registers emptyMachine
-- m2 reg0 = emptyMachine {_registers = M.insert 0 reg0 emptyRegisters}
-- terminates reg0 = null $ runResult (m2 reg0) ip instructions
-- runResult machine ip instructions = r1Repeat
-- where
-- r1Repeat = snd $ fst $ result
-- result =
-- runState (
-- runReaderT (
-- runWriterT executeInstructions2
-- )
-- (ip, instructions)
-- )
-- machine
executeInstructions1 =
do (_, instrs) <- ask
m <- get
if (_pc m == 28) then do
tell [(_registers m)!1]
else do
when (_pc m >= 0 && _pc m < length instrs)
$
do executeInstruction
executeInstructions1
executeInstructions2 =
do (_, instrs) <- ask
m0 <- get
let r1 = (trace ("R1 = " ++ (show $ (_registers m0)!1) ++ " :: " ++ (show $ S.size (_history m0)))) $ (_registers m0)!1
if (_pc m0 == 28 && (S.member r1 (_history m0))) then do
-- abort as found a loop
tell $ [_previous m0]
else do
when (_pc m0 == 28)
$
do
let m0' = m0 { _history = S.insert ((_registers m0)!1) (_history m0)
, _previous = (_registers m0)!1 }
-- let x = trace ("PC = 28, register 1 = " ++ (show ((_registers m0)!1))) $! True
put m0'
m <- get
when (_pc m >= 0 && _pc m < length instrs)
$
do executeInstruction
executeInstructions2
executeInstruction :: ProgrammedMachine
executeInstruction =
do (pcIs, instrs) <- ask
m <- get
let instr = instrs!!(_pc m)
let memory0 = _registers m
let memory1 = M.insert pcIs (fromIntegral (_pc m)) memory0
let memory2 = perform instr memory1
let pc' = fromIntegral ((memory2!pcIs) + 1)
-- let aaa = trace ("pc: " ++ show (_pc m) ++ " m0: " ++ show memory0 ++ " m1: " ++ show memory1 ++ "m2: " ++ show memory2 ++ "pc': " ++ show pc') $! True
let m' = m {_registers = memory2, _pc = pc'}
put m'
perform :: Instruction -> Memory -> Memory
-- perform instr memory | ((memory!5 == 7) || ((memory!5 == 3) && (memory!1 == 1))) && (trace ("Perform " ++ show instr ++ " " ++ show memory) False) = undefined
-- perform instr memory | trace ("Perform " ++ show instr ++ " " ++ show memory) False = undefined
perform (Addr a b c) !memory = M.insert c (memory!a + memory!b) memory
perform (Addi a b c) !memory = M.insert c (memory!a + b) memory
perform (Mulr a b c) !memory = M.insert c (memory!a * memory!b) memory
perform (Muli a b c) !memory = M.insert c (memory!a * b) memory
perform (Banr a b c) !memory = M.insert c (memory!a .&. memory!b) memory
perform (Bani a b c) !memory = M.insert c (memory!a .&. b) memory
perform (Borr a b c) !memory = M.insert c (memory!a .|. memory!b) memory
perform (Bori a b c) !memory = M.insert c (memory!a .|. b) memory
perform (Setr a b c) !memory = M.insert c (memory!a) memory
perform (Seti a b c) !memory = M.insert c a memory
perform (Gtir a b c) !memory = M.insert c (if a > (memory!b) then 1 else 0) memory
perform (Gtri a b c) !memory = M.insert c (if (memory!a) > b then 1 else 0) memory
perform (Gtrr a b c) !memory = M.insert c (if (memory!a) > (memory!b) then 1 else 0) memory
perform (Eqir a b c) !memory = M.insert c (if a == memory!b then 1 else 0) memory
perform (Eqri a b c) !memory = M.insert c (if (memory!a) == b then 1 else 0) memory
perform (Eqrr a b c) !memory = M.insert c (if (memory!a) == (memory!b) then 1 else 0) memory
-- evaluate :: Machine -> Location -> Integer
-- evaluate _ (Literal i) = i
-- evaluate m (Register r) = M.findWithDefault 0 r (registers m)
type Parser = Parsec Void Text
sc :: Parser ()
sc = L.space (skipSome spaceChar) CA.empty CA.empty
lexeme = L.lexeme sc
integer = lexeme L.decimal
symb = L.symbol sc
instructionsP = (,) <$> headerP <*> many instructionP
instructionP = choice [ addrP, addiP, mulrP, muliP, banrP, baniP,
borrP, boriP, setrP, setiP, gtirP, gtriP, gtrrP,
eqirP, eqriP, eqrrP ]
headerP = symb "#ip" *> integer
addrP = Addr <$> (try (symb "addr") *> integer) <*> integer <*> integer
addiP = Addi <$> (try (symb "addi") *> integer) <*> integer <*> integer
mulrP = Mulr <$> (try (symb "mulr") *> integer) <*> integer <*> integer
muliP = Muli <$> (try (symb "muli") *> integer) <*> integer <*> integer
banrP = Banr <$> (try (symb "banr") *> integer) <*> integer <*> integer
baniP = Bani <$> (try (symb "bani") *> integer) <*> integer <*> integer
borrP = Borr <$> (try (symb "borr") *> integer) <*> integer <*> integer
boriP = Bori <$> (try (symb "bori") *> integer) <*> integer <*> integer
setrP = Setr <$> (try (symb "setr") *> integer) <*> integer <*> integer
setiP = Seti <$> (try (symb "seti") *> integer) <*> integer <*> integer
gtirP = Gtir <$> (try (symb "gtir") *> integer) <*> integer <*> integer
gtriP = Gtri <$> (try (symb "gtri") *> integer) <*> integer <*> integer
gtrrP = Gtrr <$> (try (symb "gtrr") *> integer) <*> integer <*> integer
eqirP = Eqir <$> (try (symb "eqir") *> integer) <*> integer <*> integer
eqriP = Eqri <$> (try (symb "eqri") *> integer) <*> integer <*> integer
eqrrP = Eqrr <$> (try (symb "eqrr") *> integer) <*> integer <*> integer
successfulParse :: Text -> (Integer, [Instruction])
successfulParse input =
case parse instructionsP "input" input of
Left _error -> (0, []) -- TIO.putStr $ T.pack $ parseErrorPretty err
Right instructions -> instructions