fix: both-wrong draws + double TTS entrance fix

Scoring: both-wrong is now a DRAW — equal scores (3/3), no winner,
symmetric minimal damage. Garbage answers no longer beat reasonable
ones just by being faster. Both-wrong narrations reflect the draw.

Entrance: removed duplicate announceDeepIntro() call from FightViewer
(was already called inside playEntrance). Removed _resetPositions()
after entrance (entrance already places fighters at home positions,
the extra reset caused a visible snap/reset).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Dorian
2026-03-12 20:09:11 +00:00
co-authored by Claude Opus 4.6
parent 017d0e3e4c
commit 6f0eb92ebb
2 changed files with 18 additions and 27 deletions
+15 -20
View File
@@ -4,7 +4,7 @@ import {
MARGIN_TO_DAMAGE_SCALE, LOSER_DAMAGE_BASE, ARENA_DAMAGE_MULTIPLIER,
MAX_COMBO_STACKS, COMBO_DAMAGE_PER_STACK,
FACTUAL_FASTER_BASE, FACTUAL_SLOWER_BASE, SPEED_ADVANTAGE_MULTIPLIER, SPEED_RATIO_MULTIPLIER,
ONE_CORRECT_WINNER_BASE, CONFIDENCE_BONUS, NO_ANSWER_SCORE, BOTH_WRONG_FASTER, BOTH_WRONG_SLOWER,
ONE_CORRECT_WINNER_BASE, CONFIDENCE_BONUS, NO_ANSWER_SCORE, BOTH_WRONG_SLOWER,
RETRO_SPEED_BONUS_CAP,
LARGE_WIN_MARGIN, CLOSE_MATCH_MARGIN, WHIFF_NARRATION_THRESHOLD,
TIMEOUT_WINNER_SCORE, SCORE_ROUNDING_FACTOR,
@@ -145,11 +145,10 @@ export function scoreRound(
scoreA = NO_ANSWER_SCORE + (responseA.answer ? 1 : 0)
scoreB = ONE_CORRECT_WINNER_BASE + correctB * CONFIDENCE_BONUS
} else {
// Both wrong -- speed tiebreaker in low range
// Both wrong -- DRAW, nobody wins for getting it wrong
resultType = 'both-wrong'
const aFaster = responseA.timeMs <= responseB.timeMs
scoreA = aFaster ? BOTH_WRONG_FASTER : BOTH_WRONG_SLOWER
scoreB = aFaster ? BOTH_WRONG_SLOWER : BOTH_WRONG_FASTER
scoreA = BOTH_WRONG_SLOWER
scoreB = BOTH_WRONG_SLOWER
}
} else {
// No answers defined — both get base score (all challenges should be factual)
@@ -175,6 +174,17 @@ export function scoreRound(
const narration = winnerId
? generateNarration(challenge, winnerName!, loserName!, margin, isCritical, resultType)
: resultType === 'both-wrong'
? pick([
`Both bots WHIFFED! ${botA.name} and ${botB.name} got it wrong. Nobody wins this one!`,
`DOUBLE MISS! Neither ${botA.name} nor ${botB.name} could answer that. Embarrassing for everyone.`,
`${botA.name} and ${botB.name} both hallucinated! Nobody scores. The crowd throws things.`,
`Two wrong answers! ${botA.name} and ${botB.name} cancel each other out in wrongness.`,
`NOBODY got it right! ${botA.name} and ${botB.name} both swing and miss. Draw!`,
`${botA.name}: wrong. ${botB.name}: also wrong. The real winner is whoever stopped watching.`,
`Both bots confidently stated incorrect answers! ${botA.name} and ${botB.name} share the L equally.`,
`WRONG and WRONG! Neither ${botA.name} nor ${botB.name} had any idea. The crowd demands smarter bots.`,
])
: pick([
`Dead even! ${botA.name} and ${botB.name} are perfectly matched. Like two politicians blaming each other.`,
`IT'S A TIE! ${botA.name} and ${botB.name} cancel each other out like Congress!`,
@@ -240,21 +250,6 @@ function generateNarration(
const critPrefix = isCritical ? 'CRITICAL HIT! ' : ''
const isFactual = challenge.scoring === 'factual'
// Both bots got it WRONG — narration must reflect that
if (isFactual && resultType === 'both-wrong') {
const bothWrong = [
`${critPrefix}Both bots WHIFFED! Nobody got it right but ${winner} failed FASTER. Is that even a win?`,
`${critPrefix}DOUBLE MISS! ${winner} and ${loser} both got it wrong! ${winner} wins on speed alone. Participation trophy energy.`,
`${critPrefix}Two wrong answers! ${winner} was wrong FASTER than ${loser}. Speed isn't everything, but here we are.`,
`${critPrefix}Neither bot had a clue! ${winner} guessed quicker. ${loser} took their time being equally wrong.`,
`${critPrefix}BOTH WRONG! The only thing ${winner} beat ${loser} at was being wrong first. The crowd boos.`,
`${critPrefix}${winner} AND ${loser} both hallucinated! ${winner} at least hallucinated with conviction. Speed win.`,
`${critPrefix}NOBODY got the answer! ${winner} earns the world's saddest victory by responding faster. Tragic.`,
`${critPrefix}Wrong and wrong! ${winner} speedran being incorrect! ${loser} at least took time to think about it. Both failed.`,
]
return pick(bothWrong)
}
if (isFactual && margin > LARGE_WIN_MARGIN) {
const bigWins = [
`${critPrefix}${winner} NAILS IT! ${loser} didn't even come close. Embarrassing, honestly.`,