edit: I did the wrong quest
I wasn't very happy with myself on this one. Part 1 wound up being simple enough, but part 2 took me for a loop. My disappointment was that I asked an AI for a hint, but instead it coded up a solution revolved around checking cycle dependency.
I tried to at least redo it in a functional way, since I'm writing this in FSharp, but I ran into a bunch of hiccups around indexes and index tracking. That always costs me so much time. This took me forever and I'm near the bottom of the rankings haha.
Overall, I wasn't terribly happy with how I did on this one. Oh, I wound up switching all of the types to BigInteger because I was lost as to what was happening. It turned out to be indexes and orderings, but I thought that maybe something was getting truncated.
/// Map from (prev, curr) pair to position
type PositionMap = Map<BigInteger * BigInteger, BigInteger * BigInteger>
let rec findCycle (pairToNextPair: PositionMap) startPair currentPair acc =
if currentPair = startPair && List.length acc > 0 then
Some (List.rev acc)
else
match Map.tryFind currentPair pairToNextPair with
| None -> None
| Some nextPair ->
findCycle pairToNextPair startPair nextPair (snd currentPair :: acc)
let rec eni2' score (n:BigInteger) (exp:BigInteger) (m:BigInteger) (pairMap: PositionMap) (scores:BigInteger list) iter =
if iter > exp then scores |> List.rev |> List.skip (max 0 (List.length scores - 5)) |> toBigInt
else
let newScore = (score * n) % m
let key = (score, newScore)
match Map.tryFind key pairMap with
| Some _ ->
match findCycle pairMap key key [] with
| Some cycle ->
let remaining = int64 (exp - iter)
let cycleValues = cycle
let cycleLength = List.length cycleValues |> int64
let scoresLength = List.length scores |> int64
let totalLength = scoresLength + 1L + remaining // scores + newScore + remaining
let needCount = min 5L totalLength
let startPos = max 0L (totalLength - needCount)
let scoresReversed = List.rev scores
let final5 =
[startPos..totalLength - 1L]
|> List.map (fun pos ->
if pos < scoresLength then
// Position is in scores (scores is reversed, so oldest is at end)
scoresReversed.[int pos]
elif pos = scoresLength then
// Position is newScore
newScore
else
let cycleOffset = pos - scoresLength
let cyclePos = cycleOffset % cycleLength
cycleValues.[int cyclePos]
)
// final 5 comes out in reverse order
final5 |> List.rev |> toBigInt
| None ->
eni2' newScore n exp m (Map.add key ((newScore, (newScore * n) % m)) pairMap) (newScore::scores) (iter + BigInteger 1)
| None ->
let nextPair = (newScore, (newScore * n) % m)
eni2' newScore n exp m (Map.add key nextPair pairMap) (newScore::scores) (iter + BigInteger 1)
let eni2 (n) (exp) (m) = eni2' (BigInteger 1) n exp m Map.empty [] (BigInteger 1)
let part2 (input: IDictionary<string, BigInteger> array)=
input
|> Array.map (fun line ->
//printfn $"""running line {line.AsEnumerable() |> Seq.map(fun kv -> kv.Key + "=" + kv.Value.ToString()) |> fun x -> String.Join(", ", x)}"""
let a = eni2 line["A"] line["X"] line["M"]
let b = eni2 line["B"] line["Y"] line["M"]
let c = eni2 line["C"] line["Z"] line["M"]
let ret = a + b + c
//printfn $"found {ret}"
ret
) |> Seq.max
let part2Answer =
File.ReadAllLines "Inputs/Q01_P02.txt"
|> part1Lines
|> part2