When both players win at the same time and neither wins in more ways, the rules call for a tiebreaker game (Sudden Death). TCG ONE has no Sudden Death today: such a game ends as a draw. This topic collects what we know so far, as a brief for implementing it. Details on how it should work will be added later.
1. What exists today: simultaneous wins (commits 0483abd98, a35fc10f8, 2026-10-24)
Reports: forum 15266, twins 10877, 12601, 5996.
Before: simultaneous Knock Outs were handled one Pokémon at a time, and the game ended at the first player who took their last Prize card (or had no Pokémon left). The other Knock Out never gave its Prize card.
0483abd98 (engine)
engine/src/main/java/tcgwars/logic/GameManager.javawin(PlayerType, WinCondition): every “last Prize card” (NOPRIZE) and “no Pokémon left” (NOPOKEMON) win now goes through here instead ofendGame. Outside of a resolution window it callsendGameat once.resolvingSimultaneousWins(Runnable): while it runs, wins are only collected per player (EnumSetofWinCondition). Afterwards: whoever wins in more ways wins (with a log line naming both players’ win conditions). An equal number of ways is a draw:endGame(null, ...), with the log line “Both players won at the same time: … Sudden Death isn’t available, so the game is a draw.” It can be nested (an inner call just runs the effects).
engine/src/main/java/tcgwars/logic/effect/basic/CheckFaint.java: each round of Knock Outs (checkAll) runs insideresolvingSimultaneousWins, so all Knock Outs and their Prize cards resolve first.engine/src/main/java/tcgwars/logic/effect/basic/Knockout.java: no Benched Pokémon →win(..., NOPOKEMON);takePrizesstops once the taker has no Prize cards left (instead of when the game ended).engine/src/main/java/tcgwars/logic/effect/basic/TakePrize.java: the last Prize card →win(..., NOPRIZE);checkPrizeschecks both players inside oneresolvingSimultaneousWins.- Attacks that run two
new Knockout(...)themselves wrap them inbg.gm().resolvingSimultaneousWins { ... }:- Beedrill TEU 5, Destiny Stinger (
engine/src/main/java/tcgwars/logic/impl/gen7/TeamUp.groovy) - Annihilape SSP 100, Destined Fight (
engine/src/main/java/tcgwars/logic/impl/gen9/SurgingSparks.groovy)
- Beedrill TEU 5, Destiny Stinger (
- Tests in
engine/src/test/resources/tcgwars/logic/test/ng/test_cases.groovy. The rule is also written down in.cursor/rules/bug-triage.mdc(Engine facts, “Both players win at the same time”).
a35fc10f8 (korstark): korstark/src/main/java/net/tcgone/korstark/service/CareerService.java, the KANTO_CIRCUIT achievement check threw a NullPointerException on a draw (gameLog.getWinnerId() is null). That failed the post-game payload, which is retried and runs calculateScore again on every retry. Now it is user.getId().equals(gameLog.getWinnerId()).
2. What the rules say
Current rulebook (docs/rules/pokemon-tcg-rulebook.md, the September 2026 web rulebook, p. 21):
- “What if both players win at the same time?”: you win by taking your last Prize card, or when your opponent has no Benched Pokémon to replace their Knocked Out (or removed) Active Pokémon. “If this happens, play a tiebreaker game. However, if you win in both ways and your opponent wins in only one way, you are the victor!”
- “What’s a tiebreaker game?”: “Both players set up as though they were playing a normal game, including setting up 6 Prize cards and flipping a coin to decide who goes first. The first player to take a Prize card (or to otherwise have fewer Prize cards remaining than their opponent) wins. The other ways to win still apply, but mainly it’s a race to see who can take the first Prize card!”
Older rulings: Sudden Death with 1 Prize card (Compendium, compendium.pokegym.net):
- Plasma Freeze FAQ, May 16, 2013 (TPCi): “the player with the most win conditions met wins the game. However, if both players have the same number of win conditions, the game is declared a Draw and a one-prize Sudden Death game takes place to determine the winner.”
- HS Triumphant FAQ, Nov 4, 2010 (PUI) and Nov 2, 2017 (TPCi), about Palkia & Dialga LEGEND’s Time Control and Nihilego-GX’s Symbiont GX adding Prize cards during Sudden Death: “A Sudden Death match is complete when a player has taken all of their prizes, regardless of whether there is more than one or not.”
- May 3, 2018 (TPCi): “both players win at same time. Play a sudden death game to decide the outcome.”
- HS Undaunted FAQ, Sep 9, 2010 (Drifblim’s Take Away): if neither player has Benched Pokémon, “it is a tie and a game of Sudden Death must be played.”
- EX era (PUI): Mar 3, 2005 (Rocket’s Hitmonchan-EX’s Strikes Back gives both players their last Prize card → Sudden Death); Mar 10, 2005 (Electrode HL’s Mass Destruction: last Prize card vs. no Bench → Sudden Death; two win conditions vs. one → the Electrode player wins).
- WotC era: Fossil FAQ (Selfdestruct Magneton, both take their last Prize card): the player who can still replace their Active Pokémon wins, “Otherwise, you play Sudden Death”, citing the rulebook’s Expert Rules “What Happens if Both Players Win at the Same Time?”. WotC Chat Sep 13, 2001 Q30: last Prize card vs. no Bench → “Neither, it goes to Sudden Death”. The Compendium doesn’t quote the WotC rulebook’s Prize count for Sudden Death; check a scan of the Base Set rulebook before relying on it.
So the eras differ:
| Era | Tiebreaker | Win condition |
|---|---|---|
| WotC to at least 2018 (per the rulings above) | Sudden Death, 1 Prize card each | take all your Prize cards (the 1, or more if a card added some), or the other ways to win |
| Current rulebook (2026) | Tiebreaker game, 6 Prize cards each | first player to take a Prize card (or have fewer left than the opponent), or the other ways to win |
| All eras | more win conditions than the opponent wins outright; equal → tiebreaker |
Open: which rule each format/RuleSet should use, and when the rulebook switched from 1 Prize to “6 Prizes, first Prize wins” (check older rulebook PDFs).
3. What exists today for draws
- Engine:
endGame(null, winCondition)inGameManagerlogs “Game ends with a draw.” and queues the game-end animation withwinnerName = "Draw"(TargetPlayer.DEFAULT). There is noDRAWvalue inWinCondition(common-rpc/src/main/java/tcgwars/logic/WinCondition.java); a simultaneous-win draw uses the shared win condition orOTHER. - Play client:
common-ui/src/main/resources/tcgwars/common/ui/game/game_end.jsshows “Draw!”; the lobby’s game panel (ui/src/main/java/tcgwars/ui/lobby/UGamePanel.java) shows “Winner: Draw”. - core:
GameService.processGameEndedEvent(core/src/main/java/tcgwars/service/engine/GameService.groovy) saves theGameLogwithwinnerIdnull and pushes the post-game payload; the results channel message is skipped when there is no winner. - korstark skips draws for: the three-win bonus (
CareerService.checkThreeWinBonus), theKANTO_CIRCUITand DECKOUT achievements, the WinGames quest (model/quest/WinGames.java), career campaign progress (CareerCampaignService, line ~103), the ranked season rating (ScoreService.saveRatingonly runs with a winner and not onGAME_ERROR), and the winner-only parts of the score inScoreService.calculateScore. - Bug (open follow-up): the Elo update in
ScoreService.calculateScore(~line 470) gives each playerResult.Winonly if they are the winner, otherwiseResult.Loss. So any game with no winner (a draw orGAME_ERROR) is an Elo loss for both players.EloRatingCalculator.Result.Draw(0.5) exists but is unused. The ranked season’s Glicko-2 code also has an unusedRatingPeriodResults.addDraw(korstark/src/main/java/org/goochjs/glicko2/RatingPeriodResults.java).
4. Where draws (or simultaneous wins) come from
- Simultaneous wins (section 1): any
CheckFaintround, e.g. damage to both Active Pokémon, recoil, Poison/Burn in Pokémon Checkup, and the double-KO attacks. - Both players out of game time with no extension left:
BetweenTurns.handleGameTimers(engine/src/main/java/tcgwars/logic/effect/basic/BetweenTurns.java) ends the game as a draw,endGame(null, TIMEOUT). The rulebook has no Sudden Death for this; this is our own timer. - Epic Splash environment effect: both players have all 5 Eeveelutions →
endGame(null, EEVEELUTION)(engine/src/main/java/tcgwars/logic/effect/environment/EpicSplashEffect.java). - Engine error:
endGame(null, GAME_ERROR)(GameManager,util/Future.java). Not a real draw, but korstark treats it like one for Elo (see the bug above). - Cards that Knock Out both Active Pokémon themselves:
- Beedrill TEU 5, Destiny Stinger: wrapped (0483abd98).
- Annihilape SSP 100, Destined Fight: wrapped (0483abd98).
- Forretress LHB 45, Double KO (
engine/src/main/contrib/src/tcgwars/logic/impl/gen8/LegendaryHeartbeat.groovy, ~line 1041, in the contrib submodule): not wrapped yet; its twonew Knockout(...)calls inafterDamagestill end the game at the first win.
- Not audited: other attacks that call
new Knockout(self).run(bg)directly while the attack also Knocks Out the Defending Pokémon (git grepnew Knockout(self)lists about 20, in gen1, gen4 contrib, gen5 to gen9), and the directendGame(..., NOPOKEMON)calls inTcgStatics.removePCSandJungle.java. Outside aresolvingSimultaneousWinswindow those end the game before the other side’s win is counted.
5. What implementing Sudden Death would need (open list, not a design)
Engine / game setup
- Instead of
endGame(null, ...)on equal win counts, end the game with a result that says “go to Sudden Death” (a newWinConditionor flag inGameEndedEvent), so core can start the tiebreaker. - A new game with the same two decks, same format/
RuleSet/environment effects/custom format settings, new coin flip for who goes first.Battleground.startingPrizeCountalready comes fromCreateBattlegroundRequest.starting_prize_count(common-rpc/src/main/proto/updater.proto, set inGameService.groovy~line 719, also used by custom formats), so a 1-Prize game is mostly a parameter. - If the current rulebook’s variant is wanted for modern formats: 6 Prizes and “first player to take a Prize card (or have fewer Prizes left) wins”, which needs a new win check (not a parameter).
- Prize-adding effects during Sudden Death (Time Control, Symbiont GX): per the rulings the player must take all Prize cards, which the 1-Prize setup gives for free.
- Can Sudden Death itself end in a tie? (Another Sudden Death, or a draw after N tries?)
- Timers: does Sudden Death get fresh game time, and what if both time out in it?
- Fix or wrap the remaining double-KO paths in section 4 first, so Sudden Death triggers wherever it should.
core / lobby
- Link the two games (e.g. a
suddenDeathOfgame id onGame/GameLog), so the result counts once. - What if a player leaves, logs out or surrenders between the games, or refuses the Sudden Death game?
- Which game types offer it: ranked/casual/career/campaign/tournament/solo (vs AI) — or a draw where Sudden Death makes no sense?
- Spectators and the replay/game log for the pair of games.
korstark (career, ranked, quests)
- Count the pair as one game with the Sudden Death winner, or as a draw plus a game? This decides Elo, the ranked season rating, win streaks/three-win bonus, quests (WinGames, play-N-games quests), achievements (KANTO_CIRCUIT, deck-out ones,
POWER_OF_ONE), campaign progress, coins/ink/bits fromcalculateScore, and the win countScoreServicereads fromGameLog.winnerId. - Fix the Elo-loss-on-draw bug (section 3) either way, using
Result.Draw/addDrawfor draws that stay draws (timeouts, Epic Splash) and skippingGAME_ERROR.
UI (play client ui/common-ui, front3)
- A game-end screen for “Both players won: Sudden Death!” and a hand-off into the new game (desktop and touch layouts).
- Showing a Sudden Death game as such: label, 1 Prize card, and the result in the lobby’s game panel and in front3’s game history.