#!/usr/bin/env python3 """ Fluggesellschaft — Quiz-Fragen-Generator. Pro Stadt werden ZWEI Fragen erzeugt: question: "In welchem Land liegt X?" (analog Busfahrt) questionContinent: "Auf welchem Kontinent liegt X?" (neu, weltweit relevant) Die Engine wird pro Auftrag eine der beiden Fragen zufällig (oder deterministisch pro Auftrags-ID) wählen. Deterministischer RNG via mulberry32-Hash auf city.id — stabile Re-Runs. """ import json import sys from pathlib import Path ROOT = Path(__file__).resolve().parents[1] / "assets" / "data" CITIES_PATH = ROOT / "cities.json" CONTINENTS = ["Europa", "Asien", "Afrika", "Nordamerika", "Südamerika", "Ozeanien", "Antarktis"] def mulberry32(seed): state = [seed & 0xFFFFFFFF] def nx(): state[0] = (state[0] + 0x6D2B79F5) & 0xFFFFFFFF t = state[0] r = ((t ^ (t >> 15)) * (1 | t)) & 0xFFFFFFFF r = ((r + ((r ^ (r >> 7)) * (61 | r))) & 0xFFFFFFFF) ^ r return ((r ^ (r >> 14)) & 0xFFFFFFFF) / 4294967296.0 return nx def slug_hash(s): h = 2166136261 for ch in s: h = (h ^ ord(ch)) & 0xFFFFFFFF h = (h * 16777619) & 0xFFFFFFFF return h # Heuristik: Kontinent aus Land ableiten (mehr Genauigkeit als auf lat/lon basieren) COUNTRY_CONTINENT = { # Europa (alle Bus-Länder) "Österreich": "Europa", "Deutschland": "Europa", "Schweiz": "Europa", "Italien": "Europa", "Frankreich": "Europa", "Spanien": "Europa", "Portugal": "Europa", "Belgien": "Europa", "Niederlande": "Europa", "Luxemburg": "Europa", "Polen": "Europa", "Tschechien": "Europa", "Slowakei": "Europa", "Ungarn": "Europa", "Slowenien": "Europa", "Kroatien": "Europa", "Bosnien und Herzegowina": "Europa", "Serbien": "Europa", "Montenegro": "Europa", "Nordmazedonien": "Europa", "Albanien": "Europa", "Bulgarien": "Europa", "Rumänien": "Europa", "Griechenland": "Europa", "Türkei": "Europa", "Vereinigtes Königreich": "Europa", "Großbritannien": "Europa", "Irland": "Europa", "Dänemark": "Europa", "Schweden": "Europa", "Norwegen": "Europa", "Finnland": "Europa", "Island": "Europa", "Estland": "Europa", "Lettland": "Europa", "Litauen": "Europa", "Ukraine": "Europa", "Belarus": "Europa", "Moldawien": "Europa", "Russland": "Europa", "Liechtenstein": "Europa", "Monaco": "Europa", "Malta": "Europa", "Zypern": "Europa", "Andorra": "Europa", "San Marino": "Europa", "Vatikan": "Europa", # Nordamerika "USA": "Nordamerika", "Vereinigte Staaten": "Nordamerika", "Kanada": "Nordamerika", "Mexiko": "Nordamerika", "Kuba": "Nordamerika", # Südamerika "Brasilien": "Südamerika", "Argentinien": "Südamerika", "Ecuador": "Südamerika", "Kolumbien": "Südamerika", "Peru": "Südamerika", "Bolivien": "Südamerika", "Chile": "Südamerika", "Venezuela": "Südamerika", "Uruguay": "Südamerika", "Paraguay": "Südamerika", # Afrika "Marokko": "Afrika", "Tunesien": "Afrika", "Ägypten": "Afrika", "Senegal": "Afrika", "Nigeria": "Afrika", "Kenia": "Afrika", "Äthiopien": "Afrika", "Südafrika": "Afrika", "Algerien": "Afrika", "Libyen": "Afrika", "Ghana": "Afrika", # Asien "Japan": "Asien", "China": "Asien", "Indien": "Asien", "Thailand": "Asien", "Singapur": "Asien", "Südkorea": "Asien", "Indonesien": "Asien", "Vietnam": "Asien", "Philippinen": "Asien", "Malaysia": "Asien", "Iran": "Asien", "Saudi-Arabien": "Asien", "Vereinigte Arabische Emirate": "Asien", # Ozeanien "Australien": "Ozeanien", "Neuseeland": "Ozeanien", } def pick_distractors(seed_str, correct, pool): pool = [p for p in pool if p != correct] rng = mulberry32(slug_hash(seed_str)) shuffled = pool[:] for i in range(len(shuffled)-1, 0, -1): j = int(rng() * (i+1)) shuffled[i], shuffled[j] = shuffled[j], shuffled[i] return shuffled[:2] def build_country_question(city, all_countries): distractors = pick_distractors(city["id"], city["country"], all_countries) rng = mulberry32(slug_hash(city["id"] + "_pos")) pos = int(rng() * 3) choices = distractors[:] choices.insert(pos, city["country"]) return { "prompt": {"de": f"In welchem Land liegt {city['title']}?", "easy": f"In welchem Land liegt {city['title']}?"}, "choices": choices, "correctIndex": pos, } def build_continent_question(city): correct = city.get("continent") or COUNTRY_CONTINENT.get(city["country"], "Europa") pool = [c for c in CONTINENTS if c != correct] rng = mulberry32(slug_hash(city["id"] + "_cont")) shuffled = pool[:] for i in range(len(shuffled)-1, 0, -1): j = int(rng() * (i+1)) shuffled[i], shuffled[j] = shuffled[j], shuffled[i] distractors = shuffled[:2] pos = int(mulberry32(slug_hash(city["id"] + "_cpos"))() * 3) choices = distractors[:] choices.insert(pos, correct) return { "prompt": {"de": f"Auf welchem Kontinent liegt {city['title']}?", "easy": f"Auf welchem Kontinent liegt {city['title']}?"}, "choices": choices, "correctIndex": pos, } def main(): with CITIES_PATH.open(encoding="utf-8") as f: cities = json.load(f) all_countries = sorted({c["country"] for c in cities}) print(f"Cities: {len(cities)}, Countries: {len(all_countries)}") # Continent-Property setzen falls fehlt for c in cities: if not c.get("continent"): c["continent"] = COUNTRY_CONTINENT.get(c["country"], "Europa") added = 0 for c in cities: if "question" not in c: c["question"] = build_country_question(c, all_countries) added += 1 if "questionContinent" not in c: c["questionContinent"] = build_continent_question(c) with CITIES_PATH.open("w", encoding="utf-8") as f: json.dump(cities, f, ensure_ascii=False, indent=2) f.write("\n") print(f"OK -- {added} Land-Fragen + Kontinent-Fragen ergaenzt.") if __name__ == "__main__": sys.exit(main())