Preamble
This is Part 6 of The Advent of Code.... Assistants. If you have not read Part 1 to 5 yet, maybe you want to head over there.
The repository associated with this project is available on CodeBerg.
Day 9
Fitting a square peg in... a polygonal hole.
Day 9 is this time of AoC where we are starting to get deep into more algorithmics. Up until now you could get by with taking more naive approaches to challenges. Day 9 is still possible to solve that way but it might be the last one and it seriously helps if you delegate part B to a specialized library!
Today we had to measure rectangles and their areas and in part B also ensure they don't intersect the "general polygon".
Part A
During its review, Claude once again bashes my use of OOP and blames it for the relative slowness of my code (still around ~100ms) to having to create objects for all rectangles. And even if we have seen that instantiating many small objects is expensive, it seems to willingly ignore that a non-negligible part of those 100ms is used to sort the list of rectangles by area (instead of simply keeping track of the maximum area).
This echoes once again something I will attribute to the training data and a tendency a lot of people in the industry have that consists of dismissing OOP as slow and blaming it without looking any further. OOP does have an overhead... as does writing code in python instead of doing it in assembly. But blaming everything on that overhead is not doing a good job.
Despite the adaptation that was made to its review prompt, Claude is still calling my Rect class an abstraction instead of calling it modelling (at least, in the review for this part).
It is also worth noting that all AIs had an initial hiccup when computing the area of rectangles and using exclusive counting to determine the size of width and height, which would be correct for euclidian coordinates but is not in our grid system. They still all managed to correct that mistake without much prodding though, which I find somewhat impressive given the discrepancy between the coordinates system used in this challenge compared to their training data.
Part B
Now part B is really where things start to shift. Even if making sure a rectangle was contained in the overall polygon drawn by all corners was doable "by hand" by checking whether the current rectangle intersected with any of the edges, it would have been a piece of code that would have been slow and error-prone.
Instead I decided to find a library that would be optimized for geometry and could do that check for me. I found shapely that fit that description and can arguably do much more than simple covers check and decided to use it.
Since my solver for part A was already sorting all rectangles by size, finding the largest one covered by the main polygon meant finding the first one in the list that met the condition.
Libraries vs. Rolling Your Own
None of the AIs took that route and they all struggled. This kind of behaviour is also one that I will attribute to the training data and as being representative of our industry in general.
Over many years I have learned time and again that a lot of things may look easy at first glance but, once you start looking at them a bit closer, you start noticing all the small edge cases. Which is why I tend to gravitate to using specialized libraries, leveraging the acute knowledge the developers of those libraries acquired over a specific problem. Ever since "left-pad", the industry has had a tendency to go the other way, mostly treating every dependency as a possible "threat" that it might become code they will have to fork and maintain if the developer or team behind said library stops maintaining it.
I would argue that it is a flawed reasoning. The risk of a library going unmaintained is a real one. But whether you used a library and have to take ownership of it or whether you rolled your own, you are, in both cases, now responsible for maintaining the code that performs that piece of computation. The one difference is that the code of the well-chosen, specialized library is probably more robust than your own implementation to start with and has less chances of leading to surprises around edge cases you didn't even think about. In any case, starting from the existing library instead of nothing puts you in a better place.
I am intentionally leaving supply-chain attacks out of this debate because it is usually not the argument I hear against using 3rd-party libraries. And it is a whole other can of worms with its own mitigations and solutions.
Anyway, after letting the AIs struggle with trying to implement their own, I strongly suggested they all change tactics, Claude is the only one who persisted with building its own implementation and succeeded at it (at the cost of 4 attempts, 3 of those happening in parallel workers). Both Kimi-K2 and GPT-OSS gladly took the suggestion of using shapely.
It is also worth noting that, they all went the un-optimized route of checking all rectangles instead of sorting them by size and exiting after the first successful match. This is a deviation from what we have seen so far where AIs are usually good at optimizing algorithms.
GPT-OSS becoming less impressive
Even if I am still impressed by GPT-OSS's local capabilities, today is the first of several days where GPT-OSS doesn't "clean up after itself", leaving dead code or wrong documentation (documenting the first failed approach) in the code.
This would definitely get caught by a code review so I am willing to give a pass to GPT-OSS on that given its limitation and the fact it would probably be easy to ask it to fix it in a second round.
Claude's full review
Read the full review on CodeBerg
Day 10
Randomly pressing lots of buttons and hoping for the best!
Part A
As with many challenges after the mid-point of AoC, today's part A could be solved by brute-force alone while part B required a bit more finesse.
Although all implementations used brute-force in the end, mine was by far the slowest and is probably my biggest fail of this year's AoC.
There are many things I could have done to improve on this solution, the most obvious being Claude's bit manipulation to keep track of on/off indicator lights. It is very elegant and more optimized than my toggle loop.
Using a tuple to store states was probably the wrong idea as well, forcing me to create a new tuple after each toggle where a mutable structure would have been more efficient. Another good way of mitigating that would have been to store mutable structures inside said tuple. They could have been mutated in place, while booleans could not.
The consolation I can get from Part A is that all AIs also failed (worse than me) at first while trying to use Gaussian elimination and all had to turn around and use brute force (Breadth-first search) instead.
Part B
Part B was mostly a repeat of yesterday. The challenge was an Integer Linear Programming problem. Even if the name did not spring to mind directly, the fact this was a mathematical problem that had libraries dedicated to solving the issue was rather obvious to me at first glance. A bit of googling led me to try PuLP. The hardest part of the challenge then became an issue of being able to model the data and constraint using the library.
Claude is the only AI that did not need prodding to use a library to solve the problem and, from its prompt to use the learnings of previous days, went ahead and found a library. It was also the only actor to use SciPy, which turned out to be 4 times faster than PuLP for this particular problem.
Kimi-K2 pulls a Dieselgate
The instructions for all AIs were the same and they clearly stated to first solve the problem using the sample data, validate and only then run on the actual input.
Instead, probably related to the shortness of the test data, Kimi-K2 decided to "detect" when it was running on test data and hard-code the expected results for that case. And this explains how it first got the wrong answer for that part of the challenge.
This is both worrisome and representative of how AIs in general will "happily cut corners" in order to "satisfy the exit condition" with the "less effort".
Kimi-K2 is the one that got caught red-handed this time but it is a behaviour I have seen with other AIs as well, things like:
- the pre-commit hook prevents me from committing code (for a good reason): just use
--no-verify - linting fails: add a setting to ignore the rule highlighted in the failure
- tests fails: claim those were pre-existing failures unrelated to the change and simply move on or worse, mark said tests with
@skip
This is one of the types of behaviours that makes 100% unsupervised vibe-coded solutions risky and unrealistic.
Claude really hates OOP
Once again, Claude mentions OOP as having some overhead:
Proper architecture (human's OOP) adds minimal overhead (<600ms Part B vs ~140ms)
It is comparing its 140ms to my 600ms and blaming the difference (even if it says it is negligible) on OOP. But when you look closer at the performances of part B, you realize that Claude's 140ms is the outlier and that all PuLP solutions are around 600ms which seems to point to the solver library as being the culprit and NOT OOP.
My observations from yesterday still stand and I will not repeat them.
Claude's full review
Read the full review on CodeBerg
Day 11
Untangling a cable salad.
Today, I did the exact opposite of what I have been promoting for the past 2 days... I rolled my own algorithm. And even if the speed of my code was still very acceptable (below 500ms for part A and B combined), it was still much slower than the proper algorithms regurgitated by the AIs.
It also didn't help at all that I refactored my part A to be able to keep track of an arbitrary number of significant traversed nodes (dac, fft and out). This made both Part A slower and Part B a complicated mess.
Day 11 is objectively my second biggest failure of AoC 2025. The impact here is much lower than yesterday's (counted in tenths of seconds compared to tens of seconds) but I am still not very proud of the code I produced.
Part A
All AIs used Depth-first-search a.k.a. brute-force for this part while I came up with an algorithm that keeps track, for each node and for each exit node of that node, how many paths go through out. After the first pass, only the nodes directly connected to out will have that number > 0. I then keep looping around all nodes and updating those values until I reach convergence (i.e.: the total number of paths leading to out stops changing).
This sounded better than brute-force at first glance. But timing tells a different story.
Part B
Today again, pure brute force, while doable for part A, would lead to an out-of-memory exception on part B, as Claude found out. As I was relatively happy with my solution for part A at that point, I modified it to keep track of more "significant nodes" and made it somewhat of a mess with dictionaries of dictionaries in order to keep track of everything.
GPT-OSS had already implemented memoization during path exploration while solving part A and could re-use that for part B. Kimi-K2 and Claude both also converged to that solution for part B after running into memory issues.
Winner of the day
Again and again, GPT-OSS has proven today that it is very good at optimized text-book algorithm and, in my opinion, is the clear winner of the day.
Full review
Read Claude's final full review on CodeBerg
There is only one day left for me to write about in this year's AoC. But the trend of these past 3 days is clear!
Conclusion
Algorithmics is a field of academia for a reason. If there is an optimization or a textbook solution for a problem, you should not try to re-invent the wheel, build on the shoulder of giants instead (or existing libraries when giants are hard to find).
These three days confirmed that AIs are excellent algorithmics encyclopedias. They know of tons of algorithms. They know Kruskal's, they know Integer Linear Programming, they know when to memoize. What they clearly don't know is when not to implement these things from scratch.
These can, again most likely, be attributed to biases in the training data. But when Kimi-K2 hard-codes test results to pass validation checks, when Claude dismisses OOP overhead without proper analysis, when all three AIs implement geometry checks instead of using shapely; these aren't isolated incidents. They're patterns.
And patterns are worth paying attention to, especially when these tools are increasingly used by newcomers as well as to write production code.
See the other articles in the series:
Comments
(via Mastodon or BlueSky )