-
Notifications
You must be signed in to change notification settings - Fork 0
/
Day16.fs
247 lines (206 loc) · 7.12 KB
/
Day16.fs
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
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
namespace Adventofcode2021
module Day16 =
[<Literal>]
let InputFile = "Day16Input.txt"
type Limit =
| BitLimit of int * int
| PackageLimit of int * int
type LengthTypeId =
| BitLength
| PacketCount
type TypeId =
| Sum
| Product
| Min
| Max
| LiteralValue
| Gt
| Lt
| Eq
type Content =
| LiteralValues of int64 list
| OperatorSubPackages of Packet list
and Packet =
{ Version: int
TypeId: TypeId
Contents: Content
LengthTypeId: int option }
let parseNibble =
function
| '0' -> [ 0; 0; 0; 0 ]
| '1' -> [ 0; 0; 0; 1 ]
| '2' -> [ 0; 0; 1; 0 ]
| '3' -> [ 0; 0; 1; 1 ]
| '4' -> [ 0; 1; 0; 0 ]
| '5' -> [ 0; 1; 0; 1 ]
| '6' -> [ 0; 1; 1; 0 ]
| '7' -> [ 0; 1; 1; 1 ]
| '8' -> [ 1; 0; 0; 0 ]
| '9' -> [ 1; 0; 0; 1 ]
| 'A' -> [ 1; 0; 1; 0 ]
| 'B' -> [ 1; 0; 1; 1 ]
| 'C' -> [ 1; 1; 0; 0 ]
| 'D' -> [ 1; 1; 0; 1 ]
| 'E' -> [ 1; 1; 1; 0 ]
| 'F' -> [ 1; 1; 1; 1 ]
| _ -> failwith "unknown nibble"
let parse (s: string) =
[ for i in 0 .. s.Length - 1 -> parseNibble s.[i] ]
|> List.concat
let parseFirst3Bits (bits: int list) =
bits.[0] * 4 + bits.[1] * 2 + bits.[2] * 1
let parseVersion = parseFirst3Bits
let parseTypeId bits =
match parseFirst3Bits bits with
| 0 -> Sum
| 1 -> Product
| 2 -> Min
| 3 -> Max
| 4 -> LiteralValue
| 5 -> Gt
| 6 -> Lt
| 7 -> Eq
| _ -> failwith "unsupported typeid"
let getGroups (bits: int list) =
let rec helper (bits: int list) groups =
let nextGroup = bits |> List.take 5
let bits' = bits |> List.skip 5
let groups' = List.append groups [ nextGroup |> List.skip 1 ]
let isLastGroup = nextGroup.[0] = 0
if isLastGroup then
groups'
else
helper bits' groups'
let groups = helper bits List.empty
let bits' = bits |> List.skip (groups.Length * 5)
groups, bits'
let bitsValue (bits: int list) =
seq {
for i in 0 .. bits.Length - 1 do
let factor = int64 (2. ** i)
let bit = int64 bits.[bits.Length - 1 - i]
yield bit * factor
}
|> Seq.sum
let valueFromGroups groups = List.concat groups |> bitsValue
let parseLiteralValue (bits: int list) =
let groups, bits' = getGroups bits
let value = valueFromGroups groups
value, bits'
let rec parseOperator (bits: int list) =
let lengthTypeId =
match bits.[0] with
| 0 -> BitLength
| 1 -> PacketCount
| _ -> failwith "bad bit"
let bits' = bits |> List.skip 1
match lengthTypeId with
| BitLength ->
let lengthBits = bits' |> List.take 15
let length = bitsValue lengthBits
let bits'' = bits' |> List.skip 15
let limit = Limit.BitLimit(0, int length) |> Some
let subPackages, bits''' = parsePackage limit List.empty bits''
subPackages, bits'''
| PacketCount ->
let lengthBits = bits' |> List.take 11
let length = bitsValue lengthBits
let bits'' = bits' |> List.skip 11
let limit = Limit.PackageLimit(0, int length) |> Some
let subPackages, bits''' = parsePackage limit List.empty bits''
subPackages, bits'''
and parsePackage (limit: Limit option) (packages: Packet list) (bits: int list) =
if bits.Length <= 7
&& bits |> List.forall (fun b -> b = 0) then
packages, List.empty
else
let version = bits |> List.take 3 |> parseVersion
let bits' = bits |> List.skip 3
let typeId = bits' |> List.take 3 |> parseTypeId
let bits'' = bits' |> List.skip 3
let (newPackages: Packet list), bits''' =
match typeId with
| TypeId.LiteralValue ->
let value, bits''' = parseLiteralValue bits''
let p: Packet =
{ Version = version
TypeId = typeId
Contents = LiteralValues [ value ]
LengthTypeId = None }
[ p ], bits'''
| _ ->
let subPackages, bits''' = parseOperator bits''
let p: Packet =
{ Version = version
TypeId = typeId
Contents = OperatorSubPackages subPackages
LengthTypeId = None }
[ p ], bits'''
let packages' = List.append packages newPackages
let limit', limitReached =
match limit with
| None -> None, false
| Some (Limit.BitLimit (x, n)) ->
let bitsParsed = bits.Length - bits'''.Length
let x' = x + bitsParsed
Some(Limit.BitLimit(x', n)), x' = n
| Some (Limit.PackageLimit (x, n)) ->
let x' = x + newPackages.Length
Some(Limit.PackageLimit(x', n)), x' = n
if List.isEmpty bits || limitReached then
packages', bits'''
else
parsePackage limit' packages' bits'''
let rec sumVersions sum (packages: Packet list) =
match packages with
| [] -> sum
| p :: rest ->
let sum' = sum + p.Version
let subSum =
match p.Contents with
| LiteralValues _ -> 0
| OperatorSubPackages subs -> sumVersions 0 subs
let sum'' = sum' + subSum
sumVersions sum'' rest
let day16 () =
InputFile
|> System.IO.File.ReadAllText
|> parse
|> parsePackage None List.empty
|> fst
|> sumVersions 0
let rec calc (p: Packet) =
let values = p.Contents |> getValues
match p.TypeId with
| Sum -> values |> List.reduce (+)
| Product -> values |> List.reduce (*)
| Min -> values |> List.min
| Max -> values |> List.max
| Gt ->
if values.[0] > values.[1] then
1L
else
0L
| Lt ->
if values.[0] < values.[1] then
1L
else
0L
| Eq ->
if values.[0] = values.[1] then
1L
else
0L
| LiteralValue -> values.[0]
and getValues (contents: Content) =
match contents with
| Content.LiteralValues v -> v
| Content.OperatorSubPackages p -> p |> List.map calc
let day16Part2 () =
InputFile
|> System.IO.File.ReadAllText
|> parse
|> parsePackage None List.empty
|> fst
|> List.head
|> calc