TinyGames · how it works
Fifty-two cards, four free cells, nothing hidden. Every deal is seeded and every seed is checked winnable before it ships, so a lost game is always yours to have lost.
Draft. It is built, compiled and tested like every other game, and it is
not on the hub: "draft": true in build/games.json keeps it off the cards,
out of the sitemap and without a readme page until the rules are real. Take the
flag off when it is worth playing.
cards.ts is the rules and touches nothing - no canvas, no engine, no
storage. freecell.ts is everything that draws or listens. The split is what
makes the hard half testable: node test-cards.js runs against the shipped,
minified JavaScript rather than the source, which is how a minification fault
gets caught.
Every screen except the run itself belongs to the shared shell, so this game
has one scene of its own, play. The menu, the pause card, the game-over
screen and the high-score table come from src/shared/scenes.ts and look and
behave the same here as in the other twenty.
FreeCell hides nothing - every card is face up from the first moment. So a deal you cannot win is one you were never able to win, and you have no way of telling which kind you were given. Every deal here is handed to a solver and thrown away unless the solver finishes it, so a lost game is always yours to have lost.
The solver looks at single-card moves only. That is not a simplification: a supermove of five cards is exactly five single moves through the free cells, so a solver over single moves is complete. The multi-card move belongs to the interface, where it saves the player from performing a shuffle they have already worked out.
The first version was a depth-first search with a seen-set and a sensible move ordering. Measured over forty deals it proved three, spending the whole 120,000-position budget on the other thirty-seven - eighteen seconds to get almost nothing. Regrouping the moves by kind made it worse, not better: one of forty.
The problem was never the ordering. FreeCell's space is far too wide to wander through; what is needed is a sense of which positions are getting closer. Best-first search over the same move generator, guided by how buried the next needed card of each suit is, proves 39 of 40 - median 555 positions rather than 120,000, and 2.9 seconds for all forty rather than eighteen.
The frontier is capped and culled to its better half, which makes this a beam search rather than a complete one. That matters for what a failure means: it is always "not proved winnable", never "proved unwinnable", so the deal is discarded. The search fails in the safe direction by construction.
A card only goes home automatically once both opposite-colour foundations have passed it - so nothing still in play could need it. Playing greedily loses winnable games: a red six sent up early is a six that some black five needed. That rule is what makes the automatic play safe, and it also keeps the search from having to reason about it at all.
Tap a card to pick it up, tap where it should go. Tapping a card part-way down a column takes it and everything below it, if that is a sequence the table has room to move - and both halves of that are checked, because they are different questions. Tapping the same place twice puts it back down.
Undo is a main control, not a hidden courtesy. The deal is guaranteed winnable, so a player who ruins one has ruined something that definitely could have been won; taking the move back is the game working.
Cards are drawn rather than loaded - fifty-two images would be the first binary assets on this site and would need their own licence note. A rounded rectangle, a rank and a suit glyph scales to any size and cannot 404.
"draft": true off the entry in build/games.jsonAdvertisement