diff --git a/AdventOfCode2022.App/AdventOfCode2022.App.fsproj b/AdventOfCode2022.App/AdventOfCode2022.App.fsproj index 12df020..580885c 100644 --- a/AdventOfCode2022.App/AdventOfCode2022.App.fsproj +++ b/AdventOfCode2022.App/AdventOfCode2022.App.fsproj @@ -26,6 +26,7 @@ + diff --git a/AdventOfCode2022.App/Inputs.fs b/AdventOfCode2022.App/Inputs.fs index d7cdcd5..cadaaf1 100644 --- a/AdventOfCode2022.App/Inputs.fs +++ b/AdventOfCode2022.App/Inputs.fs @@ -2,5 +2,5 @@ namespace AdventOfCode2022.App [] module Inputs = - let days = Array.init 16 (fun day -> Assembly.readResource $"Day%i{day + 1}.txt") + let days = Array.init 17 (fun day -> Assembly.readResource $"Day%i{day + 1}.txt") let inline day (i : int) = days.[i - 1] diff --git a/AdventOfCode2022.App/Program.fs b/AdventOfCode2022.App/Program.fs index 9a60b73..df409f8 100644 --- a/AdventOfCode2022.App/Program.fs +++ b/AdventOfCode2022.App/Program.fs @@ -61,7 +61,7 @@ type Benchmark16To20 () = [] member _.Setup () = Run.shouldWrite <- false - [] + [] member val Day = 0 with get, set [] diff --git a/AdventOfCode2022.App/Run.fs b/AdventOfCode2022.App/Run.fs index 5dc6130..bd9f0d2 100644 --- a/AdventOfCode2022.App/Run.fs +++ b/AdventOfCode2022.App/Run.fs @@ -232,6 +232,20 @@ module Run = if shouldWrite then printfn "%i" output + let day17 (partTwo : bool) (input : string) = + let day17 = input.TrimEnd () + + if not partTwo then + let output = Day17.part1 day17 + + if shouldWrite then + printfn "%i" output + else + let output = Day17.part2 day17 + + if shouldWrite then + printfn "%i" output + let allRuns = [| day1 @@ -250,4 +264,5 @@ module Run = day14 day15 day16 + day17 |] diff --git a/AdventOfCode2022.Test/AdventOfCode2022.Test.fsproj b/AdventOfCode2022.Test/AdventOfCode2022.Test.fsproj index 551bf3c..7e1ebfe 100644 --- a/AdventOfCode2022.Test/AdventOfCode2022.Test.fsproj +++ b/AdventOfCode2022.Test/AdventOfCode2022.Test.fsproj @@ -24,6 +24,7 @@ + @@ -40,6 +41,7 @@ + diff --git a/AdventOfCode2022.Test/Day16.fs b/AdventOfCode2022.Test/Day16.fs index 93497fd..1a28e30 100644 --- a/AdventOfCode2022.Test/Day16.fs +++ b/AdventOfCode2022.Test/Day16.fs @@ -22,8 +22,8 @@ Valve JJ has flow rate=21; tunnel leads to valve II [] let ``seq behaviour`` () = - Day16.ofSeq [ 1 ; 2 ; 3 ; 16 ] - |> Day16.toSeq + IntSet.ofSeq [ 1 ; 2 ; 3 ; 16 ] + |> IntSet.toSeq |> List.ofSeq |> shouldEqual [ 1 ; 2 ; 3 ; 16 ] diff --git a/AdventOfCode2022.Test/Day17.fs b/AdventOfCode2022.Test/Day17.fs new file mode 100644 index 0000000..02bf20f --- /dev/null +++ b/AdventOfCode2022.Test/Day17.fs @@ -0,0 +1,31 @@ +namespace AdventOfCode2022.Test + +open NUnit.Framework +open FsUnitTyped +open AdventOfCode2022 + +[] +module TestDay17 = + + let input = ">>><<><>><<<>><>>><<<>>><<<><<<>><>><<>>" + + [] + let ``Part 1, given`` () = + Day17.part1 (input.TrimEnd ()) |> shouldEqual 3068 + + [] + let ``Part 1`` () = + let input = Assembly.readResource "Day17.txt" + + Day17.part1 (input.TrimEnd ()) |> shouldEqual 3127 + + + [] + let ``Part 2, given`` () = + Day17.part2 (input.TrimEnd ()) |> shouldEqual 1514285714288L + + [] + let ``Part 2`` () = + let input = Assembly.readResource "Day17.txt" + + Day17.part2 (input.TrimEnd ()) |> shouldEqual 1542941176480L diff --git a/AdventOfCode2022.Test/Inputs/Day17.txt b/AdventOfCode2022.Test/Inputs/Day17.txt new file mode 100644 index 0000000..30b097d --- /dev/null +++ b/AdventOfCode2022.Test/Inputs/Day17.txt @@ -0,0 +1 @@ +>>><>><<<<><<<>>><><<<<>>>><<<><>><>>><<<><<>>><<<><<><>>><<<><<<<><>>>><<<<>><<>>>><<<>><<<<>>><<<>>>><<<<>>>><<<><>>>><>>><><<>>><<<<>><<>>>><<><<>><<<<><<<><<<>><<>>><<<>>>><<<<><<<>><<><<<>><>>><<>>><<<<>>><><<<<>><<<<>><>>>><><>>><<<<><<>>><<<<>><<<<>>>><<<>><<>><<<<>>>><<><<<<>>>><<<<>>><<<<>>><<<>>>><><>><>>>><<<>>>><<>><<>>>><<<<>><>>><><<<<>>>><<<<><<><><<<>>>><<<><<>>><<<<>>><<>>><>>>><><<<>>><><<<>>>><>>><<><>><<<>><<<<>><<<<>><>><<<<><>>>><>>><<<>><>><<<<>>><<>><<<><<<<>>><>>><>>><>>><<>>><<<<>><<<>><<<<>>><<>><<<>><>>><<<<>>>><<<<><<>>><<<>>>><<<>>><<<>><<<<>>><<><>><<>><<>>><<<>>><<<>>>><<<<>>>><<<<>>><<<<>><<<>>>><><<>>><>>>><<<<>>>><<<>>>><<<<><><>>><<><<<>>>><<<><<<<><<>><<<><<>>><<<<><<><<>><<<>>><<>>><<<>>>><<><<<<><<<>><>>>><<<<>>><<<><<<<>><<<>><<<<>><>><<<>>>><<<>><<>>>><<<<>>>><<>>><>>><>>>><<>>><<<>>><<<>>><<<><><<>>>><><<<>>>><<<<>>>><>><<>><<<>>><>>>><<>>>><><>><>>><<<><<<>>><<<<><<>><<<<>>><<<>><>>>><<<<>><<<<>>><<>>>><<<<>>><<<>>><<<><<<><<>><><<><<<<>>><<<>>>><<<<><<<>>><><<<<>>>><<<>>>><<<><<<>>>><<<<>>>><<<>>>><<<>>><>><<>>>><<<<>><<>><<<<>>>><<><<<<>><<<<>><<<>>>><<<><<<>>><<>>><<<<>><>>><>>>><>><>>>><<><<>><<<<>>><<>><>><>><<<>><<>>>><<>>><>>>><>>><>>>><>>><<<>>><<<<><><<<>>><<<>>><<<>>>><<><<>>><>>><>>><>>><<<<>><<<<>>><<>>><<>><>>><<>>>><<>>><<>>><<>><>>>><<><<>>><<<>><<<<>>>><<><<>>>><<<>><<<><<<<>>>><<<<><><<>>><>>><<<>><>><>><<<<>>><>>><>>>><<<<>>>><><<<><<<><>>>><<<>>><>><<<<>><<<>>><<<<>><<<><<<>>><<>><>>>><>>><<<<>>>><><<><<>>><<<<>><<<<><>>>><<<>>><<<<>><<<><><<<<>><<<<>>><>>>><>>>><<>>><<<<>><<<>><<<><<>><<<>>><<><<<<><<<<>>><<<<>>>><<>>><>>><<><<>>>><<>>><>>>><<<><<>>>><<<>>><<>>><<>><<<<>>><><>>>><<<>>>><<<<>>><<<>>><>><>>>><>><>><<<<>><<<>>><<<<>>>><<>><><<<>>><<<<>><>>><>>><><<>><<<>>><>>>><>>>><<><<<>><>><<<<>>>><<<<>><<>>><<>>>><>><<<<>><><<>>><<<>><<<<><<>>>><<<><>>>><<<>><<<><<<>>><<>><<>><<<>>>><<<<>>><<<<><<<>><<<<><<>><<<<>><<<><>><<>>>><<>><<<<>><<>>><>><<>><<>>>><<><<<>><<<<><<<<>>><<<>>>><>>><<<>>><><<<>><<<<>>><<<<><<<>>>><<><<<><<>>>><>>><<>><<<<>>><>><<<<>><><>>>><>><<>><<<>>><<<>>>><><<>>>><<>>>><<<<>><>><<<>><<<><<<<><<>>><><<<<>>><>>>><<<>>><<<<>>><>>>><><<<<>>><><<<<>><<<><<<>>><<<>><<>>><>><<>>>><>><<>><<<<>><<>>>><<>>><<<>><>>><<><<<<>><<<>><>>><<<<><>><<<<>><><<<<>><<<>>>><<<<>><<<<>>><<<<><<>><<><<<<><><<>>><>>>><<<<>>><<>>>><<>><><>>><<<<>>><><>>><<<>><<>><<>><<<<>>>><<<>>><<<>><>>><<<<>><<><>>>><<<>>>><<<>>><>>>><<<<><>>>><>><<<<>><<<>><<>>><<><<>>><<>>><<<>>><>>><<<>>><<><<><>>>><<<>><<<<>>><>><<>><<<>>>><>>><<<<>><>>>><<<<>><<<>><<<>>>><><<>>><<>><<>><<>>><>><<<><<<<>><>>><<>><<<>><>>>><<<<>><<<>>><<<<>>>><<<>><<<<>>>><>><>><<<<><<<<>>>><<>><>><<<<>>><<>>><<<>>><<>>>><<<>>>><>>><>><<<>>><><<<>><>>><<<><>><<<>><<<><<<<>>><<><<<>>>><<<<><<>>><<<<>><><<<><<>><<<<>>>><<<<>><<<<>><<>>>><<>><<<<><<<>>><<>>><<<<>>>><<><><<<>>><<>><>>><<<<>>><<<>>><><<<<><<>><<<><<>><<<<>>><<>>><<<<>>>><<<<><<>><<>>><>>><<<<><<<<>><<>>>><><><<>>>><>>><>><<<<>>>><<<<>><<<<>>>><<<<>>>><<<<><<<>>>><>><<>>>><>>>><>>><<<><><>>>><<>>>><<<>>>><<>>>><<<>><<<>>>><<<<>><<>>><>><<<<>>>><<<><<>>><<><<<<>>><<>>><<<>>><<<<>>>><<<<>>>><<>>>><<<>>>><>><<<<>>><<<<><<<<>>><<<<>><<<<><><<<<>>><<>>><<<>><<<>>>><<<<>><<>>><<<>>><<<<>>>><>><<<<><<<<>>>><<<<>><<<>>><<<<>>>><<>>><>><><<>>><<<>>>><<<<>><<><<<>>>><>>>><<><<<>><<<>><<<>>><<<<>>>><>><<><<<<>>>><<<<>>>><>><<<<>><<>><<>><<<><<<<>><<><<<><<>><><<<>>><<<<><>><>><><<<>>><<>>><><<<>><>>>><>>>><>><>><>><>>>><<<<>>><>>>><>>>><<>>>><<<<>>><<><<<>>>><<<>>>><<<<>><<<<>><<><<>><>>>><<<>>>><<<>>><>><<>>>><<>><<>>><>>>><<>>><>>><<<>><>><<<>>>><<<<>>>><<<>><<<<>>><<<<>>><>>>><<>>><<>>><<>>><>>>><<<<>>><<>><>>>><>>><<<<>>><<>>><>>><<<<>><<><>>><<><<<<>>>><<<<><<>>>><<<<>>><>><<<>>>><<>><<<<><<<<>>><>>>><<<<>>>><<<<>><>><>>>><<<<><<<<>>><<>><<<<>>>><<<><>>>><<<<><<>>>><<<<>>><<<<><><<<<>>><><<<>><>>><<<<>><<><<<<>><>>><<<<>>>><<<<>><<><<>>><<<<>>><<>><<<<>><>>><<>>>><>><>><<<<>><<<>><<<>><<>>><>><<>>><>>>><<>><>><<<<>>><<<><<<><><<<<>>><<<>><<<<><<>><<<>><<<<><>>>><<><<<>><><<<>>><<<>>><<<<>>><<<<><><<><>>><><>><><>>>><<>>>><<><>>><<>>>><><>>>><<><<<<>>>><>>><<>>><<<><<<>>><<<<>>>><<<<><<>>>><>>><>>>><<<<><<<>><<<<>>>><<<>>>><<<<>>><<<<><<<><<<<><<<<>>><<<<>>><<<>>>><>>><<<>>>><<<>><<<<>>>><<<<>>><<<<><<<<>>><<>>>><<<><<<<><<<>>>><<<><<<<><<<<><>>>><><<<<>>>><<>>>><>><>>><<>>>><<<<><<<><>><<>>>><<<<>>><<><>>>><<<<>>><>>><<<<>>><>>><<<>>>><>>><<<<><<<<>><>>><>>>><>>>><<<><><>><<>>>><<<<><><<<<>>><<<><>><<>>>><<<<>>>><<<<>>>><<<<>><<>>><<<<>>>><<<<><<<>><>>>><>><<<><<<>>><>><<<<><<<<>><<>><<>><<>>>><>><<<<>>><<<<>>>><<<<><<><>>><<<<>>>><>><<<>>><><<>>><<<<>>>><>><<>><<<>><>><><<>>><<<<>>><<><<<<><<>>>><<<<>>>><<<>>>><><>>>><<><<<><<>>><<>>>><<<>>>><<<>><<<<>><>>><><<<<>>>><>>><<<<>>>><>><<>>><>>>><<<>>>><<><<>>><<<>>><<>><>><<>>>><<<<>><>><<<<>><<><<><<<<>>>><<<<><<<>><>>><<<<><>><<<><<>><><><<>>>><<<<>>><<<><<<<><<>>><<<>>><<>>>><>>>><<>>><<>>>><<<<>><<>>><>>><><<>><<<<>>><<<><<<>><<>><<<>>><>>><<<<>><<<<><>>><>><<>>><<<>>>><<<<>>><>>><>>><<>>>><<<>><<<>>><>><<>>>><<>><<<<>><>><>>><><<>>><<<>><<<>>><<>>><<<<>>>><<><<<<>>><>><<<<>>>><<<<><>>><<<<>>><<><<<<>><<<<>><<>>>><>>><<><>>>><<<<>>><<<><<<>>>><<>>>><<<<>>><>><<<<><<<<>>>><>><<<>><>>>><>>>><><<<>>><<>>><<>><<<<>>><<<><<<>><<<>><<<>><<<<>>>><>><<>>>><>><<<<>>><><<<<><<><>><<<<>>>><<<><>>>><<<<>>><<>>>><<<>>>><<<>><<>><<<><<<<>><<<>>>><<>><><<<>>>><<<>><<<<>><>>>><<<>><<>><<>><<<>>><<>>>><<>>><>><>>>><<<<>>><>>><<>>>><><<<>><<<<>>>><>>><><<<>><<<>>><<<<>>><>>>><><<>>>><<<><<<<>>><<<>><>>><><<>>><>>>><<><><<><<<<>>>><>><>><<>>><><<<<>>>><<<<>>><>>>><<>>><<<>><>><<>>>><<><>>>><<<<>>><<<>>><<<<><<<<><>><<<>><<<>>>><<<<>><<<<>>><>>><<><<<<>>><<<><<><<<>>><<<<>>>><<><<<<>>><>><<>>>><<><<<>>>><<><<<>><<<<><<<<>>><<<<>>>><<<<>><>>>><<<><<<>>>><<<>>>><<<>>><>><>><<<>><<><>>>><<<<>><<<>>><<<><><<>>>><>><>>><>>>><<<>>>><<>>>><<>>>><<><>>><<>>><<>>>><<>>><><<<<>>>><<<>>><<<<>><<>>><<<<>><>>><>>>><>><><<><<<<>>>><>><<<<>>><<><<>>>><<>>><<<<>>><<<<>>>><<<>>>><<>>><<>>><<<><>>><<<><<>><<>><<<<><<<><<<<><<<><<<<>>>><<<>><>><<>>>><<<<>>>><<>>>><>>><<>>><<>>>><<<>>>><<<<>>>><<>>><<>>>><<>><<><<<<>><<<<>>><<<<>>><<>>><<<<>>><<<>>>><<>><<>><<<>>><><<>>><><<<><<<>>>><<<>>>><<<>><<>><<>><<>>>><<<><<<<>>>><>><<<<>>><<<>>><>>><<>>><<>>>><<<>><<<<>><>><<<<><<>>><<<<>>>><<<<>><><>>>><>>>><><<<>>>><<<<>>><<<<>>>><<<<><<>>><<>>>><>>>><<<<>>><>>><<>>><<<><<>><>>>><<>><>>><>><>>><<<<><<<>>>><<>>>><>>><>>><>>>><<<><<<<>>><<<>>>><<<><>>><<<>><>><>><<<>><<<>>><<<>>>><>><><>>><<<><><<<<><<>><<<<>>><<<>><<<<>>><<<>>><<<<>>><<<>>><<<>>><<<>>>><<<<>>>><<<<><<<>>><<><<>>><<<<>>>><<>><<<<><>><<<<>><>><<<>>><<>>>><>><>>><<<>><<<><<<>><<<>><<<<>>><<<<>>><<<<>>><<<>>>><<<>><<<<>><<><<<>>><>>>><<>>><<<<><<>>><>>>><<<<>><<>>><<><>>><>>><<<<>>><<><<<><>>><<<>>><<>>><<<<><<><<<<>><<<<><<<>>><>><<>>>><<<>>>><<<<><<><<<<><<<<>>><>><<<><<>>>><<<<>>>><<<<>>><<<>>><<<>>><<<<><<<>>>><<<<>><<<>><<<<>><><>><>><><<<>><<<>>><>>>><<<>><<<<>><><>>>><<<<>>><<<<>>>><<<>><<<>>>><<<>>><<<>>><<<>><<>>>><><<<><<>>>><<<>><<>><<>>>><>>>><>>>><<>>>><<><<<>>>><<<>><<>>><<><<<<>>><<<<>><<<>><>><<<>>>><<<><<>>>><<<><<>><<>>><<<>>><<><>>><<<>>><<<>>><>>>><<<<><<<<>>><>>>><<<>>><<><<>><<<<>>>><<<<><<>>>><<<>>><<<><<>>>><<<<><>><<<>>>><<<<>>>><<><>>><<<>><<<>>>><<>>>><<>>>><<>>><<>><<>>><<<<><<<>>>><>><<<<>><><<>>><>><<<<><>><<>><><><>>><<>>>><>>>><<<<>><<<>>><<<<>>>><<<<>>><<<>>>><<>>><<>>><><>>>><<>><><<>>><<>>>><<<>>><<<<>>><<<>>><<>>><<<<>><<<<>><<<<>><<<>><<<<>>>><<<>>>><<<<><<<<>>>><>>><<<<>>>><<<>>><<<<><<<<><<><<<<>>>><>>>><>>><<<>>>><<><<<><<<<>><<<<>>><<>>>><<>><<<>>>><>>><>>>><>>><<>><<<>>>><<<>>><<>>>><>>><<<>>><<>><<>>><<<>>>><<>>>><<><<<>>><<><<<>><<<<><<<>><<><>><<<>>><<>>><<<>>><<<<><<<<><<<<><><>>>><<<>><>>><>>><<<>>>><>>><>><<<<>>><<<><<<>>>><>>><<<<><<<>>>><<<<>>>><<<><>><<<>><<<<>>><<<<>><<<<>><><<<<>>>><>>>><<>><<<<><<<><<<>>><<<>><<><<><>>><<<<>><<>>><<>>>><<<<><<<<><<<<>>><<>><<<<>>>><<<<>>>><<<>>>><<<<>><<<>><<<<>>>><<<<><<>><<<>>>><<><<<>><<<><<><<<<>>><>>>><<>>>><<<<>>>><<>><<<>>><>><<>>>><<<>>>><>>><<<<>>>><>>>><<>><><<<<>>><<>>><<>><<><>><<<>>>><>>>><>>>><><<<<>><<<>>><<<>>>><<<<><<<<>>>><<>>><<>>>><<>>>><><<><<>>><<><<>>><<><>>>><<>><<<>><<><<<<>>><<<>>>><<<<><>><<>>><<<>>>><<>>>><<>>>><<<>>>><<<<>>>><<>>>><<<><><>><>>><<<><<<>>>><><<<<>>>><<<<>>>><<>>><>>>><>>><<<<>>><<<>>><<<><>><<<<><<<<>>><>>>><>><<<>>><<<><<<<>>>><<>>><<<<><<<<><>><<<>>>><<<<>>><>>><<<<>><<>><<<<>>>><>>><<<<>><<<<><<><><<<<>><<<>><<<>><<<>><<<>>><><<>>>><<><<<<>><<<><<<<>>><<<<>><><>><>>>><<<<>>><<><<<>><<<><<>>><>><>><<<>>>><<>>>><>><<<<>><<<<><<<<>>><<<<>><<<>>>><<<<>>>><<><>>><<<>>><<>><<>><><<><<>>>><<<><<>>><<><<>>><<>>>><<<<>><<>>><<<>>><<<>>>><<><>>><<>>><>>><<<><<<><<>>>><<<<>>><<<<>>><<<>>><<<<>><><>>>><<<>>>><>><>>><<<>>><<<<>><>>>><>>><<<><<<><<<<><<<<>>><<<><<<><>>><<<<><<<<>><>><<>>><<<>>><<<<><<>>><<<>>>><>>>><<<>>><<<<>>><<<<><>>>><<>><<<><<<>>><<<><<<<>>>><<>><<<>><>><<<<>>><>>><<<<><><<<<>>><<>>>><<>><>>>><<<>><>>><<<>><<<<>>>><<<>><<<<><<>>><>>>><<>><<<<>>>><>><<>><<<<>>><<>>><>>>><><><><<<<>>><>>><>>>><<<>><<><<<><>><<<<>><>><<><<<>>>><><<<>>><<>>><<>><<<<><<<<><<<<>><<<>>>><><<>>>><<>>><<><<<>><<<>><<<<>>>><>>>><<<<>>><<<<>>><<<<>><>>>><<>>>><>><><<>>>><<<><><<<<>><<>><<><<<<>>><<>><>><<<>>>><<>><<>>>><<>>><<<<>>><<<<>>>><<<>>>><>>><>>>><<<<>>>><<<<><<<><<>>><<<>>><<><>>>><<><<<>>><<<<><<<>><<<<>><<>>><<><<<>>><>><>><<<>>><<>>>><><<>>><><<<>><<<<>>>><<>><><<<>>>><><<<<><<<>>>><<<<>><<>>>><>>>><<>><<<<><<<<>>><<>>>><<>>><<>>>><<<>>>><<>><<><>><<<>><<<>>><<>>><<<<>>><<>><<<>><<<<>><<<><<>>><<<<>>><<<><<<<>><<>><<<<>><<>><<>>><<<>>><<>>><<>><<<>><<<>>>><>>><>><<<<>>><<>>>><>>>><<<<>>>><>>><<<<>>><<<>>>><<<<>>>><<<>>><<>>>><<<>><>>>><<<<>><<<>><><><<<><>>>><<<>><><>>>><<<>>><<<<>>>><<>>>><<>><<>>><<<<>>>><<<><>>>><>><<>>><<<>>><<<<><<<>><<<<><<<>>><>>><<>>><<><>>><<>>>><><<>>>><>>>><<<<><<<><><<>><<<>><<><<<>><<><<>><<<<>>>><<<>><<<<>>><<<<>>>><<<>><<<><<<>>>><<<>>>><><><<><><>>><<<>>>><>>><<<>>><<<<>>><><<<>>>><>><>>><<><<><<>>>><<<<>>><<<<>>>><><><<>><<<<>>><<<<>>><<<<><<<>><<<>>><<<<>>><<>><<>>><<<<>><<<><<<<><<<>>><<>>><<<>>><>>< diff --git a/AdventOfCode2022/AdventOfCode2022.fsproj b/AdventOfCode2022/AdventOfCode2022.fsproj index 562e8fd..849abb2 100644 --- a/AdventOfCode2022/AdventOfCode2022.fsproj +++ b/AdventOfCode2022/AdventOfCode2022.fsproj @@ -8,6 +8,7 @@ + @@ -26,6 +27,7 @@ + diff --git a/AdventOfCode2022/Day16.fs b/AdventOfCode2022/Day16.fs index a66efe4..f5ebf41 100644 --- a/AdventOfCode2022/Day16.fs +++ b/AdventOfCode2022/Day16.fs @@ -82,57 +82,11 @@ module Day16 = fun v1 v2 -> go Set.empty v1 v2 |> Option.get - type NodeSet = int64 - - let inline setNode (set : NodeSet) (nodeId : int) : NodeSet = set ||| (1L <<< nodeId) - let inline getNode (set : NodeSet) (nodeId : int) : bool = set &&& (1L <<< nodeId) <> 0 - let ofSeq (nodes : Node seq) : NodeSet = (0L, nodes) ||> Seq.fold setNode - - let toSeq (nodes : NodeSet) : Node seq = - seq { - let mutable nodes = nodes - let mutable count = 0 - - while nodes > 0 do - if nodes % 2L = 1L then - yield count - - nodes <- nodes >>> 1 - count <- count + 1 - } - - let count (nodes : NodeSet) : int = - let mutable nodes = nodes - let mutable ans = 0 - - while nodes > 0 do - if nodes % 2L = 1L then - ans <- ans + 1 - - nodes <- nodes >>> 1 - - ans - - - let first (nodes : NodeSet) : int = - let mutable nodes = nodes - let mutable count = 0 - let mutable ans = 0 - let mutable keepGoing = true - - while keepGoing && nodes > 0 do - if nodes % 2L = 1L then - ans <- count - keepGoing <- false - - nodes <- nodes >>> 1 - count <- count + 1 - - ans - let part1 (lines : string seq) : int = let valves, aaNode = parse lines - let allTaps = valves |> Map.filter (fun _ (x, _) -> x > 0) |> Map.keys |> ofSeq + + let allTaps = + valves |> Map.filter (fun _ (x, _) -> x > 0) |> Map.keys |> IntSet.ofSeq let getShortestPathLength = getShortestPathLength valves @@ -143,15 +97,15 @@ module Day16 = use ptr = fixed pathWeightsStorage let pathWeights = Arr2D.zeroCreate ptr valves.Count valves.Count #endif - for v1 in toSeq allTaps do - for v2 in toSeq allTaps do + for v1 in IntSet.toSeq allTaps do + for v2 in IntSet.toSeq allTaps do let length = getShortestPathLength v1 v2 Arr2D.set pathWeights v1 v2 length let rec go (timeRemainingOnCurrentPath : int) (headingTo : Node) - (alreadyOn : NodeSet) + (alreadyOn : IntSet) (currentWeight : int) (remaining : int) = @@ -161,7 +115,7 @@ module Day16 = go (timeRemainingOnCurrentPath - 1) headingTo alreadyOn currentWeight (remaining - 1) else - let alreadyOn = setNode alreadyOn headingTo + let alreadyOn = IntSet.set alreadyOn headingTo let mutable allTaps = allTaps &&& (~~~alreadyOn) let mutable count = 0 @@ -216,7 +170,8 @@ module Day16 = let valves, aaNode = parse lines let valvesIndexed = valves |> Map.values |> Array.ofSeq - let allTaps = valves |> Map.filter (fun _ (x, _) -> x > 0) |> Map.keys |> ofSeq + let allTaps = + valves |> Map.filter (fun _ (x, _) -> x > 0) |> Map.keys |> IntSet.ofSeq let getShortestPathLength = getShortestPathLength valves @@ -228,8 +183,8 @@ module Day16 = let pathWeights = Arr2D.zeroCreate ptr valves.Count valves.Count #endif - for v1 in toSeq allTaps do - for v2 in toSeq allTaps do + for v1 in IntSet.toSeq allTaps do + for v2 in IntSet.toSeq allTaps do let length = getShortestPathLength v1 v2 Arr2D.set pathWeights v1 v2 length @@ -238,7 +193,7 @@ module Day16 = (journey2 : int) (headingTo1 : Node) (headingTo2 : Node) - (alreadyOn : NodeSet) + (alreadyOn : IntSet) (currentWeight : int) (remaining : int) = @@ -249,14 +204,14 @@ module Day16 = elif journey1 = 0 && journey2 > 0 then let addToWeight = - if getNode alreadyOn headingTo1 then + if IntSet.contains alreadyOn headingTo1 then 0 else (remaining - 1) * (fst valvesIndexed.[headingTo1]) let newWeight = addToWeight + currentWeight - let alreadyOn = setNode alreadyOn headingTo1 + let alreadyOn = IntSet.set alreadyOn headingTo1 let mutable allTaps = allTaps &&& ~~~alreadyOn let mutable node = 0 @@ -292,14 +247,14 @@ module Day16 = elif journey2 = 0 && journey1 > 0 then let addToWeight = - if getNode alreadyOn headingTo2 then + if IntSet.contains alreadyOn headingTo2 then 0 else fst valvesIndexed.[headingTo2] * (remaining - 1) let newWeight = addToWeight + currentWeight - let alreadyOn = setNode alreadyOn headingTo2 + let alreadyOn = IntSet.set alreadyOn headingTo2 let mutable allTaps = allTaps &&& ~~~alreadyOn let mutable node = 0 @@ -336,13 +291,13 @@ module Day16 = else // Both reached destination at same time let addToWeight1 = - if getNode alreadyOn headingTo1 then + if IntSet.contains alreadyOn headingTo1 then 0 else (remaining - 1) * fst valvesIndexed.[headingTo1] let addToWeight2 = - if getNode alreadyOn headingTo2 then + if IntSet.contains alreadyOn headingTo2 then 0 else (remaining - 1) * fst valvesIndexed.[headingTo2] @@ -353,11 +308,11 @@ module Day16 = else addToWeight1 + currentWeight - let alreadyOn = setNode (setNode alreadyOn headingTo1) headingTo2 + let alreadyOn = IntSet.set (IntSet.set alreadyOn headingTo1) headingTo2 let nextChoices = allTaps &&& ~~~alreadyOn - if count nextChoices >= 2 then + if IntSet.count nextChoices >= 2 then let mutable maxVal = Int32.MinValue let mutable next1 = nextChoices @@ -395,12 +350,12 @@ module Day16 = 0 else // nextChoices.Count = 1 - let next = first nextChoices + let next = IntSet.first nextChoices go 100000 (Arr2D.get pathWeights next headingTo2) next next alreadyOn newWeight (remaining - 1) let startChoices = allTaps - |> toSeq + |> IntSet.toSeq |> Seq.map (fun startNode -> startNode, getShortestPathLength aaNode startNode) |> Map.ofSeq diff --git a/AdventOfCode2022/Day17.fs b/AdventOfCode2022/Day17.fs new file mode 100644 index 0000000..a7cc675 --- /dev/null +++ b/AdventOfCode2022/Day17.fs @@ -0,0 +1,288 @@ +namespace AdventOfCode2022 + +open System +open System.Collections.Generic +open FSharp.Collections.ParallelSeq + +#if DEBUG +open Checked +#else +#nowarn "9" +#endif + + +[] +module Day17 = + + /// Returns the nodes, and also the "AA" node. + let parse (line : string) : Direction array = + Array.init + line.Length + (fun i -> + match line.[i] with + | '<' -> Direction.Left + | '>' -> Direction.Right + | c -> failwithf "unexpected char %c" c + ) + + let printGrid (arr : Arr2D) (currentTop : int) = + for row in currentTop - 4 .. arr.Height - 1 do + for col in 0..6 do + match Arr2D.get arr col row with + | 0 -> printf "." + | 1 -> printf "@" + | 2 -> printf "#" + | _ -> failwith "oh no" + + printfn "" + + printfn "--------------------" + + let towerHeight (maxPossibleHeight : int) (grid : Arr2D<_>) = + let mutable towerHeight = 0 + let mutable stillLooking = true + + while stillLooking do + let row = maxPossibleHeight - towerHeight - 1 + let mutable anyOn = false + + for col in 0..6 do + if Arr2D.get grid col row <> 0 then + anyOn <- true + + if not anyOn then + stillLooking <- false + else + towerHeight <- towerHeight + 1 + + towerHeight + + let introduceRock (shape : bool[][]) startGrid currentBase = + for row in shape.Length - 1 .. -1 .. 0 do + for col in 0 .. shape.[0].Length - 1 do + if shape.[row].[col] then + let x = 2 + col + let y = currentBase + row - shape.Length + 1 + Arr2D.set startGrid x y 1 + + let moveJet (direction : Direction) (currentBase : int) (startGrid : Arr2D) : unit = + match direction with + | Direction.Left -> + let mutable canMove = true + + for row in currentBase .. -1 .. currentBase - 3 do + if Arr2D.get startGrid 0 row = 1 then + canMove <- false + else + for col in 1..6 do + if Arr2D.get startGrid col row = 1 && Arr2D.get startGrid (col - 1) row = 2 then + canMove <- false + + if canMove then + for row in currentBase .. -1 .. currentBase - 3 do + for col in 0..5 do + if Arr2D.get startGrid (col + 1) row = 1 then + Arr2D.set startGrid col row 1 + Arr2D.set startGrid (col + 1) row 0 + + if Arr2D.get startGrid 6 row = 1 then + Arr2D.set startGrid 6 row 0 + | Direction.Right -> + let mutable canMove = true + + for row in currentBase .. -1 .. currentBase - 3 do + if Arr2D.get startGrid 6 row = 1 then + canMove <- false + else + for col in 0..5 do + if Arr2D.get startGrid col row = 1 && Arr2D.get startGrid (col + 1) row = 2 then + canMove <- false + + if canMove then + for row in currentBase .. -1 .. currentBase - 3 do + for col in 6..-1..1 do + if Arr2D.get startGrid (col - 1) row = 1 then + Arr2D.set startGrid col row 1 + Arr2D.set startGrid (col - 1) row 0 + + if Arr2D.get startGrid 1 row = 1 then + Arr2D.set startGrid 0 row 0 + | _ -> failwith "Unexpected direction" + + /// Returns the new currentBase if we're still falling, or None if we're not + /// still falling. + let fallOnce (currentBase : int) (startGrid : Arr2D) : int option = + let mutable isFalling = true + + // Fall one place. Can we fall? + if currentBase = startGrid.Height - 1 then + isFalling <- false + else + for row in currentBase .. -1 .. currentBase - 3 do + for col in 0..6 do + if Arr2D.get startGrid col row = 1 && Arr2D.get startGrid col (row + 1) = 2 then + isFalling <- false + + if isFalling then + for row in currentBase .. -1 .. currentBase - 3 do + for col in 0..6 do + if Arr2D.get startGrid col row = 1 then + Arr2D.set startGrid col (row + 1) 1 + Arr2D.set startGrid col row 0 + + Some (currentBase + 1) + else + for row in currentBase .. -1 .. currentBase - 3 do + for col in 0..6 do + if Arr2D.get startGrid col row = 1 then + // Freeze in place + Arr2D.set startGrid col row 2 + + None + + let findCurrentTop (currentTop : int) (startGrid : Arr2D) : int = + let mutable currentTop = currentTop + + for row in currentTop - 1 .. -1 .. currentTop - 4 do + for col in 0..6 do + if Arr2D.get startGrid col row = 2 then + currentTop <- row + + currentTop + + let shapes = + [| + [| [| true ; true ; true ; true |] |] + [| + [| false ; true ; false |] + [| true ; true ; true |] + [| false ; true ; false |] + |] + [| + [| false ; false ; true |] + [| false ; false ; true |] + [| true ; true ; true |] + |] + Array.init 4 (fun _ -> [| true |]) + Array.init 2 (fun _ -> [| true ; true |]) + |] + + let part1 (line : string) : int = + let directions = parse line + + let maxPossibleHeight = + shapes + |> Array.map Array.length // if each shape stacked perfectly on top + |> Array.sum + |> fun i -> i * (2022 / 5 + 1) + +#if DEBUG + let startGrid = Arr2D.zeroCreate 7 maxPossibleHeight +#else + let startGridBacking = Array.zeroCreate (7 * maxPossibleHeight) + use ptr = fixed startGridBacking + let startGrid = Arr2D.zeroCreate ptr 7 maxPossibleHeight +#endif + + let mutable currentTop = maxPossibleHeight + let mutable jetCount = 0 + + for count in 0 .. 2022 - 1 do + let shape = shapes.[count % shapes.Length] + + let mutable currentBase = currentTop - 4 + + introduceRock shape startGrid currentBase + + // Set it falling + let mutable isFalling = true + + while isFalling do + // Move by jet. + moveJet directions.[jetCount % directions.Length] currentBase startGrid + jetCount <- (jetCount + 1) % directions.Length + + match fallOnce currentBase startGrid with + | Some newCurrentBase -> currentBase <- newCurrentBase + | None -> isFalling <- false + + // Set new currentTop + currentTop <- findCurrentTop currentTop startGrid + + towerHeight maxPossibleHeight startGrid + + let part2 (line : string) : int64 = + let directions = parse line + + let maxPossibleHeight = + shapes + |> Array.map Array.length // if each shape stacked perfectly on top + |> Array.sum + |> fun i -> i * (100000000 / 5 + 1) + +#if DEBUG + let startGrid = Arr2D.zeroCreate 7 maxPossibleHeight +#else + let startGridBacking = Array.zeroCreate (7 * maxPossibleHeight) + use ptr = fixed startGridBacking + let startGrid = Arr2D.zeroCreate ptr 7 maxPossibleHeight +#endif + + let mutable currentTop = maxPossibleHeight + + let mutable shapeCount = 0 + let mutable jetCount = directions.Length + let seenJetCounts = HashSet () + let mutable fromLastCycle = ValueNone + let mutable skippedFromCycle = -1L + + let limit = 1000000000000L + let mutable remainingStones = limit + + while remainingStones > 0 do + for count in 0 .. shapes.Length - 1 do + shapeCount <- shapeCount + 1 + remainingStones <- remainingStones - 1L + let shape = shapes.[count] + + let mutable currentBase = currentTop - 4 + + introduceRock shape startGrid currentBase + + // Set it falling + let mutable isFalling = true + + while isFalling do + // Move by jet. + moveJet directions.[jetCount % directions.Length] currentBase startGrid + jetCount <- (jetCount + 1) % directions.Length + + match fallOnce currentBase startGrid with + | Some newCurrentBase -> currentBase <- newCurrentBase + | None -> isFalling <- false + + // Set new currentTop + currentTop <- findCurrentTop currentTop startGrid + + // Try and find a duplicate. + if not (seenJetCounts.Add jetCount) then + match fromLastCycle with + | ValueNone -> + let towerHeight = towerHeight maxPossibleHeight startGrid + seenJetCounts.Clear () + seenJetCounts.Add jetCount |> ignore + fromLastCycle <- ValueSome (shapeCount, towerHeight) + | ValueSome (prevShapeCount, prevTowerHeight) -> + let towerHeight = towerHeight maxPossibleHeight startGrid + let heightGainedPerCycle = towerHeight - prevTowerHeight + let piecesPerCycle = shapeCount - prevShapeCount + + let remainingCycles = (limit - int64 shapeCount) / int64 piecesPerCycle + skippedFromCycle <- remainingCycles * int64 heightGainedPerCycle + + remainingStones <- (limit - int64 shapeCount) % int64 piecesPerCycle + seenJetCounts.Clear () + + let towerHeight = towerHeight maxPossibleHeight startGrid + + int64 towerHeight + skippedFromCycle diff --git a/AdventOfCode2022/IntSet.fs b/AdventOfCode2022/IntSet.fs new file mode 100644 index 0000000..1f9eb85 --- /dev/null +++ b/AdventOfCode2022/IntSet.fs @@ -0,0 +1,52 @@ +namespace AdventOfCode2022 + +type IntSet = int64 + +[] +module IntSet = + + let inline set (set : IntSet) (nodeId : int) : IntSet = set ||| (1L <<< nodeId) + let inline contains (set : IntSet) (nodeId : int) : bool = set &&& (1L <<< nodeId) <> 0 + let ofSeq (nodes : int seq) : IntSet = (0L, nodes) ||> Seq.fold set + + let toSeq (nodes : IntSet) : int seq = + seq { + let mutable nodes = nodes + let mutable count = 0 + + while nodes > 0 do + if nodes % 2L = 1L then + yield count + + nodes <- nodes >>> 1 + count <- count + 1 + } + + let count (nodes : IntSet) : int = + let mutable nodes = nodes + let mutable ans = 0 + + while nodes > 0 do + if nodes % 2L = 1L then + ans <- ans + 1 + + nodes <- nodes >>> 1 + + ans + + + let first (nodes : IntSet) : int = + let mutable nodes = nodes + let mutable count = 0 + let mutable ans = 0 + let mutable keepGoing = true + + while keepGoing && nodes > 0 do + if nodes % 2L = 1L then + ans <- count + keepGoing <- false + + nodes <- nodes >>> 1 + count <- count + 1 + + ans