15
votes
Day 3: Gear Ratios
Today's problem description: https://adventofcode.com/2023/day/3
Please post your solutions in your own top-level comment. Here's a template you can copy-paste into your comment to format it nicely, with the code collapsed by default inside an expandable section with syntax highlighting (you can replace python
with any of the "short names" listed in this page of supported languages):
<details>
<summary>Part 1</summary>
```python
Your code here.
```
</details>
Not too easy, not too hard. Had to take some breaks to help with my mother who is currently recovering from neck/back surgery. It's also a lot verbose than I've seen compared to others. I also switched over to using JetBrains IntelliJ IDEA's built-in HTTP client for sending my input vs using the excellent RapidAPI for Mac (formerly Paw)
Ran into a lot of issues with an off-by-one, but I solved it :-D
Java
Very enterprise!
This seems tricky for the third day, but it's really just another parsing problem at its core. I was able to re-use a utility class from previous years, which helps a lot with handling grids like this (they come up frequently in AoC problems).
Part 1 - Ruby
Part 2
Part 2 becomes easy when we already have everything in a searchable grid format.
Today's puzzle tripped me up for a bit and then I got stuck at part two because of a dumb issue where I set the final result to the last calculation rather than summing them up.
I'll probably go back over it tomorrow to make a better/more concise version.
Rust
Harder day three than expected -- felt more like a one-week-in problem. There were a couple of edge cases in my input that were not present in the sample, which was frustrating. I don't love the "look" of my solutions, but I guess a couple of nested for loops is common for these grid-style problems.
Anyway here are my Python solutions:
Part 1
Part 2
I thought yesterday that I'll use Nix for day 3. Oh boy, that was a mistake. At least the problem didn't ask me to walk vertically.
Part 2 took me like 2 hours due to an off-by-one error in getNumberAt, and the gearsNeighbors grid in part 2 had the bottom left written as
+, +
instead of+, -
. I had to built my own test case to find these.I can't figure out how to use
nix repl
to run a non-repl code. So instead, this file build a test derivation with dummy value, and crash on test assertion failures. For the real solutions the output will be in the derivation build result file. And of course, to compute the gear values Nix will need to build/download a new bash, gcc, etc..Language: Python
Classic AoC grid problem... Tedious as usual, but very doable. Took my time and I'm pretty happy with the result. :]
Part 1
For the first part, I decided to break the problem into: 1. Reading the schematic, 2. Finding the numbers, 3. Finding the parts. This was useful for Part 2 as I could re-use my
read_schematic
andfind_numbers
functions.Two things I typically do for grid problems:
DIRECTIONS
list I loop through so I can check easily check the neighbors.Part 2
For the second part, I just found the stars, and then I found the gears by checking if the stars are next to two numbers (which I had found previously).
GitHub Repo
In case anyone else had the same bug in their code as I did, here's a hint.
If you swap the sample input given in the puzzle instructions from this:
Original sample input
Tweaked sample input covering an extra edge case
Your code should produce the same result.
Elixir
I started on this when the puzzle released at midnight in my timezone, but ended up deciding to sleep on it because I wasn't sure how best to represent the schematic as a data structure.
I'm glad I took some time to think the structure through, because it made querying the data pretty straightforward for both parts.
The code to parse the schematic into my struct is one giant list comprehension, by far the most complicated one I've written in this language, so that was... fun.
Part 1 runs in 4.5-5ms, part 2 runs in 5-5.5ms, which I think is pretty good given everything that's going on here?
Parts 1 and 2
The
Schematic
module defines the data structure (thedefstruct
and the@type
def), logic to parse it from the input string, and functions to query it for parts 1 and 2.Since you can't implicitly "re-use" a compound value (like a list or a map) with a pointer/reference in elixir, I had to explicitly make a lookup table using the built-in
reference
type.Coordinates of individual digits, which form part of a whole number, are mapped to a common
reference
. Then, a second map maps thereference
to the number formed by the group of digits.This allows me to look up the number(s) adjacent to a symbol in logarithmic time, as well as uniquely identifying each number so I can avoid counting it more than once. The same number can also appear in multiple places in the schematic, so this helped me avoid bugs related to that as well.
Rust
This was fun! A little bit harder than yesterday but easier than day 1, I think. My solution isn't exactly optimized — there's definitely some nested looping going on that would slow things down on larger inputs — but it's pretty quick at this scale and the hard part is still just the grid parsing. Because of that, I'll put my parser in a block of its own and then the solutions will be their own little follow-on blocks.
Data structures and parser
Part 1
Part 2
After completing yesterday's puzzles I put what I've got so far up on GitHub, and I'll do my best to keep it updated as the month goes on.
Pretty nice little problem. I solved it, then went back and refactored my code into a more general grid structure out of my code, since I expect there to be more grid problems in the coming days. I also took some time to write some tests for the grid code.
With this structure, the only extra part is identifying the numbers as regions, everything else is provided by the adjacency methods in the grid structure.
I would've gotten top 1000 on this, but wasted 25 minutes fixing a stupid bug where newlines were treated as symbols. Oh well. So far all the problems have been primarily based on just parsing the input; hopefully there are some more interesting ones soon.
Solution
Longer day 3 than expected; might be because it's a weekend. Part 2 was pretty simple, at least.
Part 1
POSIX AWK doesn't seem to have a way to handle multiple matches so I had to manually implement it. Overall went pretty smoothly other than a couple off-by-one errors.
Part 2
Very minor changes. The instructions specified "adjacent to exactly two part numbers", but I didn't have any that were more than two in my input so ¯\_(ツ)_/¯
Hoping for more problems beyond parsing! But at least I got to use some functions that I haven't used in a while. Posted my solution and explanation here: https://guissmo.com/blog/advent-of-code-2023-day-3/
Here's the complete code:
Parts 1 and 2
Then it was a matter of doing
Rust
Highly involved for a day 3. I hope it's just the early problems that have had their complexity increased to combat LLMs. If the whole challenge has had its complexity scaled up this much, it's going to be really hard by day 10.
Day 3
Just wondering, do people ever use fancy spatial data structures for these 2D (or eventually 3D) AoC grid problems instead of just nested loops?
Kotlin
Using Typescript and Deno together still. I flipped my approach to solving this a few times. Originally I decided I would process the text character-by-character to look for symbols, and then also do that to look for numbers, but then I would have to do some annoying book-keeping to remember where I encountered each of a number's digits. I realized it would be much easier if I used a regex to recognize strings of multiple digits together, and then searched for symbols around it. I also used regexes to search for the symbols. I would use the slice() method on the strings of the surrounding rows to make a substring of only the positions neighboring the number, and then use a regex to search those for symbols.
For part 2, I did a similar search, but I would only look for "*" symbols neighboring the numbers, and I kept track of the locations of all "*" symbols and the numbers that neighbored them in a Map object. Then I iterated through the map looking at all locations which had exactly two numbers as neighbors.
Typescript