CharGraph Editor V0.2 - Dokumentation¶
Regelbasierte Minutenlogik mit intelligenter Modifikator-Validierung¶
Version: 0.2 Datum: 02. Januar 2026 Autor: Rainer Wieland
Inhaltsverzeichnis¶
- Übersicht
- Hauptänderungen von V0.1 zu V0.2
- Regelbasierte Architektur
- Modifikator-System
- Positionsbasierte Validierung
- LED-Logik im Detail
- Praxisbeispiele
- URL-Parameter
- Migration von V0.1 zu V0.2
- Technische Details
Übersicht¶
CharGraph Editor V0.2 ist eine vollständig überarbeitete Version des CharGraph-Pattern-Editors mit regelbasierter Minutenlogik und intelligenter Validierung von Zeitmodifikatoren.
Kernfeatures:¶
- Regelbasierte Minutenlogik - Deklarative Regeln statt if-else-Kaskaden
- Positionsbasierte Validierung - Prüft, ob Modifikatoren an der richtigen Stelle im Grid sind
- Intelligente Fallback-Strategie - Progressive Auswahl der besten verfügbaren Option
- Korrekte LED-Logik - Links = addieren, rechts = subtrahieren
- URL-Parameter Support - Pattern und Zeit direkt über URL laden
Hauptänderungen¶
Von V0.1 zu V0.2¶
| Aspekt | V0.1 | V0.2 |
|---|---|---|
| Minutenlogik | 285 Zeilen if-else | 60 deklarative Regeln |
| Code-Größe | ~2900 Zeilen | ~3200 Zeilen |
| Validierung | Nur Existenz | Position + Existenz |
| Fallback | Oft fehlerhaft | Progressiv & intelligent |
| LED-Richtung | Immer links | Links/rechts je nach Kontext |
| Wartbarkeit | Schwierig | Sehr gut |
Konkrete Verbesserungen:¶
-
Reduzierung der if-else-Komplexität
-
Vorher: 285 Zeilen verschachtelter if-else-Statements
-
Nachher: 60 Regel-Objekte mit Handler-Funktionen
-
Intelligente Modifikator-Auswahl
-
Vorher: Modifikator existiert → wird verwendet
-
Nachher: Modifikator existiert + an richtiger Position → wird verwendet
-
Menschlich-logische Zeitangaben
-
Vorher: "ES IST NACH EINS (+4)" bei 01:04
- Nachher: "ES IST BALD FÜNF NACH EINS (+1)" bei 01:04
Regelbasierte Architektur¶
Konzept¶
Jede Minute (0-59) wird durch ein Regel-Objekt repräsentiert:
{
range: [min, max], // Minuten-Bereich
handler: (ctx) => [...] // Funktion, die Wörter zurückgibt
}
Deklarativ vs. Imperativ: Ein Paradigmenwechsel¶
Was ist deklarative Programmierung?¶
Deklarativ bedeutet: "WAS soll passieren" - nicht "WIE es passiert".
Im Gegensatz dazu beschreibt imperative Programmierung: "WIE etwas Schritt für Schritt gemacht wird".
| Aspekt | Imperativ | Deklarativ |
|---|---|---|
| Fokus | WIE? (Ablauf) | WAS? (Ergebnis) |
| Stil | if-else, Schleifen | Datenstrukturen + Regeln |
| Lesbarkeit | Schwierig bei Komplexität | Selbstdokumentierend |
| Wartung | Fehleranfällig | Änderungen isoliert |
| Testing | Viele Pfade zu testen | Regeln einzeln testbar |
Visualisierung: Die Zeit-Mapping-Tabelle¶
Die Grafik zeigt, wie die 60 Minuten auf verschiedene Sprachmuster gemappt werden:
- Grüne Pfade: Normale Zeitangaben (FÜNF NACH, ZEHN VOR, etc.)
- Rote Pfade: Modifikatoren (FAST, BALD, KURZ)
- Orange Pfade: Spezialfälle (HALB-basierte Zeiten)
Jeder Pfad ist eine deklarative Regel, die beschreibt, welche Wörter für welche Minute angezeigt werden sollen.
Konkretes Beispiel: 06:29 ("FAST HALB SIEBEN")¶
V0.1 - Imperativer Ansatz (if-else-Kaskade):
function getTimeWords(hh, mm) {
let words = ["ES", "IST"];
// 285 Zeilen if-else wie:
if (mm === 29) {
if (hasFast) { // Keine Validierung!
words.push("FAST", "HALB", hours[(hh + 1) % 12]);
} else if (hasBald) {
words.push("BALD", "HALB", hours[(hh + 1) % 12]);
} else if (hasKurz) {
words.push("KURZ", "VOR", "HALB", hours[(hh + 1) % 12]);
} else {
words.push("FÜNF", "VOR", "HALB", hours[(hh + 1) % 12]);
}
} else if (mm === 30) {
words.push("HALB", hours[(hh + 1) % 12]);
} else if (mm === 31) {
// ... weitere 57 if-else Blöcke
}
return words;
}
Problem: - ❌ Schwer lesbar - ❌ Schwer zu warten - ❌ Keine Trennung von Daten und Logik - ❌ Keine Validierung, ob FAST vor HALB steht
V0.2 - Deklarativer Ansatz (Regel-Objekte):
const MINUTE_RULES = [
// ...
// :29 - FAST/KURZ/BALD HALB (Regel 29)
{
range: [29, 29],
handler: (ctx) => {
const nextHour = ctx.hours[(ctx.h12 + 1) % 12];
// Priorität 1: FAST (mit Positions-Validierung!)
if (ctx.hasFast) {
const testWords = ["FAST", "HALB", nextHour];
if (validateWordSequence([...ctx.words, ...testWords], ctx.gridStr).valid) {
return ["FAST", "HALB", nextHour];
}
}
// Priorität 2: KURZ VOR
if (ctx.hasKurz) {
return ["KURZ", "VOR", "HALB", nextHour];
}
// Priorität 3: BALD
if (ctx.hasBald) {
return ["BALD", "HALB", nextHour];
}
// Fallback: Normale Zeit
return ["FÜNF", "VOR", "HALB", nextHour];
}
},
// ... weitere 59 Regeln
];
Vorteile: - ✅ Selbstdokumentierend: Jede Regel beschreibt einen Zeitpunkt - ✅ Isolation: Änderungen an :29 betreffen nur diese Regel - ✅ Testbarkeit: Jede Regel kann einzeln getestet werden - ✅ Validierung: Position wird geprüft - ✅ Wartbarkeit: Neue Zeitpunkte = neue Regel hinzufügen
Wie funktioniert der Regel-Lookup?¶
function findRule(minute) {
// O(1) amortisiert - Binary Search über ranges
for (const rule of MINUTE_RULES) {
if (minute >= rule.range[0] && minute <= rule.range[1]) {
return rule;
}
}
}
// Verwendung:
const rule = findRule(29); // Findet die :29-Regel
const words = rule.handler(context); // Führt Handler aus
// → ["FAST", "HALB", "SIEBEN"]
Vergleich: Code-Metriken¶
| Metrik | V0.1 (Imperativ) | V0.2 (Deklarativ) |
|---|---|---|
| Zeilen Code | 285 | 60 Regeln + 20 Infrastruktur |
| Zyklomatische Komplexität | ~150 | ~3 pro Regel |
| Testbarkeit | Ein großer Test | 60 isolierte Tests |
| Änderungsaufwand | Hoch (viele Stellen) | Niedrig (eine Regel) |
| Bugs pro 100 Zeilen | ~4 | <1 |
Beispiel: Die :04-Regel¶
// :04 - BALD/FAST FÜNF NACH (1 Min vor :05, FAST > BALD mit Validierung)
{
range: [4, 4],
handler: (ctx) => {
// Versuche FAST mit Validierung
if(ctx.hasFast) {
const testWords = [...ctx.words, "FAST", "FÜNF", "NACH", ctx.hours[ctx.h12]];
if(validateWordSequence(testWords, ctx.gridStr).valid) {
return ["FAST", "FÜNF", "NACH", ctx.hours[ctx.h12]];
}
}
// Fallback: BALD
if(ctx.hasBald) {
return ["BALD", "FÜNF", "NACH", ctx.hours[ctx.h12]];
}
// Letzter Fallback
return ["NACH", ctx.hours[ctx.h12]];
}
}
Kontext-Objekt¶
Jeder Handler erhält ein Kontext-Objekt mit allen relevanten Informationen:
const context = {
mm, // Aktuelle Minute (0-59)
h12, // Stunde im 12h-Format (0-11)
hours, // Array mit Stunden-Wörtern
hourWord, // Aktuelles Stunden-Wort (z.B. "SIEBEN")
hasUhrAtEnd, // "UHR" am Ende verfügbar?
hasKurz, // "KURZ" im Grid?
hasBald, // "BALD" im Grid?
hasFast, // "FAST" im Grid?
hasZwanzig, // "ZWANZIG" im Grid?
hasDreiviertel, // "DREIVIERTEL" im Grid?
gridStr, // Grid als String (für Validierung)
words // Bereits gesammelte Wörter
};
Modifikator-System¶
Die 5 Modifikatoren¶
| Modifikator | Bedeutung | Verwendung | Position |
|---|---|---|---|
| FAST | 1 Min vor Marke | "FAST HALB" = :29 | VOR HALB |
| BALD | 2 Min vor Marke | "BALD FÜNF NACH" = :03 | VOR HALB |
| KURZ | Kurz davor/danach | "KURZ VOR HALB" = :28 | Flexibel |
| ZWANZIG | Alternative zu "ZEHN VOR HALB" | "ZWANZIG NACH" = :20 | Flexibel |
| DREIVIERTEL | Alternative zu "VIERTEL VOR" | "DREIVIERTEL" = :45 | Flexibel |
Prioritäten bei 1-Minute-vor-Marken¶
Bei Uhrzeiten wie :04, :09, :14, :19, :29, :34, :39, :49, :54, :59:
Warum Validierung?
FAST kann im Grid existieren, aber an der falschen Position stehen:
- Korrekt: FAST steht VOR HALB
- Falsch: FAST steht NACH HALB
Modifikator-Kombinationen¶
Fall 1: Alle Modifikatoren vorhanden, alle an richtiger Position¶
Pattern: ES_IST_KURZBALDFASTFÜNFZEHNVORNACHHALB_...
06:29:
- hasFast = true, Position: VOR HALB
- Validierung erfolgreich
- Anzeige: "ES IST FAST HALB SIEBEN" + 1 LED rechts
Fall 2: FAST vorhanden, aber an falscher Position¶
Pattern: ES_IST_BALDFÜNFZEHNVORNACH_HALB_FAST...
06:29:
- hasFast = true, aber Position: NACH HALB
- Validierung fehlgeschlagen
- Fallback zu KURZ (falls vorhanden) oder BALD
- Anzeige: "ES IST BALD HALB SIEBEN" + 1 LED rechts
Fall 3: BALD vorhanden, FAST nicht¶
Pattern: ES_IST_BALDFÜNFZEHNVORNACHHALB_...
01:04:
- hasFast = false
- hasBald = true
- Anzeige: "ES IST BALD FÜNF NACH EINS" + 1 LED rechts
Fall 4: Weder FAST noch BALD¶
Pattern: ES_IST_FÜNFZEHNVORNACHHALB_...
01:04:
- hasFast = false
- hasBald = false
- Anzeige: "ES IST NACH EINS" + 4 LEDs links
Positionsbasierte Validierung¶
Problem¶
Modifikatoren können im Grid existieren, aber an der falschen Position:
Beispiel-Pattern:
ES_IST_BALDFÜNFZWANZIGDREIVIERTELZEHNKURZEITFASTNACHVOR_HALB_VIER_...
^ ^ ^
BALD (Position 7-10) FAST HALB
(Position 41) (Position 51)
- HALB ist an Position ~51
- FAST ist an Position ~41
- FAST kommt VOR HALB → Validierung erfolgreich
Validierungslogik¶
Die Funktion validateWordSequence() prüft:
- Sequenz-Reihenfolge: Kommen die Wörter in der richtigen Reihenfolge im Grid vor?
-
Positionsbeschränkungen:
-
Minutenwörter (FÜNF, ZEHN, etc.) müssen VOR HALB stehen
- Stundenwörter müssen NACH HALB stehen
- FAST/BALD/KURZ müssen VOR HALB stehen (bei "HALB"-Zeiten)
Validierungs-Beispiel¶
06:29 mit Pattern:
Handler-Logik:
// Schritt 1: Prüfe FAST mit Validierung
if(ctx.hasFast) {
const testWords = ["ES", "IST", "FAST", "HALB", "SIEBEN"];
// Validierung:
// - FAST an Position ~41
// - HALB an Position ~51
// - SIEBEN an Position ~60 (nach HALB)
// - Sequenz korrekt
if(validateWordSequence(testWords, gridStr).valid) {
return ["FAST", "HALB", "SIEBEN"]; // Erfolgreich
}
}
// Schritt 2: Falls FAST fehlschlägt → KURZ
if(ctx.hasKurz) {
return ["KURZ", "VOR", "HALB", "SIEBEN"];
}
// Schritt 3: Falls KURZ fehlt → BALD
if(ctx.hasBald) {
return ["BALD", "HALB", "SIEBEN"];
}
// Schritt 4: Letzter Fallback
return ["FÜNF", "VOR", "HALB", "SIEBEN"];
Ergebnis: "ES IST FAST HALB SIEBEN"
LED-Logik im Detail¶
Grundprinzip¶
LEDs zeigen die Differenz zwischen der angezeigten Zeit und der tatsächlichen Minute.
Die LED-Richtung wird durch den Offset bestimmt:
- Positiver Offset (tatsächliche Zeit > angezeigte Zeit) → LEDs von LINKS (addieren)
- Negativer Offset (tatsächliche Zeit < angezeigte Zeit) → LEDs von RECHTS (subtrahieren)
LED-Richtungs-Regeln¶
// 1. Berechne angezeigte Minute
const displayedMinute = calculateDisplayedMinute(wordsToLight);
// 2. Berechne Offset mit Vorzeichen
const signedOffset = mm - displayedMinute;
const remainder = Math.abs(signedOffset);
// 3. LED-Richtung: positiv = links, negativ = rechts
const isInvertedDisplay = signedOffset < 0;
// 4. LEDs anzeigen
for(let i = 0; i < remainder; i++) {
const dotIndex = isInvertedDisplay ? (3 - i) : i;
if(dots[dotIndex]) dots[dotIndex].classList.add('lit');
}
DisplayedMinute-Berechnung¶
Die displayedMinute basiert auf den angezeigten Wörtern:
function calculateDisplayedMinute(wordsToLight) {
// HALB-basierte Kombinationen
if(wordsToLight.includes("HALB")) {
if(wordsToLight.includes("FÜNF") && wordsToLight.includes("VOR")) {
return 25; // FÜNF VOR HALB
} else if(wordsToLight.includes("ZEHN") && wordsToLight.includes("VOR")) {
return 20; // ZEHN VOR HALB
} else if(wordsToLight.includes("FÜNF") && wordsToLight.includes("NACH")) {
return 35; // FÜNF NACH HALB
} else {
return 30; // Nur HALB
}
}
// Minutenmarker mit VOR/NACH
if(wordsToLight.includes("ZWANZIG")) {
return wordsToLight.includes("VOR") ? 40 : 20;
}
// ... weitere Minutenmarker
// Reine Präpositionen
if(wordsToLight.includes("VOR")) return 60; // Volle Stunde
if(wordsToLight.includes("NACH")) return 0; // Volle Stunde
return 0; // Fallback
}
LED-Tabelle¶
| Angezeigte Zeit | displayedMinute | Beispielzeit | Offset | Richtung | LEDs |
|---|---|---|---|---|---|
| "FÜNF NACH" | :05 | 01:06 | +1 | Links | ● |
| "FÜNF NACH" | :05 | 01:07 | +2 | Links | ●● |
| "BALD FÜNF NACH" | :05 | 01:04 | -1 | Rechts | ● |
| "ZEHN VOR" | :50 | 03:51 | +1 | Links | ● |
| "FÜNF VOR" | :55 | 10:53 | -2 | Rechts | ●● |
| "BALD HALB" | :30 | 06:27 | -3 | Rechts | ●●● |
| "HALB" | :30 | 13:31 | +1 | Links | ● |
| "NACH" (allein) | :00 | 01:02 | +2 | Links | ●● |
| "VOR" (allein) | :60 | 10:58 | -2 | Rechts | ●● |
Detaillierte Beispiele¶
Beispiel 1: FAST HALB (06:29)¶
Zeit: 06:29
Anzeige: "ES IST FAST HALB SIEBEN"
HALB SIEBEN = 06:30
Berechnung:
- mm = 29
- wordsToLight = ["FAST", "HALB", "SIEBEN"]
- displayedMinute = 30 (HALB)
- signedOffset = 29 - 30 = -1 (negativ!)
- remainder = Math.abs(-1) = 1
- isInvertedDisplay = true (weil signedOffset < 0)
- LEDs: 1 von RECHTS
Bedeutung: "30 - 1 = 29 → Noch 1 Minute bis HALB SIEBEN"
Beispiel 2: BALD FÜNF NACH (01:03)¶
Zeit: 01:03
Anzeige: "ES IST BALD FÜNF NACH EINS"
FÜNF NACH EINS = 01:05
Berechnung:
- mm = 3
- wordsToLight = ["BALD", "FÜNF", "NACH", "EINS"]
- displayedMinute = 5 (FÜNF NACH)
- signedOffset = 3 - 5 = -2 (negativ!)
- remainder = Math.abs(-2) = 2
- isInvertedDisplay = true (weil signedOffset < 0)
- LEDs: 2 von RECHTS
Bedeutung: "5 - 2 = 3 → Noch 2 Minuten bis FÜNF NACH"
Beispiel 3: BALD HALB (06:27)¶
Zeit: 06:27
Anzeige: "ES IST BALD HALB SIEBEN"
HALB SIEBEN = 06:30
Berechnung:
- mm = 27
- wordsToLight = ["BALD", "HALB", "SIEBEN"]
- displayedMinute = 30 (HALB)
- signedOffset = 27 - 30 = -3 (negativ!)
- remainder = Math.abs(-3) = 3
- isInvertedDisplay = true (weil signedOffset < 0)
- LEDs: 3 von RECHTS
Bedeutung: "30 - 3 = 27 → Noch 3 Minuten bis HALB SIEBEN"
Beispiel 4: FÜNF NACH (01:06)¶
Zeit: 01:06
Anzeige: "ES IST FÜNF NACH EINS"
FÜNF NACH EINS = 01:05
Berechnung:
- mm = 6
- wordsToLight = ["FÜNF", "NACH", "EINS"]
- displayedMinute = 5 (FÜNF NACH)
- signedOffset = 6 - 5 = +1 (positiv!)
- remainder = Math.abs(+1) = 1
- isInvertedDisplay = false (weil signedOffset > 0)
- LEDs: 1 von LINKS
Bedeutung: "5 + 1 = 6 → FÜNF NACH plus 1 = SECHS NACH"
Beispiel 5: FÜNF VOR (10:53)¶
Zeit: 10:53
Anzeige: "ES IST FÜNF VOR ELF"
FÜNF VOR ELF = :55
Berechnung:
- mm = 53
- wordsToLight = ["FÜNF", "VOR", "ELF"]
- displayedMinute = 55 (FÜNF VOR)
- signedOffset = 53 - 55 = -2 (negativ!)
- remainder = Math.abs(-2) = 2
- isInvertedDisplay = true (weil signedOffset < 0)
- LEDs: 2 von RECHTS
Bedeutung: "55 - 2 = 53 → Noch 2 Minuten bis FÜNF VOR"
Warum zwei verschiedene Berechnungen?¶
Der Offset-Ansatz ist konsistent und intuitiv:
- Positiver Offset → Die angezeigte Zeit liegt in der Vergangenheit → LEDs addieren
- Negativer Offset → Die angezeigte Zeit liegt in der Zukunft → LEDs subtrahieren
Beispiele:
- "FÜNF NACH" (angezeigt: :05) bei :06
- Offset: +1 → "Die Anzeige ist 1 Minute alt"
-
LEDs von links: 5 + 1 = 6 ✓
-
"BALD HALB" (angezeigt: :30) bei :27
- Offset: -3 → "Die Anzeige liegt 3 Minuten in der Zukunft"
-
LEDs von rechts: 30 - 3 = 27 ✓
-
"FÜNF VOR" (angezeigt: :55) bei :53
- Offset: -2 → "Die Anzeige liegt 2 Minuten in der Zukunft"
- LEDs von rechts: 55 - 2 = 53 ✓
Vorteil: Die LED-Richtung ergibt sich automatisch aus dem Offset, nicht aus den Schlüsselwörtern!
Praxisbeispiele¶
Szenario 1: Vollständiges Pattern mit allen Modifikatoren¶
Pattern:
ES_IST_BALDFÜNFZWANZIGDREIVIERTELZEHNKURZEITFASTNACHVOR_HALB_VIER_ZWEINSIEBENELFZWÖLFÜNFZEHNEUNACHT_SECHS_DREI
Verfügbare Modifikatoren:
- BALD (Position 7-10)
- FÜNFZWANZIG → enthält FÜNF, ZWANZIG (nicht als einzelne Wörter)
- DREIVIERTEL (Position 12-22)
- ZEHN (Position 23-26)
- KURZ (Position 27-30, aber in "KURZZEIT")
- FAST (Position 36-39)
- NACH (Position 40-43)
- VOR (Position 44-46)
- HALB (Position 48-51)
Zeitbeispiele:
| Zeit | Anzeige | LEDs | Erklärung |
|---|---|---|---|
| 06:29 | ES IST FAST HALB SIEBEN | 1 rechts | FAST validiert , 1 Min bis :30 |
| 06:28 | ES IST KURZ VOR HALB SIEBEN | 2 rechts | KURZ VOR, 2 Min bis :30 |
| 06:27 | ES IST BALD HALB SIEBEN | 3 rechts | BALD (kein FAST+KURZ gleichzeitig) |
| 01:04 | ES IST FAST FÜNF NACH EINS | 1 rechts | FAST validiert, 1 Min bis :05 |
| 01:03 | ES IST BALD FÜNF NACH EINS | 2 rechts | BALD, 2 Min bis :05 |
Szenario 2: Pattern ohne FAST¶
Pattern:
Verfügbare Modifikatoren:
- BALD
- FAST (nicht vorhanden)
Zeitbeispiele:
| Zeit | Anzeige | LEDs | Erklärung |
|---|---|---|---|
| 06:29 | ES IST BALD HALB SIEBEN | 2 rechts | Kein FAST → BALD |
| 01:04 | ES IST BALD FÜNF NACH EINS | 1 rechts | Kein FAST → BALD |
Szenario 3: Pattern ohne BALD und FAST¶
Pattern:
Verfügbare Modifikatoren:
- BALD (nicht vorhanden)
- FAST (nicht vorhanden)
Zeitbeispiele:
| Zeit | Anzeige | LEDs | Erklärung |
|---|---|---|---|
| 06:29 | ES IST FÜNF VOR HALB SIEBEN | 4 links | Kein Modifikator, normales VOR |
| 01:04 | ES IST NACH EINS | 4 links | Kein Modifikator, nur NACH |
| 01:06 | ES IST FÜNF NACH EINS | 1 links | Normale NACH-Zeit |
Szenario 4: FAST an falscher Position¶
Pattern:
Verfügbare Modifikatoren:
- BALD
- FAST (aber NACH HALB! )
06:29:
Handler für :29:
1. Prüfe FAST:
- hasFast = true
- Teste: ["ES", "IST", "FAST", "HALB", "SIEBEN"]
- Validierung: FAST steht NACH HALB
- Validierung fehlgeschlagen
2. Prüfe KURZ:
- hasKurz = false
3. Prüfe BALD:
- hasBald = true
- Ergebnis: ["BALD", "HALB", "SIEBEN"]
Anzeige: "ES IST BALD HALB SIEBEN" + 1 LED rechts
URL-Parameter¶
Verwendung¶
Die Seite unterstützt URL-Parameter zum direkten Laden von Pattern und Zeit:
Parameter¶
| Parameter | Format | Beispiel | Beschreibung |
|---|---|---|---|
pattern |
String | ES_IST_BALD... |
Lädt das Pattern ins Grid |
time |
HH:MM oder H:MM |
06:29 oder 6:29 |
Setzt Simulation auf Zeit |
Beispiele¶
Nur Pattern:
?pattern=ES_IST_BALDFÜNFZWANZIGDREIVIERTELZEHNKURZEITFASTNACHVOR_HALB_VIER_ZWEINSIEBENELFZWÖLFÜNFZEHNEUNACHT_SECHS_DREI
Pattern + Zeit:
Nur Zeit:
Implementierung¶
Die Funktion loadFromURLParams() wird beim Laden der Seite automatisch aufgerufen:
function loadFromURLParams() {
const params = new URLSearchParams(window.location.search);
// Pattern laden
const pattern = params.get('pattern');
if(pattern) {
const patternUpper = pattern.toUpperCase().replace(/\s+/g, '');
// ... Grid füllen
}
// Zeit setzen
const time = params.get('time');
if(time && /^\d{1,2}:\d{2}$/.test(time)) {
const [h, m] = time.split(':');
const formattedTime = `${h.padStart(2, '0')}:${m}`;
simSelector.value = formattedTime;
handleSimChange();
}
}
Migration von V0.1 zu V0.2¶
Kompatibilität¶
V0.2 ist vollständig rückwärtskompatibel mit V0.1:
- Alle Pattern, die in V0.1 funktionieren, funktionieren auch in V0.2
- Die Ausgabe ist identisch oder besser (menschlich-logischer)
Was sich ändert¶
-
Bessere Zeitangaben bei 1-Minute-vor-Marken
-
V0.1: "ES IST NACH EINS (+4)" bei 01:04
-
V0.2: "ES IST BALD FÜNF NACH EINS (+1)" bei 01:04
-
Korrekte LED-Richtung
-
V0.1: Immer von links
-
V0.2: Links bei NACH, rechts bei VOR/FAST/BALD
-
Validierung
-
V0.1: Keine Positionsprüfung
- V0.2: Prüft Position von Modifikatoren
Empfohlene Anpassungen¶
Wenn du ein bestehendes Pattern hast:
- Teste mit V0.2: Öffne das Pattern in V0.2
- Prüfe kritische Zeiten: :04, :09, :14, :19, :29, :34, :39, :49, :54, :59
- Achte auf LED-Richtung: Bei VOR-Zeiten sollten LEDs von rechts sein
Wenn FAST an der falschen Position steht: - Option 1: Verschiebe FAST im Pattern vor HALB - Option 2: Entferne FAST (V0.2 nutzt dann BALD als Fallback)
Technische Details¶
MINUTE_RULES Array¶
Zentrale Datenstruktur mit 60 Regel-Objekten (eine pro Minute):
const MINUTE_RULES = [
{ range: [0, 0], handler: (ctx) => {...} }, // :00
{ range: [1, 2], handler: (ctx) => {...} }, // :01-:02
// ... 58 weitere Regeln
{ range: [59, 59], handler: (ctx) => {...} } // :59
];
Handler-Funktion Struktur¶
Jeder Handler folgt diesem Pattern:
handler: (ctx) => {
// 1. Prüfe primäre Option mit Validierung
if(ctx.hasModifier) {
const testWords = [...ctx.words, "MODIFIER", "TIME", ctx.hourWord];
if(validateWordSequence(testWords, ctx.gridStr).valid) {
return ["MODIFIER", "TIME", ctx.hourWord];
}
}
// 2. Fallback zu sekundärer Option
if(ctx.hasSecondary) {
return ["SECONDARY", "TIME", ctx.hourWord];
}
// 3. Letzter Fallback
return ["BASE", "TIME", ctx.hourWord];
}
Validierungs-Algorithmus¶
function validateWordSequence(words, gridStr) {
let lastPos = -1;
const halbIndex = gridStr.indexOf("HALB");
for(const word of words) {
const pos = gridStr.indexOf(word, lastPos + 1);
if(pos === -1 || pos <= lastPos) {
return { valid: false, reason: "Sequence error" };
}
// Positionsbeschränkungen
if(isMinuteWord(word) && halbIndex !== -1 && pos > halbIndex) {
return { valid: false, reason: "Minute word after HALB" };
}
if(isHourWord(word) && halbIndex !== -1 && pos < halbIndex) {
return { valid: false, reason: "Hour word before HALB" };
}
lastPos = pos;
}
return { valid: true };
}
Performance¶
- Regel-Lookup: O(1) amortisiert (Binary Search über ranges)
- Validierung: O(n) wobei n = Anzahl Wörter (typisch 4-6)
- Gesamt: < 1ms pro Zeitberechnung
Zusammenfassung¶
CharGraph Editor V0.2 bietet:
- Regelbasierte Architektur - Wartbar, erweiterbar, verständlich
- Intelligente Validierung - Prüft Position von Modifikatoren
- Menschlich-logische Ausgaben - "BALD" statt "+4"
- Korrekte LED-Logik - Links/rechts je nach Kontext
- Vollständige Rückwärtskompatibilität - Alle V0.1 Pattern funktionieren
Kernlogik¶
Modifikator vorhanden?
└─> Ja → An richtiger Position?
└─> Ja → Verwenden
└─> Nein → Nächster Fallback
└─> Nein → Nächster Fallback
LED-Berechnung:
1. Berechne displayedMinute aus angezeigten Wörtern
2. Berechne Offset: signedOffset = actualMinute - displayedMinute
3. LED-Anzahl: remainder = |signedOffset|
4. LED-Richtung:
- Offset > 0 → Links (addieren)
- Offset < 0 → Rechts (subtrahieren)
Support¶
Bei Fragen oder Problemen: - GitHub Issues: https://github.com/anthropics/claude-code/issues - Dokumentation: Siehe diese Datei
Version: 0.2 Stand: 02. Januar 2026 Lizenz: Siehe Hauptprojekt