From 423490260d370ee5d8bf1477b7d3b1b93304f8a3 Mon Sep 17 00:00:00 2001 From: Thomas Date: Fri, 21 Aug 2026 18:21:39 +0200 Subject: [PATCH] =?UTF-8?q?Weltkueche=20Teil=20B:=20Consumer-Anker=20(Herk?= =?UTF-8?q?unft=20folgt=20dem=20User-Land)=20=E2=80=94=20Pilot=20Ei/Kartof?= =?UTF-8?q?fel/Semmelbroesel?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Grundzutaten, die im Land des Users heimisch sind, kommen jetzt aus dem Land des USERS statt aus dem Gericht-Land (Carbonara-Ei in AT = AT-Ei). Opt-in pro Zutat ueber neues Feld `heimisch`; Signaturzutaten bleiben am Gericht. game.html: - buildTaskFor: neuer Consumer-Zweig (vor Heimat-Regel). correct = [User-Land, ...market], Distraktoren explizit (`distractors`) oder berechnet. Immer 5 Karten. - _wkUser(): WK_COUNTRY (AT/DE/CH/LI) -> Laender-Slug, Fallback AT. - isWorldIngredient: Zutat mit `heimisch` wird immer abgefragt (z.B. Semmelbroesel). - ING-Build: heimisch/market/distractors werden aus der Matrix mitkopiert (wurden vorher weggelassen -> Felder kamen nie im Spiel an). - Aufloesungstext: Consumer-Fall ("kaufst du, wo du einkaufst - im eigenen Land"). Daten (production-matrix.json), Thomas-Vorgaben: - eier: heimisch AT/DE/CH, market DE+NL -> AT-User: AT+DE+NL, DE-User: DE+NL - kartoffel: heimisch AT/DE/CH, market DE -> AT+DE (alle Gerichte) - semmelbroesel: heimisch AT/DE, market DE, distractors GR/TR/EG/TN/IE; jetzt abgefragt admin-weltkueche.html: Consumer-Zweig gespiegelt + AT/DE/CH-Umschalter. Nebenbefund (NICHT angefasst): correct_for wird im Spiel nie gelesen (nur Admin las es) -> mehl/majoran/safran/feta-lamm-Kuratierung wirkte bisher nur im Admin. Co-Authored-By: Claude Opus 4.8 --- App/admin-weltkueche.html | 26 +++++++- .../assets/data/production-matrix.json | 42 +++++++++---- App/sims/weltkueche/game.html | 60 ++++++++++++++++++- 3 files changed, 112 insertions(+), 16 deletions(-) diff --git a/App/admin-weltkueche.html b/App/admin-weltkueche.html index 63ee46f..574f085 100644 --- a/App/admin-weltkueche.html +++ b/App/admin-weltkueche.html @@ -65,6 +65,12 @@ Europa Exotisch +
Lade Daten…
@@ -74,6 +80,8 @@ var BASE = location.pathname.replace(/\/admin-weltkueche\.html$/, ''); var D = 'sims/weltkueche/assets/data/'; var C = {}, PM = {}, DISHES = []; var _cat = ''; +var WK_USER = 'oesterreich'; // Teil B: Land des Users (Consumer-Anker) — via Umschalter +function setUser(v){ WK_USER = v || 'oesterreich'; render(); } // ---- Spiel-Logik gespiegelt (siehe game.html buildTaskFor) ---- function mrank(m){ return m === 'eu' ? 0 : (m === 'world-far' || m === 'local') ? 2 : 1; } @@ -87,10 +95,22 @@ function wkDist(a, b){ } // Klassifiziert alle Produzenten einer Zutat für ein Gericht. // Gibt { correct:[keys], distractors:[keys], branch } zurück (Pools, ohne Zufalls-Ziehung). -function classify(ingKey, heimat){ +function classify(ingKey, heimat, isSig){ var ing = PM[ingKey]; if (!ing) return null; var producers = ing.producers || {}; var keys = Object.keys(producers).filter(function(c){ return C[c]; }); + // ── CONSUMER-REGEL (Teil B): Grundzutat, im Land des Users heimisch → kommt aus + // dem Land des Users (unabhängig vom Gericht-Land). Signaturzutaten ausgenommen. + if (!isSig && ing.heimisch && ing.heimisch.indexOf(WK_USER) >= 0 && C[WK_USER]) { + var cmarket = (ing.market||[]).filter(function(c){ return C[c] && c !== WK_USER; }); + var ccorrect = [WK_USER].concat(cmarket).slice(0,3); + if (ccorrect.length < 2){ for (var hi=0; hi<(ing.heimisch||[]).length; hi++){ var hh=ing.heimisch[hi]; if(C[hh]&&ccorrect.indexOf(hh)<0){ccorrect.push(hh);break;} } } + var cdistr; + if (ing.distractors && ing.distractors.length){ cdistr = ing.distractors.filter(function(c){ return C[c] && ccorrect.indexOf(c)<0; }); } + else { var cfar = keys.filter(function(c){ var mm=producers[c].market; return (mm==='world-far'||mm==='local') && ccorrect.indexOf(c)<0; }).sort(function(a,b){ return (producers[b].t||0)-(producers[a].t||0); }); + var cclim = (ing.climate_possible||[]).filter(function(c){ return C[c] && ccorrect.indexOf(c)<0; }); cdistr = cfar.concat(cclim); } + return { correct: ccorrect, distractors: cdistr, branch:'consumer', grav:function(){return 0;} }; + } // Heimat-Zweig: Heimat erzeugt die Zutat selbst if (heimat && C[heimat] && producers[heimat]) { var grav = function(c){ return (producers[c].t || 0) / wkDist(heimat, c); }; @@ -143,7 +163,7 @@ function render(){ var rows = ''; var dishHasWarn = false; ings.forEach(function(k){ if (q && (d.name||'').toLowerCase().indexOf(q)<0 && (PM[k].nm||k).toLowerCase().indexOf(q)<0) return; - var cl = classify(k, heimat); if(!cl) return; + var cl = classify(k, heimat, !!(d.signature && d.signature[k])); if(!cl) return; // Auffällig NUR im Heimat-Zweig: die Zutat ist lokal erzeugbar (Heimat baut // sie an), trotzdem ist ein „richtiges" Land > 1500 km weg → echter Ausreißer. // Welt-Zutaten (Pfeffer aus Vietnam etc.) sind zu Recht fern → keine Warnung. @@ -155,7 +175,7 @@ function render(){ if (onlyWarn && !warn) return; var okChips = cl.correct.map(function(c){ return ctyChip(c,'ok',k,heimat,true); }).join(''); var distChips = cl.distractors.slice(0,6).map(function(c){ return ctyChip(c,'dist',k,heimat,false); }).join(''); - rows += ''+(PM[k].ic||'')+' '+(PM[k].nm||k)+''+(cl.branch==='heimat'?'Heimat-Regel':'Welt-Zutat')+'' + rows += ''+(PM[k].ic||'')+' '+(PM[k].nm||k)+''+(cl.branch==='consumer'?'🛒 Consumer-Regel (User-Land)':(cl.branch==='heimat'?'Heimat-Regel':'Welt-Zutat'))+'' + '
richtig
'+(okChips||'—')+warn+'' + '
Distraktoren (Pool)
'+(distChips||'—')+''; }); diff --git a/App/sims/weltkueche/assets/data/production-matrix.json b/App/sims/weltkueche/assets/data/production-matrix.json index d91b292..9b966c5 100644 --- a/App/sims/weltkueche/assets/data/production-matrix.json +++ b/App/sims/weltkueche/assets/data/production-matrix.json @@ -3485,12 +3485,15 @@ "kenia" ], "fact": "Eier können prinzipiell überall produziert werden — Hühner sind extrem anpassungsfähig. Die meisten Eier in Europa werden lokal gegessen.", - "correct_for": { - "oesterreich": [ - "deutschland", - "niederlande" - ] - } + "heimisch": [ + "oesterreich", + "deutschland", + "schweiz" + ], + "market": [ + "deutschland", + "niederlande" + ] }, "fischsauce": { "nm": "Fischsauce", @@ -3989,11 +3992,14 @@ "kolumbien" ], "fact": "Die Kartoffel kommt ursprünglich aus den Anden und wurde erst nach 1565 nach Europa gebracht. In Belarus isst eine Person ~180 kg pro Jahr — Welt-Spitze.", - "correct_for": { - "oesterreich": [ - "schweiz" - ] - } + "heimisch": [ + "oesterreich", + "deutschland", + "schweiz" + ], + "market": [ + "deutschland" + ] }, "zwiebel": { "nm": "Zwiebel", @@ -4602,6 +4608,20 @@ "market": "eu" } }, + "heimisch": [ + "oesterreich", + "deutschland" + ], + "market": [ + "deutschland" + ], + "distractors": [ + "griechenland", + "tuerkei", + "aegypten", + "tunesien", + "irland" + ], "fact": "Getrocknetes, geriebenes Weißbrot — ein lokales Nebenprodukt aus der Bäckerei." }, "oel": { diff --git a/App/sims/weltkueche/game.html b/App/sims/weltkueche/game.html index d49845f..aa3f56a 100644 --- a/App/sims/weltkueche/game.html +++ b/App/sims/weltkueche/game.html @@ -534,7 +534,10 @@ async function loadWeltkuecheData() { producers: v.producers || {}, climate_possible: v.climate_possible || [], fact: v.fact || '', - more: v.more || '' + more: v.more || '', + heimisch: v.heimisch || null, // Teil B: Länder mit Eigenanteil ≥ ⅓ (Consumer-Anker) + market: v.market || null, // Teil B: kuratierte Markt-Zweitlieferanten + distractors: v.distractors || null // Teil B: explizite Distraktor-Länder }; }); DISHES = Object.entries(dishesJ) @@ -975,10 +978,19 @@ function worldIngCount(dish){ * wird im Game als "lokal verfügbar" angezeigt, aber nicht erforscht. */ function isWorldIngredient(key) { const ing = ING[key]; if (!ing) return false; + if (ing.heimisch && ing.heimisch.length) return true; // Consumer-Grundzutat (Teil B): wird immer abgefragt const real = Object.values(ing.producers || {}).filter(p => p.t && p.t > 0); return real.length >= 3; } +// Land des Users (aus teachers.country → WK_COUNTRY) auf den Länder-Slug mappen. +// Fallback AT. LI nutzt die Schweizer Markt-Logik (Zollunion, CHF). +function _wkUser() { + const map = { AT: 'oesterreich', DE: 'deutschland', CH: 'schweiz', LI: 'schweiz' }; + const raw = (typeof window !== 'undefined' && window.WK_COUNTRY) ? window.WK_COUNTRY : 'AT'; + return map[raw] || (C[raw] ? raw : 'oesterreich'); +} + /* ====================== AUFGABEN-GENERIERUNG ====================== * Neue Architektur (ab production-matrix.json): * @@ -1055,6 +1067,47 @@ function buildTaskFor(ingKey) { if (_taskCache[ingKey]) return _taskCache[ingKey]; const ing = ING[ingKey]; if (!ing) return null; + // ── CONSUMER-REGEL (Teil B): Eine Grundzutat, die im Land des USERS heimisch + // ist (Eigenanteil ≥ ~⅓), kauft man dort, wo man einkauft — im eigenen Land, + // UNABHÄNGIG davon, aus welchem Land das Gericht stammt. (Das Ei in der + // Carbonara ist in Österreich ein AT-Ei, kein italienisches.) Opt-in per + // `heimisch`-Feld; SIGNATURzutaten (dish.signature) sind ausgenommen und + // bleiben an der Herkunftsregion des Gerichts (bewusster Original-Kauf). + const _cDish = current && current.dish; + const _isSig = !!(_cDish && _cDish.signature && _cDish.signature[ingKey]); + const _user = _wkUser(); + if (!_isSig && ing.heimisch && ing.heimisch.includes(_user) && C[_user]) { + // Lösung 1 = Land des Users; Lösung 2(+3) = kuratierte Markt-Zweitlieferanten (`market`). + const market = (ing.market || []).filter(c => C[c] && c !== _user); + const correct = [_user, ...market].slice(0, 3); + if (correct.length < 2) { // Garantie: es gibt IMMER eine zweite Wahl + for (const c of (ing.heimisch || [])) { if (C[c] && !correct.includes(c)) { correct.push(c); break; } } + } + // Distraktoren: explizit kuratiert (`distractors`) ODER berechnet (fern + klimafremd). + let pool; + if (ing.distractors && ing.distractors.length) { + pool = ing.distractors.filter(c => C[c] && !correct.includes(c)); + } else { + const far = Object.entries(ing.producers || {}) + .filter(([c, i]) => C[c] && !correct.includes(c) && (i.market === 'world-far' || i.market === 'local')) + .sort((a, b) => (b[1].t || 0) - (a[1].t || 0)).map(([c]) => c); + const climate = (ing.climate_possible || []).filter(c => C[c] && !correct.includes(c)); + const imposs = impossibleCountries(correct, [...far, ...climate, ...Object.keys(ing.producers || {})]); + pool = [...far, ...climate, ...imposs]; + } + shuffleInPlace(pool); + const target = Math.max(0, 5 - correct.length); + const distr = []; + for (const c of pool) { if (distr.length >= target) break; if (!distr.includes(c) && !correct.includes(c)) distr.push(c); } + const choices = [...correct, ...distr]; + shuffleInPlace(choices); + _taskCache[ingKey] = { + correct, choices, + possible: new Set(distr.filter(c => (ing.climate_possible || []).includes(c))), + consumer: true, user: _user + }; + return _taskCache[ingKey]; + } // 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 @@ -1533,7 +1586,10 @@ function showSolved(ing,pts,bonus){ card.className="result-card ok show"; const factLine = ing.fact ? `
Wusstest du? ${esc(ing.fact)}
` : ''; // Heimat-Regel: erklären, warum die Region die Antwort ist (nicht der größte Produzent) - const sigLine = (task.signature && C[task.heimat]) + const _userN = task.user && C[task.user] ? (C[task.user].f + ' ' + C[task.user].n) : 'bei uns'; + const sigLine = task.consumer + ? `
🛒 Eine Grundzutat — die kaufst du dort, wo du einkaufst: im eigenen Land (${_userN}). Reicht die heimische Menge nicht, kommt ein naher Nachbar dazu. Nur was bei uns gar nicht wächst, reist von weit her — und was ein Gericht besonders macht, holt man aus der Herkunftsregion.
` + : (task.signature && C[task.heimat]) ? (task.label ? `
🍽️ ${task.label} gehört typisch zu diesem Gericht — solche charakteristischen Zutaten holt man von dort. Grundzutaten dagegen kauft man einfach in der Nähe.
` : `
🍽️ Eine Grundzutat: was auch bei uns wächst, kommt aus der Nähe — heimisch, dazu ein naher europäischer Lieferant, wenn es allein nicht reicht. Nur was bei uns gar nicht wächst, reist von weit her.
`)