Building a Generator: Numerical Devlog #01

I’ve been working on a new number puzzle game, on and off, for the past year. It’s somewhat of a spiritual successor of Destructomath+.

I have a longer post drafted about its origins, but today I want to get some notes down around puzzle generation. I was emailing a new friend with some details for both accountability and brainstorming together and decided to reformat some of the thread into a post.

Rules of the Game

1. You start with a grid of numbers and a set of targets
2. You must make paths on the grid that add up to the targets: using every number once, never crossing paths, and only going up/down/left/right.

Creating a Level Solver

One of the first things I worked on after the idea was solidified was a Solver; something that could take in a puzzle and determine what the solutions are, or determine if its an unsolvable puzzle. I wanted the game to potentially have unlimited puzzles, which meant I needed a way to not just generate random levels, but also confirm they were valid.

This took me a long time because there were so many possible for a given grid and I had trouble wrapping my brain around it. Thanks to ChatGPT, I built an initial version. I relied a lot on it to get a working version. But then every once in awhile I noticed “solvable” levels had invalid solutions. Annoying! Back to the drawing board.

Turning a solver into generator

My first approach for the generator was to randomly generate both the grid and the target sums, with a bit of nudging to make sure the grid was potentially solvable. I did this by comparing the total of the grid values w/ the total value of target sums. As long as those were equal, I would pass it into the solver which just did a brute-force attempt at solving the level. This took a long time and was still inaccurate.

So I updated the solver (thx ChatGPT) to actually work properly with all the mechanics I had added so far (multipliers, negative numbers, and missing blocks in the grid). This time I added test cases to have some level of confidence that it’s solving properly. I only have 8 fairly simple ones, but it’s able to properly solve them for now. Once we had tests for accuracy, the goal was to improve the speed. I didn’t want level generation taking 30s+ if a player could generate random levels all the time.

So there was some tweaking I did to enable some early pruning of paths that no longer worked for the solver. Previously I could never prune early because negative numbers meant that exceeding a target number (ex: your current selection is 16 but the largest sum is 14) didn’t mean that the puzzle was now unsolvable, because there could be a -5 somewhere. But if the number of remaining negatives was 0, then we could start to prune early. This helped speed things up a bit.

In terms of the generator approach, as mentioned it was purely random. So I would basically go back and forth between generating a level and seeing if it was solvable, but that could lead to up to 20-30s generating if the level was complex enough (ex: 5×5 grid with some special mechanics). My original approach to make this a bit smarter was to generate a grid of numbers, try to create a set of sums based on the total value of the blocks so at least they added up, and then shuffle them to see if it was solvable.

My new approach is something a big different which I’m fairly excited about, though maybe super inefficient. I had the idea to try to create lookup tables of every possible solution for a grid configuration. For example, in a 2×2 grid, there are a total of 8 possible configurations of chains that are 2-blocks long (order of the chain matters). My plan was to pre-generate every possible solution to a grid, ignoring the number values themselves. As long as we know the grid size and the number of sums, we can generate every solution. This moves almost all of the heavy-work of generating onto my side as something I can do in advance and then just compare on the users device to speed things up.

Possible solutions for a given grid

Here’s how it works:

  1. Generate a grid of random numbers for a level and select how many sums we want. Ex: 3×3 with 4 sums
  2. Pick a random solution from the lookup table. Since we have every possible solution defined, I just pick a random one. Ex: Image
  3. Map that solution onto the grid. Follow every chain to come up with the target numbers because we already know it will almost always work, since it’s calculated based on legitimate paths.
  4. Double check it still works with the solver.
The new process for generating a random level

This should lead to way less iterations (in early testing it does). My goal is to have near real-time level generation so someone could have unlimited random levels if they wanted.

I ended up generating these tables for all the grids I’m supporting (3×3, 4×4) with 2,3,4, and 5 targets for each grid. Some of the tables, stored in JSON, are like 40mb lol. There are ~120,000 possible solutions for some. This makes the app a bit heavy so my next step is to try to find better ways to store the tables. ChatGPT suggested binary so I might try to figure out how to store & read from binary files.

Time to see what we can come up with.

Raw notes of other changes I’ve been making:

3/8/2025

  • The original generator and solver were too slow and required me to manually curate levels. Plus the solver was inaccurate.
  • I tried storying the lookup tables binary but it was too complex for me + ChatGPT
  • I culled the levels because there was an over-distribution of 2-2-2-10 chains making the solutions boring, plus a small bug making it so levels could be wrong (there wouldn’t be enough blocks selected initially). This reduced level counts by 20-50% and helped with file sizes.
  • Loading the JSON files has a bit of a lag so I’m trying to add all the lookup tables to a SQLite database that loads right away to help with filesize and speed
  • After this will be some tweaking of level generation to make sure that’s fun
  • I’ve minified the UI too (edit: you can see this in the screenshots)
  • I need to decide if i want more mechanics I want to publish this! I need to decide on a business model, the first 50-150 levels that will be included for free (ex: 50 free easy, 50 free medium, 50 free hard) plus the ability to: unlock the level generator or unlock 100 new levels (which I will pre-generate)
  • The main question is: do i embed the generator into the game as something players can access?

3/10/2025

  • I had to fix the solver again because somewhere along the way it started breaking. Tests are paying off! Now we are properly generating levels with mechanics. It’s time for me to start testing difficulty levels though because i’m not sure how easy/hard the levels actually are.
  • I’m debating re-adding 5×5 levels if they make sense. Generating lookup tables there will be really time consuming, but it will be good to have those in the database.
  • I think for the business model I might do:
    • start with 50 free levels for each difficulty
    • pay $2 (each) to unlock an extra 100-150 for that difficulty
    • these will be pre-generated but I will still curate difficulty
    • pay $10 to unlock the randomizer
      • pick a difficulty
      • get it generated
  • I’m considering a hint system but I’m not sure how it works. Maybe it gives you the solution for a chain? Or maybe it tells you which chain a block is part of. I like that more. So if I press hint it turns a SUM + BLOCK Blue to show they are related.

If you want to chat about this post or give any feedback, send me an email!