From 54eb8b20e9f1e9a229150e48a486b6a8213cc85b Mon Sep 17 00:00:00 2001 From: Smaug123 Date: Fri, 8 Dec 2023 08:40:59 +0000 Subject: [PATCH] Day 8 part 2 --- .../AdventOfCode2023.FSharp.Lib.fsproj | 1 + .../AdventOfCode2023.FSharp.Lib/Arithmetic.fs | 67 +++++++++++++++++++ .../AdventOfCode2023.FSharp.Lib/Day8.fs | 49 ++++++++++++-- .../AdventOfCode2023.FSharp/Program.fs | 7 +- AdventOfCode2023.FSharp/Test/Test.fsproj | 1 + AdventOfCode2023.FSharp/Test/TestDay8.fs | 18 ++--- AdventOfCode2023.FSharp/Test/samples/day8.txt | 17 ++--- .../Test/samples/day8part1.txt | 9 +++ 8 files changed, 148 insertions(+), 21 deletions(-) create mode 100644 AdventOfCode2023.FSharp/AdventOfCode2023.FSharp.Lib/Arithmetic.fs create mode 100644 AdventOfCode2023.FSharp/Test/samples/day8part1.txt diff --git a/AdventOfCode2023.FSharp/AdventOfCode2023.FSharp.Lib/AdventOfCode2023.FSharp.Lib.fsproj b/AdventOfCode2023.FSharp/AdventOfCode2023.FSharp.Lib/AdventOfCode2023.FSharp.Lib.fsproj index 2b11007..73ba81f 100644 --- a/AdventOfCode2023.FSharp/AdventOfCode2023.FSharp.Lib/AdventOfCode2023.FSharp.Lib.fsproj +++ b/AdventOfCode2023.FSharp/AdventOfCode2023.FSharp.Lib/AdventOfCode2023.FSharp.Lib.fsproj @@ -9,6 +9,7 @@ + diff --git a/AdventOfCode2023.FSharp/AdventOfCode2023.FSharp.Lib/Arithmetic.fs b/AdventOfCode2023.FSharp/AdventOfCode2023.FSharp.Lib/Arithmetic.fs new file mode 100644 index 0000000..d2725de --- /dev/null +++ b/AdventOfCode2023.FSharp/AdventOfCode2023.FSharp.Lib/Arithmetic.fs @@ -0,0 +1,67 @@ +namespace AdventOfCode2023 + +[] +module Arithmetic = + + /// Compute floor(sqrt(i)). + let inline sqrt (i : ^a) = + if i <= LanguagePrimitives.GenericOne then + i + else + let rec go start = + let next = start + LanguagePrimitives.GenericOne + let sqr = next * next + + if sqr < LanguagePrimitives.GenericZero then + // Overflow attempted, so the sqrt is between start and next + start + elif i < sqr then + start + elif i = sqr then + next + else + go next + + go LanguagePrimitives.GenericOne + + /// Find Hcf, A, B s.t. A * a + B * b = Hcf, and Hcf is the highest common factor of a and b. + let inline euclideanAlgorithm + (a : ^a) + (b : ^a) + : {| + Hcf : ^a + A : ^a + B : ^a + |} + = + let rec go rMin1 r sMin1 s tMin1 t = + if r = LanguagePrimitives.GenericZero then + {| + Hcf = rMin1 + A = sMin1 + B = tMin1 + |} + else + let newQ = rMin1 / r + go r (rMin1 - newQ * r) s (sMin1 - newQ * s) t (tMin1 - newQ * t) + + let maxA = max a b + let minB = min a b + + let result = + go + maxA + minB + LanguagePrimitives.GenericOne + LanguagePrimitives.GenericZero + LanguagePrimitives.GenericZero + LanguagePrimitives.GenericOne + + if a = maxA then + result + else + {| + Hcf = result.Hcf + A = result.B + B = result.A + |} diff --git a/AdventOfCode2023.FSharp/AdventOfCode2023.FSharp.Lib/Day8.fs b/AdventOfCode2023.FSharp/AdventOfCode2023.FSharp.Lib/Day8.fs index f22d52c..43a17b9 100644 --- a/AdventOfCode2023.FSharp/AdventOfCode2023.FSharp.Lib/Day8.fs +++ b/AdventOfCode2023.FSharp/AdventOfCode2023.FSharp.Lib/Day8.fs @@ -2,7 +2,6 @@ namespace AdventOfCode2023 open System open System.Collections.Generic -open System.Globalization [] module Day8 = @@ -20,8 +19,9 @@ module Day8 = lines.MoveNext () |> ignore - let stepsLine = lines.Current.TrimEnd() + let stepsLine = lines.Current.TrimEnd () let steps = Array.zeroCreate stepsLine.Length + for i = 0 to stepsLine.Length - 1 do steps.[i] <- (stepsLine.[i] = 'R') @@ -49,19 +49,60 @@ module Day8 = let mutable i = 0 let mutable currentNode = "AAA" let mutable answer = 0 + while currentNode <> "ZZZ" do let instruction = data.Nodes.[currentNode] + if data.Steps.[i] then // "true" is R currentNode <- snd instruction else currentNode <- fst instruction + i <- (i + 1) % data.Steps.Length answer <- answer + 1 + answer + let inline lcm (periods : ^T[]) = + let mutable lcm = periods.[0] + let mutable i = 1 + + while i < periods.Length do + let euclid = Arithmetic.euclideanAlgorithm lcm periods.[i] + lcm <- (lcm * periods.[i]) / euclid.Hcf + i <- i + 1 + + lcm + let part2 (s : string) = let data = parse s - let mutable answer = 0 - answer + let startingNodes = + data.Nodes.Keys + |> Seq.choose (fun k -> if k.[k.Length - 1] = 'A' then Some k else None) + |> Seq.toArray + + let periods = + startingNodes + |> Array.map (fun startNode -> + let mutable i = 0 + let mutable currentNode = startNode + let mutable answer = 0ul + + while currentNode.[currentNode.Length - 1] <> 'Z' do + let instruction = data.Nodes.[currentNode] + + if data.Steps.[i] then + // "true" is R + currentNode <- snd instruction + else + currentNode <- fst instruction + + i <- (i + 1) % data.Steps.Length + answer <- answer + 1ul + + uint64 answer + ) + + lcm periods diff --git a/AdventOfCode2023.FSharp/AdventOfCode2023.FSharp/Program.fs b/AdventOfCode2023.FSharp/AdventOfCode2023.FSharp/Program.fs index 5514ffb..01889d6 100644 --- a/AdventOfCode2023.FSharp/AdventOfCode2023.FSharp/Program.fs +++ b/AdventOfCode2023.FSharp/AdventOfCode2023.FSharp/Program.fs @@ -161,13 +161,18 @@ module Program = Console.WriteLine "=====Day 8=====" do - let input = Path.Combine (dir.FullName, "day8.txt") |> File.ReadAllText + let input = + try + Path.Combine (dir.FullName, "day8part1.txt") |> File.ReadAllText + with :? FileNotFoundException -> + Path.Combine (dir.FullName, "day8.txt") |> File.ReadAllText sw.Restart () let part1 = Day8.part1 input sw.Stop () Console.WriteLine (part1.ToString ()) Console.Error.WriteLine ((1_000.0 * float sw.ElapsedTicks / float Stopwatch.Frequency).ToString () + "ms") sw.Restart () + let input = Path.Combine (dir.FullName, "day8.txt") |> File.ReadAllText let part2 = Day8.part2 input sw.Stop () Console.WriteLine (part2.ToString ()) diff --git a/AdventOfCode2023.FSharp/Test/Test.fsproj b/AdventOfCode2023.FSharp/Test/Test.fsproj index 43e52b8..9a4c538 100644 --- a/AdventOfCode2023.FSharp/Test/Test.fsproj +++ b/AdventOfCode2023.FSharp/Test/Test.fsproj @@ -25,6 +25,7 @@ + diff --git a/AdventOfCode2023.FSharp/Test/TestDay8.fs b/AdventOfCode2023.FSharp/Test/TestDay8.fs index 794869a..84f7b10 100644 --- a/AdventOfCode2023.FSharp/Test/TestDay8.fs +++ b/AdventOfCode2023.FSharp/Test/TestDay8.fs @@ -1,6 +1,5 @@ namespace AdventOfCode2023.Test -open System open AdventOfCode2023 open NUnit.Framework open FsUnitTyped @@ -9,11 +8,11 @@ open System.IO [] module TestDay8 = - let sample = Assembly.getEmbeddedResource typeof.Assembly "day8.txt" - [] let part1Sample () = - sample |> Day8.part1 |> shouldEqual 2 + Assembly.getEmbeddedResource typeof.Assembly "day8part1.txt" + |> Day8.part1 + |> shouldEqual 2 [] let part1Sample2 () = @@ -22,11 +21,14 @@ module TestDay8 = AAA = (BBB, BBB) BBB = (AAA, ZZZ) ZZZ = (ZZZ, ZZZ)""" - |> Day8.part1 |> shouldEqual 6 + |> Day8.part1 + |> shouldEqual 6 [] let part2Sample () = - sample |> Day8.part2 |> shouldEqual 0 + Assembly.getEmbeddedResource typeof.Assembly "day8.txt" + |> Day8.part2 + |> shouldEqual 6uL [] let part1Actual () = @@ -39,7 +41,7 @@ ZZZ = (ZZZ, ZZZ)""" Assert.Inconclusive () failwith "unreachable" - Day8.part1 s |> shouldEqual 0 + Day8.part1 s |> shouldEqual 19199 [] let part2Actual () = @@ -52,4 +54,4 @@ ZZZ = (ZZZ, ZZZ)""" Assert.Inconclusive () failwith "unreachable" - Day8.part2 s |> shouldEqual 0 + Day8.part2 s |> shouldEqual 13663968099527uL diff --git a/AdventOfCode2023.FSharp/Test/samples/day8.txt b/AdventOfCode2023.FSharp/Test/samples/day8.txt index 9029a1b..5b3fa58 100644 --- a/AdventOfCode2023.FSharp/Test/samples/day8.txt +++ b/AdventOfCode2023.FSharp/Test/samples/day8.txt @@ -1,9 +1,10 @@ -RL +LR -AAA = (BBB, CCC) -BBB = (DDD, EEE) -CCC = (ZZZ, GGG) -DDD = (DDD, DDD) -EEE = (EEE, EEE) -GGG = (GGG, GGG) -ZZZ = (ZZZ, ZZZ) +11A = (11B, XXX) +11B = (XXX, 11Z) +11Z = (11B, XXX) +22A = (22B, XXX) +22B = (22C, 22C) +22C = (22Z, 22Z) +22Z = (22B, 22B) +XXX = (XXX, XXX) diff --git a/AdventOfCode2023.FSharp/Test/samples/day8part1.txt b/AdventOfCode2023.FSharp/Test/samples/day8part1.txt new file mode 100644 index 0000000..9029a1b --- /dev/null +++ b/AdventOfCode2023.FSharp/Test/samples/day8part1.txt @@ -0,0 +1,9 @@ +RL + +AAA = (BBB, CCC) +BBB = (DDD, EEE) +CCC = (ZZZ, GGG) +DDD = (DDD, DDD) +EEE = (EEE, EEE) +GGG = (GGG, GGG) +ZZZ = (ZZZ, ZZZ)