Distress Signal

Description: https://adventofcode.com/2022/day/13

open System.IO
open System
open System.Text.RegularExpressions

type Packet =
| Number of int
| PacketArray of list<Packet>

let (|Regex|_|) pattern input =
    let m = Regex.Match(input, pattern)
    if m.Success then
        Some(List.tail [ for g in m.Groups -> g.Value ])
    else
        None

let readInput =
    seq {
        use sr = new StreamReader("./input.txt")

        while not sr.EndOfStream do
            let pair = (sr.ReadLine(), sr.ReadLine())
            sr.ReadLine() |> ignore
            yield pair
    }

let rec parse (packet: list<char>) (packets: list<Packet>): list<char> * list<Packet> =
    let s = packet |> Array.ofList |> String
    match s with
    | Regex "^(\d+)" [ n:string ] -> parse (packet |> List.skip n.Length) [Number (n |> int)]
    | _ ->
        match packet with
        | [] -> packet, packets
        | '[' :: tail ->
            let x, y = parse tail []
            parse x [PacketArray y]
        | ',' :: tail ->
            let x, y = parse tail []
            x, packets@y
        | ']' :: tail -> tail, packets
        | x -> failwith $"aa {x}"

let parseS (s: string): Packet =
    (parse (s |> Seq.toList) [] |> snd)[0]

let rec compare (a: Packet) (b: Packet): Option<bool>=
    match a, b with
    | Number a, Number b when a < b -> Some(true)
    | Number a, Number b when a > b -> Some(false)
    | Number a, Number b when a = b -> None
    | PacketArray a, Number b -> compare (PacketArray a) (PacketArray [Number b])
    | Number a, PacketArray b -> compare (PacketArray [Number a]) (PacketArray b)
    | PacketArray [], PacketArray [] -> None
    | PacketArray [], PacketArray _ -> Some(true)
    | PacketArray _, PacketArray [] -> Some(false)
    | PacketArray (a::at), PacketArray (b::bt) ->
        match compare a b with
        | Some x -> Some x
        | None -> compare (PacketArray at) (PacketArray bt)
    | _ -> failwith "Should not happen"

let compareS (a: string) (b: string) =
    compare (parseS a) (parseS b)

// Part 1
readInput
|> Seq.map (fun x -> x ||> compareS)
|> Seq.indexed
|> Seq.filter (fun (_, x) ->
    match x with
    | Some true -> true
    | _ -> false)
|> Seq.sumBy (fun x -> fst x + 1)
|> printfn "%A"

// Part 2
let div2 = "[[2]]"
let div6 = "[[6]]"
let dividers = [div2; div6]
readInput
|> Seq.fold (fun acc (l, r) -> acc@[l; r]) dividers
|> Seq.sortWith (fun l r  ->
    match (compareS l r) with
    | Some true -> -1
    | _ -> 1)
|> (fun x ->
    let i2 = Seq.findIndex ((=) div2) x
    let i6 = Seq.findIndex ((=) div6) x
    (i2 + 1) * (i6 + 1))
|> printfn "%A"