2026-03-13

#07

How Match 3 Daily Puzzles Are Generated

I wanted to do a proper writeup on how the daily puzzles in Match 3 Daily are actually generated, because there’s a lot more going on behind the scenes than just “random board appears every day”. The short version is that a cloud function precomputes the daily board, verifies that it is valid and solvable, writes it to Firestore, and updates the metadata that tells the game which puzzle is current. The newer twist is that I also just added support for injecting custom puzzles directly into that pipeline. That means some daily boards can now be hand-authored by me instead of procedurally generated. The puzzles for March 12 and March 13 were the first two days where I used that system.

At a high level, the backend has a scheduled cloud function called precomputeDailyPuzzles. It runs automatically every day at midnight in the Montreal timezone. That function figures out what the current date key is, checks whether a puzzle for that date already exists and is still considered valid, and if not it creates one. Once it has a valid daily puzzle, it writes that puzzle into the daily puzzles collection and updates the meta data so the frontend can quickly know what today’s active puzzle is without having to read the whole collection of daily puzzles.

Once the function has the target date, one of the first things it can now do is check whether there is a custom puzzle waiting for that day. This is the new system I added. I can place .json puzzle files into a folder that is accessible to the cloud function.

If the function finds a matching custom puzzle file for the day it is generating, that file becomes the source of truth for the board. In other words, it does not procedurally generate a fresh puzzle in that case. It loads the custom JSON, validates its structure, normalizes the solution format if needed, and stores it as that day’s puzzle. This gives me a very clean way to inject hand-made boards into the normal daily flow. From the outside, players still just see a normal daily puzzle. The only difference is that instead of being generated by the solver pipeline, it came from a board I authored myself.

If there is no custom puzzle for the current day, the function falls back to the regular generation path. This is where the core Match 3 Daily board generation logic kicks in.

Generating a board for that kind of ruleset is not as simple as just filling a grid with random colors. The generator first needs to produce an opening state that does not already contain accidental matches. If the board starts with a bunch of auto-clears already present, then the player is not really solving the intended puzzle, the puzzle is solving part of itself. So the generation logic builds the board carefully and checks for immediate matches horizontally and vertically. If the opening board is unstable, it gets discarded.

But having a stable opening board is only step one. A board can have no starting matches and still be terrible. It might have no valid move. It might lead to a dead end. It might be impossible to fully clear under no-refill rules. So after a candidate board is created, the solver needs to analyze it. The backend searches for valid swaps, simulates their effects, applies gravity and cascades, and keeps exploring the resulting states. This is not just checking whether there is one move available right now. It is checking whether the puzzle has a path to a full clear.

To make that search practical, the solver is not just wandering blindly. It keeps track of visited board states so it does not keep re-exploring the same positions. It prioritizes meaningful moves. It also has caps and constraints so the function does not go off into the weeds chewing through cloud resources forever on a weird candidate board. If a board does not yield a good solution within the search limits, it gets rejected and the generator tries again with a new candidate.

Even after the solver finds a solution, the backend still does not just trust it and move on. There is another pass where the solution is replayed and verified step by step against the no-refill game rules. That means checking that each swap is adjacent, that the swap actually creates a match, that the cascades resolve correctly, and that the final board state is truly cleared.

If the board passes all of that, it gets written into Firestore with the board data, the solution, dimensions, color count, and version metadata. The meta documents are also updated so the frontend knows which date is current and what puzzle number should be displayed. Puzzle numbers are deterministic rather than hand-entered. They are derived from the number of days since the project’s published date.

The custom puzzle support fits into all of this structure pretty naturally. I did not want some weird side route where custom boards live in a totally different system from generated ones. I wanted both of them to end up looking like the same thing once they are in the database: a board, a solution, dimensions, color count, versioning, and a published date. The difference is just whether that data came from the procedural generator or from one of my uploaded puzzles. I wanted the workflow to be simple so I wouldn’t have to faff about any time I wanted to upload a custom puzzle.

This flexibility is useful for a few reasons. It lets me make special boards for specific days, and it also lets me steer the difficulty or shape the feel of a day more deliberately when I want to. Procedural generation is great and I’m pretty happy with how it’s currently working, but I think a lot about the difficulty curve and being able to inject my own puzzles into the flow will let me experiment with the balance before I start messing with the current board generation logic.

All of this is designed to look deceptively effortless from the outside if you just load up the game and play. But for the experience to be consistent every single day, there has to be some real infrastructure behind it. In Match 3 Daily that means a scheduled backend job, puzzle validation, metadata tracking, and now a custom injection system layered on top.

TLDR; every day, the backend figures out the target date, checks whether there is a custom JSON puzzle for that exact day, and if there is, it publishes that board. If there is not, it procedurally generates a 6x6 no-refill puzzle, solves it, verifies the solution, stores it, and updates the metadata that tells the site what today’s puzzle is. March 12 and March 13 were the first two days where I used the custom path. I’m glad that system is in now, because it opens the door to a nice mix of procedural boards and hand-made ones.

As usual, Match 3 Daily is still evolving. The core features are locked in, but I am still learning things from the data and testing. The generation pipeline is one of the most important parts of the whole project, so it felt worth documenting properly. If nothing else, now there is finally a post I can point to when someone asks how this works.