diff --git a/src/assets/i18n/de.json b/src/assets/i18n/de.json index 1875063..05bc8f1 100644 --- a/src/assets/i18n/de.json +++ b/src/assets/i18n/de.json @@ -193,7 +193,7 @@ "HIGHLIGHTS": { "P1": "Steam- und PlayStation-Sync von Spielen, Spielzeit, Erfolgen und Trophäen, dazu die Spieldauer (Time to Beat) aus IGDB.", "P2": "„Was spiele ich als Nächstes?“: sieben Reihen mit Vorschlägen, gefiltert nach Zeitbudget; ein Stimmungsfilter für die Zufallsvorschläge.", - "P3": "Auth signiert die Tokens mit Ed25519; Core holt den öffentlichen Schlüssel über die Key-ID und prüft nur." + "P3": "Auth signiert die Tokens mit Ed25519; Core holt den öffentlichen Schlüssel über die Key-ID und kann Tokens nur prüfen." } }, "CLAUDE_BOX": { @@ -241,7 +241,7 @@ }, "DIPLOMA": { "TITLE": "Diplomarbeit - Kollisionserkennung und Behandlung von komplexen Kleidungsstücken.", - "DESCRIPTION": "Selbstdurchdringungen innerhalb eines Kleidungsteils, etwa bei einem Plisseerock, in der Stoffsimulation Vidya bei assyst. Ich habe Intersection Contour Minimization an Vidyas k-DOP-Hierarchie angepasst und die Antwort um Masse und Geschwindigkeit erweitert. FH Bingen, März 2010.", + "DESCRIPTION": "Selbstdurchdringungen innerhalb eines Kleidungsteils, etwa bei einem Plisseerock, in der Stoffsimulation Vidya bei assyst. Ich habe Intersection Contour Minimization an Vidyas k-DOP-Hierarchie angepasst und die Kollisionsantwort um Masse und Geschwindigkeit erweitert. FH Bingen, März 2010.", "LINK_INTERNAL": "Projektdetails", "HIGHLIGHTS": { "P1": "Erkennung auf der vorhandenen BVH mit k-DOPs; eine gemeinsame Hierarchie hat die Zeit für Update und Traversierung halbiert.", @@ -311,27 +311,27 @@ "PLAYGROUND": { "TITLE": "Playground", "SHORT_DESCRIPTION": "Diese Seite: ein Portfolio mit Algorithmus-Visualisierungen, die im Browser laufen, einige davon auf der GPU.", - "INTRODUCTION": "Auf dieser Seite probiere ich Dinge aus. Ich wollte sehen, wie weit ein normaler Browser mit den Algorithmen kommt, die ich mag. Deshalb läuft jede Visualisierung im Client: Pfadfindung, Labyrinth-Erzeugung, Sortieren, Conways Spiel des Lebens, Fraktale in GLSL-Shadern und zwei Physiksimulationen, die als Compute Shader auf WebGPU laufen. Der Rest ist eine normale Angular-App mit deutschen und englischen Texten, einem Dark Mode und einer CI-Pipeline, die sie baut, testet und als Docker-Image in meine eigene Registry schiebt.", - "BULLET_1": "Pfadfindung mit Dijkstra und A*, Labyrinth-Erzeugung mit Prim und Kruskal, fünf Sortieralgorithmen mit Ton, Conways Spiel des Lebens und eine Vier-Farben-Karte.", + "INTRODUCTION": "Auf dieser Seite probiere ich Dinge aus. Ich wollte sehen, wie weit ein normaler Browser mit den Algorithmen kommt, die ich mag. Deshalb läuft jede Visualisierung im Client: Pfadfindung, Labyrinth-Erzeugung, Sortieren, Conways Game of Life, Fraktale in GLSL-Shadern und zwei Physiksimulationen, die als Compute Shader auf WebGPU laufen. Der Rest ist eine normale Angular-App mit deutschen und englischen Texten, einem Dark Mode und einer CI-Pipeline, die sie baut, testet und als Docker-Image in meine eigene Registry schiebt.", + "BULLET_1": "Pfadfindung mit Dijkstra und A*, Labyrinth-Erzeugung mit Prim und Kruskal, fünf Sortieralgorithmen mit Ton, Conways Game of Life und eine Vier-Farben-Karte.", "BULLET_2": "2D-Fraktale (Mandelbrot, Julia, Burning Ship, Newton) in einem GLSL-Shader und 3D-Fraktale (Mandelbulb, Mandelbox, Julia-Bulb) per Raymarching, mit Normalen aus dem Distanzfeld.", - "BULLET_3": "Ein Doppelpendel und eine Stoffsimulation laufen als Compute Shader auf WebGPU. Ohne WebGPU weichen sie auf die CPU oder WebGL aus und sagen das in einer Snackbar.", + "BULLET_3": "Ein Doppelpendel und eine Stoffsimulation laufen als Compute Shader auf WebGPU. Ohne WebGPU weichen sie auf die CPU oder WebGL aus und melden das in einer Snackbar.", "BULLET_4": "Angular mit Material und Tailwind, Signals für den Zustand, Deutsch und Englisch und ein Service Worker (PWA).", "BULLET_5": "CI auf Gitea Actions: Lint, i18n-Prüfung, Tests, Produktions-Build und ein Lighthouse-Bericht. Auf main baut sie ein Docker-Image und schiebt es in meine eigene Registry.", - "CHALLENGE_1": "Der Stoff-Solver auf der GPU. XPBD-Constraints, die sich einen Partikel teilen, lassen sich nicht parallel lösen, ohne dass es zu einem Race kommt. Ich gruppiere die Constraints per Graphfärbung in acht Phasen. So berühren sich innerhalb einer Phase nie zwei Constraints am selben Partikel, und jede Phase läuft voll parallel.", + "CHALLENGE_1": "Der Stoff-Solver auf der GPU. XPBD-Constraints, die sich einen Partikel teilen, lassen sich nicht parallel lösen, ohne eine Race Condition auszulösen. Ich gruppiere die Constraints per Graphfärbung in acht Phasen. So berühren innerhalb einer Phase nie zwei Constraints denselben Partikel, und jede Phase läuft voll parallel.", "CHALLENGE_2": "Nicht jeder Browser hat WebGPU. Beide Simulationen haben einen CPU- oder WebGL-Pfad und eine Snackbar, die sagt, dass der Fallback aktiv ist. So funktioniert die Seite überall, und niemand fragt sich, warum es langsamer ist.", "LEARNING_1": "Compute Shader verändern, wie man über einen Algorithmus denkt. Das Datenlayout und die Reihenfolge der Schreibzugriffe zählen mehr als die Mathematik.", - "LEARNING_2": "Eine kleine CI mit einer Schlüssel-Prüfung für die Übersetzungen und einem Lighthouse-Lauf fängt die langweiligen Fehler ab, eine fehlende Übersetzung oder eine langsame Seite, bevor ich sie auf der Live-Seite sehe." + "LEARNING_2": "Eine kleine CI mit einem Abgleich der Übersetzungsschlüssel und einem Lighthouse-Lauf fängt die langweiligen Fehler ab (eine fehlende Übersetzung, eine langsame Seite), bevor ich sie auf der Live-Seite sehe." }, "TRIBBLE": { "TITLE": "Self-Hosted Infrastructure", "SHORT_DESCRIPTION": "Home-Server für CI und private Dienste, dazu ein VPS für die öffentlichen Seiten.", "INTRODUCTION": "Zwei Maschinen. Zu Hause läuft auf einem Mini-PC namens „Tribble“ Gitea mit Actions-Runner und Container-Registry, dazu die privaten Dienste. Ein Hostinger-VPS liefert die öffentlichen Seiten aus, auch diese hier. Ein Git-Push baut das Image zu Hause, der VPS holt es mit Watchtower über Tailscale, und Traefik übernimmt TLS und Routing. Das Ganze läuft seit Mai 2024.", - "BULLET_1": "Hardware: ein KODLIX GD50 Mini-PC mit 8-Kern-Intel-Core-i5, 32 GB RAM und einer 512-GB-SSD.", + "BULLET_1": "Hardware: ein KODLIX GD50 Mini-PC mit einem 8-Kern-Intel-Core-i5, 32 GB RAM und einer 512-GB-SSD.", "BULLET_2": "Gitea für Git, den Actions-Runner und die Container-Registry. Diese Seite und To Be Played werden dort gebaut und getestet.", - "BULLET_3": "Private Dienste: Jellyfin für Medien, Paperless-ngx für Dokumente, AdGuard Home für DNS und Werbeblocker, Portainer, um die Container im Blick zu haben, und Traefik davor.", + "BULLET_3": "Private Dienste: Jellyfin für Medien, Paperless-ngx für Dokumente, AdGuard Home als DNS-Server und Werbeblocker, Portainer, um die Container im Blick zu haben, und Traefik davor.", "BULLET_4": "Tailscale verbindet die beiden Maschinen. Darüber holt der VPS neue Images aus der Registry zu Hause, und Watchtower tauscht den laufenden Container aus.", "BULLET_5": "Die Backups gehen auf Google Drive.", - "CHALLENGE_1": "DNS. Namen müssen aus dem Haus, über Tailscale und aus dem öffentlichen Internet auf die richtige Adresse zeigen, und AdGuard Home sitzt mittendrin. Das richtig hinzubekommen hat länger gedauert als jeder einzelne Dienst.", + "CHALLENGE_1": "DNS. Namen müssen von zu Hause aus, über Tailscale und aus dem öffentlichen Internet auf die richtige Adresse zeigen, und AdGuard Home sitzt mittendrin. Das richtig hinzubekommen hat länger gedauert als jeder einzelne Dienst.", "CHALLENGE_2": "Alles gleichzeitig zum Laufen zu bringen und es dann wiederholbar zu machen. Das erste Setup war eine lange Liste von Handgriffen. Ich habe es automatisiert, damit sich der Server neu aufsetzen lässt, ohne dass ich mir jeden Schritt merken muss.", "LEARNING_1": "Selbst hosten kostet wenig Geld und viel Zeit. Die meiste Zeit geht in die Teile, die niemand sieht: DNS, Zertifikate und Backups.", "LEARNING_2": "Ein Git-Push, der ohne Handgriff dazwischen in einem laufenden Container auf dem VPS endet, ist der Teil dieses Setups, den ich nicht mehr hergeben würde." @@ -347,7 +347,7 @@ "CHALLENGE_1": "Der Steam-Release als Ein-Personen-Technikteam. Builds, Store-Einrichtung, Upload und die Early-Access-Phase liefen alle über mich. Early Access startete im Juli 2021, die Vollversion folgte im Februar 2022.", "CHALLENGE_2": "Drei Sprachen. Jeden Text gibt es auf Englisch, Deutsch und Spanisch; für Spanisch habe ich mit einem Freund zusammengearbeitet, der Muttersprachler ist.", "CHALLENGE_3": "Die gegnerischen Einheiten. Dafür habe ich eine eigene KI und mein eigenes A*-Pathfinding auf dem Kartenraster geschrieben.", - "LEARNING_1": "Fertigmachen dauert länger als Bauen. Allein die Strecke vom Early Access bis zur Vollversion hat etwa sieben Monate gedauert.", + "LEARNING_1": "Fertigstellen dauert länger als Bauen. Allein die Strecke vom Early Access bis zur Vollversion hat etwa sieben Monate gedauert.", "LEARNING_2": "Eine klare Rollenverteilung hat ein Zwei-Personen-Team drei Jahre lang arbeitsfähig gehalten: Er war für das Spiel zuständig, ich für den Code und den Release." }, "GAME_JAMS": { @@ -360,20 +360,20 @@ "BULLET_4": "Immer 48 Stunden, von Freitag bis Sonntag, in einem Team aus zwei oder drei Leuten.", "CHALLENGE_1": "Der Umfang. 48 Stunden sind nicht viel, und mit Schlaf sind es weniger. Am Freitag zu entscheiden, was wir nicht bauen, war in jeder Jam die wichtigste Entscheidung.", "CHALLENGE_2": "Als einziger Programmierer. Alles, was sich das Team ausgedacht hat, ging durch meine Tastatur. Eine Idee musste also billig zu bauen sein, sonst wurde sie gestrichen.", - "LEARNING_1": "Eine spielbare Schleife am Samstag schlägt eine bessere Idee am Sonntag.", + "LEARNING_1": "Ein spielbarer Gameplay-Loop am Samstag schlägt eine bessere Idee am Sonntag.", "LEARNING_2": "In einer Jam finde ich heraus, ob eine Spielidee funktioniert, bevor ich Jahre in sie stecke. El Mucho hat drei Jahre gedauert; ein Jam-Spiel dauert ein Wochenende.", "LINK_COLLECTION": "Alle Spiele auf itch.io" }, "DIPLOMA": { "TITLE": "Diplomarbeit: Selbstkollisionen bei Stoffen", "SHORT_DESCRIPTION": "Selbstdurchdringungen in simulierter Kleidung erkennen und auflösen, entstanden bei assyst für die Stoffsimulation Vidya (2010).", - "INTRODUCTION": "Simulierter Stoff faltet sich in sich selbst. Ein Plisseerock durchdringt zum Beispiel sein eigenes Gewebe, und sobald zwei Teile eines Kleidungsstücks durcheinander gerutscht sind, kann eine normale Kollisionsantwort sie nicht mehr trennen. Kollisionen zwischen verschiedenen Lagen konnte Vidya schon; Selbstdurchdringungen innerhalb eines Kleidungsteils nicht. In meiner Diplomarbeit bei assyst habe ich Intersection Contour Minimization (Volino et al. 2006) an Vidya angepasst und so erweitert, dass die Antwort Masse und Geschwindigkeit berücksichtigt. Geschrieben in Java, im März 2010 an der FH Bingen abgeschlossen und für den produktiven Einsatz vorgesehen.", + "INTRODUCTION": "Simulierter Stoff faltet sich in sich selbst hinein. Ein Plisseerock durchdringt zum Beispiel sein eigenes Gewebe, und sobald zwei Teile eines Kleidungsstücks einander durchdrungen haben, kann eine normale Kollisionsantwort sie nicht mehr voneinander trennen. Kollisionen zwischen verschiedenen Lagen konnte Vidya schon; Selbstdurchdringungen innerhalb eines Kleidungsteils nicht. In meiner Diplomarbeit bei assyst habe ich Intersection Contour Minimization (Volino et al. 2006) an Vidya angepasst und so erweitert, dass die Kollisionsantwort Masse und Geschwindigkeit berücksichtigt. Geschrieben in Java, im März 2010 an der FH Bingen abgeschlossen und für den produktiven Einsatz vorgesehen.", "BULLET_1": "Die Erkennung läuft auf Vidyas vorhandener Bounding-Volume-Hierarchie mit k-DOPs. Bei Selbsttests überspringt die Traversierung die Tests an inneren Knoten, die sie nicht braucht.", "BULLET_2": "Die Schnittkontur wird aus Segmenten aufgebaut. Ein räumlicher Hash verbindet sie auch dann, wenn Gleitkommafehler kleine Lücken lassen.", - "BULLET_3": "Die Antwort ist meine Erweiterung: Sie nutzt Masse und Geschwindigkeit, sodass das Ergebnis physikalisch plausibel ist und nicht nur ein geometrischer Schubs.", + "BULLET_3": "Die Kollisionsantwort ist meine Erweiterung: Sie nutzt Masse und Geschwindigkeit, sodass das Ergebnis physikalisch plausibel ist und nicht nur ein geometrischer Schubs.", "BULLET_4": "Der Parameter h0 wird aus der mittleren Dreiecksfläche abgeleitet und muss daher nicht von Hand eingestellt werden.", "BULLET_5": "Die Methode teilt sich eine BVH mit Vidyas vorhandenem TriangleSelfCollisionDetector.", - "CHALLENGE_1": "Der Aufwand. Die Testszene hatte etwa 16.000 Partikel bei etwa 430 ms pro Schritt auf einem 2-GHz-Core-2-Duo. Eine gemeinsame BVH für meinen Detektor und den vorhandenen hat die Zeit für Update und Traversierung halbiert und den Overhead um etwa 80 % gesenkt. Zusätzlich zur vorhandenen Erkennung kostet die Methode etwa 5 %.", + "CHALLENGE_1": "Die Kosten. Die Testszene hatte etwa 16.000 Partikel bei etwa 430 ms pro Schritt auf einem 2-GHz-Core-2-Duo. Eine gemeinsame BVH für meinen Detektor und den vorhandenen hat die Zeit für Update und Traversierung halbiert und den Overhead um etwa 80 % gesenkt. Zusätzlich zur vorhandenen Erkennung kostet die Methode etwa 5 % mehr.", "CHALLENGE_2": "Die Wahl der Methode. Ich habe Baraff (2003), Bridson (2002) und Volino (2000, 2006) verglichen und die globale Variante der Intersection Contour Minimization genommen, weil sie auch Durchdringungen auflöst, die schon bestehen.", "CHALLENGE_3": "Bekannte Grenzen, dokumentiert: nahezu ebene Flächen und ein Vertex, der genau auf einer Dreiecksebene liegt, werden nicht behandelt.", "LEARNING_1": "Paper richtig lesen. Das gleiche Problem hatte vier veröffentlichte Lösungen, und die nützliche Arbeit war zu verstehen, wo jede davon scheitert.", @@ -402,10 +402,10 @@ "BULLET_3": "Öffentliche Profile, anderen Spielern folgen mit einer Inbox zu ihrem Fortschritt und Pile Points für jedes Spiel, das vom Stapel kommt.", "BULLET_4": "Deutsch und Englisch, Light- und Dark-Mode, auch auf dem Handy nutzbar. Auth, Core und Frontend werden jeweils als eigenes Docker-Image ausgeliefert.", "BULLET_5": "Über 3.400 Tests in den drei Diensten, ArchUnit-Regeln, die die Schichten getrennt halten, und Testcontainers für die echte Datenbank. Die CI führt den vollständigen Build aus und taggt jedes Image mit dem Commit.", - "CHALLENGE_1": "Login über zwei Dienste. Auth signiert die Tokens mit einem Ed25519-Schlüssel, Core kennt nur den öffentlichen Schlüssel und kann einen Login also prüfen, aber nie selbst ausstellen. Core holt diesen Schlüssel zur Laufzeit von Auth, gesucht über die Key-ID, und die Tokens liegen in HttpOnly-Cookies, kein Skript auf der Seite kann sie lesen.", + "CHALLENGE_1": "Login über zwei Dienste. Auth signiert die Tokens mit einem Ed25519-Schlüssel, Core kennt nur den öffentlichen Schlüssel und kann einen Login also prüfen, aber nie selbst ausstellen. Core holt diesen Schlüssel zur Laufzeit über die Key-ID von Auth, und die Tokens liegen in HttpOnly-Cookies, kein Skript auf der Seite kann sie lesen.", "CHALLENGE_2": "Die Limits von Steam. Jeder Steam-Aufruf ist auf zwei pro Sekunde begrenzt und wird nur dort wiederholt, wo eine Wiederholung sicher ist (Spielebibliothek, Schema, Erfolge). Ein Sync reserviert seinen Lauf zuerst in der Datenbank, damit nie zwei Syncs desselben Users gleichzeitig laufen, und der nächtliche Sync berührt nur User, die in den letzten 14 Tagen aktiv waren.", "CHALLENGE_3": "Ein Spiel vorzuschlagen ist schwerer, als es klingt. Jede Vorschlagsreihe braucht eigene Regeln, und die Listen hinter jeder Zahl müssen dieselben Regeln nutzen wie die Zahl selbst, damit ein Spiel überall gleich zählt.", - "CHALLENGE_4": "Missbrauch beim Login. Zu viele fehlgeschlagene Logins bekommen ein 429 mit Retry-After. Auch eine unbekannte E-Mail zahlt für eine volle BCrypt-Prüfung, sodass die Antwortzeit nichts verrät. Ein Header-Schutz blockt CSRF, CORS erlaubt nur exakte Origins, und das PlayStation-Token wird verschlüsselt gespeichert.", + "CHALLENGE_4": "Missbrauch beim Login. Zu viele fehlgeschlagene Logins bekommen die Antwort 429 mit Retry-After. Auch bei einer unbekannten E-Mail läuft eine volle BCrypt-Prüfung, sodass die Antwortzeit nichts verrät. Ein Header-Schutz blockiert CSRF, CORS erlaubt nur exakte Origins, und das PlayStation-Token wird verschlüsselt gespeichert.", "LEARNING_1": "Die Aufteilung in Dienste lohnt sich nur mit einem klaren Vertrag. Die Token-Claims stehen in einer Vertragsdatei, gegen die beide Dienste testen, und ein Skript, das ich von Hand starte, prüft, dass beide Kopien gleich sind.", "LEARNING_2": "Auch ein Hobbyprojekt braucht die langweiligen Teile: tägliche Datenbank-Dumps, die sieben Tage, vier Wochen und sechs Monate aufbewahrt werden, Migrationen und eine schriftlich festgehaltene Deploy-Reihenfolge. Sie haben mehr darüber entschieden, wie stabil es läuft, als jedes Feature." }