fix: enhance auto-rolling logic to handle ties and improve participant roll announcements

This commit is contained in:
2026-03-01 22:20:43 +01:00
parent 4faf1aa21d
commit 711cdd47e1
3 changed files with 65 additions and 29 deletions

View File

@@ -298,12 +298,25 @@ export async function submitRoll(matchId: string, participantId: string, value:
); );
if (allRolled) { if (allRolled) {
const sorted = [...participants] const finalRolls = participants.map((p) => ({
.map((p) => ({
...p, ...p,
rollValue: p.id === participantId ? value : p.rollValue! rollValue: p.id === participantId ? value : p.rollValue!
})) }));
.sort((a, b) => b.rollValue - a.rollValue);
const rollValues = finalRolls.map((p) => p.rollValue);
const hasTie = new Set(rollValues).size < rollValues.length;
if (hasTie) {
// Reset all rolls so everyone must roll again
for (const p of participants) {
await db
.update(matchParticipant)
.set({ rollValue: null })
.where(eq(matchParticipant.id, p.id));
}
// Stay in ROLLING state
} else {
const sorted = [...finalRolls].sort((a, b) => b.rollValue - a.rollValue);
for (let i = 0; i < sorted.length; i++) { for (let i = 0; i < sorted.length; i++) {
await db await db
@@ -317,6 +330,7 @@ export async function submitRoll(matchId: string, participantId: string, value:
.set({ state: MATCH_STATES.PICKING }) .set({ state: MATCH_STATES.PICKING })
.where(eq(match.id, matchId)); .where(eq(match.id, matchId));
} }
}
return getMatchFull(matchId); return getMatchFull(matchId);
} }

View File

@@ -122,14 +122,20 @@ export function onMatchStateChange(matchId: string, newState: string) {
if (m.state !== MATCH_STATES.ROLLING) return; if (m.state !== MATCH_STATES.ROLLING) return;
const lobby = getLobby(matchId); const lobby = getLobby(matchId);
const unrolled = m.participants.filter((p) => p.rollValue === null); const initialUnrolled = m.participants.filter((p) => p.rollValue === null);
if (unrolled.length === 0) return; if (initialUnrolled.length === 0) return;
const names = unrolled.map((p) => p.team.name).join(', '); const names = initialUnrolled.map((p) => p.team.name).join(', ');
if (lobby?.isAlive) { if (lobby?.isAlive) {
await lobby.chat(`⏰ Rolling timed out — auto-rolling for: ${names}`); await lobby.chat(`⏰ Rolling timed out — auto-rolling for: ${names}`);
} }
// Auto-roll with tie retry: keep rolling until no ties
let tieTries = 0;
let current = await getMatchFull(matchId);
while (current.state === MATCH_STATES.ROLLING && tieTries < 10) {
const unrolled = current.participants.filter((p) => p.rollValue === null);
if (unrolled.length === 0) break;
for (const p of unrolled) { for (const p of unrolled) {
const autoRoll = Math.floor(Math.random() * 100) + 1; const autoRoll = Math.floor(Math.random() * 100) + 1;
await submitRoll(matchId, p.id, autoRoll); await submitRoll(matchId, p.id, autoRoll);
@@ -138,12 +144,20 @@ export function onMatchStateChange(matchId: string, newState: string) {
await lobby.chat(`${p.team.name} auto-rolled ${autoRoll}`); await lobby.chat(`${p.team.name} auto-rolled ${autoRoll}`);
} }
} }
current = await getMatchFull(matchId);
if (current.state === MATCH_STATES.ROLLING) {
// Tie — all rolls were reset, retry
tieTries++;
if (lobby?.isAlive) {
await lobby.chat(`Tie! Auto-rolling again...`);
}
}
}
// After auto-rolling, check if state advanced to PICKING // After auto-rolling, check if state advanced to PICKING
const updated = await getMatchFull(matchId); if (current.state === MATCH_STATES.PICKING) {
if (updated.state === MATCH_STATES.PICKING) {
onMatchStateChange(matchId, MATCH_STATES.PICKING); onMatchStateChange(matchId, MATCH_STATES.PICKING);
const sorted = [...updated.participants].sort( const sorted = [...current.participants].sort(
(a, b) => (a.pickOrder ?? 99) - (b.pickOrder ?? 99) (a, b) => (a.pickOrder ?? 99) - (b.pickOrder ?? 99)
); );
if (lobby?.isAlive) { if (lobby?.isAlive) {
@@ -414,10 +428,12 @@ function setupChatHandlers(matchId: string, lobby: TournamentLobby) {
return; return;
} }
const isLastRoll = m.participants.filter((p) => p.rollValue === null).length === 1;
await submitRoll(matchId, targetParticipant.id, value); await submitRoll(matchId, targetParticipant.id, value);
console.log(`[Orchestrator] IRC roll: ${username} = ${value}`); console.log(`[Orchestrator] IRC roll: ${username} = ${value}`);
// Check if all rolled → announce pick order // Check if all rolled → announce pick order or tie
const updated = await getMatchFull(matchId); const updated = await getMatchFull(matchId);
const remaining = updated.participants.filter((p) => p.rollValue === null); const remaining = updated.participants.filter((p) => p.rollValue === null);
@@ -432,6 +448,9 @@ function setupChatHandlers(matchId: string, lobby: TournamentLobby) {
`Rolls complete! ${sorted[0]?.team.name} picks first. ` + `Rolls complete! ${sorted[0]?.team.name} picks first. ` +
`Use !pick <slot> (e.g. !pick NM1). You have 2 minutes.` `Use !pick <slot> (e.g. !pick NM1). You have 2 minutes.`
); );
} else if (isLastRoll && remaining.length === updated.participants.length) {
// All rolled the same value — everyone must reroll
await lobby.chat(`Tie! All players rolled ${value}. Please !roll again.`);
} else if (remaining.length > 0) { } else if (remaining.length > 0) {
// Tell the other player they still need to roll // Tell the other player they still need to roll
await lobby.chat( await lobby.chat(

View File

@@ -152,6 +152,7 @@ export const actions: Actions = {
} }
} }
const isLastRoll = m.participants.filter((p) => p.rollValue === null).length === 1;
const value = Math.floor(Math.random() * 100) + 1; const value = Math.floor(Math.random() * 100) + 1;
await submitRoll(params.id, myParticipant.id, value); await submitRoll(params.id, myParticipant.id, value);
@@ -160,7 +161,7 @@ export const actions: Actions = {
if (lobby) { if (lobby) {
lobby.chat(`${myParticipant.team?.name ?? 'Player'} rolled ${value} (via web)`).catch(() => {}); lobby.chat(`${myParticipant.team?.name ?? 'Player'} rolled ${value} (via web)`).catch(() => {});
// Check if all rolled → announce pick order // Check if all rolled → announce pick order or tie
const updated = await getMatchFull(params.id); const updated = await getMatchFull(params.id);
if (updated.state === 'PICKING') { if (updated.state === 'PICKING') {
const sorted = [...updated.participants].sort( const sorted = [...updated.participants].sort(
@@ -169,6 +170,8 @@ export const actions: Actions = {
lobby.chat( lobby.chat(
`Rolls complete! ${sorted[0]?.team.name} picks first. Use !pick <slot> (e.g. !pick NM1) or pick in web UI.` `Rolls complete! ${sorted[0]?.team.name} picks first. Use !pick <slot> (e.g. !pick NM1) or pick in web UI.`
).catch(() => {}); ).catch(() => {});
} else if (isLastRoll && updated.participants.every((p: any) => p.rollValue === null)) {
lobby.chat(`Tie! All players rolled ${value}. Please !roll again.`).catch(() => {});
} }
} }