Weltküche: Heimat-Regel – Zutaten folgen dem Gericht statt Zufall

Löst "warum kommt Deutschland nicht vor?": In regionalen Gerichten kommt jede
Zutat, die das Herkunftsland selbst erzeugt, von dort (Tiroler Gröstl → Tiroler
Speck/Kartoffeln aus Österreich). Nur was hier nicht wächst (Pfeffer, Olivenöl)
reist von weit her – die eigentliche Lektion. Datengetrieben: Test "erzeugt das
Heimatland die Zutat?" filtert automatisch, kein Länder-Bias (hängt am Gericht).

- dishes.json: heimat (25 Gerichte) + signature-Labels (5); exotische ohne heimat
- buildTaskFor: Heimat-Regel sticht Zufall aus, wenn Heimatland Produzent ist
- Klick-Feedback beantwortet "warum nicht Deutschland?" direkt (showWrong/explainChoice)
- Auflösung erklärt Heimat-Logik + zeigt Antwort auch wenn kein Top-5-Produzent

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-07-19 15:56:55 +02:00
parent 7d3b3693db
commit 1c5356422c
2 changed files with 144 additions and 32 deletions
+78 -6
View File
@@ -984,6 +984,30 @@ function buildTaskFor(ingKey) {
if (_taskCache[ingKey]) return _taskCache[ingKey];
const ing = ING[ingKey];
if (!ing) return null;
// HEIMAT-REGEL: In einem regionalen Gericht kommt jede Zutat, die das
// Herkunftsland selbst erzeugt, von dort — logisch nachvollziehbar (ein
// Tiroler Gröstl nutzt Tiroler Speck, Tiroler Kartoffeln …). Der Test
// „erzeugt das Heimatland diese Zutat?" filtert automatisch: Österreich
// baut Kartoffeln an (→ Österreich), aber keinen Pfeffer und kein Olivenöl
// (→ dort greift die normale Welt-Logik weiter unten). Kein Länder-Bias,
// weil die Antwort am Gericht hängt, nicht am Standort der App.
const _hDish = current && current.dish;
const heimat = _hDish && _hDish.heimat;
if (heimat && C[heimat] && ing.producers && ing.producers[heimat]) {
const correct = [heimat];
const others = Object.entries(ing.producers)
.filter(([c]) => C[c] && c !== heimat)
.sort((a, b) => (b[1].t || 0) - (a[1].t || 0))
.map(([c]) => c);
const climatePossible = (ing.climate_possible || []).filter(c => C[c] && c !== heimat);
const distr = others.slice(0, 3);
if (climatePossible.length && distr.length < 3) distr.push(climatePossible[0]);
const choices = [...correct, ...distr];
shuffleInPlace(choices);
const label = (_hDish.signature && _hDish.signature[ingKey]) || null;
_taskCache[ingKey] = { correct, choices, possible: new Set(), signature: true, heimat, label };
return _taskCache[ingKey];
}
// Alle Producer (für Klassifizierung) und EU-relevante separat (für Korrekt-Auswahl).
// Eigenkonsum-Riesen (Indien-Bananen, USA-Zitronen etc.) sind zwar echte Produzenten,
// landen aber nicht in unseren Supermärkten — also nicht als „Spiel-Slot-Korrekt" ziehen.
@@ -1109,8 +1133,13 @@ function loadIngredient(){
const task = buildTaskFor(ingKey);
current.found=[]; current.wrongHere=0; current.wrongStreak=0; current.attempted=new Set();
const two = task && task.correct.length > 1;
const frage = two
? 'finde die <b>zwei</b> Herkunftsländer aus dieser Auswahl'
: (task && task.signature
? 'aus welchem Land stammt diese Zutat in <b>diesem Gericht</b>?'
: 'aus welchem Land kommt diese Zutat häufig zu uns auf den Markt?');
document.getElementById('taskBanner').innerHTML=
`🔎 Suche: <b>${ing.nm}</b> ${two?'finde die <b>zwei</b> Herkunftsländer aus dieser Auswahl':'aus welchem Land kommt diese Zutat häufig zu uns auf den Markt?'}`;
`🔎 Suche: <b>${ing.nm}</b> ${frage}`;
document.getElementById('resultCard').className="result-card";
drawMarkers(ing); renderIngList();
}
@@ -1194,9 +1223,18 @@ function producerEconHTML(ingKey, correctKeys) {
if (!ing || !ing.producers) return '';
const all = Object.entries(ing.producers).filter(([c]) => C[c]);
all.sort((a,b) => (b[1].t||0) - (a[1].t||0));
const top5 = all.slice(0, 5);
let html = '<div class="econ-list"><div class="rlabel">Weltweite Hauptproduzenten</div>';
top5.forEach(([c, info]) => {
const shown = all.slice(0, 5);
// Richtige Antwort(en) immer zeigen — auch wenn das Heimatland kein Top-5-Produzent ist
correctKeys.forEach(ck => {
if (!shown.some(([c]) => c === ck)) {
const row = all.find(([c]) => c === ck);
if (row) shown.push(row);
}
});
const sig = _taskCache[ingKey] && _taskCache[ingKey].signature;
let html = '<div class="econ-list"><div class="rlabel">' +
(sig ? 'Die größten Produzenten zum Vergleich' : 'Weltweite Hauptproduzenten') + '</div>';
shown.forEach(([c, info]) => {
const isMatch = correctKeys.includes(c);
const cm = C[c];
const amount = info.t ? fmtTonnes(info.t) : (info.note || 'kulturell verbreitet');
@@ -1207,8 +1245,12 @@ function producerEconHTML(ingKey, correctKeys) {
<span class="prod-val">${amount}${share}</span>
</div>`;
});
if (all.length > 5) {
html += `<div class="prod-more">+ ${all.length - 5} weitere Länder</div>`;
const rest = all.length - shown.length;
if (rest > 0) {
html += `<div class="prod-more">+ ${rest} weitere Länder</div>`;
}
if (sig) {
html += `<div class="prod-more" style="color:#4a7c4e;font-weight:600">Der größte Produzent ist nicht automatisch die Antwort hier zählt, woher die Zutat für dieses Gericht kommt.</div>`;
}
html += '</div>';
return html;
@@ -1226,6 +1268,19 @@ function explainChoice(ingKey, key, isCorrect){
const info = ing && ing.producers && ing.producers[key];
const amount = info && info.t ? fmtTonnes(info.t) : '';
const animal = isAnimalIng(ingKey);
// Signatur-Zutat (Heimat-Regel): Antwort folgt dem Gericht
const sTask = _taskCache[ingKey];
if (sTask && sTask.signature) {
const heimatN = C[sTask.heimat] ? C[sTask.heimat].n : '';
const was = sTask.label || ('der/die/das ' + ing.nm);
if (isCorrect) {
return {cls:'ok', badge:'✓', text:`Dieses Gericht kommt aus <b>${heimatN}</b> ${was} ist hier heimisch und wird vor Ort erzeugt.`};
}
if (info) {
return {cls:'mid', badge:'⚠️', text:`${C[key].n} erzeugt zwar viel ${ing.nm}${amount?' ('+amount+')':''} aber in einem Gericht aus ${heimatN} stammt ${was} von dort.`};
}
// Nicht-Produzent-Fehlklick → normale „unmöglich"-Logik unten
}
if (isCorrect){
if (info && info.market === 'world-far'){
return {cls:'ok', badge:'✓', text:`<b>Welt-Hauptproduzent</b> (${amount}) — kommt aber selten in den AT-Handel.`};
@@ -1322,6 +1377,10 @@ function showSolved(ing,pts,bonus){
const flags=task.correct.map(k=>C[k].f+' '+C[k].n).join(' & ');
card.className="result-card ok show";
const factLine = ing.fact ? `<div class="more-line"><b>Wusstest du?</b> ${esc(ing.fact)}</div>` : '';
// Heimat-Regel: erklären, warum die Region die Antwort ist (nicht der größte Produzent)
const sigLine = (task.signature && C[task.heimat])
? `<div class="more-line" style="background:#eef5ec;border-left:3px solid #4a7c4e">🍽️ Das ist ein Gericht aus <b>${C[task.heimat].n}</b>. ${task.label ? ('<b>'+task.label+'</b>') : ('Der/die/das '+esc(ing.nm))} kommt von hier nur Zutaten, die bei uns gar nicht wachsen, reisen von weit her.</div>`
: '';
// Caveats für world-far/off-season Korrekt-Picks (pro Land separat)
const caveats = task.correct.map(k => {
const info = ing.producers && ing.producers[k];
@@ -1338,6 +1397,7 @@ function showSolved(ing,pts,bonus){
</div>`;
}).filter(Boolean).join('');
card.innerHTML=`<div class="rc-head">✓ Geschafft! ${esc(ing.nm)} kommt aus: ${flags}</div>
${sigLine}
${allChoicesHTML(ingKey, task, current.attempted)}
${producerEconHTML(ingKey, task.correct)}
${caveats}
@@ -1362,6 +1422,18 @@ function showWrong(key, kind, ingKey){
const c=C[key],card=document.getElementById('resultCard');
const ing = ING[ingKey];
let head, note;
// Signatur-Zutat: „Warum nicht Deutschland?" sofort beantworten
const sTask = _taskCache[ingKey];
if (sTask && sTask.signature && ing.producers && ing.producers[key]) {
card.className="result-card mid show";
const info = ing.producers[key];
const amount = info && info.t ? fmtTonnes(info.t) : 'verbreitet';
const heimatN = C[sTask.heimat] ? C[sTask.heimat].n : '';
const was = sTask.label || ('der/die/das ' + ing.nm);
card.innerHTML = `<div class="rc-head">⚠️ ${c.f} ${c.n} erzeugt zwar viel ${ing.nm} (${amount}) aber dieses Gericht kommt aus ${heimatN}, und ${was} stammt von dort.</div>`+
`<div class="note">${c.info}<br><br>🔎 Tipp: Achte auf den Namen des Gerichts er verrät oft die Herkunft.</div>`;
return;
}
if (kind === 'producer-eu-elsewhere') {
card.className="result-card mid show";
const info = ing.producers[key];