Background
The origin of this project is a discussion with a friend in which I said that I enjoyed the mental challenges that come with coding: "it’s like solving multidimensional sudokus". But on second thought, it wasn’t obvious to me that multidimensional sudokus were even possible.
What I meant by multidimensional is the following: if you pick any 2 dimensions, the resulting table must respect the sudoku rules. Each row and each column must contain all integers between 1 and 9, and each 3x3 sub-square must also contain all integers. There should be no duplicates and no missing integer.
Verifying the existence of such matrices became a logical puzzle on its own, which I thought I could solve in a week during my commute to/from work. As it turned out, it’s an extremely hard problem to solve. I couldn’t solve it through pure math and my limitations in that domain were not to blame: the brightest mathematicians have hit a wall on an adjacent problem. So I turned to brute force, that is, building an algorithm to generate all possible matrices meeting the constraints.
Despite months of trial-and-error, optimizations or complete overhaul of the algorithm, settling this issue seems desperately out of reach, even for 9x9x9 cubes : my last run lasted 3 weeks, utilizing 100% of the computing power of a top-spec MacStudio… and I could not even get the list of all possible sudokus in 2D, which was a pre-requisite for more dimensions. If you want to get into matrix frameworks like numpy, pytorch, tensorflow, or more recently mlx, this project constitutes a very good introduction.
My only source of satisfaction is that the algorithm works as expected.
In order to check the results of my algorithm, I designed it to build Sudokubes (sudokus in 3D) of different sizes. Cubes of smaller sizes are less computationally-intensive so they are faster to generate, but their key advantage is that it’s much easier to get your head around them when checking the outputs. A typical sudoku has edges of size 9, which corresponds to the square of 3 ; 3 being the size of the sub-squares. As you need an edge size that is the square of an integer, there are only two options below the typical size of a sudoku: 1, the square of 1, is useless for our problem, but 4, the square of 2, is much more interesting. There are 768 size-4 Sudokubes and they take less than 2 seconds to generate on my machine.
The Sudokube game
Size-4 Sudokubes did not only validate the algorithm, they also appeared to constitute a very good base for an actual game. Neither too easy nor too hard:
- there are 64 values (vs. 81 values in a typical sudoku)
- there are 96 conditions to meet (vs. 27 in a typical sudoku)
- 3D is a bit unsettling at first but you quickly ignore the visual representation and use the third dimension to your advantage
Once the first prototype was put together, I quickly realized that we could not choose the seeding values of a Sudokube randomly; I had to identify the unique combinations for each Sudokube to avoid seeding a game with multiple solutions. Using brute-force once again, I learnt that the smallest combination size guaranteeing uniqueness is 5: if you seed with any set of 4 values, there will be several solutions whereas a set of 5, if chosen appropriately, guarantees uniqueness. Any size-4 Sudokube has either ~1m unique combinations of 5 values or ~91k of such sets. I did not try to understand why it was either one or the other; I just used this piece of information to set the most difficult level of the game : a Sudokube seeded with only 5 values. Overall, there are over 500m seeding combinations to choose from.
We came up with seven levels of difficulty:
- at introduction level, the Sudokube is seeded with 10 values
- at beginner level, it is seeded with 9 values
- at easy level, it is seeded with 8 values. Up the this point, you can expect on average 4-5 minutes to complete a Sudokube
- at intermediate level, it is seeded with 7 values
- at advanced level, it is seeded with 6 values
- at pro level, it is seeded with 5 values, the minimum size guaranteeing uniqueness of the solution
- at expert level, helping features, such as assisted 3D navigation or validation animations, are disabled. This basically requires a full mental model of the cube in 3D, it’s really hard.
Tech details
- Backend : minimal at first, it essentially consists in a Flask app serving a random Sudokube and an associated unique combination at each API call. Hosted on Digital Ocean : we went for a droplet even though a serverless function would have been sufficient (no Flask or Gunicorn). Droplets are more expensive but functions could not be protected with an Access-Control-Allow-Origin header ; only though an API token which would have been exposed on the frontend.
- Frontend : React app, built with Vite, and hosted on Vercel. The 3D engine is powered by Three.js.
Next steps
- Mobile app, addressing the shortcomings of the responsive web design
- Monetisation : I learnt a lot during building this app which, in essence, creates value, but I am a firm believer that a project MUST be sustainable to be considered a GOOD project. Stating the obvious, monetisation primarily requires a good product, which means that [more] effort should be put into game design to achieve breakeven. Helping features (such as rewinding) also look like good candidates to base monetisation on.