Stream: advent of code

Topic: 2024 Day 3


view this post on Zulip jan kili (Dec 03 2024 at 05:24):

:partying_face: https://gitlab.com/JanCVanB/aoc-2024/-/blob/main/day-03/part-1/main.roc

view this post on Zulip Ryan Barth (Dec 03 2024 at 05:27):

Oh how i long for a Str.walkUntil

view this post on Zulip jan kili (Dec 03 2024 at 05:43):

:partying_face: https://gitlab.com/JanCVanB/aoc-2024/-/blob/main/day-03/part-2/main.roc

view this post on Zulip Luke Boswell (Dec 03 2024 at 06:00):

I've got a nice solution... but I'm getting a fun one of these if I use my template :sad:

thread '<unnamed>' panicked at crates/compiler/mono/src/ir.rs:6166:56:
called `Option::unwrap()` on a `None` value
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace

view this post on Zulip Luke Boswell (Dec 03 2024 at 06:05):

Oh found it...

broken

part1 : Str -> Result Str _

works

part1 : Str -> Result Str []

view this post on Zulip Oskar Hahn (Dec 03 2024 at 06:06):

Here is mine https://github.com/ostcar/aoc2024/blob/main/day03.roc

view this post on Zulip Luke Boswell (Dec 03 2024 at 06:07):

Ok, so here's my solution

https://github.com/lukewilliamboswell/aoc/blob/main/2024/03.roc

TIMING:
READING INPUT:  <1ms
SOLVING PART 1: 1ms
SOLVING PART 2: 1ms

view this post on Zulip Oskar Hahn (Dec 03 2024 at 06:07):

@Luke Boswell have a look at #bugs > Compiler crash when it can not find out the Err type

Another workaround the error is, if you append a |> Result.mapErr \_ -> PleaseFixMe to your function

view this post on Zulip Luke Boswell (Dec 03 2024 at 06:10):

Ah nice, I'll fix the template so others wont run into that. Just tested and it fixes the problem. Just made it this instead

Solution err : {
    year : U64,
    day : U64,
    title : Str,
    part1 : Str -> Result Str [Err]err,
    part2 : Str -> Result Str [Err]err,
} where err implements Inspect

view this post on Zulip jan kili (Dec 03 2024 at 06:10):

Luke Boswell said:

Ok, so here's my solution

Ooh...

view this post on Zulip jan kili (Dec 03 2024 at 06:12):

Oskar Hahn said:

Here is mine

Ooh...

view this post on Zulip jan kili (Dec 03 2024 at 06:16):

:laughing: I forgot List.sum exists - no regrets on List.walk 0 Num.add :sunglasses:

view this post on Zulip jan kili (Dec 03 2024 at 06:21):

I couldn't resist - @Oskar Hahn I inverted my split like yours, to drop 8 lines of code :partying_face:

view this post on Zulip Luke Boswell (Dec 03 2024 at 06:23):

view this post on Zulip jan kili (Dec 03 2024 at 06:29):

RE: Spoiler

view this post on Zulip Oskar Hahn (Dec 03 2024 at 07:28):

By the way: I don't think that splitting is a good solution. But if the solution works in a handful of milliseconds, there is no need to optimize it :smiley:

view this post on Zulip jan kili (Dec 03 2024 at 07:51):

There are so many metrics of goodness, and performance is only one!

view this post on Zulip jan kili (Dec 03 2024 at 07:54):

IMO, in most projects, the simplest solution furthers your high-level goals more than a high-performance one could.

view this post on Zulip jan kili (Dec 03 2024 at 07:57):

However, beauty/satisfaction is another metric, and that's always subjective (and sometimes aligned with high performance).

view this post on Zulip Eli Dowling (Dec 03 2024 at 08:47):

Another day another solution: https://github.com/faldor20/aoc-template/blob/c398af5fde1169b8609ef4bd6b9465de0f380ab3/examples/2024/03.roc
I was keen to try out @Luke Boswell's parser. It took me a few iterations to get the right format. But I'm happy with the result.
The performance on the other hand.....well look.... performance isn't everything right :sweat_smile:

