-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathDay5.hs
176 lines (147 loc) · 4.65 KB
/
Day5.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
module Day5 where
import Utils (wordsWhen, replace, digits, init')
data Status
= Halted
| Running
| Waiting
deriving (Show, Eq)
data Computer =
Computer
{ _memory :: [Int]
, _pos :: Int
, _status :: Status
, _inputs :: [Int]
, _outputs :: [Int]
}
deriving Show
mkComputer :: [Int] -> Computer
mkComputer memory =
Computer { _memory = memory
, _pos = 0
, _status = Running
, _inputs = []
, _outputs = []
}
data ParameterMode
= Position
| Immediate
deriving Show
getParameterMode :: Int -> ParameterMode
getParameterMode digit =
case digit of
0 -> Position
1 -> Immediate
_ -> error ("invalid parameter mode: " ++ show digit)
getParameterModes :: Int -> [ParameterMode]
getParameterModes opcode =
let modes = reverse $ map getParameterMode $ init' $ init' $ digits opcode
padding = replicate (3 - length modes) Position
in modes ++ padding
getOpcode :: Int -> Int
getOpcode = (`mod` 100)
(!!!) :: [Int] -> (Int, ParameterMode) -> Int
l !!! (i, pm) =
case pm of
Position -> l !! (l !! i)
Immediate -> l !!i
type Operation = Computer -> Computer
operationBin :: (Int -> Int -> Int) -> Operation
operationBin op computer =
let memory = _memory computer
pos = _pos computer
[inMode1, inMode2, _] = getParameterModes (memory !! pos)
in1 = memory !!! (pos + 1, inMode1)
in2 = memory !!! (pos + 2, inMode2)
out1 = memory !! (pos + 3)
memory' = replace out1 (op in1 in2) memory
in computer { _memory = memory', _pos = pos + 4 }
operationAdd :: Operation
operationAdd = operationBin (+)
operationMul :: Operation
operationMul = operationBin (*)
operationInput :: Operation
operationInput computer =
let memory = _memory computer
pos = _pos computer
out1 = memory !! (pos + 1)
(input:inputs') = _inputs computer
memory' = replace out1 input memory
in
if null (_inputs computer)
then computer { _status = Waiting }
else computer { _memory = memory', _pos = pos + 2, _inputs = inputs' }
operationOutput :: Operation
operationOutput computer =
let memory = _memory computer
pos = _pos computer
inMode1 = head $ getParameterModes (memory !! pos)
in1 = memory !!! (pos + 1, inMode1)
in computer { _pos = pos + 2, _outputs = _outputs computer ++ [in1] }
operationJumpIf :: (Int -> Bool) -> Operation
operationJumpIf op computer =
let memory = _memory computer
pos = _pos computer
[inMode1, inMode2, _] = getParameterModes (memory !! pos)
in1 = memory !!! (pos + 1, inMode1)
in2 = memory !!! (pos + 2, inMode2)
pos' =
if op in1
then in2
else pos + 3
in computer { _pos = pos' }
operationJumpIfTrue :: Operation
operationJumpIfTrue = operationJumpIf (/= 0)
operationJumpIfFalse :: Operation
operationJumpIfFalse = operationJumpIf (== 0)
operationCompare :: (Int -> Int -> Bool) -> Operation
operationCompare op computer =
let memory = _memory computer
pos = _pos computer
[inMode1, inMode2, _] = getParameterModes (memory !! pos)
in1 = memory !!! (pos + 1, inMode1)
in2 = memory !!! (pos + 2, inMode2)
out1 = memory !! (pos + 3)
memory' = replace out1 (fromEnum $ op in1 in2) memory
in computer { _memory = memory', _pos = pos + 4 }
operationLessThan :: Operation
operationLessThan = operationCompare (<)
operationEquals :: Operation
operationEquals = operationCompare (==)
operationHalt :: Operation
operationHalt computer = computer { _status = Halted }
getNext :: Computer -> Operation
getNext computer =
let opcode = getOpcode (_memory computer !! _pos computer)
in case opcode of
1 -> operationAdd
2 -> operationMul
3 -> operationInput
4 -> operationOutput
5 -> operationJumpIfTrue
6 -> operationJumpIfFalse
7 -> operationLessThan
8 -> operationEquals
99 -> operationHalt
_ -> error ("invalid opcode: " ++ show opcode)
runNext :: Operation
runNext computer = getNext computer computer
runProgram :: Operation
runProgram computer =
let computer' = runNext computer
in case _status computer' of
Halted -> computer'
Running -> runProgram computer'
Waiting -> computer' { _status = Running }
runWithInput :: Int -> [Int] -> [Int]
runWithInput id ints =
_outputs $ runProgram $ (mkComputer ints) { _inputs = [id] }
part1 :: [Int] -> [Int]
part1 = runWithInput 1
part2 :: [Int] -> [Int]
part2 = runWithInput 5
main :: IO ()
main = do
contents <- readFile "input/day5.txt"
let ints = map read (wordsWhen (== ',') contents)
putStrLn ("Part 1: " ++ show (part1 ints))
putStrLn ("Part 2: " ++ show (part2 ints))