diff --git a/django/play/consumers/game.py b/django/play/consumers/game.py index 088343e..5cd50e4 100644 --- a/django/play/consumers/game.py +++ b/django/play/consumers/game.py @@ -205,53 +205,96 @@ class GameConsumer(AsyncWebsocketConsumer): current_question = quiz_game.quiz_id.questions.all()[quiz_game.current_question_index] question_data = json.loads(current_question.data) target_location = question_data['target_location'] - tolerance_radius = question_data['tolerance_radius'] - print(f"target_location: {target_location}") - print(f"answer_location: {answer_location}") + tolerance_radius = question_data.get('tolerance_radius', 1000) # Default 1km if not set + + print(f"Raw target_location: {target_location}") + print(f"Raw answer_location: {answer_location}") print(f"tolerance_radius: {tolerance_radius}") - # Configuration - Make these easily adjustable - MAX_DISTANCE_KM = 1000 # Maximum distance for scoring (in km) - DIRECT_HIT_BONUS = 200 # Extra points for direct hit within tolerance + # Parse coordinates if they are strings + def parse_coords(coord): + if isinstance(coord, str): + try: + # Handle both comma and space separated coordinates + if ',' in coord: + lat, lng = map(float, [x.strip() for x in coord.split(',')]) + else: + # Try splitting by space if no comma + parts = coord.strip().split() + if len(parts) >= 2: + lat, lng = map(float, parts[:2]) + else: + raise ValueError("Invalid coordinate format") + return (lat, lng) + except (ValueError, AttributeError) as e: + print(f"Error parsing coordinates {coord}: {e}") + return None + elif isinstance(coord, (list, tuple)) and len(coord) >= 2: + return (float(coord[0]), float(coord[1])) + return None + + # Parse the coordinates + target_location = parse_coords(target_location) + answer_location = parse_coords(answer_location) - # Calculate base score - distance_km = geodesic(answer_location, target_location).kilometers - tolerance_km = int(tolerance_radius) / 1000 # Convert meters to kilometers - - # Calculate base score with non-linear decrease - if distance_km <= tolerance_km: - # Direct hit - full points + bonus - base_score = 1000 + DIRECT_HIT_BONUS # 1000 + 200 bonus - is_correct = True + if not target_location or not answer_location: + print("Invalid coordinates, cannot calculate score") + is_correct = False + score = 0 else: - if distance_km > MAX_DISTANCE_KM: - # Beyond max distance - 0 points - base_score = 0 - is_correct = False - else: - # Non-linear decrease using exponential falloff - # This creates a steeper drop-off at the beginning - progress = (distance_km - tolerance_km) / (MAX_DISTANCE_KM - tolerance_km) - # Using a power function to create non-linear falloff - falloff = progress ** 1.5 # 1.5 makes the falloff steeper at the beginning - base_score = 1000 * (1 - falloff) - is_correct = True - base_score = max(0, min(1000, int(base_score))) - - # Apply time penalty (reduces score based on time taken) - if is_correct and base_score > 0: - # Calculate time taken as a fraction of total time (0-1) - time_taken_sec = (int(current_question.time_per_question) * 1000 - time_remaining) / 1000 - max_time_sec = int(current_question.time_per_question) - time_fraction = min(1.0, time_taken_sec / max_time_sec) if max_time_sec > 0 else 1.0 + print(f"Parsed target_location: {target_location}") + print(f"Parsed answer_location: {answer_location}") - # Reduce points based on time taken (up to 50% reduction for answering at the last second) - time_penalty = base_score * 0.5 * time_fraction # Up to 50% penalty - final_score = max(1, base_score - time_penalty) # At least 1 point if correct - else: - final_score = base_score # 0 points for incorrect answers - - score = int(round(final_score)) + # Configuration - Make these easily adjustable + MAX_DISTANCE_KM = 1000 # Maximum distance for scoring (in km) + DIRECT_HIT_BONUS = 200 # Extra points for direct hit within tolerance + + try: + # Calculate distance + distance_km = geodesic(answer_location, target_location).kilometers + tolerance_km = float(tolerance_radius) / 1000 # Convert meters to kilometers + + print(f"Distance: {distance_km} km, Tolerance: {tolerance_km} km") + + # Calculate base score with non-linear decrease + if distance_km <= tolerance_km: + # Direct hit - full points + bonus + base_score = 1000 + DIRECT_HIT_BONUS + is_correct = True + else: + if distance_km > MAX_DISTANCE_KM: + # Beyond max distance - 0 points + base_score = 0 + is_correct = False + else: + # Non-linear decrease using exponential falloff + progress = (distance_km - tolerance_km) / (MAX_DISTANCE_KM - tolerance_km) + falloff = progress ** 1.5 # 1.5 makes the falloff steeper at the beginning + base_score = 1000 * (1 - falloff) + is_correct = True + + # Apply time penalty (reduces score based on time taken) + if is_correct and base_score > 0: + # Calculate time taken as a fraction of total time (0-1) + time_taken_sec = (int(current_question.time_per_question) * 1000 - time_remaining) / 1000 + max_time_sec = int(current_question.time_per_question) + time_fraction = min(1.0, time_taken_sec / max_time_sec) if max_time_sec > 0 else 1.0 + + # Reduce points based on time taken (up to 50% reduction for answering at the last second) + time_penalty = base_score * 0.5 * time_fraction # Up to 50% penalty + final_score = max(1, base_score - time_penalty) # At least 1 point if correct + else: + final_score = base_score # 0 points for incorrect answers + + # Apply max score limit after all calculations (including bonus) + final_score = min(final_score, 1200) # Cap at 1200 (1000 + 200 bonus) + + score = int(round(final_score)) + + except Exception as e: + print(f"Error calculating score: {e}") + is_correct = False + score = 0 # Save the answer answer_obj, created = QuizAnswer.objects.get_or_create(