reading took: 0.039441ms
part1 took: 167.797391ms
part2 took: 168.628369ms

view this post on Zulip Eli Dowling (Dec 03 2024 at 08:48):

I kind of want to try hand rolling a parser to find out why it's so crazily slow

view this post on Zulip Eli Dowling (Dec 03 2024 at 09:00):

well.... a run of perf wasn't exactly revealing :sweat_smile:
image.png

@Brendan Hansknecht is the advice for perf still to run something like this?:

roc build --optimize --profiling examples/2024/03.roc
perf record -F999 --call-graph dwarf -- ./examples/2024/03

view this post on Zulip Luke Boswell (Dec 03 2024 at 09:20):

Give samply a try maybe?

view this post on Zulip Ryan Barth (Dec 03 2024 at 09:26):

Splitting strings took me longer than everything else combined:
https://github.com/r-bar/advent24/blob/master/day03/p2.roc

Now to go see how everyone else got done so fast.

view this post on Zulip Ryan Barth (Dec 03 2024 at 09:48):

I am running my own simpler template, so I don't get fancy performance numbers out of the box.

❯ roc build --optimize p2.roc
0 errors and 0 warnings found in 800 ms
 while successfully building:

    p2
❯ poop './p2 input.txt'
Benchmark 1 (898 runs): ./p2 input.txt
  measurement          mean ± σ            min … max           outliers
  wall_time          5.50ms ±  339us    5.11ms … 6.56ms          0 ( 0%)
  peak_rss            839KB ± 14.3KB     627KB …  856KB          6 ( 1%)
  cpu_cycles         22.0M  ±  291K     21.8M  … 25.5M          84 ( 9%)
  instructions       99.3M  ± 76.5      99.3M  … 99.3M          20 ( 2%)
  cache_references   39.0K  ± 62.8K     23.0K  …  980K         133 (15%)
  cache_misses       3.37K  ±  603      2.56K  … 6.39K          24 ( 3%)
  branch_misses      36.8K  ± 1.26K     35.7K  … 45.4K          59 ( 7%)

view this post on Zulip Eli Dowling (Dec 03 2024 at 09:54):

sorry wtf is the poop command ?? :joy:

view this post on Zulip Eli Dowling (Dec 03 2024 at 09:55):

Luke Boswell said:

Give samply a try maybe?

I've not seen samply, that's cool!
I realized I needed to disable the surgical linker! it's working with both perf and samply now.

view this post on Zulip Ryan Barth (Dec 03 2024 at 09:55):

https://github.com/andrewrk/poop

view this post on Zulip Eli Dowling (Dec 03 2024 at 09:58):

Nothing particularly interersting really. but it's clearly spending its time inside the parser.
image.png

view this post on Zulip Eli Dowling (Dec 03 2024 at 10:03):

okay, so basically all it's doing is concatenating strings:
image.png

view this post on Zulip Eli Dowling (Dec 03 2024 at 10:37):

AHA! I understand what's happening! @Luke Boswell
you report your fancy errors when a parser fails, but my parser is failing almost 3 times for every single input :sweat_smile:
Because It's trying inputs sequentially.

It'd be interesting to explore a version of the parser library that handles failure quickly. We could potentially thread a config object through the parser that enables verbose error logging or fast error logging.
That's what rust's nom seems to do and I understand it to be very very fast

view this post on Zulip Eli Dowling (Dec 03 2024 at 10:37):

okay @Luke Boswell I removed every instance of string interpolation in your passing lib:

reading took: 0.032032ms
part1 took: 1.232324ms
part2 took: 1.291891ms⏎

Not a bad 120x speed up :sweat_smile:

view this post on Zulip Eli Dowling (Dec 03 2024 at 10:40):

https://www.google.com/search?client=firefox-b-d&q=nom+error+handling Is an interesting read on this

view this post on Zulip Nicola Peduzzi (Dec 03 2024 at 11:40):

