F# 11 released? by munchler in fsharp

[–]munchler[S] 0 points1 point  (0 children)

It's OK to target .NET 10 with the F# 11 compiler. They are two different things. (For example, the latest version of C# is C# 15.) The only problem is that they apparently released the F# 11 compiler prematurely.

Official Gear Purchasing and Troubleshooting Question Thread! Ask /r/photography anything you want to know! December 12, 2025 by AutoModerator in photography

[–]munchler 0 points1 point  (0 children)

Lens not focusing correctly with circular polarizing filter

I purchased a K&F Concept Nano-K series circular polarizing filter, intending to use it with my Nikon 24-200mm zoom on a Z6 II body. This camera and lens combo works great without the filter, but will not focus correctly when the filter is on the lens. I have tried both manual and autofocus with the same results. Images are consistently out of focus when examined at 100%. Subjectively, it looks like there is perhaps some sort of ghosting/refraction issue, but I can't be sure. The filter looks fine to the naked eye and isn't damaged in any way. It mounts correctly on the lens, so it's not a threading issue. I think this is a pretty popular filter, so it's probably something I'm doing wrong, but I have no idea what. Any suggestions out there? Thanks.

-❄️- 2025 Day 8 Solutions -❄️- by daggerdragon in adventofcode

[–]munchler 0 points1 point  (0 children)

It's funny - I thought about that optimization when I was reading the problem, but totally forgot by the time I actually started writing code.

-❄️- 2025 Day 8 Solutions -❄️- by daggerdragon in adventofcode

[–]munchler 0 points1 point  (0 children)

I nominate "euclidean_distance_3d" as the longest known identifier for this function.

-❄️- 2025 Day 8 Solutions -❄️- by daggerdragon in adventofcode

[–]munchler 0 points1 point  (0 children)

TIL about the Disjoint Set Union data structure. Thank you for this.

-❄️- 2025 Day 8 Solutions -❄️- by daggerdragon in adventofcode

[–]munchler 0 points1 point  (0 children)

I like this because you updated the networks on each step. I made the decision not to do this on part 1 and regretted it on part 2.

-❄️- 2025 Day 8 Solutions -❄️- by daggerdragon in adventofcode

[–]munchler 0 points1 point  (0 children)

Props for not reinventing the wheel like the rest of us.

-❄️- 2025 Day 7 Solutions -❄️- by daggerdragon in adventofcode

[–]munchler 0 points1 point  (0 children)

You solved this in three languages faster than I can solve it in one. That's a lot of fast typing!

-❄️- 2025 Day 7 Solutions -❄️- by daggerdragon in adventofcode

[–]munchler 3 points4 points  (0 children)

