fix: enhance auto-rolling logic to handle ties and improve participant roll announcements
This commit is contained in:
@@ -298,12 +298,25 @@ export async function submitRoll(matchId: string, participantId: string, value:
|
||||
);
|
||||
|
||||
if (allRolled) {
|
||||
const sorted = [...participants]
|
||||
.map((p) => ({
|
||||
const finalRolls = participants.map((p) => ({
|
||||
...p,
|
||||
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++) {
|
||||
await db
|
||||
@@ -317,6 +330,7 @@ export async function submitRoll(matchId: string, participantId: string, value:
|
||||
.set({ state: MATCH_STATES.PICKING })
|
||||
.where(eq(match.id, matchId));
|
||||
}
|
||||
}
|
||||
|
||||
return getMatchFull(matchId);
|
||||
}
|
||||
|
||||
@@ -122,14 +122,20 @@ export function onMatchStateChange(matchId: string, newState: string) {
|
||||
if (m.state !== MATCH_STATES.ROLLING) return;
|
||||
|
||||
const lobby = getLobby(matchId);
|
||||
const unrolled = m.participants.filter((p) => p.rollValue === null);
|
||||
if (unrolled.length === 0) return;
|
||||
const initialUnrolled = m.participants.filter((p) => p.rollValue === null);
|
||||
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) {
|
||||
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) {
|
||||
const autoRoll = Math.floor(Math.random() * 100) + 1;
|
||||
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}`);
|
||||
}
|
||||
}
|
||||
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
|
||||
const updated = await getMatchFull(matchId);
|
||||
if (updated.state === MATCH_STATES.PICKING) {
|
||||
if (current.state === 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)
|
||||
);
|
||||
if (lobby?.isAlive) {
|
||||
@@ -414,10 +428,12 @@ function setupChatHandlers(matchId: string, lobby: TournamentLobby) {
|
||||
return;
|
||||
}
|
||||
|
||||
const isLastRoll = m.participants.filter((p) => p.rollValue === null).length === 1;
|
||||
|
||||
await submitRoll(matchId, targetParticipant.id, 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 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. ` +
|
||||
`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) {
|
||||
// Tell the other player they still need to roll
|
||||
await lobby.chat(
|
||||
|
||||
@@ -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;
|
||||
await submitRoll(params.id, myParticipant.id, value);
|
||||
|
||||
@@ -160,7 +161,7 @@ export const actions: Actions = {
|
||||
if (lobby) {
|
||||
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);
|
||||
if (updated.state === 'PICKING') {
|
||||
const sorted = [...updated.participants].sort(
|
||||
@@ -169,6 +170,8 @@ export const actions: Actions = {
|
||||
lobby.chat(
|
||||
`Rolls complete! ${sorted[0]?.team.name} picks first. Use !pick <slot> (e.g. !pick NM1) or pick in web UI.`
|
||||
).catch(() => {});
|
||||
} else if (isLastRoll && updated.participants.every((p: any) => p.rollValue === null)) {
|
||||
lobby.chat(`Tie! All players rolled ${value}. Please !roll again.`).catch(() => {});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user