Resolve "Fragentyp: Ort suchen" #270

Merged
jvs merged 13 commits from 85-fragentyp-ort-suchen into master 2025-11-01 18:41:42 +00:00
Showing only changes of commit fa544d8228 - Show all commits

View File

@@ -205,53 +205,96 @@ class GameConsumer(AsyncWebsocketConsumer):
current_question = quiz_game.quiz_id.questions.all()[quiz_game.current_question_index] current_question = quiz_game.quiz_id.questions.all()[quiz_game.current_question_index]
question_data = json.loads(current_question.data) question_data = json.loads(current_question.data)
target_location = question_data['target_location'] target_location = question_data['target_location']
tolerance_radius = question_data['tolerance_radius'] tolerance_radius = question_data.get('tolerance_radius', 1000) # Default 1km if not set
print(f"target_location: {target_location}")
print(f"answer_location: {answer_location}") print(f"Raw target_location: {target_location}")
print(f"Raw answer_location: {answer_location}")
print(f"tolerance_radius: {tolerance_radius}") print(f"tolerance_radius: {tolerance_radius}")
# Configuration - Make these easily adjustable # Parse coordinates if they are strings
MAX_DISTANCE_KM = 1000 # Maximum distance for scoring (in km) def parse_coords(coord):
DIRECT_HIT_BONUS = 200 # Extra points for direct hit within tolerance 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
# Calculate base score # Parse the coordinates
distance_km = geodesic(answer_location, target_location).kilometers target_location = parse_coords(target_location)
tolerance_km = int(tolerance_radius) / 1000 # Convert meters to kilometers answer_location = parse_coords(answer_location)
# Calculate base score with non-linear decrease if not target_location or not answer_location:
if distance_km <= tolerance_km: print("Invalid coordinates, cannot calculate score")
# Direct hit - full points + bonus is_correct = False
base_score = 1000 + DIRECT_HIT_BONUS # 1000 + 200 bonus score = 0
is_correct = True
else: else:
if distance_km > MAX_DISTANCE_KM: print(f"Parsed target_location: {target_location}")
# Beyond max distance - 0 points print(f"Parsed answer_location: {answer_location}")
base_score = 0
# 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 is_correct = False
else: score = 0
# 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
# 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))
# Save the answer # Save the answer
answer_obj, created = QuizAnswer.objects.get_or_create( answer_obj, created = QuizAnswer.objects.get_or_create(