143 lines
5.0 KiB
Forth
143 lines
5.0 KiB
Forth
|
|
module EveryBodyCodes2025.QuestOne
|
||
|
|
|
||
|
|
open System
|
||
|
|
open System.Collections.Generic
|
||
|
|
open System.IO
|
||
|
|
open System.Linq
|
||
|
|
open System.Numerics
|
||
|
|
open System.Threading.Tasks
|
||
|
|
|
||
|
|
let eni (n:BigInteger) (exp:BigInteger) (m:BigInteger) =
|
||
|
|
let mutable remainders = []
|
||
|
|
let mutable score = BigInteger 1
|
||
|
|
for _ in (BigInteger 1)..exp do
|
||
|
|
score <- (score * n) % m
|
||
|
|
remainders <- score::remainders
|
||
|
|
List.rev remainders // Reverse to get correct order (oldest first)
|
||
|
|
|
||
|
|
// let test1 = eni 2 4 5 |> fun l -> String.Join("", l) |> int64
|
||
|
|
// let test2 = eni 3 5 16 |> fun l -> String.Join("", l) |> int64
|
||
|
|
|
||
|
|
let part1Lines (input:String array) =
|
||
|
|
input |> Array.map (fun line ->
|
||
|
|
line.Split(" ") |> Array.map(fun segment ->
|
||
|
|
let parts = segment.Split("=")
|
||
|
|
let alias = parts[0]
|
||
|
|
let num = parts[1] |> BigInteger.Parse
|
||
|
|
(alias, num)
|
||
|
|
)
|
||
|
|
|> dict
|
||
|
|
)
|
||
|
|
|
||
|
|
let toBigInt (list:'a list) = String.Join("", list) |> BigInteger.Parse
|
||
|
|
|
||
|
|
let part1 (input: IDictionary<string, BigInteger> array)=
|
||
|
|
input
|
||
|
|
|> Seq.map (fun line ->
|
||
|
|
let a = eni line["A"] line["X"] line["M"] |> toBigInt
|
||
|
|
let b = eni line["B"] line["Y"] line["M"] |> toBigInt
|
||
|
|
let c = eni line["C"] line["Z"] line["M"] |> toBigInt
|
||
|
|
a + b + c
|
||
|
|
) |> Seq.max
|
||
|
|
|> fun x ->
|
||
|
|
printfn $"{x}"
|
||
|
|
x
|
||
|
|
|
||
|
|
let part1Answer =
|
||
|
|
File.ReadAllLines "Inputs/Q01_P01.txt"
|
||
|
|
|> part1Lines
|
||
|
|
|> part1
|
||
|
|
|
||
|
|
/// 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.mapi (fun i 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 test2 =
|
||
|
|
let a = eni2 5 6 15
|
||
|
|
let b = eni2 9 16 15
|
||
|
|
let c = eni2 7 18 15
|
||
|
|
let r = a + b + c
|
||
|
|
printfn $"{r}"
|
||
|
|
r
|
||
|
|
|
||
|
|
|
||
|
|
let part2Answer =
|
||
|
|
File.ReadAllLines "Inputs/Q01_P02.txt"
|
||
|
|
|> part1Lines
|
||
|
|
|> part2
|
||
|
|
|> fun x ->
|
||
|
|
printfn $"part2 {x}"
|
||
|
|
x
|
||
|
|
|
||
|
|
let part2Answer' file =
|
||
|
|
File.ReadAllLines file
|
||
|
|
|> part1Lines
|
||
|
|
|> part2
|
||
|
|
|> fun x ->
|
||
|
|
printfn $"part2 {x}"
|
||
|
|
x
|