A good set of questions would be something between the advent of code - where the problems are hard because the spec is so bad on purpose - and project Euler - where the spec is so exact you don't really need a computer to solve the problems with enough thinking.
Something like 'plot the histogram of collatz sequence lengths of the first 100,000 numbers'.
On a side note, I remember when the website got hacked [1][2].
Many people, including myself, migrated to other platforms, but Project Euler problems always remained math-focused compared to the myriad of other websites like LeetCode and HackerRank, among others, listing programming-focused problems, which eventually popularized the use in modern tech interviews.
Maths isn't my strongest suit, and I have no academic comp-sci background, so there's been a number of these I sort of brute force and then go read the answers in the thread; or I brute force the first few integers in the sequence and then try and wrap my head around what https://oeis.org/ is attempting to tell me about them.
It has challenged me a bit on some of my fundamentals with programming, really making me think about efficiency etc.
While I've done most of the problems in rust so far, I've been having to refresh my knowledge of Go recently, so I've started porting answers between the two languages, and it's definitely helping there a bunch.
I don't remember the problem number or its title, but it involved starting from the top-left corner of a 2D grid and finding how many possible paths there are to get to the bottom-right corner while only moving either down or right.
My naive solution was a brute-force depth-first recursive search. On my CPU at the time, it took about 3 minutes to solve. I thought, the logic of this is incredibly simple, why not do it in assembly?
My assembly solution took 5 minutes.
I decompiled the compiled C code to see what it had done, but I couldn't understand it. My assembly knowledge was too basic.
Thinking on it now, I wonder if a modern compiler would solve the entire problem at compile-time and just hard-code the answer to be output at runtime.
> Starting in the top left corner of a 2x2 grid, and only being able to move to the right and down, there are exactly 6 routes to the bottom right corner.
─────────┐ ────┐ ────┐
┌───┬───┐│ ┌───│───┐ ┌───│───┐
│ │ ││ │ │ │ │ │ │
├───┼───┤│ ├───└────┐ ├───│───┤
│ │ ││ │ │ ││ │ │ │
└───┴───┘│ └───┴───┘│ └───│───┘
▼ ▼ └────▶
│┌───┬───┐ │┌───┬───┐ │┌───┬───┐
││ │ │ ││ │ │ ││ │ │
└─────────┐ └────┐───┤ │├───┼───┤
│ │ ││ │ │ │ ││ │ │
└───┴───┘│ └───│───┘ │└───┴───┘
▼ └────▶ └─────────▶
> How many such routes are there through a 20x20 grid?I attacked roughly the first 250 problems in order. The early problems build on each other to introduce new topics. I also got good at figuring out the right search term to find some random paper in number theory, combinatorics, probability, whatever.
Later problems introduced new, more niche areas, like chromatic polynomials and impartial & partisan game theory. But by then, I found it much easier to figure out what part of math a problem was based on and how to find relevant literature.
It helps to be really really stubborn, and to have the patience to let a problem stew in my brain, sometimes for weeks at a time. That seems to help lead to that Eureka moment.
Good job on the ASCII art, btw.
Is it 21! ?
My favorite is https://projecteuler.net/problem=113, "Non-Bouncy Numbers." It takes some clever tricks to figure out but doesn't require any significant background knowledge, and the optimizations required to get it to run within 60 seconds (at least for my approach) all felt reasonable.
20 Rs, 20 Ds in a 20x20 grid.
Example pattern: RRDDDR...D (40 letters)
Basically the number of permutations, with repetition, of 20 Rs and 20 Ds.
Edit: ...and yes, it seems that brute-forcing (counting to one trillion) takes more time than I expected.
https://projecteuler.net/problem=81
As for a single problem, I'm fond of PE589, "Poohsticks Marathon". That was my 501st solution, two years after first attempting it (solved 5 years ago, yikes). I like it because it's a problem with a 95% difficulty rating, so very tough, but the development team slotted it in as an easy problem (problems normally get scheduled in batches of 6 with a cadence of medium/easy/medium/easy/medium/hard). Once I solved it, I agreed that it was relatively easy, in that it uses techniques introduced by early PE problems, but something about it makes using those techniques unexpectedly difficult.
int sum(int n) {
int sum = 0;
for(int i = 0; i < n; i++) {
sum +=i;
}
return sum;
}
clang, with -O2, will turn this into the polynomial (n+1)*n//2. It can also do similar transformations for multiple loops.https://godbolt.org/z/so6neac33
So if you do a brute force solution which could have been reduced to a polyomial, clang has a shot of doing just that.
Compilers are not smarter than you - that's daft. The nutters that program the compilers and tweak and twiddle them, are better informed about how to deliver faster machine code for a given task.
One of your super powers is to know when to say: "fuck it, I'm trotting off and getting by with a five minutes runtime".
o1 thought for 105 seconds, cycling through many relevant-sounding status messages like "looking for patterns," before writing a collection of thematic but flawed thoughts. The "Calculation Steps" approach is incorrect, but correctly implemented by the code.
It flubs a basic calculation that it correctly implements in python: "10^16 mod (10^9 + 7) = 49" (it's actually 930000007)
but succeeds in a seemingly harder calculation: "the modular inverse of 12 modulo 10^9 + 7 is 83333334"
Finally, o1 claims the code prints "0" when it actually prints "982790507" (both wrong answers).
Note: input was copied from the html-only Project Euler url since the formulas in the human-optimized url are not copyable: https://projecteuler.net/minimal=912
I know that that critique isn't new to anyone but it makes me think about how it would be cool if there were a code puzzler site that is specifically geared towards little self-contained tasks that are more to do with forcing you to get familiar with the common everyday tasks of software development.
Some example puzzlers could be like:
- open an http server on port 80
- open a file and write this data to it
- write temporary files to a location, deleting them when process exits
- query a database
- deal with such and such error scenario
- find a way to test this component
- bundle this code as an executable
- sanitize user input here
- make this behavior configurable
- take the config from environment variable variable and/or config file and/or arguments
- parse this data file
You do get a bit of parsing and file handling with Advent of Code but imagine a series of dozens of small problems that grill you on every corner of the python filesystem api. Would be a lot less dry than reading docs cover to cover.
> https://rosettacode.org/wiki/Rosetta_Code
This might be a little bit nearer to what you look for.
---
> it would be cool if there were a code puzzler site that is specifically geared towards little self-contained tasks
In my opinion the tasks on Project Euler or LeetCode are much more self-contained than what you suggest as alternatives: many of your suggestions are deeply intertwined with libraries (and understanding them) for doing the respective task instead of being self-contained programming tasks.
people do like to say they use PE for learning new languages, but I doubt that is a useful exercise beyond maybe the first dozen problems or so. And even then, if the solution isn't obvious to you, you're doing two things at once - learning a language and solving a math puzzle. I don't see why people would sign up to get frustrated like that.
Here's a talk from an llvm conference with the details.
There will be clear rules (business logic), UI, etc.
It's a confined enough problem that you can implement it without too much effort but deep enough that you can get a feel for how that programming language, framework, whatever works.
Plus there's a near endless set to choose from and it's easily scalable to the level of complexity you want. If it works add AI players, network play, etc.
I've done a fair amount of Advent of Code and I wouldn't say it's at all "focused" on this. The vast majority of the questions use hash tables and graph traversal as the full extent of their use of math/DS/algos.
There's always one or two puzzles every year that require some particular math/CS insight but most of them just need you to know BFS and/or how to write a parser.
Your examples are also not bad, but they seem to be primarily concerned with "getting familiar with a new programming language" in the context of writing a web server, which is one of the parts of programming I try to stay away from. Most of your examples require less familiarity with the language's features and more with libraries you might use, which is less interesting to me personally (then again, I'm a PL fan and I write compilers for a living).
Meanwhile, I like AoC because I've used language features to take the fairly straightforward potential implementations and write them more elegantly in the language I choose. e.g. I use Monads in Haskell, or use Rust's easy threading to parallelize solutions, etc.
For me, learning a new programming language is largely uninteresting unless it changes the fundamental "shape" I think in, rather than what the exact names of the libraries I use change to. e.g. I already know Java so I'm not really going to bother "learning" C#. I already know Python so I don't really bother diving deep into Ruby, etc. However, I learn Haskell, Rust, Forth, Erlang, Scheme, etc.
I suppose a decent game project could achieve these things too, but the real fun of Chip-8 is in throwing different ROMs at it and debugging the issues until it’s perfect enough to play all your favorite games!
Other scaling-of-inputs could include: Text with line-lengths over 2 GB, numbers above 2^60, data designed such that naive nested-loop solutions (quadratic scaling) take over a year to compute the answer, etc...
Basically, force developers to solve the problem robustly with: streaming, parallelism, efficient algorithms with good big-O properties, correct data type choice (including intermediate accumulator values!), and so forth.
It could be a three-star challenge feature added to the current version. It wouldn't even require large downloads: a Python script or something similar could be used to generate arbitrarily large inputs. (Alternatively, a common CDN-cacheable prefix with a distinct suffix per competitor.)
You get to recognize the effect - if I see a problem that's clearly number-theory related and with a limit of 10^12, I know they're looking for a sublinear algorithm, probably O(n^(2/3)) thanks to various multiplicative function ideas that appear over and over.
The text always clearly states how your code has to behave, albeit it doesn't spell out every edge case you might overlook. Real world specs on the other hand is often contradictory and impossible to fullfil.
I find it nice to learn new languages via data structure puzzles, because to me the data structures of a language feel like the grammar and once I have that down everything else falls into place
Or is that your point? That coding like a pro means gluing those things together?
After that, I like to invent a big gnarly scenario that I don't have to completely solve, but one that takes me well out of the range of typical cookie-cutter tutorials. I want to find all the sharp edges on a new language/library/framework.
The answer actually came to me in the shower this morning.
Isn’t that the whole fun?
I think a lot of people here would be surprised how much of a step up this is from classic leetcode or DSA stuff. I have been involved in introducing people to AoC and helping them and the amount of people who have basic knowledge of algorithms but struggled to parse the input from a file was a little shocking to me at first. Of course, I do not blame anyone for not knowing something, classic academic courses can be misguided sometimes.
This doesn't negate the fact that there is somewhat of a lack in problems with more outside world interaction and it would be cool to see more of that.
I actually use this as a learning trick. Pick two or three things to learn simultaneously, then when I get stuck on one aspect, switch to another. When I finally switch back, I often find the background time I gave my brain to process the problem means I'll now be much faster to get unstuck on the original issue.
There's definitely ways this can go sideways, and it's not for everybody, but I find it pretty effective.
https://austinhenley.com/blog/challengingprojects.html https://austinhenley.com/blog/morechallengingprojects.html
1. Yes, solving these puzzles would mean often mean using a library.
2. However, for most of the things I listed above, in most languages, a competent software developer should be able to, and most likely would just use the standard library.
3. Why not both? I can imagine a catalog with thousands of problem sets. Some may challenge you to (re-)implement some existing functionality yourself (as a super basic example, re-implement the java Optional::flatMap method or something). Others could challenge you to make use of existing implementations. Learning to make use of stdlib and other libraries and tools in the ecosystem is part of one's growth, and also so is working through those tools, tinkering, trying to think how you would have implemented them, and getting a better understanding of their internals (or at least, an understanding of how their internals MIGHT work)
When employers or team mates ask you if you "know" or "are competent in" Java they don't care if you know how to work with lists, arrays, loops, hashmaps and sets. Well, I mean, that's table stakes. They're asking if you're familiar with the idiosyncrasies of the language with respect to those above concerns.
I learned so much poring over the solutions of earlier solvers. Without that knowledge, there's no way I could have gotten lots of later problems.
Sure, problem #15 is very early and I happened to know the answer right off, back the first day I started solving PE problems. But even so, there are some gems in the posts from other solvers.
There are some much later problems where some obscure technique gets mentioned, even though the problem is doable without that technique. But then later on, there are other problems where that technique is practically required. I can think of multiple 100% difficulty problems which were actually much easier than that for me, because I had already seen and tried out the techniques that enable a fast solution.
And sorry, not going to mention any of those techniques. A lot of the fun I have in solving PE problems is that incremental increase in knowledge as time goes on.
I don’t understand this problem (I didn’t tackle it myself) but wanted to see how quickly chatgpt could solve it and how far along it would go.
First it made a naive solution that would work until 10^6. Then we used that output to verify improved versions.
And we managed to improve it until 10^10 only. (Staying within a minute timeout.)
It did a DP approach (it suggested itself). I suggested to try numba and numpy. And with that it managed until 10^10. I think it’s still brute forcing it and one might leverage much better techniques in order to reach 10^16.
I also have some stand-alone modules, one to solve generalized Pell equations, another to find a polynomial given a sequence via the differences (e.g. 2, 5, 10, 17, first differences 3, 5, 7, second 2, 2 is enough to find n^2+1). There's another to find the closed form for a sequence as a linear recurrence.
Some solvers have much more extensive libraries, but I tend to grab bits of code from old solutions to reuse on the fly.