This commit is contained in:
Patrick Stevens
2022-12-17 01:15:56 +00:00
committed by GitHub
parent 966a12fad1
commit c4be907aa7
11 changed files with 630 additions and 44 deletions

View File

@@ -33,8 +33,17 @@ jobs:
run: dotnet build --no-restore --configuration ${{matrix.config}}
- name: Test
run: dotnet test --no-build --verbosity normal --configuration ${{matrix.config}}
- name: Run app
run: dotnet run --project AdventOfCode2022.App/AdventOfCode2022.App.fsproj --no-build --configuration ${{matrix.config}}
run-app:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Setup .NET
uses: actions/setup-dotnet@v3
with:
dotnet-version: 7.0.x
- name: Run app
run: dotnet run --project AdventOfCode2022.App/AdventOfCode2022.App.fsproj --configuration Release
check-format:
runs-on: ubuntu-latest
@@ -67,7 +76,7 @@ jobs:
name: Run link checker
all-required-checks-complete:
needs: [check-format, build, shellcheck, linkcheck]
needs: [check-format, build, shellcheck, linkcheck, run-app]
runs-on: ubuntu-latest
steps:
- run: echo "All required checks complete."

View File

@@ -25,6 +25,7 @@
<EmbeddedResource Include="..\AdventOfCode2022.Test\Inputs\Day13.txt" />
<EmbeddedResource Include="..\AdventOfCode2022.Test\Inputs\Day14.txt" />
<EmbeddedResource Include="..\AdventOfCode2022.Test\Inputs\Day15.txt" />
<EmbeddedResource Include="..\AdventOfCode2022.Test\Inputs\Day16.txt" />
</ItemGroup>
<ItemGroup>

View File

@@ -2,5 +2,5 @@ namespace AdventOfCode2022.App
[<RequireQualifiedAccess>]
module Inputs =
let days = Array.init 15 (fun day -> Assembly.readResource $"Day%i{day + 1}.txt")
let days = Array.init 16 (fun day -> Assembly.readResource $"Day%i{day + 1}.txt")
let inline day (i : int) = days.[i - 1]

View File

@@ -57,6 +57,22 @@ type Benchmark11To15 () =
[<GlobalCleanup>]
member _.Cleanup () = Run.shouldWrite <- true
type Benchmark16To20 () =
[<GlobalSetup>]
member _.Setup () = Run.shouldWrite <- false
[<Params(16)>]
member val Day = 0 with get, set
[<Params(false, true)>]
member val IsPartOne = false with get, set
[<Benchmark>]
member this.Benchmark () : unit =
Run.allRuns.[this.Day - 1] (not this.IsPartOne) (Inputs.day this.Day)
[<GlobalCleanup>]
member _.Cleanup () = Run.shouldWrite <- true
module Program =
@@ -72,6 +88,7 @@ module Program =
let _summary = BenchmarkRunner.Run<Benchmark1To5> config
let _summary = BenchmarkRunner.Run<Benchmark6To10> config
let _summary = BenchmarkRunner.Run<Benchmark11To15> config
let _summary = BenchmarkRunner.Run<Benchmark16To20> config
0
| _ ->

View File

@@ -218,6 +218,20 @@ module Run =
if shouldWrite then
printfn "%i" output
let day16 (partTwo : bool) (input : string) =
let day16 = input.Split '\n'
if not partTwo then
let output = Day16.part1 day16
if shouldWrite then
printfn "%i" output
else
let output = Day16.part2 day16
if shouldWrite then
printfn "%i" output
let allRuns =
[|
day1
@@ -235,4 +249,5 @@ module Run =
day13
day14
day15
day16
|]

View File

@@ -23,6 +23,7 @@
<Compile Include="Day13.fs" />
<Compile Include="Day14.fs" />
<Compile Include="Day15.fs" />
<Compile Include="Day16.fs" />
<EmbeddedResource Include="Inputs\Day1.txt" />
<EmbeddedResource Include="Inputs\Day2.txt" />
<EmbeddedResource Include="Inputs\Day3.txt" />
@@ -38,6 +39,7 @@
<EmbeddedResource Include="Inputs\Day13.txt" />
<EmbeddedResource Include="Inputs\Day14.txt" />
<EmbeddedResource Include="Inputs\Day15.txt" />
<EmbeddedResource Include="Inputs\Day16.txt" />
</ItemGroup>
<ItemGroup>

View File

@@ -0,0 +1,49 @@
namespace AdventOfCode2022.Test
open NUnit.Framework
open FsUnitTyped
open AdventOfCode2022
[<TestFixture>]
module TestDay16 =
let input =
"""Valve AA has flow rate=0; tunnels lead to valves DD, II, BB
Valve BB has flow rate=13; tunnels lead to valves CC, AA
Valve CC has flow rate=2; tunnels lead to valves DD, BB
Valve DD has flow rate=20; tunnels lead to valves CC, AA, EE
Valve EE has flow rate=3; tunnels lead to valves FF, DD
Valve FF has flow rate=0; tunnels lead to valves EE, GG
Valve GG has flow rate=0; tunnels lead to valves FF, HH
Valve HH has flow rate=22; tunnel leads to valve GG
Valve II has flow rate=0; tunnels lead to valves AA, JJ
Valve JJ has flow rate=21; tunnel leads to valve II
"""
[<Test>]
let ``seq behaviour`` () =
Day16.ofSeq [ 1 ; 2 ; 3 ; 16 ]
|> Day16.toSeq
|> List.ofSeq
|> shouldEqual [ 1 ; 2 ; 3 ; 16 ]
[<Test>]
let ``Part 1, given`` () =
Day16.part1 (input.Split '\n') |> shouldEqual 1651
[<Test>]
let ``Part 1`` () =
let input = Assembly.readResource "Day16.txt"
Day16.part1 (input.Split '\n') |> shouldEqual 1751
[<Test>]
let ``Part 2, given`` () =
Day16.part2 (input.Split '\n') |> shouldEqual 1707
[<Test>]
let ``Part 2`` () =
let input = Assembly.readResource "Day16.txt"
Day16.part2 (input.Split '\n') |> shouldEqual 2207

View File

@@ -0,0 +1,60 @@
Valve QP has flow rate=0; tunnels lead to valves IS, DG
Valve MC has flow rate=0; tunnels lead to valves XX, QQ
Valve OT has flow rate=7; tunnels lead to valves OE, BL, DJ, JS, LS
Valve CZ has flow rate=0; tunnels lead to valves IC, ZL
Valve GI has flow rate=0; tunnels lead to valves OM, GF
Valve YB has flow rate=0; tunnels lead to valves DQ, MX
Valve EJ has flow rate=0; tunnels lead to valves GB, ES
Valve IS has flow rate=19; tunnels lead to valves AS, OB, QP
Valve WI has flow rate=21; tunnels lead to valves SS, AK
Valve JS has flow rate=0; tunnels lead to valves OT, HV
Valve UR has flow rate=0; tunnels lead to valves OM, ZI
Valve UC has flow rate=0; tunnels lead to valves QX, NG
Valve BL has flow rate=0; tunnels lead to valves YW, OT
Valve AK has flow rate=0; tunnels lead to valves WI, AL
Valve QQ has flow rate=16; tunnels lead to valves MC, WH, MS, IY
Valve PW has flow rate=0; tunnels lead to valves ZL, EK
Valve AS has flow rate=0; tunnels lead to valves IS, MS
Valve ZL has flow rate=9; tunnels lead to valves CD, QX, PW, CZ, PQ
Valve OB has flow rate=0; tunnels lead to valves HS, IS
Valve OE has flow rate=0; tunnels lead to valves IC, OT
Valve AL has flow rate=0; tunnels lead to valves VX, AK
Valve AM has flow rate=0; tunnels lead to valves OM, YW
Valve QX has flow rate=0; tunnels lead to valves UC, ZL
Valve DJ has flow rate=0; tunnels lead to valves OT, ST
Valve ZI has flow rate=0; tunnels lead to valves VX, UR
Valve PQ has flow rate=0; tunnels lead to valves ZL, YW
Valve OM has flow rate=22; tunnels lead to valves GI, AM, EK, UR
Valve NG has flow rate=13; tunnels lead to valves UC, HS, GF
Valve AA has flow rate=0; tunnels lead to valves UJ, ES, JP, HY, ST
Valve HY has flow rate=0; tunnels lead to valves GZ, AA
Valve MS has flow rate=0; tunnels lead to valves AS, QQ
Valve JK has flow rate=0; tunnels lead to valves YW, GB
Valve JP has flow rate=0; tunnels lead to valves AA, PF
Valve ST has flow rate=0; tunnels lead to valves AA, DJ
Valve CD has flow rate=0; tunnels lead to valves SS, ZL
Valve ES has flow rate=0; tunnels lead to valves EJ, AA
Valve PF has flow rate=0; tunnels lead to valves JP, HV
Valve RL has flow rate=0; tunnels lead to valves GB, IC
Valve IY has flow rate=0; tunnels lead to valves QQ, SN
Valve UJ has flow rate=0; tunnels lead to valves IC, AA
Valve HS has flow rate=0; tunnels lead to valves NG, OB
Valve WH has flow rate=0; tunnels lead to valves QQ, MX
Valve YA has flow rate=0; tunnels lead to valves GB, HV
Valve SN has flow rate=0; tunnels lead to valves IY, DG
Valve GF has flow rate=0; tunnels lead to valves GI, NG
Valve YW has flow rate=8; tunnels lead to valves GZ, JK, BL, PQ, AM
Valve DG has flow rate=17; tunnels lead to valves QP, SN
Valve MX has flow rate=11; tunnels lead to valves WH, YB
Valve DQ has flow rate=0; tunnels lead to valves YB, HV
Valve SS has flow rate=0; tunnels lead to valves CD, WI
Valve HV has flow rate=4; tunnels lead to valves YA, DQ, TO, JS, PF
Valve GB has flow rate=6; tunnels lead to valves LS, RL, JK, EJ, YA
Valve EK has flow rate=0; tunnels lead to valves OM, PW
Valve LS has flow rate=0; tunnels lead to valves GB, OT
Valve IC has flow rate=5; tunnels lead to valves CZ, OE, UJ, TO, RL
Valve XX has flow rate=0; tunnels lead to valves MC, FM
Valve VX has flow rate=25; tunnels lead to valves ZI, AL
Valve GZ has flow rate=0; tunnels lead to valves HY, YW
Valve FM has flow rate=20; tunnel leads to valve XX
Valve TO has flow rate=0; tunnels lead to valves IC, HV

