Hinter den Kulissen

Regionsfarbe ist in Meowdoku strukturell, keine Dekoration

Jede Zelle auf einem Meowdoku-Spielfeld ist eine einzige flache Füllfarbe, und nichts sonst markiert, wo eine Region endet und die nächste beginnt. Das macht die Palette zu einem funktionierenden Teil des Rätsels statt zu einem Anstrich: Wählt man die Farben zweier benachbarter Regionen schlecht, wird das Feld unlesbar, bevor auch nur eine Katze gesetzt ist. Das Spiel behandelt es bereits so — was noch nicht geprüft wurde, ist, ob dieselbe Palette eine Farbsehschwäche übersteht. Genau das untersucht dieser Beitrag.

Was eine Region tatsächlich markiert

Man sucht in der Board-Komponente vergeblich nach etwas, das eine Grenze zwischen zwei Regionen zieht. Jede Zelle bekommt denselben 1px-Haarlinienrahmen, egal ob die Nachbarzelle zur eigenen Region gehört oder zu einer völlig anderen. Das Einzige, was sich von Zelle zu Zelle ändert, ist die Füllfarbe: kein Rahmen, kein Symbol, keine Textur.

Das ist eine bewusste Designentscheidung, kein Versehen — eine deutliche Naht an jeder Regionsgrenze wäre eine weitere Sache, die auf einem Feld mit ohnehin schon zwölf Farben und bis zu hundert Zellen gezeichnet werden müsste. Aber das bedeutet, dass Farbe eine Information trägt, die sonst nirgendwo existiert. „Zu welcher Region gehört diese Zelle" ist die erste Tatsache, von der jede Regel im Spiel abhängt, und die Füllfarbe ist der einzige Ort, an dem diese Tatsache festgehalten wird.

Warum das die Palette tragend macht

Sobald Farbe das einzige Grenzsignal ist, lesen sich zwei benachbarte Regionen mit ähnlichen Farben nicht mehr als zwei Regionen — sie lesen sich als ein einziger, seltsam geformter Fleck, und jede Schlussfolgerung, die davon abhängt, sie zu unterscheiden, wird vom Hinsehen zum Zusammenkneifen der Augen.

Das Modul, das diese Farben zuweist, sagt genau das in seinen eigenen Kommentaren: Eine naive Zuweisung „often produce[s] clashes like red next to rose or orange next to yellow — the player has to stare to tell whether it's one block, and 'which region is this' is the premise of every reasoning step in this game“ (erzeugt oft Kollisionen wie Rot neben Rosa oder Orange neben Gelb — die Spielerin muss hinstarren, um zu erkennen, ob es sich um einen einzigen Block handelt, und „welche Region ist das“ ist die Voraussetzung für jeden Denkschritt in diesem Spiel). Dieser Satz ist die vollständige Begründung für alles, was folgt.

Der Algorithmus: echtes Graph-Coloring, keine gemischte Liste

`assignRegionColors` baut zunächst einen Nachbarschaftsgraphen der Regionen auf: Zwei Regionen gelten als benachbart, wenn Zellen eine Kante teilen — bewusst über 4er- statt 8er-Nachbarschaft, denn eine diagonale Berührung wird visuell nicht als ein Block missverstanden und ist daher keine Färbungsbedingung. Anschließend werden die Regionen nach Nachbarnzahl abgearbeitet, und für jede wird die freie Palettenposition gewählt, die auf dem eigenen Farbring der Palette am weitesten von jeder bereits gefärbten Nachbarin entfernt ist; Gleichstände gehen an die Position, die die zwölf Farbtöne insgesamt am weitesten auseinanderzieht.

Das ist Graph-Coloring dem Namen und der Funktion nach: Regionen sind Knoten, geteilte Kanten sind Bedingungen, Palettenpositionen stehen für Farben. Die einzige Abweichung von der klassischen Formulierung ist der Ring selbst — nah beieinanderliegende Positionen (Rot und Orange) gelten als ähnlicher als gegenüberliegende (Rot und Petrol), was der tatsächlichen Wirkung dieser Farbtöne nebeneinander entspricht.

Wie viel Spielraum die Palette wirklich hat

Die Palette hat zwölf Positionen. Ein Spielfeld nutzt nie alle davon: Die Regionenzahl entspricht der Feldgröße, und die Feldgröße erreicht höchstens zehn.

Regionen im Einsatz gegenüber der 12-Positionen-Palette, nach Schwierigkeitsgrad.
SpielfeldRegionen (= Feldgröße)Freie Palettenpositionen
Leicht 5×557
Normal 7×775
Schwer 9×993
Extrem 10×10102

