1e51ef7def
- 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>
141 lines
5.9 KiB
Python
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())
|