diff --git a/.github/workflows/dotnet.yaml b/.github/workflows/dotnet.yaml index e9b2aeb..9f02513 100644 --- a/.github/workflows/dotnet.yaml +++ b/.github/workflows/dotnet.yaml @@ -20,7 +20,7 @@ jobs: - name: Setup .NET uses: actions/setup-dotnet@v3 with: - dotnet-version: 6.0.x + dotnet-version: 7.0.x - name: Restore dependencies run: dotnet restore - name: Build @@ -33,10 +33,10 @@ jobs: steps: - name: Checkout uses: actions/checkout@v3 - - name: Setup .NET SDK v6.0.x + - name: Setup .NET SDK v7.0.x uses: actions/setup-dotnet@v3 with: - dotnet-version: 6.0.x + dotnet-version: 7.0.x - name: Prepare .NET tools run: dotnet tool restore - name: Run Fantomas diff --git a/.gitignore b/.gitignore index cba49bc..fc2427c 100644 --- a/.gitignore +++ b/.gitignore @@ -5,3 +5,4 @@ riderModule.iml /_ReSharper.Caches/ .idea/ *.sln.DotSettings.user +.vs/ diff --git a/AdventOfCode2022.Test/AdventOfCode2022.Test.fsproj b/AdventOfCode2022.Test/AdventOfCode2022.Test.fsproj index ebcfe74..846c41e 100644 --- a/AdventOfCode2022.Test/AdventOfCode2022.Test.fsproj +++ b/AdventOfCode2022.Test/AdventOfCode2022.Test.fsproj @@ -1,8 +1,9 @@ - net6.0 - false + net7.0 + false + true @@ -11,10 +12,12 @@ - - + + + + diff --git a/AdventOfCode2022.Test/Day1.fs b/AdventOfCode2022.Test/Day1.fs index 3925f07..a26af65 100644 --- a/AdventOfCode2022.Test/Day1.fs +++ b/AdventOfCode2022.Test/Day1.fs @@ -1,5 +1,6 @@ namespace AdventOfCode2022.Test +open System open AdventOfCode2022 open NUnit.Framework open FsUnitTyped @@ -7,6 +8,23 @@ open FsUnitTyped [] module TestDay1 = + let testInput = + """1000 +2000 +3000 + +4000 + +5000 +6000 + +7000 +8000 +9000 + +10000 +""" + [] let ``Part 1`` () = let input = Assembly.readResource "Day1.txt" @@ -19,43 +37,9 @@ module TestDay1 = [] let ``Part 1, given example`` () = - """1000 -2000 -3000 - -4000 - -5000 -6000 - -7000 -8000 -9000 - -10000 -""" - |> fun s -> s.Split '\n' - |> Day1.part1 - |> shouldEqual 24000 + testInput.Split Environment.NewLine |> Day1.part1 |> shouldEqual 24000 [] let ``Part 2, given example`` () = - """1000 -2000 -3000 - -4000 - -5000 -6000 - -7000 -8000 -9000 - -10000 -""" - |> fun s -> s.Split '\n' - |> Day1.part2 - |> shouldEqual 45000 + testInput.Split Environment.NewLine |> Day1.part2 |> shouldEqual 45000 diff --git a/AdventOfCode2022.Test/Day2.fs b/AdventOfCode2022.Test/Day2.fs index 7978f19..5ceb019 100644 --- a/AdventOfCode2022.Test/Day2.fs +++ b/AdventOfCode2022.Test/Day2.fs @@ -8,14 +8,14 @@ open AdventOfCode2022 [] module TestDay2 = - [] - let ``Part 1, given`` () = + let testInput = """A Y B X C Z""" - |> fun s -> s.Split System.Environment.NewLine - |> Day2.part1 - |> shouldEqual 15 + + [] + let ``Part 1, given`` () = + testInput.Split Environment.NewLine |> Day2.part1 |> shouldEqual 15 [] let ``Part 1`` () = @@ -25,12 +25,7 @@ C Z""" [] let ``Part 2, given`` () = - """A Y -B X -C Z""" - |> fun s -> s.Split System.Environment.NewLine - |> Day2.part2 - |> shouldEqual 12 + testInput.Split Environment.NewLine |> Day2.part2 |> shouldEqual 12 [] let ``Part 2`` () = diff --git a/AdventOfCode2022.Test/Day4.fs b/AdventOfCode2022.Test/Day4.fs index 74a48b5..7cb1ee3 100644 --- a/AdventOfCode2022.Test/Day4.fs +++ b/AdventOfCode2022.Test/Day4.fs @@ -8,15 +8,18 @@ open AdventOfCode2022 [] module TestDay4 = - [] - let ``Part 1, given`` () = + let testInput = """2-4,6-8 2-3,4-5 5-7,7-9 2-8,3-7 6-6,4-6 2-6,4-8""" - |> fun s -> s.Split System.Environment.NewLine + + [] + let ``Part 1, given`` () = + testInput + |> fun s -> s.Split Environment.NewLine |> Day4.part1 |> shouldEqual 2 @@ -29,16 +32,10 @@ module TestDay4 = |> Day4.part1 |> shouldEqual 433 - [] let ``Part 2, given`` () = - """2-4,6-8 -2-3,4-5 -5-7,7-9 -2-8,3-7 -6-6,4-6 -2-6,4-8""" - |> fun s -> s.Split System.Environment.NewLine + testInput + |> fun s -> s.Split Environment.NewLine |> Day4.part2 |> shouldEqual 4 diff --git a/AdventOfCode2022.Test/Day5.fs b/AdventOfCode2022.Test/Day5.fs new file mode 100644 index 0000000..9a9b0a4 --- /dev/null +++ b/AdventOfCode2022.Test/Day5.fs @@ -0,0 +1,46 @@ +namespace AdventOfCode2022.Test + +open System +open NUnit.Framework +open FsUnitTyped +open AdventOfCode2022 + +[] +module TestDay5 = + + let testInput = + """ [D] +[N] [C] +[Z] [M] [P] + 1 2 3 + +move 1 from 2 to 1 +move 3 from 1 to 3 +move 2 from 2 to 1 +move 1 from 1 to 2 +""" + + [] + let ``Part 1, given`` () = + let lines = StringSplitEnumerator.make '\n' testInput + Day5.part1 lines |> shouldEqual "CMZ" + + [] + let ``Part 1`` () = + let input = Assembly.readResource "Day5.txt" + + let lines = StringSplitEnumerator.make '\n' input + Day5.part1 lines |> shouldEqual "BSDMQFLSP" + + + [] + let ``Part 2, given`` () = + let lines = StringSplitEnumerator.make '\n' testInput + Day5.part2 lines |> shouldEqual "MCD" + + [] + let ``Part 2`` () = + let input = Assembly.readResource "Day5.txt" + + let lines = StringSplitEnumerator.make '\n' input + Day5.part2 lines |> shouldEqual "PGSQBFLDP" diff --git a/AdventOfCode2022.Test/Inputs/Day5.txt b/AdventOfCode2022.Test/Inputs/Day5.txt new file mode 100644 index 0000000..1230363 --- /dev/null +++ b/AdventOfCode2022.Test/Inputs/Day5.txt @@ -0,0 +1,512 @@ + [Q] [B] [H] + [F] [W] [D] [Q] [S] + [D] [C] [N] [S] [G] [F] + [R] [D] [L] [C] [N] [Q] [R] +[V] [W] [L] [M] [P] [S] [M] [M] +[J] [B] [F] [P] [B] [B] [P] [F] [F] +[B] [V] [G] [J] [N] [D] [B] [L] [V] +[D] [P] [R] [W] [H] [R] [Z] [W] [S] + 1 2 3 4 5 6 7 8 9 + +move 1 from 4 to 1 +move 2 from 4 to 8 +move 5 from 9 to 6 +move 1 from 1 to 3 +move 5 from 8 to 3 +move 1 from 1 to 5 +move 4 from 3 to 6 +move 14 from 6 to 2 +move 5 from 4 to 5 +move 7 from 7 to 2 +move 24 from 2 to 3 +move 13 from 3 to 2 +move 1 from 7 to 9 +move 1 from 9 to 5 +move 7 from 2 to 6 +move 3 from 1 to 7 +move 3 from 6 to 3 +move 2 from 7 to 1 +move 1 from 7 to 5 +move 2 from 2 to 6 +move 2 from 1 to 4 +move 9 from 5 to 1 +move 1 from 6 to 3 +move 4 from 5 to 4 +move 1 from 2 to 7 +move 4 from 6 to 2 +move 7 from 2 to 3 +move 2 from 2 to 6 +move 2 from 2 to 3 +move 2 from 5 to 4 +move 1 from 7 to 3 +move 4 from 6 to 7 +move 19 from 3 to 6 +move 3 from 7 to 4 +move 1 from 7 to 8 +move 1 from 8 to 1 +move 2 from 1 to 3 +move 10 from 3 to 2 +move 3 from 3 to 8 +move 1 from 3 to 9 +move 1 from 9 to 6 +move 11 from 6 to 8 +move 2 from 3 to 8 +move 6 from 4 to 3 +move 3 from 4 to 1 +move 7 from 2 to 8 +move 1 from 3 to 6 +move 6 from 8 to 5 +move 1 from 4 to 6 +move 9 from 6 to 9 +move 6 from 3 to 8 +move 1 from 3 to 5 +move 10 from 1 to 3 +move 11 from 8 to 7 +move 1 from 3 to 5 +move 1 from 1 to 8 +move 5 from 9 to 2 +move 1 from 6 to 3 +move 5 from 3 to 6 +move 1 from 3 to 5 +move 4 from 6 to 4 +move 1 from 5 to 9 +move 6 from 2 to 4 +move 2 from 2 to 9 +move 5 from 5 to 1 +move 2 from 1 to 7 +move 10 from 8 to 3 +move 1 from 8 to 6 +move 3 from 6 to 3 +move 6 from 4 to 2 +move 8 from 3 to 8 +move 3 from 4 to 8 +move 4 from 2 to 1 +move 3 from 5 to 3 +move 4 from 7 to 6 +move 2 from 9 to 3 +move 1 from 2 to 9 +move 1 from 2 to 3 +move 2 from 4 to 8 +move 1 from 7 to 9 +move 5 from 7 to 8 +move 2 from 7 to 3 +move 14 from 3 to 2 +move 3 from 9 to 5 +move 1 from 3 to 1 +move 1 from 7 to 4 +move 3 from 9 to 8 +move 7 from 8 to 9 +move 7 from 2 to 5 +move 2 from 3 to 7 +move 2 from 7 to 6 +move 16 from 8 to 9 +move 4 from 6 to 5 +move 1 from 2 to 5 +move 21 from 9 to 5 +move 3 from 9 to 3 +move 6 from 1 to 4 +move 1 from 1 to 9 +move 1 from 1 to 4 +move 2 from 6 to 3 +move 3 from 4 to 6 +move 3 from 4 to 8 +move 1 from 9 to 4 +move 2 from 4 to 6 +move 4 from 3 to 6 +move 1 from 3 to 4 +move 1 from 4 to 9 +move 1 from 9 to 8 +move 1 from 8 to 6 +move 6 from 2 to 1 +move 2 from 8 to 4 +move 6 from 1 to 8 +move 23 from 5 to 9 +move 1 from 4 to 7 +move 1 from 7 to 1 +move 22 from 9 to 7 +move 4 from 8 to 7 +move 1 from 5 to 2 +move 1 from 1 to 9 +move 2 from 8 to 4 +move 6 from 6 to 3 +move 2 from 9 to 5 +move 18 from 7 to 4 +move 18 from 4 to 5 +move 1 from 2 to 7 +move 1 from 8 to 4 +move 6 from 7 to 2 +move 5 from 4 to 5 +move 1 from 3 to 1 +move 1 from 7 to 2 +move 4 from 3 to 4 +move 1 from 3 to 4 +move 1 from 1 to 7 +move 1 from 5 to 8 +move 3 from 4 to 3 +move 3 from 3 to 8 +move 2 from 8 to 3 +move 2 from 4 to 8 +move 2 from 7 to 5 +move 1 from 7 to 9 +move 2 from 3 to 1 +move 1 from 9 to 7 +move 4 from 2 to 3 +move 1 from 8 to 9 +move 2 from 1 to 8 +move 2 from 2 to 4 +move 1 from 9 to 1 +move 4 from 6 to 8 +move 1 from 2 to 7 +move 1 from 4 to 7 +move 4 from 8 to 2 +move 1 from 4 to 3 +move 1 from 1 to 9 +move 4 from 8 to 1 +move 2 from 2 to 1 +move 3 from 3 to 9 +move 2 from 7 to 1 +move 32 from 5 to 1 +move 1 from 8 to 7 +move 6 from 5 to 1 +move 2 from 7 to 6 +move 1 from 9 to 5 +move 1 from 3 to 2 +move 1 from 5 to 9 +move 2 from 6 to 1 +move 1 from 3 to 7 +move 1 from 9 to 8 +move 36 from 1 to 4 +move 1 from 8 to 9 +move 5 from 4 to 9 +move 6 from 9 to 3 +move 2 from 2 to 9 +move 3 from 1 to 9 +move 1 from 3 to 2 +move 30 from 4 to 8 +move 1 from 7 to 5 +move 1 from 3 to 5 +move 3 from 3 to 4 +move 2 from 8 to 5 +move 3 from 9 to 8 +move 3 from 9 to 3 +move 19 from 8 to 6 +move 2 from 3 to 5 +move 3 from 4 to 3 +move 1 from 4 to 7 +move 8 from 1 to 8 +move 1 from 3 to 2 +move 1 from 7 to 6 +move 4 from 5 to 3 +move 1 from 1 to 7 +move 2 from 5 to 4 +move 1 from 9 to 4 +move 12 from 6 to 2 +move 1 from 7 to 8 +move 6 from 2 to 9 +move 3 from 6 to 7 +move 2 from 7 to 5 +move 6 from 2 to 3 +move 8 from 3 to 5 +move 5 from 6 to 8 +move 5 from 3 to 6 +move 1 from 9 to 4 +move 1 from 9 to 8 +move 5 from 5 to 9 +move 3 from 4 to 6 +move 1 from 4 to 9 +move 1 from 7 to 5 +move 1 from 3 to 5 +move 8 from 9 to 2 +move 3 from 9 to 6 +move 27 from 8 to 2 +move 10 from 6 to 9 +move 1 from 6 to 4 +move 1 from 4 to 9 +move 2 from 5 to 6 +move 5 from 5 to 3 +move 2 from 6 to 9 +move 5 from 3 to 2 +move 12 from 9 to 3 +move 5 from 3 to 1 +move 3 from 1 to 5 +move 1 from 9 to 8 +move 1 from 5 to 2 +move 1 from 2 to 1 +move 1 from 1 to 6 +move 1 from 5 to 3 +move 34 from 2 to 4 +move 8 from 3 to 9 +move 1 from 6 to 1 +move 1 from 8 to 5 +move 4 from 2 to 8 +move 3 from 8 to 7 +move 1 from 7 to 2 +move 7 from 9 to 8 +move 1 from 9 to 6 +move 2 from 5 to 1 +move 1 from 6 to 9 +move 1 from 9 to 5 +move 2 from 2 to 5 +move 5 from 8 to 6 +move 2 from 8 to 5 +move 1 from 1 to 3 +move 12 from 4 to 6 +move 2 from 7 to 1 +move 4 from 1 to 6 +move 3 from 2 to 3 +move 1 from 8 to 5 +move 1 from 2 to 6 +move 1 from 1 to 9 +move 1 from 9 to 5 +move 16 from 4 to 1 +move 4 from 3 to 1 +move 8 from 1 to 8 +move 1 from 4 to 1 +move 6 from 5 to 8 +move 1 from 5 to 7 +move 12 from 6 to 9 +move 7 from 1 to 5 +move 2 from 1 to 7 +move 1 from 7 to 1 +move 9 from 9 to 6 +move 15 from 6 to 2 +move 2 from 9 to 7 +move 4 from 4 to 5 +move 2 from 2 to 9 +move 3 from 7 to 5 +move 2 from 1 to 3 +move 1 from 7 to 1 +move 10 from 2 to 3 +move 6 from 8 to 6 +move 3 from 9 to 2 +move 14 from 5 to 6 +move 1 from 8 to 4 +move 5 from 8 to 2 +move 2 from 2 to 3 +move 24 from 6 to 1 +move 3 from 1 to 2 +move 9 from 2 to 9 +move 1 from 4 to 3 +move 1 from 4 to 2 +move 1 from 8 to 4 +move 23 from 1 to 4 +move 3 from 2 to 4 +move 2 from 1 to 2 +move 1 from 8 to 4 +move 3 from 3 to 5 +move 3 from 3 to 4 +move 3 from 5 to 8 +move 3 from 2 to 7 +move 2 from 3 to 8 +move 15 from 4 to 3 +move 2 from 4 to 1 +move 19 from 3 to 9 +move 1 from 7 to 2 +move 1 from 2 to 5 +move 1 from 5 to 4 +move 1 from 7 to 6 +move 1 from 7 to 4 +move 3 from 8 to 3 +move 1 from 8 to 4 +move 5 from 3 to 8 +move 1 from 3 to 6 +move 22 from 9 to 2 +move 17 from 2 to 6 +move 3 from 9 to 3 +move 9 from 4 to 9 +move 6 from 4 to 9 +move 5 from 2 to 6 +move 1 from 4 to 2 +move 1 from 4 to 9 +move 1 from 1 to 6 +move 19 from 9 to 2 +move 4 from 8 to 7 +move 1 from 1 to 5 +move 1 from 5 to 3 +move 1 from 8 to 1 +move 1 from 8 to 2 +move 4 from 3 to 7 +move 12 from 6 to 1 +move 3 from 7 to 3 +move 7 from 2 to 7 +move 9 from 2 to 6 +move 4 from 2 to 6 +move 13 from 1 to 4 +move 8 from 6 to 4 +move 16 from 4 to 8 +move 12 from 7 to 6 +move 3 from 8 to 3 +move 1 from 1 to 2 +move 4 from 3 to 8 +move 5 from 8 to 9 +move 27 from 6 to 8 +move 2 from 3 to 7 +move 2 from 2 to 8 +move 2 from 7 to 5 +move 1 from 5 to 9 +move 1 from 5 to 1 +move 1 from 6 to 9 +move 2 from 6 to 2 +move 2 from 2 to 6 +move 2 from 9 to 2 +move 3 from 4 to 3 +move 1 from 1 to 9 +move 5 from 9 to 8 +move 1 from 9 to 5 +move 2 from 2 to 6 +move 2 from 4 to 6 +move 1 from 3 to 7 +move 1 from 5 to 6 +move 1 from 6 to 7 +move 6 from 6 to 8 +move 2 from 7 to 5 +move 2 from 3 to 2 +move 34 from 8 to 1 +move 1 from 5 to 6 +move 1 from 5 to 3 +move 1 from 6 to 1 +move 32 from 1 to 8 +move 23 from 8 to 4 +move 1 from 2 to 1 +move 24 from 8 to 4 +move 1 from 3 to 6 +move 47 from 4 to 6 +move 2 from 6 to 1 +move 3 from 1 to 5 +move 1 from 2 to 1 +move 3 from 5 to 7 +move 21 from 6 to 2 +move 3 from 7 to 8 +move 2 from 1 to 6 +move 8 from 6 to 4 +move 4 from 8 to 9 +move 3 from 2 to 8 +move 4 from 4 to 2 +move 2 from 2 to 5 +move 4 from 9 to 8 +move 2 from 1 to 5 +move 11 from 6 to 1 +move 14 from 2 to 6 +move 2 from 4 to 3 +move 1 from 2 to 9 +move 3 from 2 to 9 +move 20 from 6 to 5 +move 2 from 4 to 2 +move 4 from 9 to 1 +move 8 from 8 to 9 +move 1 from 6 to 9 +move 14 from 5 to 2 +move 10 from 2 to 7 +move 7 from 9 to 6 +move 1 from 6 to 8 +move 6 from 2 to 6 +move 1 from 2 to 5 +move 1 from 3 to 5 +move 9 from 6 to 3 +move 1 from 5 to 2 +move 9 from 7 to 3 +move 12 from 3 to 2 +move 9 from 5 to 9 +move 1 from 8 to 6 +move 3 from 3 to 5 +move 1 from 7 to 6 +move 14 from 2 to 6 +move 3 from 9 to 7 +move 6 from 1 to 2 +move 5 from 1 to 8 +move 10 from 6 to 9 +move 4 from 5 to 6 +move 3 from 2 to 4 +move 9 from 9 to 7 +move 1 from 8 to 7 +move 3 from 9 to 6 +move 3 from 3 to 7 +move 1 from 5 to 1 +move 15 from 7 to 1 +move 2 from 8 to 5 +move 2 from 5 to 4 +move 1 from 7 to 4 +move 1 from 3 to 1 +move 15 from 6 to 7 +move 2 from 4 to 9 +move 3 from 4 to 7 +move 18 from 1 to 6 +move 1 from 8 to 9 +move 6 from 9 to 7 +move 3 from 6 to 8 +move 1 from 1 to 2 +move 2 from 9 to 5 +move 2 from 2 to 9 +move 16 from 6 to 3 +move 15 from 3 to 7 +move 2 from 8 to 4 +move 1 from 3 to 7 +move 3 from 4 to 9 +move 2 from 1 to 9 +move 26 from 7 to 4 +move 1 from 2 to 1 +move 7 from 9 to 8 +move 1 from 2 to 5 +move 2 from 5 to 2 +move 8 from 7 to 5 +move 1 from 7 to 3 +move 1 from 3 to 9 +move 2 from 2 to 7 +move 1 from 6 to 4 +move 4 from 8 to 9 +move 1 from 1 to 3 +move 1 from 5 to 6 +move 2 from 5 to 7 +move 17 from 4 to 9 +move 6 from 4 to 9 +move 1 from 3 to 4 +move 6 from 7 to 9 +move 3 from 5 to 6 +move 2 from 7 to 9 +move 4 from 8 to 9 +move 4 from 6 to 4 +move 8 from 4 to 6 +move 1 from 8 to 4 +move 3 from 5 to 2 +move 2 from 4 to 3 +move 1 from 7 to 9 +move 2 from 3 to 5 +move 4 from 6 to 9 +move 1 from 6 to 1 +move 36 from 9 to 4 +move 2 from 5 to 3 +move 3 from 2 to 1 +move 3 from 1 to 4 +move 14 from 4 to 1 +move 1 from 8 to 5 +move 4 from 1 to 3 +move 5 from 9 to 5 +move 2 from 5 to 8 +move 1 from 8 to 9 +move 4 from 9 to 6 +move 3 from 5 to 8 +move 1 from 5 to 6 +move 2 from 1 to 6 +move 2 from 9 to 7 +move 6 from 6 to 4 +move 1 from 1 to 3 +move 29 from 4 to 6 +move 7 from 3 to 4 +move 1 from 8 to 9 +move 3 from 1 to 6 +move 4 from 1 to 4 +move 1 from 8 to 4 +move 4 from 4 to 3 +move 15 from 6 to 8 +move 9 from 4 to 9 +move 1 from 7 to 9 +move 8 from 8 to 3 +move 3 from 6 to 7 +move 1 from 1 to 2 +move 4 from 7 to 6 +move 7 from 8 to 5 +move 1 from 8 to 4 +move 2 from 5 to 7 +move 1 from 2 to 4 +move 5 from 6 to 1 +move 4 from 3 to 2 diff --git a/AdventOfCode2022/AdventOfCode2022.fsproj b/AdventOfCode2022/AdventOfCode2022.fsproj index 36ef0a6..4626d63 100644 --- a/AdventOfCode2022/AdventOfCode2022.fsproj +++ b/AdventOfCode2022/AdventOfCode2022.fsproj @@ -1,16 +1,19 @@  - net6.0 - true + net6.0 + true + true - - - - - + + + + + + + diff --git a/AdventOfCode2022/Day5.fs b/AdventOfCode2022/Day5.fs new file mode 100644 index 0000000..18ef5f1 --- /dev/null +++ b/AdventOfCode2022/Day5.fs @@ -0,0 +1,96 @@ +namespace AdventOfCode2022 + +open System + +type Day5Instruction = + { + Count : int + From : int + To : int + } + +[] +module Day5 = + let private parseDrawing (enumerator : byref) : char list array = + let piles = ResizeArray () + + while enumerator.MoveNext () && not (enumerator.Current.IsWhiteSpace ()) do + let s = enumerator.Current + + if s.Contains ']' then + for i in 1..4 .. s.Length - 1 do + let pile = (i - 1) / 4 + + if not (Char.IsWhiteSpace s.[i]) then + // TODO(perf): don't do this on every iteration + while piles.Count <= pile do + piles.Add (ResizeArray ()) + + piles.[pile].Add s.[i] + + Array.init piles.Count (fun i -> List.init piles.[i].Count (fun j -> piles.[i].[j])) + + let rec private parseInstruction (enumerator : byref) = + let outputs = ResizeArray () + + while (enumerator.MoveNext ()) && (not (enumerator.Current.IsWhiteSpace ())) do + let s = enumerator.Current + use mutable spaces = StringSplitEnumerator.make' ' ' s + + StringSplitEnumerator.chomp "move" &spaces + + let i = StringSplitEnumerator.consumeInt &spaces + + StringSplitEnumerator.chomp "from" &spaces + + let from = StringSplitEnumerator.consumeInt &spaces + + StringSplitEnumerator.chomp "to" &spaces + + let toDest = StringSplitEnumerator.consumeInt &spaces + + if spaces.MoveNext () then + failwith "got too many entries" + + { + From = from + To = toDest + Count = i + } + |> outputs.Add + + List.init outputs.Count (fun i -> outputs.[i]) + + let parse (s : StringSplitEnumerator) : char list array * Day5Instruction list = + use mutable enumerator = s + + let piles = parseDrawing &enumerator + let instructions = parseInstruction &enumerator + piles, instructions + + let part1 (lines : StringSplitEnumerator) : string = + let piles, instructions = parse lines + + let rec go (instructions : _ list) (piles : _ list array) = + match instructions with + | [] -> piles + | instr :: rest -> + piles.[instr.To - 1] <- List.rev piles.[instr.From - 1].[0 .. instr.Count - 1] @ piles.[instr.To - 1] + piles.[instr.From - 1] <- piles.[instr.From - 1].[instr.Count ..] + + go rest piles + + String (go instructions piles |> Array.map List.head) + + let part2 (lines : StringSplitEnumerator) : string = + let piles, instructions = parse lines + + let rec go (instructions : _ list) (piles : _ list array) = + match instructions with + | [] -> piles + | instr :: rest -> + piles.[instr.To - 1] <- piles.[instr.From - 1].[0 .. instr.Count - 1] @ piles.[instr.To - 1] + piles.[instr.From - 1] <- piles.[instr.From - 1].[instr.Count ..] + go rest piles + + String (go instructions piles |> Array.map List.head) diff --git a/AdventOfCode2022/EfficientString.fs b/AdventOfCode2022/EfficientString.fs new file mode 100644 index 0000000..ea23e6e --- /dev/null +++ b/AdventOfCode2022/EfficientString.fs @@ -0,0 +1,93 @@ +namespace AdventOfCode2022 + +open System +open System.IO +open System.Runtime.CompilerServices + +type EfficientString = System.ReadOnlySpan + +[] +module EfficientString = + + let inline isEmpty (s : EfficientString) : bool = s.IsEmpty + + + let inline ofString (s : string) : EfficientString = s.AsSpan () + + let inline toString (s : EfficientString) : string = s.ToString () + + let inline trimStart (s : EfficientString) : EfficientString = s.TrimStart () + + let inline slice (start : int) (length : int) (s : EfficientString) : EfficientString = s.Slice (start, length) + + let inline equals (a : string) (other : EfficientString) : bool = + MemoryExtensions.Equals (other, a.AsSpan (), StringComparison.Ordinal) + + /// Mutates the input to drop up to the first instance of the input char, + /// and returns what was dropped. + /// If the char is not present, deletes the input. + let takeUntil<'a> (c : char) (s : EfficientString byref) : EfficientString = + let first = s.IndexOf c + + if first < 0 then + let toRet = s + s <- EfficientString.Empty + toRet + else + let toRet = slice 0 first s + s <- slice (first + 1) (s.Length - first - 1) s + toRet + +[] +[] +type StringSplitEnumerator = + internal + { + Original : EfficientString + mutable Remaining : EfficientString + mutable InternalCurrent : EfficientString + SplitOn : char + } + + interface IDisposable with + member this.Dispose () = () + + member this.Current : EfficientString = this.InternalCurrent + + member this.MoveNext () = + if this.Remaining.Length = 0 then + false + else + this.InternalCurrent <- EfficientString.takeUntil this.SplitOn &this.Remaining + true + + member this.GetEnumerator () = this + +[] +module StringSplitEnumerator = + + let make (splitChar : char) (s : string) : StringSplitEnumerator = + { + Original = EfficientString.ofString s + Remaining = EfficientString.ofString s + InternalCurrent = EfficientString.Empty + SplitOn = splitChar + } + + let make' (splitChar : char) (s : ReadOnlySpan) : StringSplitEnumerator = + { + Original = s + Remaining = s + InternalCurrent = EfficientString.Empty + SplitOn = splitChar + } + + let chomp (s : string) (e : byref) = + if not (e.MoveNext ()) || not (EfficientString.equals s e.Current) then + failwithf "expected '%s'" s + + let consumeInt (e : byref) : int = + if not (e.MoveNext ()) then + failwith "expected an int, got nothing" + + Int32.Parse e.Current