Selbst bei Extrem bleiben zwei Positionen ungenutzt. Der Rückfall des Algorithmus für den Fall zu weniger Farben greift bei keinem echten Spielfeld — er existiert für einen Fall, den die aktuelle Schwierigkeitsleiter nie erreicht.

Was der Algorithmus bei normalem Sehen tatsächlich bringt

Gemessen als CIE-Lab-ΔE (ein gängiges Maß für wahrgenommenen Farbabstand — grob gilt: unter 10 wirkt auf den ersten Blick ähnlich, über 30 eindeutig unterschiedlich), hier der kleinste Abstand zwischen zwei benachbarten Regionen auf einem Feld, gemittelt über dreihundert frisch erzeugte Spielfelder je Schwierigkeitsgrad:

Mittelwert des am nächsten liegenden benachbarten Regionspaars pro Spielfeld (ΔE, helles Theme, normales Sehen), 300 Spielfelder je Schwierigkeitsgrad.
SpielfeldassignRegionColorsNachbarschaftsblind (Regions-ID → Palettenposition)
Leicht 5×536.028.0
Normal 7×734.328.6
Schwer 9×933.528.1
Extrem 10×1032.527.5

Der Algorithmus bringt bei jedem Schwierigkeitsgrad durchgängig etwa ein Fünftel mehr Trennung. Wichtig ist, genau zu sagen, was das nicht ist: Bei nur fünf bis zehn Farbtönen aus zwölf gut verteilten hat auch eine nachbarschaftsblinde Zuweisung auf keinem der hier untersuchten zwölfhundert Spielfelder eine echte Kollision (ΔE unter 20) erzeugt — die Palette hat bei diesen Regionenzahlen schon von sich aus genug Spielraum. Was der Algorithmus beseitigt, ist Glück, keine häufige Katastrophe. Er macht aus „wahrscheinlich in Ordnung“ eine entworfene Garantie — genau die Garantie, die dieser Beitrag als Nächstes einer Farbsehschwäche unterzieht.

Was eine Rot-Grün-Farbsehschwäche verändert

Protanopie und Deuteranopie, die beiden häufigen Formen der Rot-Grün-Farbenblindheit, betreffen zusammen etwa 1 von 12 Männern. Beide stauchen einen Teil des Farbraums zusammen, den normales trichromatisches Sehen getrennt hält — die Achse durch Rot, Orange, Gelbgrün und Grün —, sodass Farben an entgegengesetzten Enden dieser Achse am Ende fast gleich aussehen können.

Um das an dieser Palette zu messen statt nur abstrakt zu beschreiben, schickt dieser Beitrag jede Farbe durch eine Standard-Dichromasie-Simulation (eine lineare RGB-Matrixtransformation, aus derselben Familie, die auch Browser- und Betriebssystem-Barrierefreiheitswerkzeuge nutzen) und misst ΔE am Ergebnis neu. Das ist eine Simulation mit voller Ausprägung, eine Worst-Case-Grenze — keine Aussage, dass jede farbenblinde Spielerin genau diese Zahlen sieht, denn die meisten Rot-Grün-Schwächen sind eine Frage des Grades, kein Alles-oder-nichts-Zusammenbruch.

Das gemessene Ergebnis: ein Zusammenbruch auf fast jedem Spielfeld

Beim erneuten Durchlauf derselben dreihundert Spielfelder je Schwierigkeitsgrad, wieder mit dem am nächsten liegenden benachbarten Paar — diesmal unter der simulierten Sehschwäche:

Dasselbe am nächsten liegende benachbarte Regionspaar, mittleres ΔE unter simulierter Protanopie und Deuteranopie, im Vergleich zu normalem Sehen.
SpielfeldNormales SehenProtanopieDeuteranopieSpielfelder mit einem Paar unter ΔE 12
Leicht 5×536.010.118.597%
Normal 7×734.39.016.597%
Schwer 9×933.58.415.399%
Extrem 10×1032.58.113.595%

ΔE 12 ist ein gewählter Grenzwert, keine feste psychophysische Linie — er markiert ungefähr den Bereich, in dem zwei große, benachbarte Flächen eher als eine Farbe statt als zwei gelesen würden, deutlich unter der Trennung, auf die diese Palette eigentlich ausgelegt ist. Unter Protanopie fällt das am nächsten liegende Paar bei jedem Schwierigkeitsgrad im Mittel auf einstellige Werte; unter Deuteranopie hält es sich etwas besser, fällt aber trotzdem um mehr als die Hälfte. So oder so liegen bei 95 bis 99 % der zwölfhundert untersuchten Spielfelder mindestens ein benachbartes Paar unter diesem Grenzwert.

Es ist fast immer dasselbe Paar

