Better than me, I got tripped up by tricks in both parts.
And then, of course, I had to optimize, because honestly, who can wait 15 ms for this? My final solution looks like it was coded by a madman on a cocaine bender, but runs in about 150 microseconds for part 1, and 1 ms for part 2.
I spoke too soon. I got part 1 without any problem, and thought I was done. Forgot there was a part 2, and didn’t realize it until I noticed that other people had gold stars and mine was only silver. That part definitely tripped me up. Finally figured it out though. Hmm, if this is the difficulty level for day 1, it doesn’t bode well.
I have to say, I didn’t love my solution to Day 3.
Day 3 Spoiler
For part 1, I use a regular expression on each row to identify the numbers, then look for symbols in the surrounding spaces. This isn’t too bad.
It’s part 2 where I I have to look for “*”, then identify numbers in the surrounding areas. I mean, it works, but it’s a lot of if statements, and I have to work backwards and forwards to get the numbers.
Well, I’m finally caught up. I just finished Day 3 part 2, barely in time for Day 4.
I think my new strategy is going to be:
Look at the puzzle as soon as it’s posted.
If I can’t immediately think of a plausible approach, then go to bed and sleep on it. No point struggling with it if I’m barely awake.
Day 3 Spoiler
Since I’ve built a decent library of text processing functions, I separated the input data into lines of digits and lines of symbols. In each case the excluded characters are replaced with spaces so that the included characters keep their original position. The symbol lines are stored in a circular buffer of length 3, because it’s only necessary to keep the previous, current and next line. Then process the digit data line by line comparing the digit positions with the symbol positions in the 3 symbol buffer entries.
This was one of those lucky situations where the Part 1 method was easy to upgrade for part 2. I just added a dictionary so that if a valid part number is found with the gear symbol ‘*’, the * position and part number value are added to the dictionary. If there’s already an entry in the dictionary for that * symbol, then a valid gear is found, and the gear ratio is calculated.
Regular expressions is the smart way to solve these puzzles, but I rather enjoy thinking about and implementing my own algorithms for these puzzles. Up until now I’ve used them old reliable loops to solve everything.
For the puzzle of day 3 I padded the game data with periods at the top, bottom, left and right, to reduce the number of if statements I had to use in my loops.
I also used a multi-iteration approach, where I replaced all special symbols with periods in the first iteration to simplify the code used to get the number positions. That was then used in combination with forward and backward lookup lines to determine adjacent positions and build up a dictionary of positional information, that was then used during the second iteration to calculate the answers.
I have always found it interesting in the programming world how many different and creative approaches there are to solve the same problem.
Last year was my first attempt at this. I quickly found out that the explanation of what needs to be done is often very confusing, and the only way to figure it out is to analyze the example very carefully.
Using the new Set class, I looked for the Intersection of each group, then
if winners.Count <> 0 then
total = total + 2 ^ ( winners.Count - 1 )
end if
For part 2, it was essentially the same code but I kept a counter array. For the next winner indexes, I incremented those by the number of cards in the current index.
(I was too tired to stay up tonight, but then snapped awake at around 2:30 am or so, and eventually decided I wasn’t going back to sleep. )