Files
Adminator 1e51ef7def Nachtrag: alle bisher untracked Ordner + hängende Änderungen mit-committen
- Konzept/, didaktik_geografie/, didaktik_simulation/, v2-modules/, v2-platform/
- 12 code-workspace-Files
- STATUS-*.md
- viele M/D/R-Änderungen an bereits getrackten Files
- .gitignore verstärkt: **/.humaninput/, **/secret_keys.txt

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-07-08 02:27:02 +02:00

141 lines
5.9 KiB
Python

#!/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())