Leaned to use parser in Roc with this one! loved it

https://github.com/thenikso/advent-of-code-2024-roc/blob/main/03.roc

view this post on Zulip Paul Stanley (Dec 03 2024 at 12:26):

I also leaned heavily on @Luke Boswell and his excellent parser. Congratulations on the documentation, whose copious use of examples really suits my horrible way of learning things. For Part 2 I just split on do() and don't(), crossing my fingers that they would follow in a regular pattern ... and found that they did. I felt a bit guilty not doing Part 2 from first to last as a parser -- but the splitting strategy actually gets things done faster, no doubt because my parser is not efficiently constructed and part 2 is therefore dealing with a much shorter string to parse.

view this post on Zulip Anthony Bullard (Dec 03 2024 at 14:32):

I just implemented a simple parser, walking the chars through a state machine. Easy peasy:

https://github.com/gamebox/aoc-2024/blob/main/day3/puzzle2/main.roc

view this post on Zulip Anthony Bullard (Dec 03 2024 at 14:34):

I could probably get it faster if I removed all of the string work and added a case for each piece of each op

view this post on Zulip Anthony Bullard (Dec 03 2024 at 14:55):

Interesting, while doing that I got a very strange compiler crash:

thread '<unnamed>' panicked at crates/compiler/mono/src/ir/decision_tree.rs:777:17:
IntLiteral([44, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], U8)
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace

view this post on Zulip Anthony Bullard (Dec 03 2024 at 14:58):

During mono?

view this post on Zulip Anthony Bullard (Dec 03 2024 at 15:06):

Updated the above with the current code. Have to head out for work...let me know if anyone has any ideas

view this post on Zulip Anthony Bullard (Dec 03 2024 at 15:06):

Looks like the compiler is looking for an Identifier or a an Underscore pattern, but it's getting an IntLiteral

view this post on Zulip Anthony Bullard (Dec 03 2024 at 15:07):

This is the relevant code:

                debug_assert!(
                    matches!(pattern, Pattern::Identifier(_) | Pattern::Underscore,),
                    "{pattern:?}",
                );

view this post on Zulip Anton (Dec 03 2024 at 15:09):

thread '<unnamed>' panicked at crates/compiler/mono/src/ir/decision_tree.rs:777:17:
IntLiteral([44, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], U8)
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace

I could not find any existing issue about this, can you file a new one?

view this post on Zulip Anthony Bullard (Dec 03 2024 at 15:11):

I'll have to figure out a minimal repro

view this post on Zulip Anthony Bullard (Dec 03 2024 at 15:11):

I can try to do that tonight

view this post on Zulip Anthony Bullard (Dec 03 2024 at 15:11):

Damn AOC really bites into my limited contribution time

view this post on Zulip Anton (Dec 03 2024 at 15:14):

I'll have to figure out a minimal repro

Feel free to toss in the entire thing if you're short on time :)

view this post on Zulip Anthony Bullard (Dec 03 2024 at 15:21):

Ok

view this post on Zulip Anthony Bullard (Dec 03 2024 at 15:35):

Created: https://github.com/roc-lang/roc/issues/7302

view this post on Zulip Anthony Bullard (Dec 03 2024 at 21:27):

Updated this with a lot of debugging findings

view this post on Zulip Luke Boswell (Dec 03 2024 at 22:28):

Eli Dowling said:

okay Luke Boswell I removed every instance of string interpolation in your passing lib:

reading took: 0.032032ms
part1 took: 1.232324ms
part2 took: 1.291891ms⏎

Not a bad 120x speed up :sweat_smile:

@Eli Dowling do you have anything more on this I could see? I'm interested to know if there is anything here we can roll into the parser for others to benefit from

view this post on Zulip Anthony Bullard (Dec 03 2024 at 22:33):

Haven't seen Eli's solution, but that's the exact thing I was trying to do when my compilation blew up. Hoping to see similar improvements :-)

view this post on Zulip Brendan Hansknecht (Dec 03 2024 at 22:54):

