-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathrun.hs
156 lines (134 loc) · 3.91 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
{-# LANGUAGE BangPatterns #-}
{-# LANGUAGE BlockArguments #-}
import AoC.Parse (numP)
import Control.Monad (when)
import Control.Monad.State (State, execState, get, gets, modify')
import Data.HashMap.Strict (HashMap)
import qualified Data.HashMap.Strict as HashMap
import Data.Void (Void)
import Text.Megaparsec hiding (State)
import Text.Megaparsec.Char
type Parser = Parsec Void String
unsafeRight :: Show a => Either a b -> b
unsafeRight (Right x) = x
unsafeRight (Left x) = error $ show x
type Reg = Char
data ProgramState = PState { program :: HashMap Int (Program ())
, pointer :: Int
, programLength :: Int
, regs :: HashMap Reg Int
, operations :: HashMap String Int }
type Program = State ProgramState
setReg :: Reg -> Int -> ProgramState -> ProgramState
setReg r v s = s { regs = HashMap.insert r v (regs s) }
regLookup :: Reg -> HashMap Reg Int -> Int
regLookup = HashMap.findWithDefault 0
incrementOp :: String -> ProgramState -> ProgramState
incrementOp str s =
let ops = operations s
prev = HashMap.findWithDefault 0 str ops
in
s { operations = HashMap.insert str (prev + 1) ops }
reg :: Reg -> ProgramState -> Int
reg r = regLookup r . regs
regValP :: Parser (Program Int)
regValP = (pure <$> numP) <|> (gets . reg <$> asciiChar)
operation :: (Int -> Int -> Int)
-> Reg
-> Program Int
-> Program ()
operation op x y = do
vx <- gets (reg x)
vy <- y
modify' (setReg x (vx `op` vy))
operationP :: (Int -> Int -> Int)
-> String
-> Parser (Program ())
operationP op str = do
_ <- string str
_ <- spaceChar
x <- asciiChar
_ <- spaceChar
y <- regValP
pure do
operation op x y
modify' (incrementOp str)
jnzP :: Parser (Program ())
jnzP = do
_ <- string "jnz "
x <- regValP
_ <- spaceChar
y <- regValP
pure do
vx <- x
when (vx /= 0) do
vy <- y
modify' (\s -> s { pointer = pointer s + vy - 1 })
modify' $ incrementOp "jnz"
setP :: Parser (Program ())
setP = do
_ <- string "set "
x <- asciiChar
_ <- spaceChar
y <- regValP
pure do
vy <- y
modify' (incrementOp "set" . setReg x vy)
mulP :: Parser (Program ())
mulP = operationP (*) "mul"
subP :: Parser (Program ())
subP = operationP (-) "sub"
modP :: Parser (Program ())
modP = operationP mod "mod"
parseInstruction :: Parser (Program ())
parseInstruction =
choice [ setP
, mulP
, modP
, jnzP
, subP
]
parseAll :: String -> [Program ()]
parseAll =
map unsafeRight
. map (parse parseInstruction "")
. lines
eval :: Program ()
eval = do
s <- get
case HashMap.lookup (pointer s) (program s) of
Just instr -> do
instr
modify' (\s' -> s' { pointer = pointer s' + 1 })
eval
Nothing -> pure ()
part1 :: [Program ()] -> Int
part1 instr =
let initial = PState { pointer = 0
, program = HashMap.fromList (zip [0..] instr)
, programLength = length instr
, regs = HashMap.empty
, operations = HashMap.empty }
final = execState eval initial
in
operations final HashMap.! "mul"
-- Translated code, not sure if it's even worth it to come up with a
-- general solution. See disassembled.py for the imperative version.
part2 :: a -> Int
part2 _ = go 1 108400 125400 0
where go :: Int -> Int -> Int -> Int -> Int
go !a !b !c !h =
case (check b, b == c) of
(False, False) -> go a (b + 17) c h
(True, False) -> go a (b + 17) c (h + 1)
(False, True) -> h
(True, True) -> h + 1
check b = any (p b) [2..b-1]
p b d =
let (q, r) = b `divMod` d
in r == 0 && q >= 2
main :: IO ()
main = do
input <- parseAll <$> readFile "input.txt"
print (part1 input)
print (part2 input)