kaboot/constants.ts

113 lines
3.6 KiB
TypeScript

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));