BE AWESOME
Settings
Games
📅 August 3, 2026 · ⏱️ 5 min read

Solitaire: Implementing the Most-Cloned Card Game Correctly

No room to reinvent Klondike — just to implement it correctly and get out of the way, including an honest admission about unwinnable deals.

This post is about Solitaire: Implementing the Most-Cloned Card Game Correctly
Play Solitaire →

Klondike solitaire is probably the single most-implemented card game in computing history, which put a specific kind of pressure on this one: there's no room to reinvent anything here, only to implement the well-known rules correctly and make the interface feel as close to a real deck as a browser tab reasonably can. Build descending, alternating- color sequences on the tableau, sort cards up by suit into the foundations, draw from the stock pile when you're stuck. Everyone who plays this already knows the rules. My job was making sure the implementation never gets in the way of them.

Drag-and-drop has to validate before it commits

Dragging a card onto a pile has to check whether that specific move is actually legal — right color alternation, right descending rank, right suit for a foundation — before the card visually settles into place, not after. Committing a drop first and then reverting it if the move turns out to be illegal is a worse feel than validating before the drop ever visually resolves, because a reverted card creates a jarring little snap-back that reads as the game glitching rather than the game correctly rejecting an invalid move. Getting the timing of that check right — validate during the drag, resolve cleanly on release — mattered more to how polished this feels than almost anything else in the build.

Auto-play had to guess correctly, not just quickly

Double-clicking a card sends it to a foundation automatically if it's eligible, and the tricky part isn't the mechanical act of moving it — it's correctly figuring out which foundation pile a given card should go to when more than one might technically accept it, or when sending it there now would strand a card underneath it that you'd want later. I kept the auto-play logic deliberately conservative: it only fires when a move is unambiguous and safe, and leaves genuinely judgment-call situations for the player to resolve manually by dragging. An auto-play feature that occasionally makes a move you didn't actually want, just because it was technically legal, would train players to distrust double-clicking entirely, which defeats the entire point of the shortcut existing.

Why both draw-1 and draw-3 exist from day one

Draw-1 and draw-3 aren't just a difficulty toggle, they're different games in terms of what information is available to you. In draw-1, every card in the stock pile is eventually reachable and playable, which makes the game more about sequencing than memory. In draw-3, cards you can't currently use get grouped with two others and cycled past, which means information genuinely gets buried and recovering it requires cycling through the whole pile again. Supporting only one of these would have meant picking a single skill profile to test. Supporting both from launch, rather than adding the harder mode later, meant new players could start with the more forgiving draw-1 rules and grow into draw-3 once they wanted a real challenge, without waiting on a future update to get there.

Dealing a shuffled deck fairly is not as trivial as it sounds

Shuffling a 52-card deck and dealing it into the tableau and stock pile sounds like the simplest part of the whole build, and it's the part I was most careful about anyway. A biased shuffle — one where certain cards are subtly more likely to land in certain positions because of how the randomization is implemented — would be invisible to a casual player but would quietly change how winnable different deals actually are. I used a proper unbiased shuffling approach rather than a naive sort-by-random-key trick, specifically because the naive version is a well-known source of subtle bias in exactly this kind of card-shuffling code, and it's the sort of bug that could sit unnoticed for a long time while slowly skewing which games are actually winnable.

The interface goal was invisibility, not innovation

I didn't try to improve on how physical solitaire feels to play — no redesigned layout, no modernized card-flow gimmick. The actual goal was for the interface to disappear entirely behind the familiarity of the game itself, so that anyone who's played solitaire with a real deck feels immediately at home without reading a single instruction. That's a strange kind of design goal to optimize for — success looks like nobody noticing the interface at all, rather than anybody praising it. For a game this universally known, I think that's exactly the right bar to aim for, even though it's a genuinely unsatisfying one to point at and say "look what I built."

One thing I'm not solving yet, and I want to be honest about it: nothing currently guarantees a dealt game is actually winnable. Real Klondike deals can be, and often are, unsolvable no matter how well you play them — that's a known property of the physical game too, not a bug specific to this implementation. I'm leaving that as-is for launch, matching how the physical card game has always worked, though it's something I expect to revisit once I've watched enough people hit a genuinely dead deal and have no way to know whether they misplayed or were just never going to win that particular shuffle. For now, matching the real game's own imperfection felt more honest than quietly changing the rules of something this well-known without saying so. If solvability guarantees come later, I'd rather they come as a clearly labeled, deliberate choice than as a quiet default nobody asked for. For now, this deals a fair, properly shuffled hand and gets out of the way, which is exactly what solitaire has always asked of whatever's dealing the cards, a real deck or otherwise. If nothing else, it's a reminder that not every game needs new mechanics to justify existing — sometimes the job is making an old one feel exactly right in a new format, and knowing when to stop adding things is its own kind of design decision, even when it's the least exciting sentence to write in an entire devlog post.

Hello!