Last active
December 9, 2021 07:52
-
-
Save pblasucci/1310907b361deb469dcff8d61a61d78f to your computer and use it in GitHub Desktop.
Some quick-and-dirty playing around with Advent of Code 2021, Day 6 (https://adventofcode.com/2021/day/6)... and, no, this soluion won't actually scale up to larger inputs.
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
type [<Measure>] daysUntilSpawn | |
type Fish = uint8<daysUntilSpawn> | |
let inline fish value : Fish = | |
LanguagePrimitives.ByteWithMeasure<daysUntilSpawn> value | |
let [<Literal>] ReadyToSpawn : Fish = 0uy<daysUntilSpawn> | |
let [<Literal>] ResetFish : Fish = 6uy<daysUntilSpawn> | |
let [<Literal>] SpawnFish : Fish = 8uy<daysUntilSpawn> | |
let rec (|MustSpawn|AgeOneDay|) fish = | |
match fish with | |
| ReadyToSpawn -> MustSpawn ResetFish | |
| _ -> AgeOneDay (fish - 1uy<daysUntilSpawn>) |
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
#load "AoC_Day6_Common.fs" | |
open System | |
type [<Measure>] toBeSpawned | |
let evolvePreservingOrder allFish = | |
// Here we compute the old fish and the young fish separately, | |
// then combine them by putting the new fish at the end. | |
// | |
// It makes for better pretty printing. ;-) | |
let seed = (List.empty<Fish>, 0UL<toBeSpawned>) | |
let aged', eggs' = | |
(seed, allFish) ||> List.fold (fun (aged, eggs) fish -> | |
match fish with | |
| MustSpawn fish' -> (fish' :: aged, eggs + 1UL<toBeSpawned>) | |
| AgeOneDay fish' -> (fish' :: aged, eggs) | |
) | |
(List.rev aged') @ (SpawnFish |> List.replicate (int eggs')) | |
let simulateVerbose totalDays allFish = | |
let family = ResizeArray([allFish]) | |
for generation in 0ul .. totalDays - 1ul do | |
let cohort = evolvePreservingOrder family[int generation] | |
family.Add(cohort) | |
Seq.readonly family | |
// DEBUG 18 days | |
[ 3uy; 4uy; 3uy; 1uy; 2uy ] | |
|> List.map fish | |
|> simulateVerbose 18ul | |
|> Seq.iteri (fun generation cohort -> | |
printfn $"After %2i{generation} days: %s{String.Join(',', cohort)}" | |
) |
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
#load "AoC_Day6_Common.fs" | |
let evolve allFish = | |
// Here, since we really only care about total number of fish, | |
// we interleave old and young and get a simpler solution. | |
allFish |> List.collect (fun fish -> | |
match fish with | |
| MustSpawn fish' -> [ fish'; SpawnFish ] | |
| AgeOneDay fish' -> List.singleton fish' | |
) | |
let simulate totalDays allFish = | |
[1ul .. totalDays] | |
|> List.fold (fun current _ -> evolve current) allFish | |
|> List.length | |
(* ___ TEST _________________________________________________________________ *) | |
#r "nuget: FsCheck" | |
open FsCheck | |
type Tests() = | |
static let firstGeneration = | |
[ 3uy; 4uy; 3uy; 1uy; 2uy ] |> List.map fish | |
static member ``18 days yields 26 fish``() = | |
firstGeneration |> simulate 18ul = 26 | |
static member ``80 days yields 5934 fish``() = | |
firstGeneration |> simulate 80ul = 5934 | |
Check.All<Tests>({ Config.Quick with MaxTest = 1 }) |
Thanks! Nicely spotted. 👍 That's a good simplification. 🙂
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
|> List.map (something that produces a list)
|> List.collect id
can be simplified to just
|> List.collect (something that produces a list) ;-)
One less loop to worry about.
I have to say, this looks pretty darn clean!