Blown way through my time budget and there's a bunch of low hanging fruit (data dependencies in LCM, for example) but I'm late for work. Co-authored-by: Smaug123 <patrick+github@patrickstevens.co.uk> Reviewed-on: #8
111 lines
3.1 KiB
Forth
111 lines
3.1 KiB
Forth
namespace AdventOfCode2023
|
|
|
|
open System
|
|
open System.Collections.Generic
|
|
|
|
[<RequireQualifiedAccess>]
|
|
module Day8 =
|
|
|
|
type Instructions =
|
|
{
|
|
/// "true" is 'R'
|
|
Steps : bool array
|
|
|
|
Nodes : Dictionary<string, string * string>
|
|
}
|
|
|
|
let parse (s : string) =
|
|
use mutable lines = StringSplitEnumerator.make '\n' s
|
|
|
|
lines.MoveNext () |> ignore
|
|
|
|
let stepsLine = lines.Current.TrimEnd ()
|
|
let steps = Array.zeroCreate stepsLine.Length
|
|
|
|
for i = 0 to stepsLine.Length - 1 do
|
|
steps.[i] <- (stepsLine.[i] = 'R')
|
|
|
|
let dict = Dictionary ()
|
|
|
|
while lines.MoveNext () do
|
|
if not lines.Current.IsEmpty then
|
|
use mutable line = StringSplitEnumerator.make' ' ' lines.Current
|
|
line.MoveNext () |> ignore
|
|
let key = line.Current.ToString ()
|
|
line.MoveNext () |> ignore
|
|
line.MoveNext () |> ignore
|
|
let v1 = line.Current.Slice(1, line.Current.Length - 2).ToString ()
|
|
line.MoveNext () |> ignore
|
|
let v2 = line.Current.Slice(0, line.Current.Length - 1).ToString ()
|
|
dict.[key] <- (v1, v2)
|
|
|
|
{
|
|
Steps = steps
|
|
Nodes = dict
|
|
}
|
|
|
|
let part1 (s : string) =
|
|
let data = parse s
|
|
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 startingNodes = ResizeArray ()
|
|
|
|
for key in data.Nodes.Keys do
|
|
if key.[key.Length - 1] = 'A' then
|
|
startingNodes.Add key
|
|
|
|
let periods =
|
|
Array.init
|
|
startingNodes.Count
|
|
(fun startNode ->
|
|
let mutable i = 0
|
|
let mutable currentNode = startingNodes.[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
|