aboutsummaryrefslogtreecommitdiff
path: root/aoc-2022-dotnet/Day16/Program.fs
blob: a2c4b2ecdf2dcd1a80af54afc563b8c46c06d485 (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
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
module Day16

open System.IO
open FParsec
open Common

type ValveName = string

type Valve =
    { Flow: int
      Neighbours: ValveName list }

let parseValve =
    let pSingleOrMulti str = pstring str .>>. opt (pchar 's')
    let pValveName: Parser<ValveName, _> = anyString 2

    let pValve =
        pstring "Valve " >>. pValveName
        .>> pstring " has flow rate="
        .>>. pint32
        .>> pstring "; "
        .>> pSingleOrMulti "tunnel"
        .>> pchar ' '
        .>> pSingleOrMulti "lead"
        .>> pstring " to "
        .>> pSingleOrMulti "valve"
        .>> pchar ' '
        .>>. sepBy1 pValveName (pstring ", ")
        |>> (fun ((name, flow), neighbours) -> (name, { Flow = flow; Neighbours = neighbours }))

    Util.parse pValve

let distancesFrom valves start =
    let neighbours v = (Map.find v valves).Neighbours

    let rec bfsExplore visited acc =
        function
        | (v, depth) :: queue ->
            bfsExplore
                (Set.add v visited)
                (((start, v), depth) :: acc)
                (queue
                 @ (neighbours v
                    |> List.filter (Util.notIn visited)
                    |> List.map (fun n -> (n, depth + 1))))
        | [] -> acc

    bfsExplore Set.empty [] [ (start, 0) ]

let buildGraph input =
    let valves = input |> Seq.map parseValve |> Map.ofSeq

    let flows =
        valves
        |> Map.map (fun _ v -> v.Flow)
        |> Map.filter (fun v f -> f > 0 || v = "AA")

    let distances =
        valves
        |> Map.keys
        |> Seq.collect (distancesFrom valves)
        |> Map.ofSeq
        |> Map.filter (fun (f, t) _ ->
            f <> t
            && Map.containsKey f flows
            && Map.containsKey t flows)

    flows, distances

let solution input =
    let (flows, distances) = buildGraph input

    let rec findMaxPressure current remainingTime closedValves =
        let closedValves = closedValves |> Set.remove current

        closedValves
        |> Seq.choose (fun t ->
            let remainingTime = remainingTime - (distances[(current, t)] + 1)

            if remainingTime > 0 then
                Some(
                    flows[t] * remainingTime
                    + findMaxPressure t remainingTime closedValves
                )
            else
                None)
        |> Util.maxOrZero

    findMaxPressure "AA" 30 (flows |> Map.keys |> Set.ofSeq)

let test = File.ReadLines("test.txt")
assert (solution test = 1651)

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