View File

@@ -25,6 +25,11 @@
<Compile Include="Day13.fs" />
<Compile Include="Day14.fs" />
<Compile Include="Day15.fs" />
<Compile Include="Day16.fs" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="FSharp.Collections.ParallelSeq" Version="1.2.0" />
</ItemGroup>
</Project>

View File

@@ -61,17 +61,6 @@ module Day15 =
let inline manhattan (p1 : Coordinate) (p2 : Coordinate) : int = abs (p1.X - p2.X) + abs (p1.Y - p2.Y)
let inline cantorBijection (x : int) (y : int) : int64 =
let sum = x + y
let major =
if sum % 2 = 0 then
int64 (sum / 2) * int64 (sum + 1)
else
int64 sum * int64 ((sum + 1) / 2)
major + int64 y
let inline couldBeBeacon
(sensors : ResizeArray<Coordinate>)
(closestManhattans : int[])
@@ -163,14 +152,14 @@ module Day15 =
let mutable xIndex = 0
while answer = -1L && xIndex < sensorXCoords.Length - 1 do
while answer <> 1L && xIndex < sensorXCoords.Length - 1 do
let xMin = sensorXCoords.[xIndex]
xIndex <- xIndex + 1
let xMax = sensorXCoords.[xIndex]
let mutable yIndex = 0
while answer = -1L && yIndex < sensorYCoords.Length - 1 do
while answer <> 1L && yIndex < sensorYCoords.Length - 1 do
let yMin = sensorYCoords.[yIndex]
yIndex <- yIndex + 1
let yMax = sensorYCoords.[yIndex]
@@ -195,6 +184,42 @@ module Day15 =
else
plusXPlusYConstraint <- min plusXPlusYConstraint (-manhattan + sensor.X + sensor.Y - 1)
let yMax =
if
plusXPlusYConstraint <> Int32.MaxValue
&& plusXMinusYConstraint <> Int32.MaxValue
then
min ((plusXPlusYConstraint - plusXMinusYConstraint) / 2) yMax
else
yMax
let xMax =
if
plusXPlusYConstraint <> Int32.MaxValue
&& minusXPlusYConstraint <> Int32.MaxValue
then
min ((plusXPlusYConstraint - minusXPlusYConstraint) / 2) xMax
else
xMax
let xMin =
if
minusXMinusYConstraint <> Int32.MaxValue
&& minusXPlusYConstraint <> Int32.MaxValue
then
max xMin (-(minusXMinusYConstraint + minusXPlusYConstraint) / 2)
else
xMin
let yMin =
if
minusXMinusYConstraint <> Int32.MaxValue
&& plusXMinusYConstraint <> Int32.MaxValue
then
max yMin (-(minusXMinusYConstraint + plusXMinusYConstraint) / 2)
else
yMin
// (fst constraints).{x, y} <= (snd constraints), and also xMin <= x <= xMax and
// yMin <= y <= yMax.
let mutable falsified = false
@@ -221,32 +246,6 @@ module Day15 =
falsified <- true
if not falsified then
// The most likely way for there to be no slack is if equality holds throughout both conjugate pairs
// of constraints.
// (It's also possible for there to be no slack by having one of the "x <= 33" constraints binding.
// See below for the treatment of this case.)
if
minusXMinusYConstraint = -plusXPlusYConstraint
&& plusXMinusYConstraint = -minusXPlusYConstraint
then
let y = (plusXPlusYConstraint - plusXMinusYConstraint) / 2
let x = (plusXPlusYConstraint - minusXPlusYConstraint) / 2
if xMin <= x && x <= xMax && yMin <= y && y <= yMax then
answer <- int64 x * 4000000L + int64 y
if answer = -1L then
// In fact one of the xMax/xMin or yMax/yMin constraints binds.
let mutable xIndex = 0
while answer = -1 && xIndex < sensorXCoords.Length do
xIndex <- xIndex + 1
// Try with this x.
let _x = sensorXCoords.Length
for _sensor in sensors do
// Construct the necessary inequalities, then solve them.
failwith "I couldn't be bothered to implement this"
answer <- int64 xMax * 4000000L + int64 yMax
answer

