-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathDay23.hs
146 lines (121 loc) · 3.47 KB
/
Day23.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
module Main where
import Parser
import Utilities
import Control.Applicative
import Data.Map (Map, (!))
import qualified Data.Map as Map
data Reg = A | B | C | D
deriving (Show, Eq, Ord, Enum, Bounded)
type Offset = Int
data Value = Value Int | Reg Reg
deriving Show
data Instruction
= Copy Value Value
| Incr Value
| Decr Value
| JNZ Value Value
| Toggle Value
deriving Show
type CodeAddr = Int
type Code = Map CodeAddr Instruction
type Input = Code
parse :: String -> Input
parse = Map.fromList . zip [0..] . map (runParser instruction) . lines
where
instruction =
Copy <$ string "cpy " <*> val <* char ' ' <*> val <|>
Incr <$ string "inc " <*> val <|>
Decr <$ string "dec " <*> val <|>
JNZ <$ string "jnz " <*> val <* char ' ' <*> val <|>
Toggle <$ string "tgl " <*> val
val = Value <$> int <|> Reg <$> reg
reg = A <$ char 'a' <|> B <$ char 'b' <|> C <$ char 'c' <|> D <$ char 'd'
toggle :: Instruction -> Instruction
toggle (Copy src dest) = JNZ src dest
toggle (Incr v) = Decr v
toggle (Decr v) = Incr v
toggle (JNZ val off) = Copy val off
toggle (Toggle off) = Incr off
type Registers = Map Reg Int
data State = State CodeAddr Registers Code
deriving Show
value :: Value -> Registers -> Int
value (Value n) _ = n
value (Reg r) regs = regs!r
zeroRegisters :: Registers
zeroRegisters = Map.fromList [(r, 0) | r <- allValues]
initState :: Code -> State
initState code = State 0 (Map.insert A 7 zeroRegisters) code
fetch :: State -> Maybe Instruction
fetch (State pc _regs code) = Map.lookup pc code
execute :: Instruction -> State -> State
execute (Copy val (Reg r)) (State pc regs code) =
State (pc+1) (Map.insert r (value val regs) regs) code
execute (Incr (Reg r)) (State pc regs code) =
State (pc+1) (Map.adjust (+1) r regs) code
execute (Decr (Reg r)) (State pc regs code) =
State (pc+1) (Map.adjust (subtract 1) r regs) code
execute (JNZ val addr) (State pc regs code)
| value val regs /= 0 = State (pc+offset) regs code
| otherwise = State (pc+1) regs code
where
offset = value addr regs
execute (Toggle addr) (State pc regs code) =
State (pc+1) regs (Map.adjust toggle (pc+offset) code)
where
offset = value addr regs
execute _ (State pc regs code) = State (pc+1) regs code
run :: State -> Registers
run s0 = regs
where
State _ regs _ = whileJust step s0
step s = do
instr <- fetch s
return (execute instr s)
solve1 :: Input -> Int
solve1 code = run (initState code) ! A
run_code :: Input -> Int -> Registers
run_code code i = run (State 0 (Map.insert A i zeroRegisters) code)
testInput :: String
testInput =
"cpy 2 a\n\
\tgl a\n\
\tgl a\n\
\tgl a\n\
\cpy 1 a\n\
\dec a\n\
\dec a\n"
tests1 :: [(String, Int)]
tests1 = [(testInput, 3)]
-- Part Two --
{-
Pseudocode of first 19 instructions:
a := n
FOR i := n-1 DOWNTO 1 DO
a := a*i
toggle instruction 16+2*i
When n >= 6, this exits the first loop with the code from 18 changed to:
18 cpy 1 c
19 cpy 73 c
20 cpy 80 d
21 inc a
22 dec d
23 jnz d -2
24 dec c
25 jnz c -5
which is equivalent to
a := a + 73*80
-}
combination :: Int -> Int
combination n
| n < 6 = error "loops forever"
| otherwise = product [1..n] + 73*80
solve2 :: Input -> Int
solve2 _ = combination 12
main :: IO ()
main = do
s <- readFile "input/23.txt"
let input = parse s
putStr (unlines (failures "solve1" (solve1 . parse) tests1))
print (solve1 input)
print (solve2 input)