diff --git a/server/src/adminApi.ts b/server/src/adminApi.ts index 6819698..ec1bb5b 100644 --- a/server/src/adminApi.ts +++ b/server/src/adminApi.ts @@ -809,36 +809,136 @@ const ACTION_EVENTS = [ 'p1_propose','p1_no_offer','p2_snatch','p2_accept','p2_force','p2_no_force','p2_reject','p1_shame','p1_no_shame','p1_report','p1_no_report' ]; +// Calculate scores based on game variant and role +function calculateScore(pavoTokens: number, eloteTokens: number, role: string): number { + if (role === 'P1') { + return pavoTokens * 1 + eloteTokens * 2; + } else if (role === 'P2') { + return pavoTokens * 2 + eloteTokens * 1; + } + return 0; +} + +// Check if event triggers score calculation for the game variant +function isScoreEvent(kind: string, gameVariant: string): boolean { + const variant = (gameVariant || '').toUpperCase(); + + switch (variant) { + case 'G1': + case 'G2': + case 'G5': + return ['p1_no_offer', 'p2_accept', 'p2_reject', 'p2_snatch'].includes(kind); + case 'G3': + return ['p1_no_offer', 'p2_accept', 'p2_reject', 'p1_shame', 'p1_no_shame'].includes(kind); + case 'G4': + return ['p1_no_offer', 'p2_accept', 'p2_reject', 'p1_report', 'p1_no_report'].includes(kind); + default: + return false; + } +} + async function sendPlayersActionsUpdate(client?: Response) { try { const nameManager = NameManager.getInstance(); const uuids = nameManager.getAllKnownUuids?.() || []; + const players = uuids.map((uuid: string) => { const history = nameManager.getSystemHistory(uuid) || []; const counts: any = Object.fromEntries(ACTION_EVENTS.map(k => [k, 0])); const detailedHistory: any[] = []; + // Track room scores for this specific player + const playerRoomScoreMap = new Map>(); + + // Track round/variant/increment combinations per room for this player + const roomGameTracker = new Map>(); // roomId -> "round-variant" -> increment + + // First pass: process all events for score calculation for (const entry of history) { const kind = (entry as any)?.kind || ''; + const roomId = (entry as any)?.roomId; + const round = (entry as any)?.round; + const gameVariant = (entry as any)?.gameVariant || (entry as any)?.variant; + const role = (entry as any)?.role; + const pavoTokens = (entry as any)?.pavoTokens || 0; + const eloteTokens = (entry as any)?.eloteTokens || 0; + + // Process score calculation if this is a score-triggering event + if (roomId && round && gameVariant && isScoreEvent(kind, gameVariant) && (pavoTokens > 0 || eloteTokens > 0)) { + // Track game progression for increment calculation + if (!roomGameTracker.has(roomId)) { + roomGameTracker.set(roomId, new Map()); + } + + const gameKey = `${round}-${gameVariant}`; + const currentIncrement = roomGameTracker.get(roomId)!.get(gameKey) || 0; + roomGameTracker.get(roomId)!.set(gameKey, currentIncrement + 1); + + // Calculate score + const score = calculateScore(pavoTokens, eloteTokens, role); + + // Add to this player's room score history + if (!playerRoomScoreMap.has(roomId)) { + playerRoomScoreMap.set(roomId, []); + } + + playerRoomScoreMap.get(roomId)!.push({ + round: parseInt(round), + variant: gameVariant, + increment: currentIncrement + 1, + score, + role + }); + } + } + + // Second pass: process action events for counts and detailed history + for (const entry of history) { + const kind = (entry as any)?.kind || ''; + const roomId = (entry as any)?.roomId; + const round = (entry as any)?.round; + const gameVariant = (entry as any)?.gameVariant || (entry as any)?.variant; + const role = (entry as any)?.role; + if (!ACTION_EVENTS.includes(kind)) continue; - if (!isActionMade(kind, (entry as any)?.role)) continue; + if (!isActionMade(kind, role)) continue; counts[kind] = (counts[kind] || 0) + 1; // Include detailed event info detailedHistory.push({ kind, - round: (entry as any)?.round, - gameVariant: (entry as any)?.gameVariant || (entry as any)?.variant, - roomId: (entry as any)?.roomId + round, + gameVariant, + roomId }); } + + // Build RoomScoreHistory array for this player + const roomScoreHistory = Array.from(playerRoomScoreMap.entries()).map(([roomId, scores]) => ({ + roomId, + scores: scores.sort((a, b) => { + // Sort by round, then variant, then increment + if (a.round !== b.round) return a.round - b.round; + if (a.variant !== b.variant) return a.variant.localeCompare(b.variant); + return a.increment - b.increment; + }) + })); + const total = ACTION_EVENTS.reduce((acc, k) => acc + (counts[k] || 0), 0); return { uuid, name: nameManager.getPlayerName(uuid) || null, counts, total, - detailedHistory + detailedHistory, + rawHistory: history, + roomScoreHistory }; }).filter((p: any) => p.total > 0 || p.name);