bfc90b78de
Alle $MODULE_HIGHLIGHTS gegen echte Daten (Klasse 102) verifiziert und auf die tatsaechlichen Feldnamen umgestellt (viele waren veraltet/leer, z.B. farmer, sonnensystem, energiemanager, busfahrt, logistik, heli, tourismus*). - kofferdetektiv NEU: Geloest-Quote, Erstversuch, Fehlversuche, Oe Hinweise - weltkueche/fluss NEU - fluggesellschaft NEU (aus Submit-Schema) - Berechnete Quoten via num/den -> Prozent (Erfolgsquote statt Rohzahl): kofferdetektiv, weltkueche, busfahrt, logistik, energiemanager, eu-werkstatt, sonnensystem, tourismustal, fluggesellschaft - heli: Planung/Start/Landung/Level; sim-metrics: Level + Oe Hinweise ergaenzt Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
186 lines
8.6 KiB
JavaScript
186 lines
8.6 KiB
JavaScript
/* ============================================================================
|
|
* GeoGraSim — gemeinsames Sim-Kennzahlen-Schema
|
|
* ----------------------------------------------------------------------------
|
|
* Pro Simulation eine KURATIERTE, menschlich benannte Auswahl der wirklich
|
|
* relevanten Ergebnis-/Live-Felder mit Anzeigetyp (Sterne, Prozentbalken,
|
|
* Verhältnis, Geld, Temperatur, Zahl, Text, Ja/Nein). Alles NICHT Gelistete
|
|
* (technische Felder wie activeMs, phase, dishId …) wird ausgeblendet.
|
|
*
|
|
* renderSimMetrics(simId, params) → HTML-Grid mit grafischen Kacheln.
|
|
* Wiederverwendbar für Drilldown UND Ergebnis-Tabellen.
|
|
*
|
|
* Typen: stars | pct | pctInv | ratio(+t) | money | temp | num(+u) | text | bool | boolInv
|
|
* ==========================================================================*/
|
|
(function (root) {
|
|
'use strict';
|
|
|
|
var SIM_METRICS = {
|
|
klima: [
|
|
{k:'stars',type:'stars',l:'Sterne'},
|
|
{k:'final_temperature',type:'temp',l:'Temperatur'},
|
|
{k:'final_flooded_pct',type:'pctInv',l:'Überflutung'},
|
|
{k:'final_co2',type:'num',l:'CO₂',u:'ppm',dir:'lo'},
|
|
{k:'final_budget',type:'money',l:'Budget'},
|
|
{k:'final_population',type:'num',l:'Bevölkerung'},
|
|
{k:'measures_bought',type:'num',l:'Maßnahmen'}
|
|
],
|
|
heli: [
|
|
{k:'sterneGesamt',type:'stars',l:'Sterne'},
|
|
{k:'stars',type:'stars',l:'Sterne'},
|
|
{k:'planPct',type:'pct',l:'Planung'},
|
|
{k:'startScore',type:'starsSmall',l:'Start'},
|
|
{k:'landingScore',type:'starsSmall',l:'Landung'},
|
|
{k:'level',type:'num',l:'Level'},
|
|
{k:'missionTitle',type:'text',l:'Mission'},
|
|
{k:'failed',type:'boolInv',l:'geschafft'}
|
|
],
|
|
busfahrt: [
|
|
{k:'stars',type:'stars',l:'Sterne'},
|
|
{k:'wonOrders',type:'ratio',t:'totalOrders',l:'Aufträge gewonnen'},
|
|
{k:'quizFirstTry',type:'ratio',t:'totalOrders',l:'Quiz Erstversuch'},
|
|
{k:'avgTapKm',type:'num',l:'Ø Abweichung',u:'km',dir:'lo'}
|
|
],
|
|
farmer: [
|
|
{k:'stars',type:'stars',l:'Sterne'},
|
|
{k:'hit_rate',type:'pct',l:'Trefferquote'},
|
|
{k:'year',type:'num',l:'Jahr erreicht'},
|
|
{k:'crops_used',type:'num',l:'Sorten'}
|
|
],
|
|
sonnensystem: [
|
|
{k:'completed',type:'ratio',t:'total',l:'Aufgaben gelöst'},
|
|
{k:'firstTry',type:'bool',l:'Erstversuch'}
|
|
],
|
|
'eu-werkstatt': [
|
|
{k:'firstTryHits',type:'ratio',t:'stepsTotal',l:'Erste Wahl'},
|
|
{k:'muelleimerCount',type:'num',l:'verworfen',dir:'lo'},
|
|
{k:'endType',type:'text',l:'Ergebnis'}
|
|
],
|
|
energiemanager: [
|
|
{k:'stars',type:'stars',l:'Sterne'},
|
|
{k:'blocksBalanced',type:'ratio',t:'blocksTotal',l:'Blöcke ausbalanciert'},
|
|
{k:'daysPlayed',type:'ratio',t:'daysTotal',l:'Tage gespielt'}
|
|
],
|
|
weltkueche: [
|
|
{k:'stars',type:'stars',l:'Sterne'},
|
|
{k:'completedDishes',type:'ratio',t:'totalDishes',l:'Gerichte'},
|
|
{k:'mistakesInDish',type:'num',l:'Fehlklicks',dir:'lo'},
|
|
{k:'dishName',type:'text',l:'Aktuelles Gericht'},
|
|
{k:'ingSolved',type:'ratio',t:'ingTotal',l:'Zutaten gelöst'},
|
|
{k:'progressPct',type:'pct',l:'Fortschritt'}
|
|
],
|
|
logistik: [
|
|
{k:'stars',type:'stars',l:'Sterne'},
|
|
{k:'ordersDelivered',type:'ratio',t:'totalOrders',l:'Ausgeliefert'},
|
|
{k:'ordersLate',type:'num',l:'verspätet',dir:'lo'},
|
|
{k:'finalBalance',type:'money',l:'Bilanz'},
|
|
{k:'balance',type:'money',l:'Konto'}
|
|
],
|
|
fluss: [
|
|
{k:'stars',type:'stars',l:'Sterne'},
|
|
{k:'biodiv',type:'pct',l:'Biodiversität'},
|
|
{k:'economy',type:'pct',l:'Wirtschaft'},
|
|
{k:'flood',type:'pctInv',l:'Hochwasser'},
|
|
{k:'food',type:'pct',l:'Ernährung'}
|
|
],
|
|
kofferdetektiv: [
|
|
{k:'cases_solved',type:'ratio',t:'cases_total',l:'Fälle gelöst'},
|
|
{k:'correct_first_try',type:'num',l:'Erstversuch'},
|
|
{k:'wrong_guesses',type:'num',l:'Fehlversuche',dir:'lo'},
|
|
{k:'hints_used_avg',type:'num',l:'Ø Hinweise',dir:'lo'}
|
|
],
|
|
tourismustal: [
|
|
{k:'goalsDone',type:'ratio',t:'goalsTotal',l:'Ziele erreicht'},
|
|
{k:'satisfaction',type:'pct',l:'Zufriedenheit'},
|
|
{k:'nature',type:'pct',l:'Naturwert'},
|
|
{k:'money',type:'money',l:'Konto'},
|
|
{k:'year',type:'num',l:'Jahr'},
|
|
{k:'buildings',type:'num',l:'Gebäude'},
|
|
{k:'maxGuests',type:'num',l:'Gäste (max)'}
|
|
],
|
|
tourismusregion: [
|
|
{k:'score',type:'pct',l:'Gesamtscore'},
|
|
{k:'goalsDone',type:'ratio',t:'goalsTotal',l:'Ziele erreicht'}
|
|
]
|
|
};
|
|
SIM_METRICS['klima-3d'] = SIM_METRICS.klima;
|
|
|
|
function esc(s){ return String(s==null?'':s).replace(/[&<>"]/g,function(c){return{'&':'&','<':'<','>':'>','"':'"'}[c];}); }
|
|
function ampPct(v){ return v>=75?'ivm-g':v>=55?'ivm-li':v>=40?'ivm-y':v>=25?'ivm-o':'ivm-r'; }
|
|
function stars(n){
|
|
n = Math.max(0, Math.min(5, Math.round(n)));
|
|
return '<span class="ivm-stars">'+'★'.repeat(n)+'<span class="ivm-star-off">'+'★'.repeat(5-n)+'</span></span>';
|
|
}
|
|
function bar(pct, cls){
|
|
pct = Math.max(0, Math.min(100, Math.round(pct)));
|
|
return '<span class="ivm-bar"><i class="'+cls+'" style="width:'+pct+'%"></i></span>';
|
|
}
|
|
function money(v){
|
|
var n = Math.round(v), cls = n<0?'ivm-r':'ivm-g';
|
|
return '<span class="ivm-v '+cls+'">'+n.toLocaleString('de-AT')+' €</span>';
|
|
}
|
|
function num(v, u){ return '<span class="ivm-v">'+(typeof v==='number'?v.toLocaleString('de-AT'):esc(v))+(u?' <span class="ivm-u">'+u+'</span>':'')+'</span>'; }
|
|
|
|
function starsBlock(n, small){
|
|
n = Math.max(0, Math.min(5, Math.round(n)));
|
|
var c = ampPct(n/5*100);
|
|
var cls = 'ivm-stars'+(small?' sm':'')+' '+c;
|
|
return '<span class="'+cls+'">'+'★'.repeat(n)+'<span class="ivm-star-off">'+'★'.repeat(5-n)+'</span></span>'+bar(n/5*100, c);
|
|
}
|
|
function renderVal(f, v, src){
|
|
switch (f.type) {
|
|
case 'stars': return starsBlock(v, false);
|
|
case 'starsSmall': return starsBlock(v, true);
|
|
case 'pct': return '<span class="ivm-v '+ampPct(v)+'">'+Math.round(v)+'<span class="ivm-u">%</span></span>'+bar(v, ampPct(v));
|
|
case 'pctInv': return '<span class="ivm-v '+ampPct(100-v)+'">'+Math.round(v)+'<span class="ivm-u">%</span></span>'+bar(v, ampPct(100-v));
|
|
case 'ratio': { var tot=src[f.t]; if(tot==null) return num(v); var pct=tot>0?(v/tot*100):0;
|
|
return '<span class="ivm-v '+ampPct(pct)+'">'+v+'<span class="ivm-u">/ '+tot+'</span></span>'+bar(pct, ampPct(pct)); }
|
|
case 'money': return money(v);
|
|
case 'temp': { var t=Math.round(v*10)/10, c=t<=2?'ivm-g':t<=3?'ivm-y':'ivm-r';
|
|
return '<span class="ivm-v '+c+'">'+t.toLocaleString('de-AT')+' °C</span>'; }
|
|
case 'bool': return v?'<span class="ivm-v ivm-g">✓ ja</span>':'<span class="ivm-v ivm-r">✗ nein</span>';
|
|
case 'boolInv': return v?'<span class="ivm-v ivm-r">✗ nein</span>':'<span class="ivm-v ivm-g">✓ ja</span>';
|
|
case 'text': return '<span class="ivm-v ivm-txt">'+esc(v)+'</span>';
|
|
default: return num(v, f.u);
|
|
}
|
|
}
|
|
|
|
function renderSimMetrics(simId, params){
|
|
if (!params || typeof params!=='object') return '';
|
|
var src = (params.results && typeof params.results==='object') ? params.results : params;
|
|
var schema = SIM_METRICS[simId] || [];
|
|
var seen = {}, out = [];
|
|
schema.forEach(function(f){
|
|
if (seen[f.l]) return; // Doppel-Label (z. B. stars/sterneGesamt) nur einmal
|
|
var v = src[f.k];
|
|
if (v==null || v==='') return;
|
|
seen[f.l] = 1;
|
|
out.push('<div class="ivm"><span class="ivm-l">'+esc(f.l)+'</span>'+renderVal(f, v, src)+'</div>');
|
|
});
|
|
return out.length ? '<div class="ivm-grid">'+out.join('')+'</div>'
|
|
: '<span style="font-size:.72rem;color:#8a8a8a">keine auswertbaren Parameter</span>';
|
|
}
|
|
|
|
// Plottbare (numerische) Kennzahlen einer Sim, dedupliziert nach Label.
|
|
var PLOTTABLE = {stars:1, starsSmall:1, pct:1, pctInv:1, ratio:1, temp:1, money:1, num:1};
|
|
function simPlotMetrics(simId){
|
|
var seen = {}, out = [];
|
|
(SIM_METRICS[simId] || []).forEach(function(f){
|
|
if (!PLOTTABLE[f.type] || seen[f.l]) return;
|
|
seen[f.l] = 1; out.push(f);
|
|
});
|
|
return out;
|
|
}
|
|
// true = höher ist besser (grün oben). temp/pctInv und dir:'lo' → niedriger ist besser.
|
|
function metricHigherBetter(f){
|
|
if (f.dir === 'lo') return false;
|
|
if (f.dir === 'hi') return true;
|
|
return !(f.type === 'pctInv' || f.type === 'temp');
|
|
}
|
|
|
|
root.SIM_METRICS = SIM_METRICS;
|
|
root.renderSimMetrics = renderSimMetrics;
|
|
root.ggsStarsHtml = stars;
|
|
root.simPlotMetrics = simPlotMetrics;
|
|
root.metricHigherBetter = metricHigherBetter;
|
|
})(typeof window !== 'undefined' ? window : this);
|