Dieser Zusammenbruch verteilt sich nicht gleichmäßig. Isoliert man, welches Farbpaar auf jedem betroffenen Spielfeld den Schaden verursacht, dominiert bei jedem Schwierigkeitsgrad ein Paar: Rot neben Gelbgrün, vertreten in 97 bis 99 % der Spielfelder mit irgendeinem Zusammenbruch. Isoliert betrachtet misst dieses Paar bei normalem Sehen ein ΔE von 54,0 — deutlich unterscheidbar —, fällt aber unter Protanopie auf 9,5, ein Rückgang von 82 %.

Der Grund liegt in der eigenen Ringreihenfolge der Palette. Rot und Gelbgrün liegen drei Positionen auseinander auf dem Zwölf-Farben-Ring, was `assignRegionColors` als bequemen, nicht benachbarten Abstand behandelt. Doch dieses Maß setzt ein Farbrad voraus, das für normales Sehen kalibriert ist, und Protanopie/Deuteranopie stauchen gerade das Segment des Rads, an dessen entgegengesetzten Enden diese beiden Farbtöne liegen — sodass ein vom Algorithmus als sicher eingestufter Abstand trotzdem zusammenbricht.

Die eigenen Quellcode-Kommentare der Palette markieren außerdem ein sich alle drei Positionen wiederholendes Hell/Mittel/Dunkel-Helligkeitsmuster — ein Abstand von genau drei Positionen legt zwei Farben also immer in dieselbe Helligkeitsbande (Violett und Petrol, ein kleinerer sekundärer Übeltäter, teilen das ebenfalls: ΔE von 74,0 fällt unter Protanopie auf 8,2, der stärkste gemessene Einzelabfall, tritt aber seltener auf). Bei diesem Ringabstand ist der Farbton das Einzige, was zwei Farben unterscheidet, und Farbton ist genau das, was eine Rot-Grün-Schwäche abträgt.

Was jetzt tatsächlich hilft

Im Spiel gibt es heute keinen Farbenblind-Modus und keine Rahmenlinie zwischen Regionen als Rückfallebene — genau deshalb hat das Problem nirgendwo, wo es sich verstecken könnte. Die Abhilfe, die wirklich funktioniert, liegt außerhalb des Spiels: macOS, Windows, iOS und Android bieten alle einen systemweiten Farbsehschwäche-Filter (Einstellungen → Bedienungshilfen → Farbfilter), und weil das Problem hier eine Stauchung von Farbtönen ist, trennt ein auf die Schwäche der Spielerin abgestimmter Filter genau die Farbtöne wieder, die diese Messung als zusammenbrechend zeigt. Hineinzoomen hilft nicht — es ist ein Farbproblem, kein Größenproblem.

Sollte irgendwann eine überarbeitete Palette kommen, zeigt diese Messung, wo man ansetzen sollte: bei der Beziehung mit Ringabstand 3, nicht bei Ringabstand 1. Offensichtlich nahe beieinanderliegende Paare werden vom Algorithmus bereits auseinandergezogen und überstehen die Simulation meist unbeschadet. Es sind die Paare, die der Algorithmus derzeit für sicher getrennt hält, die es nicht sind.

Wie das gemessen wurde

Alle Zahlen stammen aus dem eigenen Code des Spiels: `generatePuzzleSync` für die Spielfelder, `assignRegionColors` und `regionAdjacency` aus src/engine/palette.ts für die Färbung, ausgeführt genau so, wie es das echte Spiel tut. Der Farbabstand ist CIE76-ΔE im Lab-Raum; die Farbsehschwäche-Simulation wendet eine standardmäßige lineare RGB-Dichromasie-Matrix an (Viénot/Brettel-Familie), bevor zurückgerechnet und neu gemessen wird. Der nachbarschaftsblinde Vergleich bildet die eigene numerische ID jeder Region direkt auf eine Palettenposition ab und umgeht `assignRegionColors` vollständig.

Das sind Eigenschaften der Palette und des Generators, die dieses Spiel derzeit ausliefert. Die Testsuite dieses Beitrags leitet jede Tabelle bei jedem Lauf neu aus den echten Modulen ab, sodass eine künftige Änderung an Palette oder Generator den Build scheitern lässt, statt eine veraltete Zahl auf der Seite stehen zu lassen.

So schaut man sich das nächste Spielfeld an

Öffne ein neues Spielfeld und finde die zwei benachbarten Regionen, deren Farben sich am ähnlichsten anfühlen — die meisten Spielfelder haben mindestens ein Paar, das einen zweiten Blick wert ist. Das ist der Sicherheitsspielraum der Palette bei der Arbeit, wie geplant. Es ist auch eine gute Seite, um einen Systemfilter für Rot-Grün-Sehen zu testen, falls vorhanden.

Spielfelder werden in deinem Browser erzeugt. Nichts von dem, was du spielst, wird irgendwohin gesendet.