Taps can be unreliable inside this app's built-in browser. For the full experience, use its menu to open this page in your browser.
Skip to content

Scoring a Game You Can Undo Forever

Most games on this site have an obvious thing to count. Distance travelled. Blocks stacked. Mines cleared. FreeCell has almost nothing, and it took me a while to notice why.

The problem is that FreeCell is reversible. Not “you can press undo” reversible — structurally reversible. A card can go from a column into a free cell and back to a different column, and then back again, indefinitely, with the board no better off than when you started. There are only four places in the whole game where something irreversible happens, and all four of them are a card going home to a foundation.

So any score that counts moves is a score you can farm. Give a point per move and the best strategy is to shuffle one card between two slots until you get bored. That is not a hypothetical: this site has shipped that mistake before, in a puzzle game whose efficiency bonus was measured against the whole campaign rather than the part you actually played, so pressing skip ten times outscored finishing it honestly.

Count the one thing that cannot be taken back

The score is bestHome × 20, where bestHome is a high-water mark — the most cards that have ever been on the foundations at one time during this deal. Not the number currently there. The most there has ever been.

That distinction is doing real work, because FreeCell lets you pull a card back off a foundation to free up a move. That is an authentic rule and a genuinely useful one — sometimes the two of hearts needs to come back out so a black ace has somewhere to land. But it is also an obvious exploit if the score counts what is currently home: send the ace up, take it down, send it up again, repeat.

With a high-water mark there is nothing to exploit, so the rule can stay in. I like that one decision bought the other. There is a test that pulls a card off a foundation and re-sends it ten times and asserts the score is unchanged and that exactly one score report was ever sent.

Undo had to roll the mark back too, or home, undo, home would farm points just as happily. The game snapshots the whole board before every move — fifty-two cards is small enough that cloning the lot beats writing an inverse operation per move type — so bestHome simply rides along inside the snapshot and comes back with the cards.

The finishing bonus had to decay

Winning adds a flat 600 plus a bonus that starts at 500 and loses two points per move and one per second. The decay is not decoration. Without it, a player who had already won could pad their move count for free, and — worse — the obvious “reward efficiency” framing would quietly punish the undo button, which is the one thing in this game that should never cost you anything. Undo is free here. Making a hundred extra moves is not.

I checked that by building two finished games differing only in move count and asserting the padded one scores less. Three lines of test, and it is the kind of thing that is obvious once written down and completely invisible otherwise.

The rule that looks arbitrary and is not

The other number in this game that looks made up is the limit on how many cards you can move at once: (empty free cells + 1) × 2^(empty columns), and if the column you are moving into is one of the empty ones, it does not count towards the exponent.

That is not a difficulty dial. A multi-card move in FreeCell is shorthand — you could always perform it by hand, one card at a time, using the free cells and any empty column as scratch space. The formula is just how many cards that scratch space can actually hold. And the destination column cannot be scratch space, because you are about to fill it, which is why it is subtracted.

Newcomers trip over this more than any other rule, so a refused move says “Only 2 cards can move at once — free a cell or empty a column” rather than just declining. Naming the fix rather than the rule is a lesson this site learned the hard way from Spider Solitaire, where a blocked deal correctly stated its condition and left a player stuck for twenty-five minutes anyway.

The solver I deleted

One thing I wanted to prove and could not: that the deals this game produces are winnable. FreeCell is famous for it — essentially every random deal can be solved — and it would have been nice to demonstrate that on my own shuffle rather than cite it.

So I wrote a solver. Best-first search, then plain depth-first with move ordering and a visited set that canonicalises the board by sorting the columns, since columns are interchangeable. It solved zero deals out of eight at a budget of ten thousand positions, at roughly a millisecond each. A competitive FreeCell solver is somebody’s multi-year project, and mine was not going to become one in an afternoon.

The reason I deleted it rather than lowering the bar is the asymmetry. Finding a solution proves a deal is winnable. Failing to find one proves nothing at all — it is a statement about the solver. A test that can only produce the second kind of result is a test that reports on itself, and keeping it around would have meant a number in a log that looked like evidence and was not.

What survived is the half that was always worth having. The solver came with an independent statement of FreeCell’s rules, written from the rules of the game rather than from the shipped code, and that is now checked against the real implementation on live mid-game positions — 2,280 probes of “can this card go there”, “can it go home”, “is it safe to send”, “how many can move”, with zero disagreements. That is a genuine cross-check rather than the game agreeing with itself.

And one test that was simply wrong

Two assertions failed on the first run of the move-limit tests. I had built a board where a three-card run was correctly refused and a two-card run was correctly allowed, and the two-card case kept failing.

The run started with the eight of hearts and I had aimed it at a column topped by a ten. An eight does not go on a ten. Both runs were pointed at the same destination column, which meant the second one could never be testing the move limit — it was being refused on rank and colour, the way it should be. The game was right and the board was mine.

That is the recurring shape of it. Nearly every failure in testing a card game turns out to be an illegal position I constructed myself, and the tell is almost always that the thing failing is the thing I least expected to break. FreeCell is here if you want to try breaking the scoring yourself.

Cookies help us measure how the site is used and, later, show relevant ads. Games work either way. See our Privacy Policy.