From 20a45caf8f17cea6acce80ac3f31ff77c35d971d Mon Sep 17 00:00:00 2001 From: Smaug123 Date: Tue, 12 Dec 2023 19:04:41 +0000 Subject: [PATCH] Part 2 --- .../AdventOfCode2023.FSharp.Lib/Day12.fs | 131 +++++++++++++----- .../AdventOfCode2023.FSharp/Program.fs | 6 +- AdventOfCode2023.FSharp/Test/TestDay12.fs | 4 +- 3 files changed, 106 insertions(+), 35 deletions(-) diff --git a/AdventOfCode2023.FSharp/AdventOfCode2023.FSharp.Lib/Day12.fs b/AdventOfCode2023.FSharp/AdventOfCode2023.FSharp.Lib/Day12.fs index 3ac1f18..4a38605 100644 --- a/AdventOfCode2023.FSharp/AdventOfCode2023.FSharp.Lib/Day12.fs +++ b/AdventOfCode2023.FSharp/AdventOfCode2023.FSharp.Lib/Day12.fs @@ -7,61 +7,125 @@ open System.Globalization [] module Day12 = - let rec solve (line : ReadOnlySpan) (groups : IReadOnlyList) (currentGroupIndex : int) = + let rec solve + (dict : Dictionary) + (line : ReadOnlySpan) + (groups : IReadOnlyList) + (currentGroupIndex : int) + : 'a + = if line.Length = 0 then - if currentGroupIndex = groups.Count then LanguagePrimitives.GenericOne else LanguagePrimitives.GenericZero + if currentGroupIndex = groups.Count then + LanguagePrimitives.GenericOne + else + LanguagePrimitives.GenericZero + elif currentGroupIndex = groups.Count then + if line.Contains '#' then + LanguagePrimitives.GenericZero + else + LanguagePrimitives.GenericOne else + + match dict.TryGetValue ((line.Length, currentGroupIndex)) with + | true, v -> v + | false, _ -> + + let remaining = + let mutable remaining = -1 + + for i = currentGroupIndex to groups.Count - 1 do + remaining <- remaining + groups.[i] + 1 + + remaining + + if remaining > line.Length then + dict.Add ((line.Length, currentGroupIndex), LanguagePrimitives.GenericZero) + LanguagePrimitives.GenericZero + else + match line.[0] with | '#' -> - if currentGroupIndex >= groups.Count then LanguagePrimitives.GenericZero else - let mutable isOk = true - for i = 1 to groups.[currentGroupIndex] - 1 do - if isOk && (i >= line.Length || (line.[i] <> '#' && line.[i] <> '?')) then - isOk <- false - if not isOk then LanguagePrimitives.GenericZero else - if groups.[currentGroupIndex] < line.Length then - if line.[groups.[currentGroupIndex]] = '#' then - LanguagePrimitives.GenericZero - else - solve (line.Slice (groups.[currentGroupIndex] + 1)) groups (currentGroupIndex + 1) + if currentGroupIndex >= groups.Count then + LanguagePrimitives.GenericZero else - solve ReadOnlySpan<_>.Empty groups (currentGroupIndex + 1) - | '.' -> - solve (line.Slice 1) groups currentGroupIndex - | '?' -> - let ifDot = solve (line.Slice 1) groups currentGroupIndex - let ifHash = - if currentGroupIndex >= groups.Count then LanguagePrimitives.GenericZero else let mutable isOk = true + for i = 1 to groups.[currentGroupIndex] - 1 do if isOk && (i >= line.Length || (line.[i] <> '#' && line.[i] <> '?')) then isOk <- false - if not isOk then LanguagePrimitives.GenericZero else - if groups.[currentGroupIndex] < line.Length then - if groups.[currentGroupIndex] < line.Length && line.[groups.[currentGroupIndex]] = '#' then + + if not isOk then + LanguagePrimitives.GenericZero + else if groups.[currentGroupIndex] < line.Length then + if line.[groups.[currentGroupIndex]] = '#' then LanguagePrimitives.GenericZero else - solve (line.Slice (groups.[currentGroupIndex] + 1)) groups (currentGroupIndex + 1) + solve dict (line.Slice (groups.[currentGroupIndex] + 1)) groups (currentGroupIndex + 1) else - solve ReadOnlySpan<_>.Empty groups (currentGroupIndex + 1) + solve dict ReadOnlySpan<_>.Empty groups (currentGroupIndex + 1) + | '.' -> solve dict (line.Slice 1) groups currentGroupIndex + | '?' -> + let afterMark = line.IndexOfAnyExcept ('?', '#') + + if afterMark >= 0 && groups.[currentGroupIndex] > afterMark then + // this group would extend into a dot if this were filled in! + let firstHash = line.IndexOf '#' + + if firstHash >= 0 && firstHash < afterMark then + // this group *is* filled in, contradiction + LanguagePrimitives.GenericZero + else + solve dict (line.Slice afterMark) groups currentGroupIndex + else + + let ifDot = solve dict (line.Slice 1) groups currentGroupIndex + dict.TryAdd ((line.Length - 1, currentGroupIndex), ifDot) |> ignore + + let ifHash = + if currentGroupIndex >= groups.Count then + LanguagePrimitives.GenericZero + else + let mutable isOk = true + + for i = 1 to groups.[currentGroupIndex] - 1 do + if isOk && (i >= line.Length || (line.[i] <> '#' && line.[i] <> '?')) then + isOk <- false + + if not isOk then + LanguagePrimitives.GenericZero + else if groups.[currentGroupIndex] < line.Length then + if + groups.[currentGroupIndex] < line.Length + && line.[groups.[currentGroupIndex]] = '#' + then + LanguagePrimitives.GenericZero + else + solve dict (line.Slice (groups.[currentGroupIndex] + 1)) groups (currentGroupIndex + 1) + else + solve dict ReadOnlySpan<_>.Empty groups (currentGroupIndex + 1) + ifDot + ifHash | _ -> - if currentGroupIndex = groups.Count then LanguagePrimitives.GenericOne else LanguagePrimitives.GenericZero - + if currentGroupIndex = groups.Count then + LanguagePrimitives.GenericOne + else + LanguagePrimitives.GenericZero let part1 (s : string) = use mutable lines = StringSplitEnumerator.make '\n' s let mutable answer = 0uL let arr = ResizeArray () + for line in lines do if not line.IsEmpty then arr.Clear () use ints = StringSplitEnumerator.make' ',' (line.Slice (line.IndexOf ' ' + 1)) + for int in ints do arr.Add (Int32.Parse (int, NumberStyles.None, CultureInfo.InvariantCulture)) - let solved = solve line arr 0 + let solved = solve (Dictionary ()) line arr 0 answer <- answer + solved answer @@ -71,19 +135,24 @@ module Day12 = let mutable answer = 0uL let arr = ResizeArray () + for line in lines do if not line.IsEmpty then arr.Clear () - let spaceIndex =line.IndexOf ' ' + let spaceIndex = line.IndexOf ' ' + for _ = 0 to 4 do use ints = StringSplitEnumerator.make' ',' (line.Slice (spaceIndex + 1)) + for int in ints do arr.Add (Int32.Parse (int, NumberStyles.None, CultureInfo.InvariantCulture)) let sliced = line.Slice(0, spaceIndex).ToString () - let line = String.Concat (sliced, '?', sliced, '?', sliced, '?', sliced, '?', sliced) - let solved = solve (line.AsSpan()) arr 0 + let line = + String.Concat (sliced, '?', sliced, '?', sliced, '?', sliced, '?', sliced) + + let solved = solve (Dictionary ()) (line.AsSpan ()) arr 0 printfn $"%s{line} : %i{solved}" answer <- answer + solved diff --git a/AdventOfCode2023.FSharp/AdventOfCode2023.FSharp/Program.fs b/AdventOfCode2023.FSharp/AdventOfCode2023.FSharp/Program.fs index 9e656f8..311e9d4 100644 --- a/AdventOfCode2023.FSharp/AdventOfCode2023.FSharp/Program.fs +++ b/AdventOfCode2023.FSharp/AdventOfCode2023.FSharp/Program.fs @@ -219,7 +219,11 @@ module Program = sw.Restart () let data = Day11.parse input sw.Stop () - Console.Error.WriteLine ((1_000.0 * float sw.ElapsedTicks / float Stopwatch.Frequency).ToString () + "ms parse") + + Console.Error.WriteLine ( + (1_000.0 * float sw.ElapsedTicks / float Stopwatch.Frequency).ToString () + + "ms parse" + ) sw.Restart () let part1 = Day11.solve data 2uL diff --git a/AdventOfCode2023.FSharp/Test/TestDay12.fs b/AdventOfCode2023.FSharp/Test/TestDay12.fs index 3245ade..9993a08 100644 --- a/AdventOfCode2023.FSharp/Test/TestDay12.fs +++ b/AdventOfCode2023.FSharp/Test/TestDay12.fs @@ -16,9 +16,7 @@ module TestDay12 = [] let part2Sample () = - sample - |> Day12.part2 - |> shouldEqual 525152uL + sample |> Day12.part2 |> shouldEqual 525152uL [] let part1Actual () =