Eli Dowling said:

well.... a run of perf wasn't exactly revealing :sweat_smile:
image.png

Brendan Hansknecht is the advice for perf still to run something like this?:

roc build --optimize --profiling examples/2024/03.roc
perf record -F999 --call-graph dwarf -- ./examples/2024/03

I'm late to the party. Yes with the caveat that on Linux you want --linker=legacy and it works best of the platform is built with debug info.

view this post on Zulip Eli Dowling (Dec 04 2024 at 00:44):

Luke Boswell said:

Eli Dowling said:

okay Luke Boswell I removed every instance of string interpolation in your passing lib:

reading took: 0.032032ms
part1 took: 1.232324ms
part2 took: 1.291891ms⏎

Not a bad 120x speed up :sweat_smile:

Eli Dowling do you have anything more on this I could see? I'm interested to know if there is anything here we can roll into the parser for others to benefit from

I'll look into it, but currently I made it faster by basically removing all error logging... Not something you want.

I'd say we need to thread a parser config through, and make the logs a tag union, either just a tag indicating the current node that failed which is what you do normally, it's full string log if we have the debug config setting.

Definitely take a look at the link to nom I shared where they talk about efficient error handling :)

Another thing we could do is have a parser function that specifically allows attaching errors to the parser. So if it gets an error it adds in a message, that wya the user can give their own parser errors! Angstrom in ocaml does this, and I like it :)

view this post on Zulip Isaac Van Doren (Dec 04 2024 at 01:20):

Here's my solution, I thought this one was fun https://github.com/isaacvando/aoc/blob/main/2024/3.roc

view this post on Zulip Anthony Bullard (Dec 04 2024 at 01:37):

Eli Dowling said:

Another thing we could do is have a parser function that specifically allows attaching errors to the parser. So if it gets an error it adds in a message, that wya the user can give their own parser errors! Angstrom in ocaml does this, and I like it :)

This is also what’s done in FParsec which is what I used for the parser for my language. I was about to explore it more once I started to rewrite the parser - but that’s on hold for now, maybe indefinitely.

view this post on Zulip Anthony Bullard (Dec 04 2024 at 03:17):

Anthony Bullard said:

Created: https://github.com/roc-lang/roc/issues/7302

I finally fixed this through some convoluted means of moving all literal matching out into the guards. I don't know if that's why, but the solution is about the same as the old one. So a terrific waste of time learning moment. :smile:

view this post on Zulip Jesse Nutt (Dec 04 2024 at 04:14):

It's a little under the wire, but here's my stab at it: https://github.com/Saalico/aoc_2024/blob/main/day3/main.roc

I'm very much a newb at coding, especially roc, so open to suggestions/call outs if any of you are so inclined. Happy coding!

view this post on Zulip Robin Camarasa (Dec 04 2024 at 08:29):

Already one day late in the advent of code :smile: https://github.com/RobinCamarasa/Advent-of-code/blob/master/2024/day03/RobinCamarasa/main.roc

view this post on Zulip Ghislain (Dec 04 2024 at 19:24):

Jesse Nutt said:

It's a little under the wire, but here's my stab at it: https://github.com/Saalico/aoc_2024/blob/main/day3/main.roc

I'm very much a newb at coding, especially roc, so open to suggestions/call outs if any of you are so inclined. Happy coding!

I like your solution witch looks very concise but you're lucky that the input didn't test your failures :sweat_smile: (I've seen the same issue in several other solutions)

Your response to mul(2,4)3,5) might be 23 but the good one is 8.

view this post on Zulip Jesse Nutt (Dec 05 2024 at 02:46):

Ghislain said:

Jesse Nutt said:

It's a little under the wire, but here's my stab at it: https://github.com/Saalico/aoc_2024/blob/main/day3/main.roc

I'm very much a newb at coding, especially roc, so open to suggestions/call outs if any of you are so inclined. Happy coding!

