Supply Stacks

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

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

type Ship(numberOfStacks: int, isCrateMover9001: bool) =
    let commandPattern = Regex(@"move (?<number>\d*) from (?<from>\d*) to (?<to>\d*)")
    let stacks: char[][] = Array.create numberOfStacks Array.empty

    let rearrange (moveFrom: int, moveTo: int, number: int) : unit =
        let leftCrates = stacks[moveFrom].Length - number

        let moved =
            stacks[moveFrom]
            |> Seq.ofArray
            |> Seq.skip leftCrates
            |> (fun x -> if not isCrateMover9001 then Seq.rev x else x)
            |> Array.ofSeq

        stacks[moveTo] <- Array.append stacks[moveTo] moved
        stacks[moveFrom] <- Array.take leftCrates stacks[moveFrom]

    member _.Rearrange(command: string) : unit =
        let m = commandPattern.Match(command)
        let moveFrom = (m.Groups["from"].Value |> int) - 1
        let moveTo = (m.Groups["to"].Value |> int) - 1
        let number = m.Groups["number"].Value |> int

        rearrange (moveFrom, moveTo, number)

    member _.PlaceCargo(index: int, c: char) : unit =
        stacks[index] <- Array.insertAt 0 c stacks[index]

    member _.GetTopCrates() : string =
        let topCrate (stack: char[]) : string =
            match Array.tryLast stack with
            | Some x -> string x
            | None -> ""

        stacks |> Array.fold (fun acc x -> acc + topCrate x) ""

type Input() =

    member _.Compute(part: int) =
        use stream = new StreamReader "./input.txt"

        let mutable endOfFile = false
        let mutable line = stream.ReadLine()

        let numberOfStacks = (line.Length + 1) / 4
        let ship = Ship(numberOfStacks, (part = 2))

        while line[1] <> '1' do
            let crates = line |> Seq.splitInto numberOfStacks |> Seq.indexed

            for (i, x) in crates do
                if x[1] <> ' ' then ship.PlaceCargo(i, x[1]) else ()

            line <- stream.ReadLine()

        line <- stream.ReadLine()

        while (not endOfFile) do
            line <- stream.ReadLine()

            if (line = null) then endOfFile <- true
            else if (part = 1) then ship.Rearrange(line)
            else ship.Rearrange(line)

        ship.GetTopCrates()

let input = Input()
let part = (Environment.GetCommandLineArgs()[1]) |> int

printfn "%s" (input.Compute(part))