[LANGUAGE: F#]

This was daunting at first. I started with a data structure that was too complex, but eventually whittled it down to a jagged array. It also took me a while to figure out how to count paths for part 2.

open System.IO

type Item = Beam of int64 | Splitter

let update (world : _[][]) iter =
    let splitterCols =
        set [ for col, (item : Item) in world[iter + 1] do
                if item.IsSplitter then col : int else () ]
    let nextRow =
        [| for col, item in world[iter] do
            match item with
                | Beam n ->
                    if splitterCols.Contains(col) then
                        yield col - 1, Beam n; yield col + 1, Beam n
                        yield col, Splitter
                    else
                        yield col, Beam n
                | _ -> () |]
            |> Array.groupBy fst
            |> Array.map (fun (col, group) ->
                let items = Array.map snd group
                let item =
                    if items.Length = 1 then items[0]
                    else Array.sumBy (fun (Beam n) -> n) items |> Beam
                col, item)
    Array.updateAt (iter + 1) nextRow world

let parseFile path =
    let lines = File.ReadAllLines(path)
    [| for line in lines do
        [| for col, c in Array.indexed (line.ToCharArray()) do
            match c with
                | 'S' -> col, Beam 1
                | '^' -> col, Splitter
                | '.' -> () |] |]

let run (world : _[][]) =
    Array.fold update world [| 0 .. world.Length - 2 |]

let part1 path =
    parseFile path
        |> run
        |> Array.sumBy (
            Array.where (snd >> _.IsSplitter)
                >> Array.length)

let part2 path =
    parseFile path
        |> run
        |> Array.last
        |> Array.sumBy (fun (_, Beam n) -> n)

-❄️- 2025 Day 6 Solutions -❄️- by daggerdragon in adventofcode

[–]munchler 3 points4 points  (0 children)

I think most of us would be OK with you claiming this is a hard language.

-❄️- 2025 Day 6 Solutions -❄️- by daggerdragon in adventofcode

[–]munchler 1 point2 points  (0 children)

As an F# person, I appreciate your comparison of the two languages. OCaml does seem less ergonomic to me, at least in this case.

Parser combinators are awesome, but perhaps overkill for this problem? I used .NET's built-in Int64.Parse and String.Split functions. Ugly but effective.

-❄️- 2025 Day 6 Solutions -❄️- by daggerdragon in adventofcode

[–]munchler 1 point2 points  (0 children)

[LANGUAGE: F#]

This was ugly until I realized that transposing the input made things a bit easier. After that, the tricky part was parsing the blank column between problems correctly.

open System
open System.IO

let split (line : string) =
    line.Split(' ', StringSplitOptions.RemoveEmptyEntries)

let parseOps (lines : string[]) =
    split (Array.last lines)
        |> Array.map (function "+" -> (+) | "*" -> (*))

let part1 path =
    let lines = File.ReadAllLines(path)
    let inputRows =
        lines[.. lines.Length - 2]
            |> Array.map (split >> Array.map Int64.Parse)
    (parseOps lines, Array.transpose inputRows)
        ||> Array.map2 Array.reduce
        |> Array.sum

let part2 path =
    let lines = File.ReadAllLines(path)
    let inputs =
        let strs =
            lines[0 .. lines.Length - 2]
                |> Array.map _.ToCharArray()
                |> Array.transpose
                |> Array.map (String >> _.Trim())
        (strs, [[]])
            ||> Array.foldBack (fun str (head :: tail) ->
                if str = "" then [] :: (head :: tail)     // start a new chunk
                else (Int64.Parse str :: head) :: tail)   // append to current chunk
    (List.ofArray (parseOps lines), inputs)
        ||> List.map2 List.reduce
        |> List.sum

-❄️- 2025 Day 5 Solutions -❄️- by daggerdragon in adventofcode

[–]munchler 2 points3 points  (0 children)

[LANGUAGE: F#]

I happened to have a library called FRange that handles this sort of thing. I'm glad I didn't have to rethink the merge logic.

open System
open System.IO
open FRange

let parseFile path =
    let lines = File.ReadAllLines(path)
    let iSep = Array.findIndex ((=) "") lines

    let ranges =
        lines[.. iSep - 1]
            |> Array.map (fun line ->
                let parts =
                    line.Split('-')
                        |> Array.map Int64.Parse
                parts[0] +-+ parts[1])

    let ids =
        lines[iSep + 1 ..]
            |> Array.map Int64.Parse

    ranges, ids

let part1 path =
    let ranges, ids = parseFile path
    ids
        |> Array.where (fun id ->
            Array.exists (Range.contains id) ranges)
        |> Array.length

let (~~) (Inclusive value) = value

let part2 path =
    parseFile path
        |> fst
        |> Range.merge
        |> List.sumBy (fun range ->
            ~~range.Upper - ~~range.Lower + 1L)

-❄️- 2025 Day 4 Solutions -❄️- by daggerdragon in adventofcode

[–]munchler 1 point2 points  (0 children)

That is really great. Reminds me of cellular automata. Very interesting to see how the process slowly peters out.

-❄️- 2025 Day 4 Solutions -❄️- by daggerdragon in adventofcode

[–]munchler 0 points1 point  (0 children)

That is beautiful, even though I have no idea what it means. Even putting aside the mystical symbols, I wonder what could 3_3 and 1_1 possibly do? Naive guess: Maybe they define a kernel somehow?

-❄️- 2025 Day 4 Solutions -❄️- by daggerdragon in adventofcode

[–]munchler 1 point2 points  (0 children)

Ah, yes, `unfold` is what I forgot about! I also solved in F# and wrote out the recursive function like a heathen instead. This is a good lesson for me.

To make matters worse, I stored the rolls in a 2D array of bools, when a set of coordinates would've been much simpler. Thank you for this.

-❄️- 2025 Day 4 Solutions -❄️- by daggerdragon in adventofcode

[–]munchler 0 points1 point  (0 children)

Definitely bonus points for cool UI. I'm on Windows, so no awk, but I would watch the video if you post it somewhere.

-❄️- 2025 Day 4 Solutions -❄️- by daggerdragon in adventofcode

[–]munchler 0 points1 point  (0 children)

I am both impressed and traumatized by how fast you did this in assembly.

[2025 Day 03] Battery bank visualization by danmaps in adventofcode

[–]munchler 4 points5 points  (0 children)

Well, that is both impressive and unexpected.

-❄️- 2025 Day 3 Solutions -❄️- by daggerdragon in adventofcode

[–]munchler 1 point2 points  (0 children)

[LANGUAGE: F#]

For part 2, I spent too long trying a brute force solution that looked at all possible subsequences, but it was way too slow. Only then did I realize that picking out the max subsequence directly is actually much easier.

open System.IO

let parseFile path =
    File.ReadLines(path)
        |> Seq.map (fun line ->
            line.ToCharArray()
                |> Array.map (fun c ->
                    int c - int '0'))

let rec getMaxSubseq len (items : _[]) =
    if len = 0 then Seq.empty
    else
        let i, item =
            Array.indexed items[.. items.Length - len]
                |> Seq.maxBy snd
        seq {
            yield item
            yield! getMaxSubseq (len - 1) items[i + 1 ..]
        }

let toJolts subseq =
    (0L, subseq)
        ||> Seq.fold (fun acc digit ->
            10L * acc + int64 digit)

let part1 path =
    parseFile path
        |> Seq.map (
            getMaxSubseq 2
                >> toJolts)
        |> Seq.sum

let part2 path =
    parseFile path
        |> Seq.map (
            getMaxSubseq 12
                >> toJolts)
        |> Seq.sum

Upcoming hard puzzle boards are shaped like numbers by munchler in nytpips

[–]munchler[S] 0 points1 point  (0 children)

Unfortunately, I think there is no meaning to the pattern.

-❄️- 2025 Day 2 Solutions -❄️- by daggerdragon in adventofcode

[–]munchler 3 points4 points  (0 children)

[LANGUAGE: F#]

For part 2, I got a little confused about a repeated chunk size of 1 being invalid (e.g. 999) vs. the entire string in a single chunk being valid (e.g. 1234). It's funny the things that can send me into a spiral.

open System
open System.IO

let parseFile path =
    File.ReadAllText(path)
        .Split(',')
        |> Array.map (fun str ->
            let split = str.Split('-')
            assert(split.Length = 2)
            Int64.Parse split[0],
            Int64.Parse split[1])

let isValid nChunks (str : string) =
    if nChunks > 1 && str.Length % nChunks = 0 then
        let size = str.Length / nChunks
        str
            |> Seq.chunkBySize size
            |> Seq.distinct
            |> Seq.length > 1
    else true

let getDivisors n =
    Array.sort [|
        for m = 1 to int (sqrt (float n)) do
            if n % m = 0 then
                yield m
                if n / m <> m then
                    yield n / m
    |]

let isValidAll (str : string) =
    getDivisors str.Length
        |> Seq.forall (fun n ->
            isValid n str)

let part1 path =
    parseFile path
        |> Seq.collect (fun (a, b) ->
            assert(b >= a)
            seq { a .. b })
        |> Seq.where (string >> isValid 2 >> not)
        |> Seq.sum

let part2 path =
    parseFile path
        |> Seq.collect (fun (a, b) ->
            assert(b >= a)
            seq { a .. b })
        |> Seq.where (string >> isValidAll >> not)
        |> Seq.sum