-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathDay11.fsx
212 lines (169 loc) · 6.28 KB
/
Day11.fsx
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
open System.IO
type OperationArgument =
| Old
| Constant of uint64
type Operation =
{ FirstArgument: OperationArgument
SecondArgument: OperationArgument
Action: uint64 -> uint64 -> uint64 }
type Monkey =
{ Id: int
Throws: uint64
Items: uint64 list
Operation: Operation
TestNumber: uint64
Test: uint64 -> uint64 }
let getMonkeyId (line: string) =
line.Split(" ").[1].Replace(":", "") |> int
let skipPattern (pattern: string) (line: string) =
line.Substring(pattern.Length, line.Length - pattern.Length)
let getStartingItems (line: string) =
let pattern = "Starting items: "
let idsParts = line |> skipPattern pattern
idsParts.Split(", ")
|> Array.map (fun i -> uint64 i)
|> List.ofArray
let getOperation (line: string) =
let pattern = "Operation: "
match line
|> skipPattern pattern
|> (fun l -> l.Split " ")
with
| [| "new"; "="; firstArgument; operator; secondArgument |] ->
let getArgument arg =
if arg = "old" then
Old
else
Constant(uint64 arg)
{ FirstArgument = getArgument firstArgument
SecondArgument = getArgument secondArgument
Action =
match operator with
| "+" -> (fun a b -> a + b)
| "-" -> (fun a b -> a - b)
| "/" -> (fun a b -> a / b)
| "*" -> (fun a b -> a * b)
| _ -> failwith $"invalid operator :%s{operator}" }
| _ -> failwith $"wrong input %A{line}"
let getTest (testLines: string []) =
let conditionPattern = "Test: divisible by "
let throwIfTruePattern =
"If true: throw to monkey "
let throwIfFalsePattern =
"If false: throw to monkey "
match testLines with
| [| condition; throwIfTrue; throwIfFalse |] when
condition.StartsWith(conditionPattern)
&& throwIfTrue.StartsWith(throwIfTruePattern)
&& throwIfFalse.StartsWith(throwIfFalsePattern)
->
let conditionValue =
condition |> skipPattern conditionPattern |> uint64
let trueIndex =
throwIfTrue
|> skipPattern throwIfTruePattern
|> uint64
let falseIndex =
throwIfFalse
|> skipPattern throwIfFalsePattern
|> uint64
let checkFunc =
(fun value ->
if value % conditionValue = 0UL then
trueIndex
else
falseIndex)
(checkFunc, conditionValue)
| _ -> failwith $"invalid test block %A{testLines}"
let processRound worryLevelRegulation (inputMonkeys: Monkey list) =
let rec loopMonkeys currentMonkey (monkeys: Monkey list) =
let replace items monkey =
let index =
items
|> List.findIndex (fun m -> m.Id = monkey.Id)
let before = items |> List.take index
let after = items |> List.skip (index + 1)
before @ [ monkey ] @ after
let recipients =
currentMonkey.Items
|> List.map (fun worry ->
let getArgumentValue arg =
match arg with
| Constant c -> c
| Old -> worry
let firstArgument =
getArgumentValue currentMonkey.Operation.FirstArgument
let secondArgument =
getArgumentValue currentMonkey.Operation.SecondArgument
let operationResult =
currentMonkey.Operation.Action firstArgument secondArgument
let afterRegulation =
worryLevelRegulation operationResult
let nextMonkey =
currentMonkey.Test afterRegulation
(nextMonkey, afterRegulation))
|> List.groupBy (fun (i, _) -> i)
|> List.map (fun (index, throws) ->
let recipient =
monkeys |> List.find (fun m -> uint64 m.Id = index)
let itemsToAdd =
throws |> List.map (fun (_, value) -> value)
{ recipient with Items = recipient.Items @ itemsToAdd })
let newMonkeyState =
{ currentMonkey with
Throws = currentMonkey.Throws + uint64 currentMonkey.Items.Length
Items = [] }
let itemsToReplace =
[ newMonkeyState ] @ recipients
let newState =
itemsToReplace
|> List.fold (fun items m -> m |> replace items) monkeys
if currentMonkey.Id + 1 = monkeys.Length then
newState
else
loopMonkeys newState.[currentMonkey.Id + 1] newState
loopMonkeys inputMonkeys.Head inputMonkeys
let parsedInput =
File.ReadAllLines "Data/Day11.txt"
|> Array.chunkBySize 7
|> Array.map (fun l ->
l
|> Array.map (fun l -> l.Trim())
|> Array.takeWhile (fun l -> l <> ""))
|> List.ofArray
|> List.map (fun group ->
match group with
| [| index; startItems; operation; testValue; throwIfTrue; throwIfFalse |] ->
let testFunc, testValue =
getTest [| testValue
throwIfTrue
throwIfFalse |]
{ Id = getMonkeyId index
Items = getStartingItems startItems
Operation = getOperation operation
Throws = 0UL
TestNumber = testValue
Test = testFunc }
| _ -> failwith $"incorrect group %A{group}")
let rec repeatIterations iterations worryRegulation input =
if iterations > 0 then
let result =
processRound worryRegulation input
result
|> repeatIterations (iterations - 1) worryRegulation
else
input
let solve input worryLevelFunc iterations =
input
|> repeatIterations iterations worryLevelFunc
|> List.sortByDescending (fun l -> l.Throws)
|> List.take 2
|> List.fold (fun acc mon -> acc * mon.Throws) 1UL
let part1 = solve parsedInput (fun w -> w / 3UL) 20
let part2 =
let sum =
parsedInput
|> List.map(fun monkey -> monkey.TestNumber)
|> List.fold(fun acc n -> n*acc)1UL
printfn $"SUM {sum}"
solve parsedInput (fun inp -> inp % sum) 10000