429
AdventOfCode2022/Day16.fs Normal file
View File

@@ -0,0 +1,429 @@
namespace AdventOfCode2022
open System
open FSharp.Collections.ParallelSeq
#if DEBUG
open Checked
#else
#nowarn "9"
#endif
[<RequireQualifiedAccess>]
module Day16 =
type Node = int
/// Returns the nodes, and also the "AA" node.
let parse (lines : string seq) : Map<Node, int * Node Set> * Node =
let allNodes =
lines
|> Seq.filter (not << String.IsNullOrEmpty)
|> Seq.mapi (fun i line ->
let split = line.Split "tunne" |> Array.last |> (fun s -> s.Trim ())
let split = split.Split(' ').[4..] |> Array.map (fun s -> s.Trim ',') |> Set.ofArray
line.[6..7], (i, (Int32.Parse line.[line.IndexOf '=' + 1 .. line.IndexOf ';' - 1], split))
)
|> Map.ofSeq
let result =
allNodes
|> Map.toSeq
|> Seq.map (fun (key, (node, (weight, outbound))) ->
node, (weight, (Set.map (fun x -> fst (Map.find x allNodes)) outbound))
)
|> Map.ofSeq
result, fst allNodes.["AA"]
let inline tryMax< ^a when ^a : comparison> (s : seq<'a>) : 'a option =
use enum = s.GetEnumerator ()
if not (enum.MoveNext ()) then
None
else
let mutable answer = enum.Current
while enum.MoveNext () do
answer <- max answer enum.Current
Some answer
let tryMin (s : seq<'a>) : 'a option =
use enum = s.GetEnumerator ()
if not (enum.MoveNext ()) then
None
else
let mutable answer = enum.Current
while enum.MoveNext () do
answer <- min answer enum.Current
Some answer
let getShortestPathLength (valves : Map<_, _>) : Node -> Node -> int =
let rec go (seenSoFar : Node Set) (v1 : Node) (v2 : Node) =
let v2Neighbours = snd valves.[v2]
if v1 = v2 then
Some 0
elif Set.contains v1 v2Neighbours then
Some 1
elif Set.contains v2 seenSoFar then
None
else
v2Neighbours
|> Seq.choose (go (Set.add v2 seenSoFar) v1)
|> tryMin
|> Option.map ((+) 1)
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 getShortestPathLength = getShortestPathLength valves
#if DEBUG
let pathWeights = Arr2D.zeroCreate<int> valves.Count valves.Count
#else
let pathWeightsStorage = Array.zeroCreate (valves.Count * valves.Count)
use ptr = fixed pathWeightsStorage
let pathWeights = Arr2D.zeroCreate<int> ptr valves.Count valves.Count
#endif
for v1 in toSeq allTaps do
for v2 in toSeq allTaps do
let length = getShortestPathLength v1 v2
Arr2D.set pathWeights v1 v2 length
let rec go
(timeRemainingOnCurrentPath : int)
(headingTo : Node)
(alreadyOn : NodeSet)
(currentWeight : int)
(remaining : int)
=
if remaining <= 0 then
currentWeight
elif timeRemainingOnCurrentPath > 0 then
go (timeRemainingOnCurrentPath - 1) headingTo alreadyOn currentWeight (remaining - 1)
else
let alreadyOn = setNode alreadyOn headingTo
let mutable allTaps = allTaps &&& (~~~alreadyOn)
let mutable count = 0
let mutable max = ValueNone
while allTaps > 0 do
if allTaps % 2L = 1 then
let addToWeight = fst valves.[headingTo] * (remaining - 1)
let candidate =
go
(Arr2D.get pathWeights count headingTo)
count
alreadyOn
(currentWeight + addToWeight)
(remaining - 1)
match max with
| ValueNone -> max <- ValueSome candidate
| ValueSome existingMax ->
if existingMax < candidate then
max <- ValueSome candidate
allTaps <- allTaps >>> 1
count <- count + 1
match max with
| ValueSome v -> v
| ValueNone -> currentWeight + (remaining - 1) * (fst valves.[headingTo])
let mutable startChoices = allTaps
let mutable start = 0
let mutable maxValue = Int32.MinValue
while startChoices > 0 do
if startChoices % 2L = 1L then
let distance = getShortestPathLength aaNode start
// By inspecting the graph, I can see that AA is screened off from the rest
// of the graph by the set of valves with distance at most 3 from it.
// Assume that we're going to turn them on when we pass through - this isn't
// actually fully general, but it is enough.
if distance <= 3 then
let candidate = go distance start 0 0 30
maxValue <- max maxValue candidate
startChoices <- startChoices >>> 1
start <- start + 1
maxValue
let part2 (lines : string seq) : int =
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 getShortestPathLength = getShortestPathLength valves
#if DEBUG
let pathWeights = Arr2D.zeroCreate<int> valves.Count valves.Count
#else
let pathWeightsStorage = Array.zeroCreate (valves.Count * valves.Count)
use ptr = fixed pathWeightsStorage
let pathWeights = Arr2D.zeroCreate<int> ptr valves.Count valves.Count
#endif
for v1 in toSeq allTaps do
for v2 in toSeq allTaps do
let length = getShortestPathLength v1 v2
Arr2D.set pathWeights v1 v2 length
let rec go
(journey1 : int)
(journey2 : int)
(headingTo1 : Node)
(headingTo2 : Node)
(alreadyOn : NodeSet)
(currentWeight : int)
(remaining : int)
=
if remaining <= 0 then
currentWeight
elif journey1 > 0 && journey2 > 0 then
go (journey1 - 1) (journey2 - 1) headingTo1 headingTo2 alreadyOn currentWeight (remaining - 1)
elif journey1 = 0 && journey2 > 0 then
let addToWeight =
if getNode alreadyOn headingTo1 then
0
else
(remaining - 1) * (fst valvesIndexed.[headingTo1])
let newWeight = addToWeight + currentWeight
let alreadyOn = setNode alreadyOn headingTo1
let mutable allTaps = allTaps &&& ~~~alreadyOn
let mutable node = 0
let mutable max = ValueNone
while allTaps > 0L do
// Strictly speaking we might be able to overtake the other one
// who is heading for their node, but it so happens that the
// test cases don't care about that.
if allTaps % 2L = 1L && node <> headingTo2 then
let next =
go
(Arr2D.get pathWeights node headingTo1)
(journey2 - 1)
node
headingTo2
alreadyOn
newWeight
(remaining - 1)
match max with
| ValueNone -> max <- ValueSome next
| ValueSome existingMax ->
if next > existingMax then
max <- ValueSome next
allTaps <- allTaps >>> 1
node <- node + 1
match max with
| ValueSome v -> v
| ValueNone -> go 1000000 (journey2 - 1) headingTo1 headingTo2 alreadyOn newWeight (remaining - 1)
elif journey2 = 0 && journey1 > 0 then
let addToWeight =
if getNode alreadyOn headingTo2 then
0
else
fst valvesIndexed.[headingTo2] * (remaining - 1)
let newWeight = addToWeight + currentWeight
let alreadyOn = setNode alreadyOn headingTo2
let mutable allTaps = allTaps &&& ~~~alreadyOn
let mutable node = 0
let mutable max = ValueNone
while allTaps > 0L do
// Strictly speaking we might be able to overtake the other one
// who is heading for their node, but it so happens that the
// test cases don't care about that.
if allTaps % 2L = 1L && node <> headingTo1 then
let next =
go
(journey1 - 1)
(Arr2D.get pathWeights node headingTo2)
headingTo1
node
alreadyOn
newWeight
(remaining - 1)
match max with
| ValueNone -> max <- ValueSome next
| ValueSome existingMax ->
if next > existingMax then
max <- ValueSome next
allTaps <- allTaps >>> 1
node <- node + 1
match max with
| ValueSome v -> v
| ValueNone -> go (journey1 - 1) 1000000 headingTo1 headingTo2 alreadyOn newWeight (remaining - 1)
else
// Both reached destination at same time
let addToWeight1 =
if getNode alreadyOn headingTo1 then
0
else
(remaining - 1) * fst valvesIndexed.[headingTo1]
let addToWeight2 =
if getNode alreadyOn headingTo2 then
0
else
(remaining - 1) * fst valvesIndexed.[headingTo2]
let newWeight =
if headingTo1 <> headingTo2 then
addToWeight1 + addToWeight2 + currentWeight
else
addToWeight1 + currentWeight
let alreadyOn = setNode (setNode alreadyOn headingTo1) headingTo2
let nextChoices = allTaps &&& ~~~alreadyOn
if count nextChoices >= 2 then
let mutable maxVal = Int32.MinValue
let mutable next1 = nextChoices
let mutable count1 = 0
while next1 > 0 do
if next1 % 2L = 1 then
let mutable next2 = nextChoices
let mutable count2 = 0
while next2 > 0 do
// It's never correct for both to try and turn on the same node.
if next2 % 2L = 1 && next1 <> next2 then
let candidate =
go
(Arr2D.get pathWeights count1 headingTo1)
(Arr2D.get pathWeights count2 headingTo2)
count1
count2
alreadyOn
newWeight
(remaining - 1)
maxVal <- max maxVal candidate
next2 <- next2 >>> 1
count2 <- count2 + 1
next1 <- next1 >>> 1
count1 <- count1 + 1
maxVal
elif nextChoices = 0 then
0
else
// nextChoices.Count = 1
let next = first nextChoices
go 100000 (Arr2D.get pathWeights next headingTo2) next next alreadyOn newWeight (remaining - 1)
let startChoices =
allTaps
|> toSeq
|> Seq.map (fun startNode -> startNode, getShortestPathLength aaNode startNode)
|> Map.ofSeq
let maxVal = ref Int32.MinValue
Seq.allPairs startChoices startChoices
|> Seq.filter (fun (KeyValue (n1, distance1), KeyValue (n2, distance2)) ->
// By inspecting the graph, I can see that AA is screened off from the rest
// of the graph by the set of valves with distance at most 3 from it.
// Assume that we're going to turn them on when we pass through - this isn't
// actually fully general, but it is enough.
n1 < n2 && distance1 <= 3 && distance2 <= 3
)
|> PSeq.map (fun (KeyValue (start1, distance1), KeyValue (start2, distance2)) ->
go distance1 distance2 start1 start2 0 0 26
)
|> PSeq.iter (fun s ->
lock
maxVal
(fun () ->
if s > maxVal.Value then
maxVal.Value <- s
)
)
maxVal.Value