Created
December 14, 2023 11:41
-
-
Save einarwh/707ac3556bb8008e688ff9381b5b6f67 to your computer and use it in GitHub Desktop.
Advent of Code 2023 - Day 14: Parabolic Reflector Dish - F# version
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Advent of Code 2023. Day 14: Parabolic Reflector Dish | |
// dotnet fsi aoc14.fsx | |
open System | |
open System.IO | |
let readLines = | |
File.ReadAllLines >> Array.filter ((<>) String.Empty) | |
let rollNorthStep (s1, s2) = | |
let line1 = s1 |> Seq.toList | |
let line2 = s2 |> Seq.toList | |
let zipped = List.zip line1 line2 | |
let rolled = zipped |> List.map (fun (a, b) -> if a = '.' && b = 'O' then (b, a) else (a, b)) | |
let s1' = rolled |> List.map fst |> List.toArray |> String | |
let s2' = rolled |> List.map snd |> List.toArray |> String | |
(s1', s2') | |
let rec north lines = | |
match lines with | |
| a :: b :: rest -> | |
let (a', b') = rollNorthStep (a, b) | |
a' :: north (b' :: rest) | |
| _ -> lines | |
let rollSouthStep (s1, s2) = | |
let line1 = s1 |> Seq.toList | |
let line2 = s2 |> Seq.toList | |
let zipped = List.zip line1 line2 | |
let rolled = zipped |> List.map (fun (a, b) -> if a = 'O' && b = '.' then (b, a) else (a, b)) | |
let s1' = rolled |> List.map fst |> List.toArray |> String | |
let s2' = rolled |> List.map snd |> List.toArray |> String | |
(s1', s2') | |
let rec south lines = | |
let folder upper acc = | |
match acc with | |
| [] -> [ upper ] | |
| lower :: rest -> | |
let (upper', lower') = rollSouthStep (upper, lower) | |
upper' :: lower' :: rest | |
List.foldBack folder lines [] | |
let rec rollEastStep chars = | |
match chars with | |
| 'O' :: '.' :: rest -> '.' :: rollEastStep ('O' :: rest) | |
| ch :: rest -> ch :: rollEastStep rest | |
| [] -> [] | |
let rollLineEast = Seq.toList >> rollEastStep >> List.toArray >> String | |
let east = List.map rollLineEast | |
let rec rollWestStep chars = | |
let folder left acc = | |
match acc with | |
| [] -> [ left ] | |
| right :: rest -> | |
match (left, right) with | |
| ('.', 'O') -> 'O' :: '.' :: rest | |
| _ -> left :: acc | |
List.foldBack folder chars [] | |
let rollLineWest = Seq.toList >> rollWestStep >> List.toArray >> String | |
let west = List.map rollLineWest | |
let rec tilt roll current = | |
let tilted = roll current | |
if tilted = current then current else tilt roll tilted | |
let cycle = tilt north >> tilt west >> tilt south >> tilt east | |
let calculateLoad lines = | |
let countRocks = Seq.filter ((=) 'O') >> Seq.length | |
let len = lines |> List.length | |
lines |> List.mapi (fun i line -> (len - i) * countRocks line) |> List.sum | |
let solve limit lines = | |
let rec loop n seen current = | |
if n > limit then | |
None | |
else | |
if List.contains current seen then | |
let interval = 1 + List.findIndex ((=) current) seen | |
let preceding = List.length seen - interval | |
let sequence = seen |> List.rev |> List.skip preceding | |
let ix = (limit - preceding) % (interval) | |
let load = sequence |> List.item ix |> calculateLoad | |
Some load | |
else | |
loop (n + 1) (current :: seen) (cycle current) | |
loop 0 [] lines | |
let run fileName = | |
let lines = readLines fileName |> Array.toList | |
let tiltedNorth = lines |> tilt north | |
tiltedNorth |> calculateLoad |> printfn "%d" | |
let limit = 1000000000 | |
match solve limit lines with | |
| None -> printfn "?" | |
| Some load -> printfn "%d" load | |
"input" |> run |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment