Most free mahjong games deal unwinnable boards. I built mine backwards.
An inside look at building a solvable mahjong solitaire game, extracting SVG tiles from Noto Sans Symbols 2, and running on a framework-free stack.

Stock photo for illustration only, not from the actual event
- Standard free mahjong games often use random tile drops that frequently create unwinnable boards.
- The developer fixed this by generating a solution first and deriving the board in reverse order.
- SVG artwork was extracted from Noto Sans Symbols 2 font to bypass broken Unicode defaults.
- The architecture relies entirely on vanilla PHP, vanilla JavaScript, and SQLite without npm.
Mahjong solitaire is a matching game where players remove pairs of identical tiles from a 144-tile pile, provided the tiles are free with no top stack and at least one long side open. However, the conventional way of dealing these boards routinely leads to dead ends.
The straightforward method of shuffling all 144 tiles and dropping them onto a layout is flawed. A random arrangement easily creates unwinnable states where the final four tiles are buried under each other or a pair is locked permanently. Players usually discover this twenty minutes into a game when no matches remain.
The remedy is to stop generating boards and checking them afterward, and instead generate a complete solution first and derive the board from it. Playing back that removal order guarantees that a valid solution exists before the player ever touches a tile.
Designing puzzle games using a reverse-generation approach effectively eliminates player frustration caused by unluckily randomized layouts. It highlights how backend architectural decisions directly dictate the quality of the frontend user experience and long-term engagement.
A fallback mechanism is in place if all sixty generation attempts fail, pairing adjacent positions to produce a functional board even if solvability isn't strictly guaranteed. This branch has never been triggered, but the developer kept it to ensure users see a game screen rather than a blank page.
Regarding tile faces, the Unicode mahjong block (U+1F000–U+1F02B) is frequently broken or missing in default Windows and Android font stacks, which would render the game unplayable. Consequently, the tile faces are rendered using SVG graphics.
Public-domain riichi mahjong artwork covers 33 out of 42 faces, but lacks flower tiles, season tiles, and the white dragon. To fill the gap, the missing outlines were extracted directly from the Noto Sans Symbols 2 font using fontTools.
This extraction process revealed two unexpected details. Each glyph draws its own outer and inner tile borders, which are byte-identical across bonus tiles. Dropping those border contours leaves behind clean artwork, and using the drawing area bounding box provides a consistent scale for every face.

Stock photo for illustration only, not from the actual event
Additionally, the small character in the corner representing symbols like plum or spring can be cleanly separated from the main picture. This separation allows developers to color the character and the illustration independently using a single monochrome glyph source.
Technically, the project runs on vanilla PHP, vanilla JavaScript, and SQLite without any frameworks, build steps, or npm packages. Every page is server-rendered, JavaScript handles interactive layers, and CSS provides the tile depth and shadows while SVG acts as the face plate.
The game is currently live at juju.games/mahjong for anyone willing to test their luck.
Source: Dev.to
Found something wrong in this article? Report an issue with this article
Comments
Leave a Comment