aboutsummaryrefslogtreecommitdiff
path: root/aoc-2022-dotnet/Day18/Program.fs
blob: 3784d0d5e6420fca4d5dcb7f1b22c7b600e75817 (plain)
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
module Day18

open System.IO
open FParsec
open Common

let parsePos =
    Util.parse (
        tuple3 (pint32 .>> pchar ',') (pint32 .>> pchar ',') pint32
        |>> Vec3
    )

let fullSurface cubes =
    cubes
    |> Seq.collect Vec3.neighbours6
    |> Seq.filter (Util.notIn cubes)
    |> Seq.length

let externalSurface cubes =
    let hasLava pos = Set.contains pos cubes

    let minBound = Seq.reduce (Vec3.combine min) cubes - Vec3.ones
    let maxBound = Seq.reduce (Vec3.combine max) cubes + Vec3.ones

    let neighbours pos =
        match hasLava pos with
        | true -> []
        | false -> List.filter (Vec3.boundedBy minBound maxBound) (Vec3.neighbours6 pos)

    let rec dfs visited count =
        function
        | pos :: stackTail ->
            let count' = count + if hasLava pos then 1 else 0

            match Set.contains pos visited with
            | true -> dfs visited count' stackTail
            | false -> dfs (Set.add pos visited) count' (neighbours pos @ stackTail)
        | [] -> count

    dfs Set.empty 0 [ minBound ]

let solution surface =
    Seq.map parsePos >> Set.ofSeq >> surface

let test = File.ReadLines("test.txt")
assert (solution fullSurface test = 64)
assert (solution externalSurface test = 2540)

let input = File.ReadLines("input.txt")
printfn "%d" <| solution fullSurface input
printfn "%d" <| solution externalSurface input