I like your solution witch looks very concise but you're lucky that the input didn't test your failures :sweat_smile: (I've seen the same issue in several other solutions)

Your response to mul(2,4)3,5) might be 23 but the good one is 8.

Ack! That's embarrassing! Is there really such a case that other puzzle inputs would have thrown that edge case?

Either way, let's take another swing:

part1 = \in ->
    Str.splitOn in ")"
    |> List.joinMap \x ->
        if Str.contains x "mul(" then
            Str.splitOn x "mul("
        else
            [""]
    |> List.keepIf \chunk -> (List.all (Str.toUtf8 chunk) \c -> List.contains closeToken c) && !(Str.isEmpty chunk)
    |> List.keepOks \x -> mulString x
    |> List.sum

view this post on Zulip Johannes (Dec 06 2024 at 19:31):

Hmm, why don't we use a regex here? (Because roc does not yet support it properly?)

view this post on Zulip Anton (Dec 09 2024 at 11:11):

Yes, we don't have a regex package yet

view this post on Zulip Anton (Dec 09 2024 at 19:32):

The first one solved by the Roc Claude agent:
main_claude.roc
prompt-aoc-3-part1.txt
prompt-aoc-3-part2.txt

I did have to fiddle with the prompt a bit; it was using some old Roc syntax, I told it to use intermediary variables with expect and had to show it how to properly use dbg.

view this post on Zulip Anthony Bullard (Dec 09 2024 at 19:39):

Hey, one thing that would be cool is actually recording you doing one of these end-to-end.

view this post on Zulip Anton (Dec 10 2024 at 09:56):

uhu, I might do a simple screen recording later :)

view this post on Zulip Eelco Hoekema (Dec 20 2024 at 10:15):

I am a bit slow, christmas holidays only started now, so i hope to have a little more time to spend on these puzzles.

I try to solve #3 with a parser, because, i like to understand parsers. I am not yet grasping the two type

mulParser : Parser String.Utf8 Mul
mulParser =
    const (\first -> \second -> { first: first, second: second })
    |> skip (string "mul(")
    |> keep digits
    |> skip (string ",")
    |> keep digits
    |> skip (string ")")

mParser : Parser String.Utf8 Mul
mParser =
    const identity
    |> skip (chompUntil "mul(")
    |> keep mulParser

So now i get a compiler issue stating:

── TYPE MISMATCH in src/S2024/D03.roc ──────────────────────────────────────────

Something is off with the body of the mParser definition:

64│   mParser : Parser String.Utf8 Mul
65│   mParser =
66│>      const identity #
67│>      |> skip (chompUntil "mul(")
68│>      |> keep mulParser

This Parser.keep call produces:

    Parser (List Str) Mul

But the type annotation on mParser says it should be:

    Parser (List U8) Mul

Which leaves the question, how do i map a Parser (List Str) Mul to a Parser (List U8) Mul, or more general, a how to map a Parser a x to a Parser b x?

view this post on Zulip Anton (Dec 20 2024 at 14:52):

@Luke Boswell

view this post on Zulip Brendan Hansknecht (Dec 20 2024 at 17:08):

I'm not very good at parser, but I think the fundamental issue is that chompUntil is meant to work on single characters, not full strings.

By saying chompUntil "mul(", you are requesting that the input be a List Str like ["test", "mul()", "other", "something", "mul(", "last"]. Then it would consume strings looking for exact matches until it ends up only having the list ["mul(", "last"]. I don't think this is the behaviour you want.

view this post on Zulip Brendan Hansknecht (Dec 20 2024 at 17:13):

My gut feeling is that you use something like alt to attempt to parse a mulParser. If that fails, you would fall back on a parser that just consumes a single character.

view this post on Zulip Brendan Hansknecht (Dec 20 2024 at 17:13):

Then loop that

view this post on Zulip Brendan Hansknecht (Dec 20 2024 at 17:13):

but there is probably a smarter way. I'm really not a fan of parser combinators most of the time so I don't really know how to use them well.


Last updated: Jul 06 2025 at 12:14 UTC