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

#nowarn "0342"

open System
open System.IO
open FParsec
open Common

[<StructuralEquality; CustomComparison>]
type Packet =
    | Integer of int
    | List of Packet list

    static member dividers =
        [ Packet.parse "[[2]]"
          Packet.parse "[[6]]" ]

    interface IComparable with
        member this.CompareTo other =
            match other with
            | :? Packet as p -> (this :> IComparable<_>).CompareTo p
            | _ -> failwith "Can only compare packets with other packets!"

    interface IComparable<Packet> with
        member this.CompareTo other =
            match (this, other) with
            | Integer l, Integer r -> compare l r
            | List l, List r -> compare l r
            | (Integer _ as l), (List _ as r) -> compare (List [ l ]) r
            | (List _ as l), (Integer _ as r) -> compare l (List [ r ])

    static member parse =
        let ppacket, ppacketImpl = createParserForwardedToRef ()
        let pinteger = pint32 |>> Integer

        let plist =
            between (pchar '[') (pchar ']') (sepBy ppacket (pchar ','))
            |>> List

        ppacketImpl.Value <- pinteger <|> plist

        Util.parse ppacket

let solution (transform, predicate, reducer) =
    Seq.filter (not << String.IsNullOrWhiteSpace)
    >> Seq.map Packet.parse
    >> transform
    >> Seq.indexed
    >> Seq.choose (fun (i, p) ->
        match predicate p with
        | true -> Some(i + 1)
        | false -> None)
    >> Seq.reduce reducer

let part1 =
    (Seq.chunkBySize 2
     >> Seq.map (function
         | [| l; r |] -> compare l r
         | _ -> failwith "Invalid packet groupings!"),
     (>=) 0,
     (+))

let part2 =
    (Seq.append Packet.dividers >> Seq.sort, (fun p -> List.contains p Packet.dividers), (*))

let test = File.ReadLines("test.txt")
assert (solution part1 test = 13)
assert (solution part2 test = 140)

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