Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 14 additions & 3 deletions broadcast.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ type GameState = {
lastCompleted: RoundState | null;
active: RoundState | null;
scores: Record<string, number>;
streaks: Record<string, number>;
viewerScores: Record<string, number>;
done: boolean;
isPaused: boolean;
Expand Down Expand Up @@ -300,6 +301,7 @@ function drawScoreboardSection(
label: string,
startY: number,
entryHeight: number,
streaks?: Record<string, number>,
) {
const maxScore = entries[0]?.[1] || 1;

Expand Down Expand Up @@ -343,10 +345,19 @@ function drawScoreboardSection(
const scoreText = String(score);
const scoreWidth = ctx.measureText(scoreText).width;
ctx.fillText(scoreText, WIDTH - 48 - scoreWidth, y + 18);

const streak = streaks?.[name] || 0;
if (streak >= 2) {
ctx.font = '600 14px "Inter", sans-serif';
ctx.fillStyle = "#f59e0b";
const streakText = `🔥${streak}`;
const streakWidth = ctx.measureText(streakText).width;
ctx.fillText(streakText, WIDTH - 48 - scoreWidth - streakWidth - 8, y + 18);
}
});
}

function drawScoreboard(scores: Record<string, number>, viewerScores: Record<string, number>) {
function drawScoreboard(scores: Record<string, number>, viewerScores: Record<string, number>, streaks: Record<string, number>) {
const modelEntries = Object.entries(scores).sort((a, b) => b[1] - a[1]) as [string, number][];
const viewerEntries = Object.entries(viewerScores).sort((a, b) => b[1] - a[1]) as [string, number][];

Expand All @@ -359,7 +370,7 @@ function drawScoreboard(scores: Record<string, number>, viewerScores: Record<str
ctx.fillText("STANDINGS", WIDTH - 348, 76);

const entryHeight = 52;
drawScoreboardSection(modelEntries, "AI JUDGES", 110, entryHeight);
drawScoreboardSection(modelEntries, "AI JUDGES", 110, entryHeight, streaks);

const viewerStartY = 110 + 28 + modelEntries.length * entryHeight + 16;
drawScoreboardSection(viewerEntries, "VIEWERS", viewerStartY, entryHeight);
Expand Down Expand Up @@ -632,7 +643,7 @@ function draw() {
return;
}

drawScoreboard(state.scores, state.viewerScores ?? {});
drawScoreboard(state.scores, state.viewerScores ?? {}, state.streaks ?? {});

const isNextPrompting = state.active?.phase === "prompting" && !state.active.prompt;
const displayRound = isNextPrompting && state.lastCompleted ? state.lastCompleted : (state.active ?? state.lastCompleted ?? null);
Expand Down
9 changes: 9 additions & 0 deletions frontend.css
Original file line number Diff line number Diff line change
Expand Up @@ -233,6 +233,15 @@ body {
flex-shrink: 0;
}

.streak-badge {
font-family: var(--mono);
font-size: 11px;
font-weight: 700;
padding: 2px 6px;
color: #f59e0b;
margin-left: auto;
}

.win-tag {
font-family: var(--mono);
font-size: 10px;
Expand Down
23 changes: 22 additions & 1 deletion frontend.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ type GameState = {
lastCompleted: RoundState | null;
active: RoundState | null;
scores: Record<string, number>;
streaks: Record<string, number>;
viewerScores: Record<string, number>;
done: boolean;
isPaused: boolean;
Expand Down Expand Up @@ -159,6 +160,7 @@ function ContestantCard({
isWinner,
showVotes,
voters,
streak,
viewerVotes,
totalViewerVotes,
}: {
Expand All @@ -168,6 +170,7 @@ function ContestantCard({
isWinner: boolean;
showVotes: boolean;
voters: VoteInfo[];
streak: number;
viewerVotes?: number;
totalViewerVotes?: number;
}) {
Expand All @@ -185,6 +188,11 @@ function ContestantCard({
>
<div className="contestant__head">
<ModelTag model={task.model} />
{streak >= 2 && (
<span className="streak-badge" title={`${streak} win streak`}>
🔥 {streak}
</span>
)}
{isWinner && <span className="win-tag">WIN</span>}
</div>

Expand Down Expand Up @@ -269,10 +277,12 @@ function ContestantCard({
function Arena({
round,
total,
streaks,
viewerVotingSecondsLeft,
}: {
round: RoundState;
total: number | null;
streaks: Record<string, number>;
viewerVotingSecondsLeft: number;
}) {
const [contA, contB] = round.contestants;
Expand Down Expand Up @@ -332,6 +342,7 @@ function Arena({
isWinner={isDone && votesA > votesB}
showVotes={showVotes}
voters={votersA}
streak={streaks[contA.name] || 0}
viewerVotes={round.viewerVotesA}
totalViewerVotes={totalViewerVotes}
/>
Expand All @@ -342,6 +353,7 @@ function Arena({
isWinner={isDone && votesB > votesA}
showVotes={showVotes}
voters={votersB}
streak={streaks[contB.name] || 0}
viewerVotes={round.viewerVotesB}
totalViewerVotes={totalViewerVotes}
/>
Expand Down Expand Up @@ -386,10 +398,12 @@ function GameOver({ scores }: { scores: Record<string, number> }) {
function LeaderboardSection({
label,
scores,
streaks,
competing,
}: {
label: string;
scores: Record<string, number>;
streaks?: Record<string, number>;
competing: Set<string>;
}) {
const sorted = Object.entries(scores).sort((a, b) => b[1] - a[1]);
Expand All @@ -415,6 +429,9 @@ function LeaderboardSection({
{i === 0 && score > 0 ? "👑" : i + 1}
</span>
<ModelTag model={{ id: name, name }} small />
{(streaks?.[name] || 0) >= 2 && (
<span className="streak-badge">🔥{streaks![name]}</span>
)}
<span className="lb-entry__score">{score}</span>
</div>
<div className="lb-entry__bar">
Expand All @@ -434,10 +451,12 @@ function LeaderboardSection({
function Standings({
scores,
viewerScores,
streaks,
activeRound,
}: {
scores: Record<string, number>;
viewerScores: Record<string, number>;
streaks: Record<string, number>;
activeRound: RoundState | null;
}) {
const competing = activeRound
Expand Down Expand Up @@ -466,6 +485,7 @@ function Standings({
<LeaderboardSection
label="AI Judges"
scores={scores}
streaks={streaks}
competing={competing}
/>
<LeaderboardSection
Expand Down Expand Up @@ -593,6 +613,7 @@ function App() {
<Arena
round={displayRound}
total={totalRounds}
streaks={state.streaks}
viewerVotingSecondsLeft={viewerVotingSecondsLeft}
/>
) : (
Expand All @@ -611,7 +632,7 @@ function App() {
)}
</main>

<Standings scores={state.scores} viewerScores={state.viewerScores ?? {}} activeRound={state.active} />
<Standings scores={state.scores} viewerScores={state.viewerScores ?? {}} streaks={state.streaks ?? {}} activeRound={state.active} />
</div>
</div>
);
Expand Down
8 changes: 8 additions & 0 deletions game.ts
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ export type GameState = {
completed: RoundState[];
active: RoundState | null;
scores: Record<string, number>;
streaks: Record<string, number>;
viewerScores: Record<string, number>;
done: boolean;
isPaused: boolean;
Expand Down Expand Up @@ -469,8 +470,15 @@ export async function runGame(
round.phase = "done";
if (votesA > votesB) {
state.scores[contA.name] = (state.scores[contA.name] || 0) + 1;
state.streaks[contA.name] = (state.streaks[contA.name] || 0) + 1;
state.streaks[contB.name] = 0;
} else if (votesB > votesA) {
state.scores[contB.name] = (state.scores[contB.name] || 0) + 1;
state.streaks[contB.name] = (state.streaks[contB.name] || 0) + 1;
state.streaks[contA.name] = 0;
} else {
state.streaks[contA.name] = 0;
state.streaks[contB.name] = 0;
}
// Viewer vote scoring
const vvA = round.viewerVotesA ?? 0;
Expand Down
1 change: 1 addition & 0 deletions quipslop.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -224,6 +224,7 @@ function Game({ runs }: { runs: number }) {
completed: [],
active: null,
scores: Object.fromEntries(MODELS.map((m) => [m.name, 0])),
streaks: {},
viewerScores: Object.fromEntries(MODELS.map((m) => [m.name, 0])),
done: false,
isPaused: false,
Expand Down
19 changes: 15 additions & 4 deletions server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,18 +30,26 @@ if (!process.env.OPENROUTER_API_KEY) {

const allRounds = getAllRounds();
const initialScores = Object.fromEntries(MODELS.map((m) => [m.name, 0]));
const initialStreaks: Record<string, number> = {};
const initialViewerScores = Object.fromEntries(MODELS.map((m) => [m.name, 0]));

let initialCompleted: RoundState[] = [];
if (allRounds.length > 0) {
for (const round of allRounds) {
if (round.scoreA !== undefined && round.scoreB !== undefined) {
const contAName = round.contestants[0].name;
const contBName = round.contestants[1].name;
if (round.scoreA > round.scoreB) {
initialScores[round.contestants[0].name] =
(initialScores[round.contestants[0].name] || 0) + 1;
initialScores[contAName] = (initialScores[contAName] || 0) + 1;
initialStreaks[contAName] = (initialStreaks[contAName] || 0) + 1;
initialStreaks[contBName] = 0;
} else if (round.scoreB > round.scoreA) {
initialScores[round.contestants[1].name] =
(initialScores[round.contestants[1].name] || 0) + 1;
initialScores[contBName] = (initialScores[contBName] || 0) + 1;
initialStreaks[contBName] = (initialStreaks[contBName] || 0) + 1;
initialStreaks[contAName] = 0;
} else {
initialStreaks[contAName] = 0;
initialStreaks[contBName] = 0;
}
}
const vvA = round.viewerVotesA ?? 0;
Expand All @@ -64,6 +72,7 @@ const gameState: GameState = {
completed: initialCompleted,
active: null,
scores: initialScores,
streaks: initialStreaks,
viewerScores: initialViewerScores,
done: false,
isPaused: false,
Expand Down Expand Up @@ -360,6 +369,7 @@ function getClientState() {
active: gameState.active,
lastCompleted: gameState.completed.at(-1) ?? null,
scores: gameState.scores,
streaks: gameState.streaks,
viewerScores: gameState.viewerScores,
done: gameState.done,
isPaused: gameState.isPaused,
Expand Down Expand Up @@ -647,6 +657,7 @@ const server = Bun.serve<WsData>({
gameState.completed = [];
gameState.active = null;
gameState.scores = Object.fromEntries(MODELS.map((m) => [m.name, 0]));
gameState.streaks = {};
gameState.viewerScores = Object.fromEntries(MODELS.map((m) => [m.name, 0]));
gameState.done = false;
gameState.isPaused = true;
Expand Down