import { Triangle, Diamond, Circle, Square } from 'lucide-react'; import type { GameConfig, Player, PointsBreakdown } from './types'; export const COLORS = { red: 'bg-red-600', blue: 'bg-blue-600', yellow: 'bg-yellow-600', green: 'bg-green-600', purple: 'bg-theme-primary', purpleLight: 'bg-theme-primary/70', }; export const SHAPES = { triangle: Triangle, diamond: Diamond, circle: Circle, square: Square, }; export const BOT_NAMES = [ "QuizWiz", "FastFinger", "Brainiac", "KnowItAll", "Guesser", "LuckyStrike", "OwlFan", "Kahootie", "ZigZag", "Pixel" ]; export const QUESTION_TIME = 20; // seconds export const QUESTION_TIME_MS = 20000; // milliseconds export const POINTS_PER_QUESTION = 1000; export const calculateBasePoints = (timeLeftMs: number, questionTimeMs: number, maxPoints: number = POINTS_PER_QUESTION): number => { const responseTimeMs = questionTimeMs - timeLeftMs; const responseTimeSec = responseTimeMs / 1000; const questionTimeSec = questionTimeMs / 1000; if (responseTimeSec < 0.5) { return maxPoints; } return Math.round((1 - (responseTimeSec / questionTimeSec) / 2) * maxPoints); }; interface PointsCalculationParams { isCorrect: boolean; timeLeftMs: number; questionTimeMs: number; streak: number; playerRank: number; isFirstCorrect: boolean; config: GameConfig; currentQuestionIndex?: number; } export const calculatePointsWithBreakdown = (params: PointsCalculationParams): PointsBreakdown => { const { isCorrect, timeLeftMs, questionTimeMs, streak, playerRank, isFirstCorrect, config } = params; const breakdown: PointsBreakdown = { basePoints: 0, streakBonus: 0, comebackBonus: 0, firstCorrectBonus: 0, penalty: 0, total: 0, }; if (!isCorrect) { if (config.penaltyForWrongAnswer) { breakdown.penalty = Math.round(POINTS_PER_QUESTION * (config.penaltyPercent / 100)); breakdown.total = -breakdown.penalty; } return breakdown; } breakdown.basePoints = calculateBasePoints(timeLeftMs, questionTimeMs); let pointsAfterStreak = breakdown.basePoints; if (config.streakBonusEnabled && streak >= config.streakThreshold) { const streakCount = streak - config.streakThreshold; const multiplier = config.streakMultiplier + (streakCount * (config.streakMultiplier - 1)); pointsAfterStreak = Math.round(breakdown.basePoints * multiplier); breakdown.streakBonus = pointsAfterStreak - breakdown.basePoints; } const isFirstQuestion = params.currentQuestionIndex === 0 || params.currentQuestionIndex === undefined; if (config.comebackBonusEnabled && playerRank > 3 && !isFirstQuestion) { breakdown.comebackBonus = config.comebackBonusPoints; } if (config.firstCorrectBonusEnabled && isFirstCorrect) { breakdown.firstCorrectBonus = config.firstCorrectBonusPoints; } breakdown.total = pointsAfterStreak + breakdown.comebackBonus + breakdown.firstCorrectBonus; return breakdown; }; export const calculatePoints = (params: PointsCalculationParams): number => { return calculatePointsWithBreakdown(params).total; }; export const getPlayerRank = (playerId: string, players: Player[]): number => { const sorted = [...players].sort((a, b) => b.score - a.score); return sorted.findIndex(p => p.id === playerId) + 1; }; const GOLDEN_RATIO = 0.618033988749895; export const generatePlayerColor = (index: number): string => { const hue = ((index * GOLDEN_RATIO) % 1) * 360; const saturation = 70 + (index % 3) * 10; const lightness = 45 + (index % 2) * 10; return `hsl(${Math.round(hue)}, ${saturation}%, ${lightness}%)`; }; export const PLAYER_COLORS = Array.from({ length: 50 }, (_, i) => generatePlayerColor(i));