Skip to content

Instantly share code, notes, and snippets.

@pblasucci
Last active December 9, 2021 07:52
Show Gist options
  • Save pblasucci/1310907b361deb469dcff8d61a61d78f to your computer and use it in GitHub Desktop.
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.
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>)
#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)}"
)
#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 })
@MarkBracke
Copy link

MarkBracke commented Dec 8, 2021

|> 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!

@pblasucci
Copy link
Author

pblasucci commented Dec 9, 2021

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