How the Hearts AI Works: Determinized Monte-Carlo
The opponents on LK Forge Hearts face a problem the chess engine never does: they can't see your cards. Hearts is a hidden-information game, so a search that assumes it knows the position would be searching a fiction. The Hard engine handles this with a classic technique — determinized Monte-Carlo.
Turn one hidden game into many open ones
The idea is to guess, repeatedly, and average. The engine takes the cards it has seen — its own hand and everything played so far — and deals the unseen cards out into a complete, plausible hand for everyone. That "determinized" world is a normal open game it can solve. It does this many times with different plausible deals, plays each one to the end, and scores the result. The card that gives the lowest expected points across all those imagined games is the one it plays.
Determinized Monte-Carlo: replace one hidden game with many complete guesses, solve each, and average. The guesses cancel out; a genuinely good card wins.
// determinized Monte-Carlo card choice (schematic) function chooseCard(view) { const total = new Map(); for (let s = 0; s < SAMPLES; s++) { const world = dealUnseen(view, rng); // one plausible full deal for (const card of legalCards(view)) total.set(card, (total.get(card) || 0) + playout(world, card)); // points if we play it } return minBy(total); // lowest average points }
Why guessing works
It seems strange that averaging over wrong guesses gives a good move — but any single deal might flatter a bad card by luck, and the next deal punishes it. Over enough samples those flukes wash out, and the card that is genuinely safe against the range of hands you might hold floats to the top. It is the same reasoning behind our measured study of a determinized-MC card engine, where we ran the method thousands of games deep.
It understands the moon
Hearts has a twist that wrecks naive point-counting: shooting the moon. Take all 26 penalty points and your 26 becomes 0 while everyone else gets 26. Because the engine scores each playout after that moon adjustment, stopping your moon — or going for its own when the cards line up — is not a special case bolted on; it is just another outcome the average already accounts for.
That is the whole engine: turn a hidden hand into many open ones, play each out, average, and let the moon fall out of the scoring. Curious how this method holds up under measurement? Read luck vs skill in a card-game AI, or why none of our games are chatbots in Real Game AI, Not a Chatbot.
FAQ
How does the Hearts AI decide its moves?
At the Hard level it uses determinized Monte-Carlo: it deals the unseen cards many plausible ways, plays each imagined game to the end, and picks the card with the lowest expected points. Playouts are scored after the shoot-the-moon adjustment, so it both defends the moon and can shoot it. The whole search runs in a background worker thread in your browser, so nothing is sent to a server.
How can the AI play well when it can't see the other hands?
The AI doesn't need to see them — it guesses. Determinized Monte-Carlo replaces one hidden situation with many complete, plausible ones (deals consistent with the cards played so far), solves each as if it were an open hand, and averages the verdicts. Across enough samples the guesses cancel out and a genuinely good card rises to the top.
What is shooting the moon and does the AI understand it?
Shooting the moon is taking all 26 penalty points in a deal, which flips your 26 to 0 and gives everyone else 26. Because the engine scores each playout after that moon adjustment, it treats stopping an opponent's moon — and going for its own when the cards allow — as just another outcome the average accounts for. No special-case code is needed; the same scoring handles offense and defense alike.
Does the Hearts AI use a neural network?
No — the Hearts AI is a classical search — determinized Monte-Carlo over the shipped rules engine. There is no machine-learned model and no server. Everything runs in your browser.
Take on three sampling opponents.
Play Hearts →