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

open System.IO
open System.Numerics
open FSharpPlus
open FParsec
open Common

type MonkeyName = string

module MonkeyName =
    let root = "root"
    let human = "humn"

type Operator = Complex -> Complex -> Complex

module Operator =
    let fromChar =
        function
        | '+' -> (+)
        | '-' -> (-)
        | '*' -> (*)
        | '/' -> (/)
        | c -> failwithf "Invalid operator: %c" c

    let unknownExtractor l r =
        let eq: Complex = l - r
        Complex(round <| -eq.Real / eq.Imaginary, 0)

type MonkeyJob =
    | Number of Complex
    | Operation of MonkeyName * Operator * MonkeyName

    static member unknownVariable = Number(Complex(0, 1))

    static member eval(cache: Map<MonkeyName, Complex>) =
        function
        | Number num -> num
        | Operation (left, operator, right) -> operator cache[left] cache[right]

    static member withOperator op =
        function
        | Operation (l, _, r) -> Operation(l, op, r)
        | other -> other

    static member dependencies =
        function
        | Operation (l, _, r) -> [ l; r ]
        | _ -> []

type Monkey = MonkeyName * MonkeyJob

module Monkey =
    let parse =
        let pws = pchar ' '
        let pname = anyString 4 |>> MonkeyName
        let pop = pws >>. anyChar .>> pws |>> Operator.fromChar
        let pnumber = pfloat |>> (fun n -> Number(Complex(n, 0)))
        let poperation = tuple3 pname pop pname |>> Operation
        let pjob = pnumber <|> poperation
        let pmonkey = pname .>> pstring ": " .>>. pjob |>> Monkey
        Util.parse pmonkey

let solution modification input =
    let monkeys =
        input
        |> Seq.map Monkey.parse
        |> Map.ofSeq
        |> modification

    let nodeValues =
        monkeys
        |> Map.mapValues MonkeyJob.dependencies
        |> Util.tsort
        |> List.fold (fun cache name -> Map.add name (MonkeyJob.eval cache monkeys[name]) cache) Map.empty

    int64 nodeValues[MonkeyName.root].Real

let part1 = id

let part2 =
    Map.add MonkeyName.human MonkeyJob.unknownVariable
    >> Map.change MonkeyName.root (Option.map (MonkeyJob.withOperator Operator.unknownExtractor))

let test = File.ReadLines("test.txt")
assert (solution part1 test = 152)
assert (solution part2 test = 301)

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