Telefonieren im Chat -- und die Farben nach Nachbarschaft verteilt
ZWEI SACHEN IN EINEM COMMIT, weil beide aus derselben Nacht stammen. === 1. DIE FARBEN: DER RICHTIGE ABSTAND === Filipe: "es gibt noch mehr die aehnlich aussehen von den farben also leg los alle die sich aehnlich sind von den farben wechseln." Er hatte recht, und mein Denkfehler laesst sich benennen: Ich hatte den kleinsten Abstand ueber ALLE 37 Paare maximiert -- und der liegt bei 37 Farben zwangslaeufig bei 0,10. Das ist die Packungsgrenze, kein Versaeumnis. NUR SIEHT NIEMAND ALLE 37 NEBENEINANDER. Man sieht NACHBARN. Im Browser gemessen, an der tatsaechlichen Lage auf dem Schirm -- 125 Paare, die wirklich nebeneinander stehen, 15 davon unter 0,15: Creator-Profile / Zahlen 0,1019 beide rosa-rot LIVE-Analyse / Technik 0,1030 beide orange Wunschliste / Meldungen 0,1032 beide gelbgruen Wer sieht was / Entwicklung 0,1039 beide cyan Regeln & Hilfe / Mitmachen 0,1047 beide gruen Der Treff / Anschlagbrett 0,1070 beide rosa Die Farben bleiben, ihre ZUTEILUNG aendert sich: Nachbarabstand 0,1047 -> 0,2133 (mehr als verdoppelt) Nachbarn unter 0,15 6 -> 0 Die Nachbarschaft steht in server/kachel-nachbarn.json, gemessen im Browser -- nicht aus der Struktur im Quelltext abgeleitet. Die sagt, was zusammengehoert, nicht was zusammen zu sehen ist. === 2. TELEFONIEREN IM CHAT === Filipe: "kann man machen dass die modis, rechte hand und ich auch telefonieren koennen im chat?" ... "was man selbst in die app reinsetzten kann, nicht meinen pc belastet und trotzdem vielleicht in gruppe, mit video oder einzelnd." DER TON GEHT NICHT UEBER DEN SERVER. Direkt von Browser zu Browser (WebRTC); der Server reicht nur die Verbindungsdaten weiter, ein paar Kilobyte je Anruf. Zu zweit kodiert jedes Geraet einen Strom und dekodiert einen -- die Last eines gewoehnlichen Videoanrufs. KEIN NEUER DIENST. Der Chat hatte bereits alles: `chatEreignis()` fuer den Hinweg (SSE), Push fuers Klingeln, Raeume mit mehreren Teilnehmern. Ein eigener WebSocket daneben waere eine zweite Verbindung fuer dieselbe Frage -- und die zweite wird beim naechsten Umbau vergessen. Gebaut: Ton und Video, einzeln und in Gruppe bis GRUPPE_MAX (4), Klingeln mit Annehmen/Ablehnen, Mikro und Kamera schaltbar, Gespraechsdauer, Auflegen. Der Chat bleibt daneben benutzbar -- man schreibt oft, waehrend man spricht. EIN FUND, DER OHNE PRUEFLAUF LIVE GEGANGEN WAERE: Der Server sperrt Mikrofon und Kamera per Permissions-Policy auf ALLEN Seiten. Der erste Lauf meldete "microphone is not allowed in this document" -- der Anruf haette bei JEDEM versagt, mit einer Meldung, die auf die falsche Faehrte fuehrt (man sucht an den Browsereinstellungen). Die Sperre bleibt ueberall und ist an genau EINER Stelle geoeffnet: der Chat-Seite, und nur fuer sie selbst (`self`, nicht `*`). NOCH NICHT GEBAUT -- und ausdruecklich nicht heimlich: coturn. Ohne Vermittlungsserver klappen Anrufe nur im selben Netz. Die Adressen stehen in den Einstellungen statt im Quelltext; sie lassen sich nachtragen, ohne eine Zeile zu aendern. Ein oeffentlicher STUN-Dienst als Standard kam nicht in Frage: Er saehe bei jedem Anruf die IP-Adressen beider Teilnehmer, und fuer ein Team, das ueber Moderation und Vorfaelle spricht, ist das keine Kleinigkeit. server/pruef-anruf.mjs, 40 Pruefungen. Der wichtigste Abschnitt: Wer nicht in den Raum gehoert, kommt an KEINE Route. Ein Anruf hinterlaesst keine Spur -- wer mithoert, faellt nicht auf. Gegenproben, jede zielgenau: Empfaengerpruefung der Signalisierung weg -> 1 rot Raumpruefung weg -> 5 rot (jede Route offen) Kopfzeilen-Ausnahme weg -> 4 rot Gruen: anruf, chat, chat-optik, chat-kanaele, css-klassen, namen, struktur. Co-Authored-By: Claude Opus 5 <[email protected]>
This commit is contained in:
+116
-7
@@ -225,24 +225,133 @@ console.log("");
|
||||
|
||||
/* ---------- Zuteilung: welche Farbe an welche Nummer? ----------------
|
||||
|
||||
Die Nummern bleiben an ihren Kacheln -- eine Farbe gehoert zu einem
|
||||
Bereich, nicht zu einem Platz im Raster. Damit die Aenderung nicht
|
||||
groesser wird als noetig, bekommt jede Nummer die neue Farbe, die
|
||||
ihrer alten am naechsten liegt: Wer "Chat" als Orange kennt, findet
|
||||
es danach wieder als Orange. */
|
||||
HIER LAG DER EIGENTLICHE FEHLER (18.09.2026).
|
||||
|
||||
Filipe, nachdem die Palette schon neu gerechnet war: "es gibt noch
|
||||
mehr die aehnlich aussehen von den farben also leg los alle die sich
|
||||
aehnlich sind von den farben wechseln."
|
||||
|
||||
Er hatte wieder recht, und diesmal lag es nicht an den Farben,
|
||||
sondern an ihrer VERTEILUNG. Gemessen im Browser -- 125 Paare, die
|
||||
wirklich nebeneinander stehen:
|
||||
|
||||
Creator-Profile / Zahlen 0,1019 beide rosa-rot
|
||||
LIVE-Analyse / Technik 0,1030 beide orange
|
||||
Wunschliste / Meldungen 0,1032 beide gelbgruen
|
||||
Wer sieht was / Entwicklung 0,1039 beide cyan
|
||||
Regeln & Hilfe / Mitmachen 0,1047 beide gruen
|
||||
Der Treff / Anschlagbrett 0,1070 beide rosa
|
||||
... 15 Paare unter 0,15
|
||||
|
||||
DER DENKFEHLER: Der kleinste Abstand ueber ALLE Paare wurde maximiert
|
||||
-- und der liegt bei 37 Farben zwangslaeufig bei 0,10. Das ist die
|
||||
Packungsgrenze, kein Versaeumnis. Nur sieht niemand alle 37
|
||||
nebeneinander. Man sieht NACHBARN. Zwei aehnliche Farben an
|
||||
entgegengesetzten Enden der Seite fallen nie auf; zwei
|
||||
nebeneinander sofort.
|
||||
|
||||
Die Farben bleiben also, wie sie sind -- ihre ZUTEILUNG aendert
|
||||
sich. Wer nebeneinander steht, bekommt Farben, die weit
|
||||
auseinanderliegen. Das kostet nichts und ist der ganze Unterschied.
|
||||
|
||||
WOHER DIE NACHBARSCHAFT KOMMT: aus dem Browser, gemessen an der
|
||||
tatsaechlichen Lage auf dem Schirm (server/kachel-nachbarn.json).
|
||||
Nicht aus der Struktur im Quelltext -- die sagt, was
|
||||
zusammengehoert, nicht was zusammen zu sehen ist. */
|
||||
|
||||
/* Welcher Name traegt welche Tonnummer? */
|
||||
const NAME_ZU_TON = new Map();
|
||||
for (const q of ["server/workspace.js", "workspace/assets/js/bereiche.js"]) {
|
||||
let text;
|
||||
try { text = readFileSync(q, "utf8"); } catch { continue; }
|
||||
const zeilen = text.split("\n");
|
||||
for (let i = 0; i < zeilen.length; i++) {
|
||||
const m = zeilen[i].match(/ton:\s*(\d+)/);
|
||||
if (!m) continue;
|
||||
for (let k = 0; k <= 3; k++) {
|
||||
const n = (zeilen[i - k] || "").match(/name:\s*"([^"]+)"/);
|
||||
if (n) { NAME_ZU_TON.set(n[1], Number(m[1])); break; }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let NACHBARN = [];
|
||||
try {
|
||||
const roh = JSON.parse(readFileSync("server/kachel-nachbarn.json", "utf8"));
|
||||
NACHBARN = roh.paare
|
||||
.map(([a, b]) => [NAME_ZU_TON.get(a), NAME_ZU_TON.get(b)])
|
||||
.filter(([a, b]) => a && b && a !== b);
|
||||
console.log(`${NACHBARN.length} Nachbarpaare geladen `
|
||||
+ `(gemessen am ${roh.gemessen}).`);
|
||||
} catch {
|
||||
console.log("Keine Nachbarschaftsdatei -- es wird nur global optimiert.");
|
||||
}
|
||||
|
||||
/* Die Startzuteilung: jede Nummer bekommt die neue Farbe, die ihrer
|
||||
alten am naechsten liegt. Wer "Chat" als Orange kennt, findet es
|
||||
danach wieder als Orange -- solange die Nachbarschaft das zulaesst. */
|
||||
const frei = beste.slice();
|
||||
const zuteilung = new Map();
|
||||
for (const nr of NUMMERN) {
|
||||
const alt = VORHER.get(nr);
|
||||
const alt2 = VORHER.get(nr);
|
||||
let bester = 0, bestAbw = Infinity;
|
||||
for (let i = 0; i < frei.length; i++) {
|
||||
const d = abstand(frei[i].hex, alt);
|
||||
const d = abstand(frei[i].hex, alt2);
|
||||
if (d < bestAbw) { bestAbw = d; bester = i; }
|
||||
}
|
||||
zuteilung.set(nr, frei.splice(bester, 1)[0]);
|
||||
}
|
||||
|
||||
/* Und jetzt tauschen, solange die NACHBARN davon profitieren. */
|
||||
if (NACHBARN.length) {
|
||||
const nachbarAbstand = (z) => {
|
||||
let m = Infinity;
|
||||
for (const [a, b] of NACHBARN) {
|
||||
const fa = z.get(a), fb = z.get(b);
|
||||
if (!fa || !fb) continue;
|
||||
const d = abstand(fa.hex, fb.hex);
|
||||
if (d < m) m = d;
|
||||
}
|
||||
return m;
|
||||
};
|
||||
/* Und als zweites Ziel: die SUMME der kleinen Abstaende. Zwei
|
||||
Zuteilungen mit gleichem Minimum sind nicht gleich gut -- die mit
|
||||
weniger knappen Paaren ist besser. */
|
||||
const knappe = (z, grenze) => {
|
||||
let n = 0;
|
||||
for (const [a, b] of NACHBARN) {
|
||||
const fa = z.get(a), fb = z.get(b);
|
||||
if (fa && fb && abstand(fa.hex, fb.hex) < grenze) n++;
|
||||
}
|
||||
return n;
|
||||
};
|
||||
|
||||
const vorher = nachbarAbstand(zuteilung);
|
||||
const vorherKnapp = knappe(zuteilung, 0.15);
|
||||
let lauf = true, runden = 0;
|
||||
while (lauf && runden < 60) {
|
||||
lauf = false; runden++;
|
||||
for (let i = 0; i < NUMMERN.length; i++) {
|
||||
for (let j = i + 1; j < NUMMERN.length; j++) {
|
||||
const a = NUMMERN[i], b = NUMMERN[j];
|
||||
const jetztMin = nachbarAbstand(zuteilung);
|
||||
const jetztKnapp = knappe(zuteilung, 0.15);
|
||||
const fa = zuteilung.get(a), fb = zuteilung.get(b);
|
||||
zuteilung.set(a, fb); zuteilung.set(b, fa);
|
||||
const neuMin = nachbarAbstand(zuteilung);
|
||||
const neuKnapp = knappe(zuteilung, 0.15);
|
||||
const besser = neuMin > jetztMin + 1e-9
|
||||
|| (Math.abs(neuMin - jetztMin) < 1e-9 && neuKnapp < jetztKnapp);
|
||||
if (besser) { lauf = true; } else { zuteilung.set(a, fa); zuteilung.set(b, fb); }
|
||||
}
|
||||
}
|
||||
}
|
||||
console.log(`Nachbarn: kleinster Abstand ${vorher.toFixed(4)} -> `
|
||||
+ `${nachbarAbstand(zuteilung).toFixed(4)} `
|
||||
+ `· Paare unter 0,15: ${vorherKnapp} -> ${knappe(zuteilung, 0.15)} `
|
||||
+ `(${runden} Runden)`);
|
||||
}
|
||||
|
||||
/* ---------- Bericht -------------------------------------------------- */
|
||||
|
||||
const altListe = NUMMERN.map((n) => ({ hex: VORHER.get(n), lab: oklab(VORHER.get(n)) }));
|
||||
|
||||
Reference in New Issue
Block a user