-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathDay19 🚜.fs
238 lines (193 loc) · 7.16 KB
/
Day19 🚜.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
module Day19
let day = "19"
open System
open System.IO
let nl = Environment.NewLine
type Grid<'a when 'a : equality>(jagged: 'a[][]) =
let data = jagged
let maxX = (Array.length (data.[0])) - 1
let maxY = (Array.length data) - 1
let mutable formatItem = (fun x -> x.ToString())
static member Generate<'a when 'a : equality>
(width, height, gen: int -> int -> 'a) =
[| for y in 0 .. (height - 1) do
[| for x in 0 .. (width - 1) do
gen x y |] |]
|> Grid<'a>
member _.Item
with get(x, y) = data.[y].[x]
and set(x, y) v = data.[y].[x] <- v
member _.FormatItem with get() = formatItem and set(f) = formatItem <- f
member this.AsText(x, y) = this.FormatItem (this.Item(x, y))
member _.Row with get(y) = data.[y]
member this.FormatRow = Array.map this.FormatItem >> (String.concat "")
member this.AsText(y) = this.FormatRow (this.Row(y))
member this.FormatGrid = Array.map this.FormatRow >> (String.concat nl)
member this.AsText() = this.FormatGrid data
override this.ToString() = this.AsText()
member this.Display() = printfn "%s" (this.AsText())
member this.TeeDisplay() = this.Display(); this
member _.InBounds(x, y) = x >= 0 && x <= maxX && y >=0 && y <= maxY
member this.Copy() = this.Transform (fun g x y -> g.[x,y])
member this.Flatern() =
seq{ for y in 0 .. maxY do
for x in 0 .. maxX do
yield ((x, y), data.[y].[x]) }
member _.Coords() =
seq{ for y in 0 .. maxY do
for x in 0 .. maxX do
yield (x, y) }
member this.Filter(pred) = this.Flatern() |> Seq.filter (snd >> pred)
member this.Find(pred) = this.Filter(pred) |> Seq.head |> fst
member this.NHood(x, y) =
[| for x in (x - 1)..(x + 1) do
for y in (y - 1)..(y + 1) do
if this.InBounds (x, y)
then Some this.[x,y]
else None |]
member this.Adjacent(x, y) =
let nhood = this.NHood (x, y)
Array.append nhood.[0 .. 3] nhood.[5 .. 8]
member this.Bordering(x, y) =
[| this.TryGet (x, y - 1);
this.TryGet (x + 1, y);
this.TryGet (x, y + 1);
this.TryGet (x - 1, y); |]
member this.Transform<'b when 'b : equality>
(generate: Grid<'a> -> int -> int -> 'b) : Grid<'b> =
[| for y in 0 .. maxY do
[| for x in 0 .. maxX do
generate this x y |] |]
|> Grid<'b>
member _.Corners() = [| (0, 0); (0, maxY); (maxX, maxY); (maxX, 0) |]
member this.Get((x, y)) = this.[x, y]
member this.Set((x, y)) value = this.[x, y] <- value
member this.TryGet((x, y)) =
match this.InBounds(x, y) with
| true -> Some (this.Get((x, y)))
| false -> None
type Field = Grid<char>
let fieldGrid =
Array.map (fun (s: string) -> s.ToCharArray())
>> Field
let readLines day =
Path.Combine("inputs", "input" + day + ".txt")
|> File.ReadAllLines
let lines = readLines day
let code = lines.[0]
let compile (str: string) = str.Split "," |> Array.map int64
let computer program readInput writeOutput =
let mutable relBase = 0
let memMB = 1
let memory = Array.init (memMB * 1_024 * 1_024) (fun _ -> 0L)
let read (addr: int) = memory.[int addr]
let write (addr: int) (value: int64) = memory.[addr] <- value
let halt ptr = read ptr = 99L
let posDE n = n % 100
let posC n = n % 1000 / 100
let posB n = n % 10000 / 1000
let posA n = n % 100000 / 10000
let opCode = read >> int >> posDE
let readArg offset modeFlag ptr =
let addr = ptr + offset
match ptr |> (read >> int >> modeFlag) with
| 0 -> read (int (read addr))
| 1 -> read addr
| 2 -> read ((int (read addr)) + relBase)
| u -> failwithf "Unexpected mode flag: %i (ptr: %i)" u ptr
let arg1 = readArg 1 posC
let arg2 = readArg 2 posB
let arg3 = readArg 3 posA
let writeArg offset modeFlag ptr value =
let addr = ptr + offset
match ptr |> (read >> int >> modeFlag) with
| 0 -> write (int (read addr)) value
| 1 -> failwith "Oh! Oh! Oh!"
| 2 -> write ((int (read addr)) + relBase) value
| u -> failwithf "Unexpected mode flag: %i (ptr: %i)" u ptr
let write1 = writeArg 1 posC
let write2 = writeArg 2 posB
let write3 = writeArg 3 posA
let add ptr = arg1 ptr + arg2 ptr |> write3 ptr; ptr + 4
let mult ptr = arg1 ptr * arg2 ptr |> write3 ptr; ptr + 4
let input ptr = readInput () |> write1 ptr; ptr + 2
let output ptr = writeOutput (arg1 ptr); ptr + 2
let jumpIfTrue ptr = if arg1 ptr <> 0L then int (arg2 ptr) else ptr + 3
let jumpIfFalse ptr = if arg1 ptr = 0L then int (arg2 ptr) else ptr + 3
let lessThan ptr =
(if arg1 ptr < arg2 ptr then 1L else 0L) |> write3 ptr; ptr + 4
let equals ptr =
(if arg1 ptr = arg2 ptr then 1L else 0L) |> write3 ptr; ptr + 4
let shiftRB ptr = relBase <- relBase + (int (arg1 ptr)); ptr + 2
let operation ptr =
match opCode ptr with
| 1 -> add
| 2 -> mult
| 3 -> input
| 4 -> output
| 5 -> jumpIfTrue
| 6 -> jumpIfFalse
| 7 -> lessThan
| 8 -> equals
| 9 -> shiftRB
| u -> failwithf "Unexpected opCode: %i (ptr: %i)" u ptr
let runToHalt () =
let rec run ptr =
if halt ptr then false else run ((operation ptr) ptr)
run 0
program |> Array.iteri write
runToHalt
let controlInverter () =
let mutable (inputList: int64 list) = []
let mutable (resp: int64) = -2L
let provideInput = fun () ->
let input = List.head inputList
inputList <- List.tail inputList
input
let handleOutput out = resp <- out
let sendInstr run inputs =
inputList <- inputs
run () |> ignore
resp
provideInput, handleOutput, sendInstr
let droneApi program (x: int) (y: int) =
let input, output, sendInstr = controlInverter ()
let run = computer program input output
match sendInstr run [int64 x; int64 y] with
| 0L -> '.'
| 1L -> '#'
| _ -> failwith "oops!"
let drone program =
let input, output, sendInstr = controlInverter ()
let run = computer program input output
droneApi program
let fits drone size (x, y) =
let diff = size - 1
if y >= diff && (drone (x + diff) (y - diff)) = '#'
then Some (x * 10_000 + (y - diff))
else None
let next drone x y =
// assert (drone (x + 1) (y + 1) = '#')
if drone x (y + 1) = '#'
then (x, y + 1)
else (x + 1, y + 1)
let start drone size =
let diff = size - 1
[0 .. diff]
|> List.map (fun x -> (x, diff))
|> List.filter (fun (x, y) -> drone x y = '#')
|> List.head
let leftEdge drone =
Seq.unfold
(fun (x, y) ->
let next = next drone x y
Some (next, next))
let Part1 () =
let drone = drone (compile code)
(Grid.Generate (50, 50, drone))
.Filter ((=) '#')
|> Seq.length
let Part2 () =
let drone = drone (compile code)
leftEdge drone (start drone 100)
|> Seq.pick (fits drone 100)