commits
This feels a little like bottom-up DP, I'm not too sure what the actual
name of this pattern is.
It's actually easier than expected. I was considering something crazy
like constructing a graph and doing backtracking, but after some
pen-and-paper work, we can simply get the answer by keep track of a
count, splitting the counts when encountering a splitter, and merging
the count when they overlap.
Rather straightforward: we just need to count the set intersection of
beams and splitters on every pass.
Feels like a throwaway, can't imagine this solution being recyclable in
any way for part 2.
.. and there it is. What a twist.
The annoying thing is now that the input is whitespace sensitive, I had
to account for my editor stripping trailing whitespaces on save, or `mix
format` doing the same thing. I don't want to turn those off, so alter
the get_example input instead.
This turned out to be easier than expected. Simply turn the input into
graphmemes, then transpose the matrix, then it's just a single pass,
using a state machine to hold the operation, numeric buffer, and
accumulated sum. Easy.
My first attempt was to brute-force: convert the ranges to a set of
integers, then count the size of the set.
Obviously that doesn't work, this is AoC after all. Combining ranges is
obviously the way to go. Obviously..
This feels rather brute-forcey so I was rather surprised that it ran
quickly.
Define a new Grid struct. I'm a little torn about whether to house most
of the logic in the Day04 module, but for the sake of readability (I'm
sure part 2 has something to do with doing something with reachable @s
anyway), choose to put them in the Grid module.
Fill up - from the right - a list of 12 digits first, then for each new
digit, if it's equal or higher than the leftmost digit:
* drop one number from the list of 12. Choose the first instance where
the digit on the right is higher than it, because that will lead to
the highest possible 11-digit number. If we can't find such a case,
then drop the last digit.
* then append this digit to the left
It's probably possible to build a general case for "top K" (which
extends to part 1), but not today!
I got tripped up a little by not considering the case when
def replace_top_two(digit, {top1, top2}) when digit == top1
oof!
I'm not sure if math is the most efficient approach, from both the
runtime and development time perspective, but I stuck with it because of
the sunk cost fallacy.
From the get go I wanted to do it by testing the number against
(pattern to repeat) * however many times
.. for all the factors of the number of digits of the number. Claude
taught me about "repunit" and spoonfed me the formula to get it.
And, as luck would have it, doing the same bruteforcing approach works!
All in all, not a very satisfactory experience. I'm too lazy to do it
the string manipulation way now.
Let's call the repeated sequence "twinnies"!
Also, roll with a brute force solution. A more elegant solution is
probably something along the lines of enumerating the twinnies within
the range rather than checking each of them, but I'm sure I can get
away with this for now.
Use some more idiomatic elixir patterns:
* Use pattern matching on literals instead of guards with equality
(e.g., `inc_if_zero(acc, 0)` instead of `when dial == 0`)
* Remove redundant direction_to_op
* Use Integer.mod/2 instead of our silly double rem
* Use Stream as much as possible
Getting days and setting examples will be a thing moving forward.
One thing that tripped me up was the fact that the dial can land at 0,
which would count as a click, but when it decrements into a negative
number, it doesn't click a second time!
To account for that, but keep our `count_clicks` method intact, write an
edge case for `apply_op`: when we are turning left when the dial is 0,
simply set the dial to 100.
Preemptively create a *_part_1, because we all know how AOC puzzles go...
Also create a iex target for easy clustering from livebook.
Run `mix credo` once through, and create a simple justfile command to
check the code.
Generate repo with
mix new aoc2025
I'm recreating the scaffolding for this year because I like doing just
that. Creating a new project from scratch isn't something one gets to do
often.
This feels a little like bottom-up DP, I'm not too sure what the actual
name of this pattern is.
It's actually easier than expected. I was considering something crazy
like constructing a graph and doing backtracking, but after some
pen-and-paper work, we can simply get the answer by keep track of a
count, splitting the counts when encountering a splitter, and merging
the count when they overlap.
.. and there it is. What a twist.
The annoying thing is now that the input is whitespace sensitive, I had
to account for my editor stripping trailing whitespaces on save, or `mix
format` doing the same thing. I don't want to turn those off, so alter
the get_example input instead.
This turned out to be easier than expected. Simply turn the input into
graphmemes, then transpose the matrix, then it's just a single pass,
using a state machine to hold the operation, numeric buffer, and
accumulated sum. Easy.
Fill up - from the right - a list of 12 digits first, then for each new
digit, if it's equal or higher than the leftmost digit:
* drop one number from the list of 12. Choose the first instance where
the digit on the right is higher than it, because that will lead to
the highest possible 11-digit number. If we can't find such a case,
then drop the last digit.
* then append this digit to the left
It's probably possible to build a general case for "top K" (which
extends to part 1), but not today!
I'm not sure if math is the most efficient approach, from both the
runtime and development time perspective, but I stuck with it because of
the sunk cost fallacy.
From the get go I wanted to do it by testing the number against
(pattern to repeat) * however many times
.. for all the factors of the number of digits of the number. Claude
taught me about "repunit" and spoonfed me the formula to get it.
And, as luck would have it, doing the same bruteforcing approach works!
All in all, not a very satisfactory experience. I'm too lazy to do it
the string manipulation way now.
One thing that tripped me up was the fact that the dial can land at 0,
which would count as a click, but when it decrements into a negative
number, it doesn't click a second time!
To account for that, but keep our `count_clicks` method intact, write an
edge case for `apply_op`: when we are turning left when the dial is 0,
simply set the dial to 100.