{ "APP": { "TITLE": "Playground", "COPYRIGHT": "Alle Rechte vorbehalten.", "AI_NOTICE": "Entstanden mit einem KI-gestützten Engineering-Workflow.", "AI_NOTICE_LINK": "Wie ich mit KI arbeite" }, "TOPBAR": { "ABOUT": "Über mich", "IMPRINT": "Impressum", "PROJECTS": "Projekte", "ALGORITHMS": "Algorithmen", "STOPWATCH": "Stoppuhr", "SETTINGS": "Einstellungen", "LANGUAGE": "Sprache", "APPEARANCE": "Darstellung" }, "THEME": { "LIGHT": "Hell", "DARK": "Dunkel" }, "LANG": { "LABEL": "Sprache", "EN": "Englisch", "DE": "Deutsch" }, "ABOUT": { "ALT": { "PROFILE": "Profilfoto von Andreas Dahm" }, "HELLO": "Hallo, ich bin Andreas.", "LEAD": "Ich arbeite seit 2010 als Softwareentwickler, die meiste Zeit davon in zwei recht unterschiedlichen Bereichen. Der eine ist Echtzeit-3D-Simulation: Stoffphysik, Kollisionserkennung, GPU-Shader. Der andere ist Cloud-Architektur für größere Unternehmensplattformen. Ein paar Jahre habe ich ein kleines Backend-Team geleitet, den Umbau eines großen Legacy-Monolithen Richtung Microservices geplant und zusammen mit meinem Bruder ein Spiel auf Steam veröffentlicht. Zuletzt stecke ich viel Zeit in Tooling rund um KI-gestützte Entwicklung, vor allem in die Frage, wie das Ergebnis überprüfbar bleibt. Privat baue ich Spiele und probiere Sachen mit Algorithmen aus. Das meiste davon landet irgendwann auf dieser Seite.", "ROLE": "Senior Software Entwickler / Full-Stack Entwickler / Softwarearchitekt", "LOCATION": "München · Remote", "VIEW_PROJECTS": "Projekte ansehen", "SECTION": { "SKILLS": "Fähigkeiten & Stack", "LANGUAGES": "Sprachen & Frameworks", "ARCH_CLOUD": "Architektur & Cloud", "SIM_ALGO": "Simulation & Algorithmen", "AI_ENG": "KI-gestütztes Engineering", "EXPERIENCE": "Erfahrung", "PROJECTS": "Projekte", "EDUCATION": "Ausbildung", "SHOW_EARLIER_XP": "Frühere Stationen anzeigen", "SHOW_EARLIER_EDU": "Frühere Ausbildung anzeigen", "SHOW_LESS": "Weniger anzeigen" }, "SKILLS": { "JAVA": "Java", "SPRING": "Spring Boot", "ANGULAR": "Angular", "VUE": "Vue.js", "REACT": "React", "DOCKER": "Docker", "UNITY": "Unity", "PYTHON": "Python", "CSHARP": "C#", "TYPESCRIPT": "TypeScript", "ARCH_MICROSERVICES": "Microservices", "ARCH_CLOUD": "Cloud Architecture", "ENG_ALGO": "Algorithm Design", "ENG_SIM": "3D Simulation", "ENG_GPU": "WebGPU / OpenGL / GLSL", "ENG_PERF": "Performance Optimization", "ENG_3D": "3D-Scanner Tech", "AI_AGENTS": "Claude Code / Agent SDK", "AI_MCP": "MCP-Server", "AI_CONTEXT": "Context- & Prompt-Engineering", "AI_HARNESS": "Agent-Harness-Design", "AI_GATES": "Automatisierte Review-Gates" }, "TOOLS": { "GIT": "Git", "GITHUB": "Github", "GITLAB": "Gitlab", "JENKINS": "Jenkins", "K8S": "Kubernetes / k3d", "POSTGRES": "PostgreSQL", "MONGO": "MongoDB", "GRAFANA": "Grafana/Prometheus", "DOCKER": "Docker" }, "XP": { "COMPANY9": { "COMPANY": "ColorDigital GmbH", "ROLE": "Senior Software Engineer - DMIxOS Team", "TIME": "März 2026 – heute", "HIGHLIGHTS": { "P1": "Konzeption und Implementierung der zentralen Cloud-Architektur.", "P2": "Full-Stack-Entwicklung und Optimierung neuer sowie bestehender Systemkomponenten.", "P3": "Technische Integration von Partnerunternehmen an das eigene Ökosystem." } }, "COMPANY8": { "COMPANY": "Teraport GmbH", "ROLE": "Senior Software Developer / Architect", "TIME": "Feb. 2024 – Feb. 2026", "HIGHLIGHTS": { "P1": "Architektur und Implementierung der Datenbankanbindung mit Hibernate 6.x.", "P2": "Konzeption und Entwicklung einer Full-Stack-Webanwendung für Kollisionsanalysen (Angular + Spring Boot + Docker).", "P3": "Entwicklung eines Kostenanalysetools mit dem Namen MIDO." } }, "COMPANY7": { "COMPANY": "ColorDigital GmbH", "ROLE": "Lead Software Developer", "TIME": "März 2023 – Dez. 2023", "HIGHLIGHTS": { "P1": "Planung einer neuen Cloud-Architektur zur Migration eines umfangreichen Legacy-Monolithen in eine moderne Microservice-Plattform.", "P2": "Leitung eines Teams von drei bis vier Backend-Entwickler:innen.", "P3": "Zusammenarbeit mit Partnern und Kund:innen bei der Integration ihrer Systeme in die DMIx-Cloud." } }, "COMPANY6": { "COMPANY": "ColorDigital GmbH", "ROLE": "Senior Software Developer", "TIME": "März 2021 – März 2023", "HIGHLIGHTS": { "P1": "Arbeit an der DMIx-Core-Cloud, darunter die Implementierung einer Elasticsearch-basierten Echtzeit-Suchfunktion.", "P2": "Implementierung des neuen Browser-Tools „Moodboard“ (Node.js + Vue.js).", "P3": "Entwicklung von „PAX“, einem Tool für den Datenaustausch zwischen der DMIx-Cloud und PLM/ERP-Systemen, inkl. CI/CD-Integration." } }, "COMPANY5": { "COMPANY": "Assyst GmbH", "ROLE": "Teamleitung", "TIME": "Sep. 2015 – Feb. 2021", "HIGHLIGHTS": { "P1": "Teamleitung der Vidya-Softwareabteilung, inkl. Personalverantwortung und Bewerbungsprozesse.", "P2": "Leitung mehrerer Projekte mit externen Partnern und Freelancer:innen.", "P3": "Steuerung strategischer Initiativen im Bereich 3D-Softwareentwicklung." } }, "COMPANY4": { "COMPANY": "Assyst GmbH", "ROLE": "Software Engineer", "TIME": "Apr. 2010 – Feb. 2021", "HIGHLIGHTS": { "P1": "Arbeit an Kerntechnologien wie Algorithmen, Simulation, Rendering und Kollisions­erkennung.", "P2": "Einsatz verschiedener Technologien, u. a. Java, OpenCL, OpenGL, GLSL-Shader, C++ und REST-Schnittstellen.", "P3": "Mitarbeit an nationalen und internationalen Forschungsprojekten (EU7 und ZIM)." } }, "COMPANY3": { "COMPANY": "Assyst GmbH", "ROLE": "Praktikum und Diplomarbeit", "TIME": "Apr. 2009 – Apr. 2010", "HIGHLIGHTS": { "P1": "Implementierung produktiver Funktionen in der Echtzeit-Kleidersimulationssoftware Vidya.", "P2": "Durchführung von Recherchen und Literaturstudien für die Diplomarbeit.", "P3": "Erfolgreiche Umsetzung des Diplomthemas in Vidya." } }, "COMPANY2": { "COMPANY": "TH Bingen (University of Applied Sciences)", "ROLE": "Tutor für Grundlagen der Java-Programmierung", "TIME": "Apr. 2008 – Aug. 2008", "HIGHLIGHTS": { "P1": "Erstellung praktischer und theoretischer Lehrmaterialien.", "P2": "Durchführung einer 20-stündigen Vorlesungsreihe für den Bioinformatik-Studiengang.", "P3": "Unterstützung bei praktischen Programmierübungen." } }, "COMPANY1": { "COMPANY": "TH Bingen (University of Applied Sciences)", "ROLE": "Wissenschaftliche Hilfskraft", "TIME": "Okt. 2007 – Apr. 2008", "HIGHLIGHTS": { "P1": "Evaluation verschiedener Web-2.0-Technologien.", "P2": "Implementierung einer Java-basierten Schnittstelle zu Microsoft Excel.", "P3": "Integration des MIT-Exhibit-Frameworks und Vorbereitung für die CeBIT 2008." } }, "COMPANY0": { "COMPANY": "Chamaeleon AG", "ROLE": "Ausbildung Fachinformatiker Anwendungsentwicklung", "TIME": "Jul. 2002 – Jun. 2005", "HIGHLIGHTS": { "P1": "Entwicklung in PERL, PHP und ASP.", "P2": "Portierung, Wartung und Reengineering von bestehender Software.", "P3": "Regelmäßige Durchführung von Systemtests und Qualitätskontrollen, sowie deren Dokumentation." } } }, "PROJECT": { "TBP": { "TITLE": "To Be Played", "DESCRIPTION": "Ein Backlog-Tracker für Steam und PlayStation, der meine Bibliotheken synchronisiert, Status, Bewertung und Notizen verwaltet und passend zur verfügbaren Zeit vorschlägt, was ich als Nächstes spielen kann. Drei selbst gehostete Dienste: Auth, Core und ein React-Frontend.", "LINK_EXTERNAL": "tbp.andreas-dahm.eu", "LINK_INTERNAL": "Projektdetails", "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 kann Tokens nur prüfen." } }, "CLAUDE_BOX": { "TITLE": "Claude-in-a-Box", "DESCRIPTION": "Eine Entwicklungsumgebung im Container, in der ein KI-Coding-Agent nach einem festen Regelsatz arbeitet und nicht nach dem, was ich gerade eintippe. Er zieht sich die Regeln für den Stack, den er im Repository vorfindet, und ein Git-Hook stoppt den Commit, wenn Build oder Tests rot sind.", "LINK_INTERNAL": "Projektdetails", "HIGHLIGHTS": { "P1": "Der Agent lädt die Regeln für den Stack, den er im Repo vorfindet. Ein Java-Service bekommt also andere als ein Frontend.", "P2": "Ein Commit-Hook führt Build und Tests aus und blockiert den Commit, wenn sie fehlschlagen.", "P3": "MCP-Server für Codesuche und lesenden Jira-Zugriff, dazu eigene Review-Agenten, die den Diff durchgehen." } }, "P2": { "TITLE": "Playground", "DESCRIPTION": "Diese Seite. Ein Portfolio und ein Ort, um Algorithmen im Browser auszuprobieren: Pfadfindung, Sortieren, Fraktale in GLSL sowie eine Stoff- und eine Doppelpendel-Simulation als WebGPU-Compute-Shader. Angular, Deutsch und Englisch, gebaut und ausgeliefert von meiner eigenen CI.", "LINK_EXTERNAL": "andreas-dahm.eu", "LINK_INTERNAL": "Projektdetails", "HIGHLIGHTS": { "P1": "Visualisierungen von Dijkstra bis zu 3D-Fraktalen per Raymarching, alles im Client.", "P2": "Zwei WebGPU-Compute-Simulationen (Stoff mit XPBD, Doppelpendel) mit CPU- oder WebGL-Fallback.", "P3": "Pipeline auf Gitea Actions: Lint, i18n-Prüfung, Tests, Lighthouse, Docker-Image in meine eigene Registry." } }, "P1": { "TITLE": "El Mucho", "DESCRIPTION": "Mein erstes veröffentlichtes Spiel, ein rundenbasiertes taktisches Rollenspiel, das ich mit meinem Bruder in etwa drei Jahren gemacht und 2022 auf Steam veröffentlicht habe. Er hat es entworfen, ich habe es gebaut.", "LINK_EXTERNAL": "Steam Store", "LINK_INTERNAL": "Projektdetails", "HIGHLIGHTS": { "P1": "Der gesamte Code in Unity (C#), die Builds, das Steam-Setup und der Release.", "P2": "16 Missionen, Erfolge, drei Sprachen (EN, DE, ES).", "P3": "Eine eigene KI für die gegnerischen Einheiten und mein eigenes A*-Pathfinding auf dem Kartenraster." } }, "P0": { "TITLE": "Game Jams", "DESCRIPTION": "Drei 48-Stunden-Jam-Spiele aus der BeansJam-Reihe, in Teams aus zwei oder drei Leuten. Ich war jedes Mal Programmierer und Leiter. Alle sind auf itch.io spielbar.", "LINK_EXTERNAL": "Itch.io Sammlung", "LINK_INTERNAL": "Projektdetails", "HIGHLIGHTS": { "P1": "Peter Blue and the carnival of death (2018), Arkonauts (2020), Sloth Bros (2022).", "P2": "Freitag bis Sonntag: jedes Mal ein spielbares Spiel in 48 Stunden.", "P3": "Ein günstiger Weg, herauszufinden, ob eine Spielidee funktioniert, bevor man Jahre in sie steckt." } }, "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 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.", "P2": "Vier veröffentlichte Methoden verglichen und die globale Variante der Intersection Contour Minimization gewählt.", "P3": "Etwa 20 % schneller als der vorhandene Selbstkollisions-Detektor plus Layering, für den produktiven Einsatz vorgesehen." } }, "TRIBBLE": { "TITLE": "Homeserver 'Tribble'", "DESCRIPTION": "Ein Mini-PC zu Hause betreibt Gitea mit Actions-Runner und Registry, dazu Jellyfin, Paperless-ngx und AdGuard Home. Ein VPS liefert die öffentlichen Seiten aus und holt neue Images mit Watchtower über Tailscale. Läuft seit Mai 2024, Backups auf Google Drive.", "LINK_INTERNAL": "Projektdetails", "HIGHLIGHTS": { "P1": "Gitea-CI baut und testet diese Seite und To Be Played zu Hause; Watchtower spielt die Images auf dem VPS ein.", "P2": "Traefik für TLS und Routing, Tailscale zwischen den beiden Maschinen.", "P3": "Schwierig waren DNS und ein wiederholbares Setup, nicht die Dienste selbst." } } }, "EDUCATION": { "E6": { "WHERE": "FH Bingen (University of Applied Sciences)", "WHEN": "2006 - 2010", "WHAT": "Diplom (FH), Angewandte Informatik (Schnitt 1.4)" }, "E5": { "WHERE": "FH Koblenz (University of Applied Sciences)", "WHEN": "2005 - 2006", "WHAT": "1 Semester Ingenieurinformatik" }, "E4": { "WHERE": "BBS Montabaur", "WHEN": "2002 - 2005", "WHAT": "Ausbildung zum Fachinformatiker Anwendungsentwicklung" }, "E3": { "WHERE": "BBS Andernach - Höhere Berufsbachschule (Informatik)", "WHEN": "2000 - 2002", "WHAT": "Fachhochschulreife" }, "E2": { "WHERE": "BBS Andernach - Berufsfachschule Technik (E-Technik)", "WHEN": "1998 - 2000", "WHAT": "Realschulabschluss" }, "E1": { "WHERE": "Hauptschule Hinter Burg Mayen (Albert Schweitzer Realschule plus)", "WHEN": "1995 - 1998 ", "WHAT": "Hauptschulabschluss" }, "E0": { "WHERE": "Albert Schweitzer Realschule Mayen", "WHEN": "1993 - 1995", "WHAT": "Kein Abschluss. Wechsel zur Hauptschule" } } }, "PROJECTS": { "DOWNLOAD": "Herunterladen", "READ_MORE": "Mehr erfahren", "DIAGRAM_ALT": "Architekturdiagramm", "LINK_TO_PROJECT": "Zum Projekt", "CLOSE": "Schließen", "SECTION": { "TECHNICAL": "Technische Herausforderungen", "LEARNINGS": "Learnings & Soft Skills" }, "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 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 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 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 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 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 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 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." }, "EL_MUCHO": { "TITLE": "El Mucho (Steam Release)", "SHORT_DESCRIPTION": "Ein rundenbasiertes taktisches Rollenspiel, das mein Bruder und ich auf Steam veröffentlicht haben.", "INTRODUCTION": "El Mucho ist ein rundenbasiertes taktisches Rollenspiel in einer erfundenen Welt namens Liberika, inspiriert von Langrisser. Mein Bruder und ich haben es in etwa drei Jahren neben einem Vollzeitjob gebaut und auf Steam veröffentlicht: Early Access im Juli 2021, die Vollversion im Februar 2022. Die Aufteilung war klar. Er hatte die Idee und hat die Assets, das Leveldesign, das Testen und das Balancing gemacht. Ich habe alles Technische gemacht: den Code in Unity, die Builds, das Steam-Setup und den Release.", "BULLET_1": "Unity und C#. Ein Entwickler für den gesamten Code.", "BULLET_2": "16 Missionen, Erfolge und Familienfreigabe auf Steam, veröffentlicht als Lobo Corp.", "BULLET_3": "Englisch, Deutsch und Spanisch. Die spanische Übersetzung hat ein Freund gemacht, der Muttersprachler ist.", "BULLET_4": "Grafik und Musik sind teils Open Source, teils von meinem Bruder, teils von mir.", "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": "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": { "TITLE": "Game Jams", "SHORT_DESCRIPTION": "Drei kleine Spiele, jedes in 48 Stunden von Freitag bis Sonntag entstanden.", "INTRODUCTION": "Bei einer Game Jam bekommt man am Freitagabend ein Thema und soll bis Sonntag ein spielbares Spiel abliefern. Ich habe bisher drei gemacht, alle in der BeansJam-Reihe, in Teams aus zwei oder drei Leuten. Ich war immer Programmierer und Leiter; die anderen haben Ideen, Assets und Tests beigesteuert. Alle drei Spiele gibt es auf itch.io.", "BULLET_1": "Peter Blue and the carnival of death (2018, BeansJam): meine erste Jam.", "BULLET_2": "Arkonauts (2020, Mini Beans Jam 6): Space Invaders im Weltraum.", "BULLET_3": "Sloth Bros (2022, BeansJam '22): Thema „wir müssen wach bleiben“, eines von 65 Spielen.", "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": "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 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 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": "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.", "LEARNING_2": "Erst messen, dann behaupten. Die Methode allein war etwa 20 % schneller als der vorhandene Detektor plus Layering, und diese Zahl zählt nur, weil sie aus derselben Szene auf derselben Maschine stammt." }, "CLAUDE_BOX": { "TITLE": "Claude-in-a-Box", "SHORT_DESCRIPTION": "Ein Container-Setup, um mit einem KI-Coding-Agenten nach einem festen Regelsatz zu arbeiten.", "INTRODUCTION": "Ein KI-Coding-Agent produziert den ganzen Tag Code, ohne sich zu beschweren. Das Problem ist, dass das Ergebnis meistens vernünftig aussieht, auch wenn es falsch ist, und die ganzen Kosten damit im Review landen. Also habe ich ein Docker-Setup gebaut, das den Agenten denselben Regeln unterwirft wie alle anderen, die am Repository arbeiten. Er liest die Architekturregeln des Projekts, in dem er gerade steckt, und der Build muss durchlaufen, bevor ein Commit möglich ist. Auf den Hook, der genau das durchsetzt, komme ich immer wieder zurück, weil er das Einzige ist, woran der Agent nicht vorbeikommt.", "BULLET_1": "Container mit festen Toolchains: JDKs und Maven über SDKMAN, Node und ein Home-Volume, das einen Rebuild übersteht.", "BULLET_2": "Die Regeln werden per Erkennung geladen. Ein Repo mit pom.xml bekommt die Java- und Spring-Regeln, eines mit package.json die TypeScript-Regeln, und Repos mit eigenen Architekturnotizen bekommen diese zusätzlich.", "BULLET_3": "Ein Commit-Hook ermittelt den Stack, führt den passenden Build und die Tests aus und verweigert den Commit, wenn sie fehlschlagen.", "BULLET_4": "MCP-Server für Code-Graph-Abfragen und lesende Jira-Zugriffe, dazu Review-Agenten, die einen fertigen Diff prüfen, bevor er als erledigt gilt.", "CHALLENGE_1": "Die Regeln kurz genug zu halten, dass sie noch etwas bringen. Lange allgemeine Richtlinien werden einfach ignoriert, also sind sie in Dateien pro Stack und pro Repo aufgeteilt, die nur bei Bedarf geladen werden.", "CHALLENGE_2": "Das Quality Gate so zu bauen, dass der Agent sich nicht daran vorbeireden kann. Die Prüfung läuft als Git-Hook außerhalb des Agenten, ein fehlgeschlagener Build blockiert den Commit also unabhängig davon, was der Agent über seine eigene Arbeit denkt.", "CHALLENGE_3": "Die Toolchains in einem Container ohne Root zum Laufen zu bringen. Builds, Browser und Sprach-Runtimes mussten alle in Volumes landen, die dem Entwickler-User gehören.", "LEARNING_1": "Die Formulierung der Prompts war deutlich weniger wichtig, als ich dachte. Fast der ganze Unterschied kam vom Drumherum.", "LEARNING_2": "Den Output des Agenten genauso zu reviewen und durch dieselben Gates zu schicken wie den Pull Request eines neuen Teammitglieds hat sich als die nützlichste Regel überhaupt herausgestellt." }, "TBP": { "TITLE": "To Be Played", "SHORT_DESCRIPTION": "Ein Backlog-Tracker für Steam und PlayStation, der bei der Entscheidung hilft, was als Nächstes gespielt wird.", "INTRODUCTION": "Meine Spielebibliothek hat weit mehr Spiele, als ich je durchspielen werde. To Be Played synchronisiert Steam und PlayStation, importiert Listen von GOG, Playnite, Grouvee und HowLongToBeat, verwaltet den Backlog mit Status, Bewertung und Notizen und schlägt passend zur verfügbaren Zeit vor, was ich als Nächstes spielen kann. Dahinter stehen drei Dienste: ein Auth-Service, der die Login-Tokens signiert, ein Core-Service für Bibliothek und Statistiken und ein React-Frontend. Alles läuft selbst gehostet auf demselben Server wie diese Seite.", "BULLET_1": "Steam-Sync von Spielen, Spielzeit, Erfolgen und Wunschliste, PlayStation-Sync von Titeln, Spielzeit und Trophäen, von Hand und jede Nacht, dazu die Spieldauer (Time to Beat) aus IGDB.", "BULLET_2": "„Was spiele ich als Nächstes?“: sieben Reihen mit je drei Vorschlägen, etwa der nächste Teil einer Reihe, dasselbe Studio oder Spiele, die Leute gespielt oder durchgespielt haben, denen man folgt. Ein Zeitbudget filtert jede Reihe; der Stimmungsfilter wirkt nur auf die Zufallsvorschläge.", "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 ü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 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." } }, "IMPRINT": { "TITLE": "Impressum", "PARAGRAPH": "Angaben gemäß § 5 DDG", "COUNTRY": "Deutschland", "CONTACT": "Kontakt" }, "PRIVACY": { "TITLE": "Datenschutzerklärung", "CONTROLLER": "Verantwortlicher", "SECTIONS": { "HOSTING": { "TITLE": "Hosting und Server-Logs", "TEXT": "Diese Seite wird bei Hostinger gehostet [TODO: Rechtsträger, Adresse und Aufbewahrungsdauer der Logs bestätigen; Auftragsverarbeitungsvertrag (Art. 28 DSGVO) bestätigen]. Der Webserver schreibt Server-Logs: IP-Adresse, Zeitpunkt, aufgerufene URL und User-Agent. Zweck: Betrieb und Sicherheit der Seite. Rechtsgrundlage: Art. 6 Abs. 1 lit. f DSGVO. Die Logs werden nach kurzer Zeit gelöscht [TODO: genaue Dauer]." }, "STORAGE": { "TITLE": "Lokaler Speicher", "TEXT": "Die Seite speichert nur Sprache und Design (hell oder dunkel) im localStorage des Browsers. Es gibt keine Cookies, kein Tracking und keine Analyse-Tools." }, "SERVICE_WORKER": { "TITLE": "Service Worker", "TEXT": "Ein Service Worker legt Dateien der Seite im Browser-Cache ab, damit die Seite schneller und offline lädt. Es werden keine Daten an Dritte gesendet." }, "FONTS": { "TITLE": "Schriften", "TEXT": "Schriften (Roboto, Material Icons) werden vom eigenen Server geladen. Es gibt keine Verbindung zu Google." }, "EXTERNAL_LINKS": { "TITLE": "Externe Links", "TEXT": "Links zu Gitea, LinkedIn und Codeberg werden erst beim Klick aufgerufen. Ab dann gilt die Datenschutzerklärung des jeweiligen Anbieters." }, "RIGHTS": { "TITLE": "Ihre Rechte", "TEXT": "Sie haben das Recht auf Auskunft, Berichtigung, Löschung, Einschränkung der Verarbeitung, Datenübertragbarkeit und Widerspruch (Art. 15 bis 21 DSGVO). Kontakt: die E-Mail-Adresse oben." }, "AUTHORITY": { "TITLE": "Beschwerderecht", "TEXT": "Sie können sich bei einer Aufsichtsbehörde beschweren. Zuständig: Bayerisches Landesamt für Datenschutzaufsicht (BayLDA), Promenade 18, 91522 Ansbach." } } }, "PATHFINDING": { "TITLE": "Pfadfindungsalgorithmen", "START_NODE": "Startknoten", "END_NODE": "Endknoten", "WALL": "Wand", "CLEAR_NODE": "Löschen", "DIJKSTRA": "Start Dijkstra", "ASTAR": "Start A*", "NORMAL_CASE": "Testaufbau", "EDGE_CASE": "A* Grenzfall-Aufbau", "RANDOM_CASE": "Zufälliger-Aufbau", "CLEAR_BOARD": "Leeres Gitter", "VISITED": "Besucht", "PATH": "Pfad", "COMPARE": "Vergleich (A* vs Dijkstra)", "VISITED_NODES": "Besuchte Knoten", "PATH_LENGTH": "Pfadlänge", "EXECUTION_TIME": "Ausführungszeit", "EXPLANATION": { "TITLE": "Algorithmen", "DIJKSTRA_EXPLANATION": " findet garantiert den kürzesten Weg, wenn alle Kantenkosten nicht-negativ sind. Vorteil: optimal und ohne Heuristik. Nachteil: besucht oft sehr viele Knoten (kann bei großen Grids langsamer wirken).", "ASTAR_EXPLANATION": " erweitert Dijkstra um eine Heuristik (z.B. Manhattan-Distanz) und kann dadurch wesentlich zielgerichteter suchen. Vorteil: oft deutlich schneller bei guter Heuristik; bei zulässiger Heuristik bleibt der Weg optimal. Nachteil: hängt stark von der Heuristik ab (schlechte Heuristik ≈ Dijkstra).", "DISCLAIMER": "Diese A*-Implementierung ist bewusst einfach gehalten. Es wird nur in vier Richtungen gegangen und jeder Schritt kostet 1. Die Heuristik ist minimal und dient nur dazu, das Prinzip von A* gegenüber Dijkstra zu demonstrieren. Ziel ist nicht ein optimaler oder produktionsreifer A*-Algorithmus, sondern eine anschauliche Visualisierung, wie Heuristiken die Suche beschleunigen können." }, "ALERT": { "START_END_NODES": "Bitte wählen Sie einen Start- und Endknoten aus, bevor Sie den Algorithmus starten." } }, "SORTING": { "TITLE": "Sortieralgorithmen", "ALGORITHM": "Algorithmen", "START": "Sortierung starten", "RESET": "Zurücksetzen", "GENERATE_NEW_ARRAY": "Neues Array generieren", "SOUND_ON": "Ton an", "SOUND_OFF": "Ton aus", "EXECUTION_TIME": "Ausführungszeit", "ARRAY_SIZE": "Anzahl der Balken", "EXPLANATION": { "TITLE": "Algorithmen", "BUBBLE_SORT_EXPLANATION": "vergleicht wiederholt benachbarte Elemente und tauscht sie, wenn sie in der falschen Reihenfolge stehen. Das größte Element \"blubbert\" dabei wie eine Luftblase ans Ende der Liste. Vorteil: Extrem einfach zu verstehen und zu implementieren; erkennt bereits sortierte Listen sehr schnell. Nachteil: Sehr ineffizient bei großen Listen (Laufzeit O(n²)). In der Praxis kaum genutzt.", "QUICK_SORT_EXPLANATION": "folgt dem \"Teile und Herrsche\"-Prinzip. Ein \"Pivot\"-Element wird gewählt, und das Array wird in zwei Hälften geteilt: Elemente kleiner als das Pivot und Elemente größer als das Pivot. Vorteil: Im Durchschnitt einer der schnellsten Sortieralgorithmen (O(n log n)); benötigt keinen zusätzlichen Speicher (In-Place). Nachteil: Im schlechtesten Fall (Worst Case) langsam (O(n²)), wenn das Pivot ungünstig gewählt wird. Ist nicht stabil (ändert Reihenfolge gleicher Elemente).", "HEAP_SORT_EXPLANATION": "organisiert die Daten zunächst in einer speziellen Baumstruktur (Binary Heap). Das größte Element (die Wurzel) wird entnommen und ans Ende sortiert, dann wird der Baum repariert. Vorteil: Garantiert eine schnelle Laufzeit von O(n log n), selbst im schlechtesten Fall. Benötigt fast keinen zusätzlichen Speicher. Nachteil: In der Praxis oft etwas langsamer als Quick Sort, da die Sprünge im Speicher (Heap-Struktur) den CPU-Cache schlechter nutzen.", "TIMSORT_EXPLANATION": "ist ein hybrider Sortieralgorithmus, der aus Merge Sort und Insertion Sort kombiniert ist. Er unterteilt das Array in kleine 'Runs' und sortiert jeden davon mit Insertion Sort, um sie anschließend schrittweise mit Merge Sort zusammenzuführen. Vorteil: Extrem effizient bei realen Daten, die oft teilweise sortiert sind – O(n log n) im schlechtesten und O(n) im besten Fall. Er ist der Standardalgorithmus in Python und Java. Nachteil: Komplexer zu implementieren als ein reiner Algorithmus und benötigt zusätzlichen Speicher für den Merge-Schritt.", "COCKTAIL_SORT_EXPLANATION": "(auch Shaker Sort) ist eine Erweiterung des Bubble Sort. Statt nur von links nach rechts zu gehen, wechselt er bei jedem Durchlauf die Richtung und schiebt abwechselnd das größte Element nach rechts und das kleinste nach links. Vorteil: Schneller als Bubble Sort, da kleine Elemente am Ende schneller nach vorne wandern (\"Schildkröten-Problem\" gelöst). Nachteil: Bleibt in der Laufzeitklasse O(n²), also für große Datenmengen ineffizient.", "DISCLAIMER": "Die Wahl des \"besten\" Sortieralgorithmus hängt stark von den Daten und den Rahmenbedingungen ab. In der Informatik betrachtet man oft drei Szenarien:", "DISCLAIMER_1": "Best Case: Die Daten sind schon fast sortiert (hier glänzt z.B. Bubble Sort).", "DISCLAIMER_2": "Average Case: Der statistische Normalfall.", "DISCLAIMER_3": "Worst Case: Die Daten sind maximal ungünstig angeordnet (hier bricht Quick Sort ohne Optimierung ein, während Heap Sort stabil bleibt).", "DISCLAIMER_4": "Zusätzlich gibt es fast immer einen Time-Space Trade-off (Zeit-Speicher-Kompromiss): Algorithmen, die extrem schnell sind (wie Merge Sort), benötigen oft viel zusätzlichen Arbeitsspeicher. Algorithmen, die direkt im vorhandenen Speicher arbeiten (wie Heap Sort), sparen Platz, sind aber manchmal komplexer oder minimal langsamer. Es gibt also keine \"One-Size-Fits-All\"-Lösung." } }, "GOL": { "TITLE": "Conway's Spiel des Lebens", "START": "Starten", "PAUSE": "Pause", "RANDOM_SCENE": "Zufällig", "EMPTY_SCENE": "Leer", "SIMPLE_SCENE": "Gleiter", "PULSAR_SCENE": "Pulsar", "GUN_SCENE": "Pistole", "ALIVE": "Lebend", "DEAD": "Leer", "SPEED": "Zeit pro Generation", "EXPLANATION": { "TITLE": "Erklärung", "EXPLANATION": "Das Spiel läuft schrittweise ab. Zunächst wird eine Anfangsgeneration von lebenden Zellen auf dem Spielfeld definiert. Aus der vorliegenden Generation (dem Gesamtbild des Spielfeldes) wird die Folgegeneration ermittelt. Der Zustand jeder einzelnen Zelle in der Folgegeneration ergibt sich dabei nach einfachen Regeln aus ihrem aktuellen Zustand sowie den aktuellen Zuständen ihrer acht Nachbarzellen (Moore-Nachbarschaft).", "DISCLAIMER": "Nach Conways ursprünglicher Regel lebt eine Zelle in der nächsten Runde, wenn zuvor in ihrer 3x3-Umgebung insgesamt genau drei Zellen leben, wobei sie selbst nur bei Bedarf mitgezählt wird, das heißt:", "DISCLAIMER_1": "Eine lebende Zelle lebt auch in der Folgegeneration, wenn sie entweder zwei oder drei lebende Nachbarn hat.", "DISCLAIMER_2": "Eine tote Zelle „wird geboren“ (lebt in der Folgegeneration), wenn sie genau drei lebende Nachbarn hat.", "DISCLAIMER_3": "Eine lebende Zelle „stirbt“ (ist in der Folgegeneration tot), wenn sie weniger als zwei (Vereinsamung) oder mehr als drei (Übervölkerung) lebende Nachbarn hat.", "DISCLAIMER_4": " Eine tote Zelle bleibt tot, wenn sie nicht genau drei lebende Nachbarn hat." } }, "LABYRINTH": { "TITLE": "Labyrinth-Erzeugung", "PRIM": "Erzeuge Prim's Labyrinth", "KRUSKAL": "Erzeuge Kruskal's Labyrinth", "EXPLANATION": { "TITLE": "Algorithmen", "PRIM_EXPLANATION": "startet an einem zufälligen Punkt und erweitert das Labyrinth, indem er immer eine zufällige benachbarte Wand zu einer bereits besuchten Zelle auswählt und diese öffnet. Vorteil: Erzeugt sehr gleichmäßige, natürlich wirkende Labyrinthe mit vielen kurzen Sackgassen. Visuell wirkt es wie ein organisches Wachstum von einem Zentrum aus.", "KRUSKAL_EXPLANATION": "betrachtet alle Wände des Gitters als potenzielle Wege. Er wählt zufällig Wände aus und öffnet sie nur dann, wenn die beiden angrenzenden Zellen noch nicht miteinander verbunden sind (verhindert Kreise). Vorteil: Erzeugt ein sehr komplexes Labyrinth mit vielen langen, verwinkelten Pfaden. Visuell ist es spannend, da das Labyrinth an vielen Stellen gleichzeitig entsteht und am Ende zu einem Ganzen verschmilzt.", "DISCLAIMER": "Beide Algorithmen basieren auf dem Prinzip des 'Minimal Spanning Tree' (Minimaler Spannbaum). Das bedeutet für dein Labyrinth:", "DISCLAIMER_1": "Perfektes Labyrinth: Es gibt keine geschlossenen Kreise (Loops) – jeder Punkt ist erreichbar, aber es gibt immer nur genau einen Weg zwischen zwei Punkten.", "DISCLAIMER_2": "Erreichbarkeit: Da es ein Spannbaum ist, wird garantiert jede Zelle des Gitters Teil des Labyrinths, es gibt keine isolierten Bereiche.", "DISCLAIMER_3": "Zufälligkeit: Durch die Gewichtung der Kanten mit Zufallswerten entstehen bei jedem Durchlauf völlig neue, einzigartige Strukturen.", "DISCLAIMER_4": "Anwendung: Solche Labyrinthe sind die perfekte Testumgebung für Pfadfindungsalgorithmen wie Dijkstra oder A*." } }, "FRACTAL": { "TITLE": "Fraktale", "ALGORITHM": "Algorithmen", "RESET": "Reset", "COLOR_SCHEME": "Farbschema", "MAX_ITERATION": "Maximale Auflösung", "EXPLANATION": { "TITLE": "Mathematische Kunst", "MANDELBROT_EXPLANATION": "basiert auf der iterativen Formel 'z_{n+1} = z_n^2 + c'. Sie prüft für jeden Punkt in der komplexen Ebene, ob die Zahlenfolge stabil bleibt oder ins Unendliche entkommt. Vorteil: Gilt als 'Apfelmännchen' und Mutter der Fraktale. Sie bietet eine unendliche Vielfalt an selbstähnlichen Strukturen, in die man ewig hineinzoomen kann.", "JULIA_EXPLANATION": "nutzt dieselbe Formel wie Mandelbrot, fixiert jedoch den Parameter 'c' und variiert den Startwert. Je nach Wahl von 'c' entstehen filigrane, wolkenartige Gebilde oder zusammenhanglose 'Staubwolken'. Vorteil: Ermöglicht eine enorme ästiehetische Varianz, da jede Koordinate der Mandelbrot-Menge ein völlig eigenes, einzigartiges Julia-Fraktal erzeugt.", "NEWTON_EXPLANATION": "entsteht durch die Visualisierung des Newton-Verfahrens zur Nullstellen-Suche einer komplexen Funktion. Jeder Pixel wird danach eingefärbt, zu welcher Nullstelle der Algorithmus konvergiert. Vorteil: Erzeugt faszinierende, sternförmige Symmetrien und komplexe Grenzen, an denen sich die Einzugsgebiete der Nullstellen auf chaotische Weise treffen.", "BURNING_SHIP_EXPLANATION": "ist eine variation des Mandelbrots, bei der vor jedem Iterationsschritt der Absolutbetrag der Real- und Imaginärteile genommen wird: '(|Re(z)| + i|Im(z)|)^2 + c'. Vorteil: Erzeugt eine markante, asymmetrische Struktur, die einem brennenden Schiff mit Segeln ähnelt. Das Fraktal wirkt düsterer und 'mechanischer' als die klassischen Mengen.", "DISCLAIMER": "Alle diese Fraktale basieren auf dem Prinzip der Iteration und dem Chaos-Effekt. Das bedeutet für deine Visualisierung:", "DISCLAIMER_1": "Unendliche Tiefe: Egal wie weit du hineinzoomst, es erscheinen immer neue, komplexe Strukturen, die dem Ganzen oft ähneln (Selbstähnlichkeit).", "DISCLAIMER_2": "Fluchtzeit-Algorithmus: Die Farben geben meist an, wie schnell eine Folge einen bestimmten Schwellenwert überschreitet – je schneller, desto 'heißer' oder heller die Farbe.", "DISCLAIMER_3": "Komplexe Zahlen: Die Berechnung findet nicht in einem normalen Koordinatensystem statt, sondern in der komplexen Ebene mit realen und imaginären Anteilen.", "DISCLAIMER_4": "Rechenintensität: Da für jeden Pixel hunderte Berechnungen durchgeführt werden, sind Fraktale ein klassischer Benchmark für die Performance von Grafikprozessoren (GPUs).", "DISCLAIMER_BOTTOM": "Grafikkarten rechnen standardmäßig mit 32-Bit Fließkommazahlen (float). Diese haben nur etwa 7 Stellen Genauigkeit. Bei sehr hohem Zoom (> 100.000) ist der Unterschied zwischen zwei Pixeln so winzig, dass die Grafikkarte ihn nicht mehr darstellen kann. Sie berechnet für 10 Pixel nebeneinander exakt denselben Wert -> Du siehst Blöcke oder Treppenstufen." } }, "FRACTAL3D": { "TITLE": "3D Fraktale", "ALGORITHM": "Algorithmen", "MANDELBULB": "Mandelbulb", "MANDELBOX": "Mandelbox", "JULIA": "Julia", "EXPLANATION": { "TITLE": "3D Fraktale Welten", "MANDELBULB_EXPLANATION": "gilt als der 'Heilige Gral' der 3D-Fraktale. Da komplexe Zahlen nur zweidimensional sind, nutzt dieses Fraktal sphärische Koordinaten und hohe Potenzen (meist v^8 + c), um die Mandelbrot-Menge in den Raum zu projizieren. Vorteil: Es entsteht eine organische, extrem detaillierte Struktur, die an Pflanzen, Korallen oder außerirdische Landschaften erinnert.", "MANDELBOX_EXPLANATION": "basiert nicht auf glatten Kurven, sondern auf geometrischem 'Falten' und Skalieren (Box-Folding & Sphere-Folding). Der Raum wird wie Papier immer wieder gefaltet und gespiegelt. Vorteil: Erzeugt streng geometrische, mechanisch wirkende Strukturen, die wie endlose futuristische Städte, der Borg-Würfel oder komplexe Sci-Fi-Architektur aussehen.", "JULIA_EXPLANATION": "ist das 3D-Pendant zur 2D-Julia-Menge. Während der Mandelbulb eine 'Karte' aller Fraktale ist, fixiert man hier den Parameter 'c' und variiert den Startpunkt zudem variiert es mit der Zeit. Vorteil: Anders als der massive Mandelbulb sind Julia-Bulbs oft hohle, komplexe Tunnelsysteme oder blasenartige Strukturen, die sich perfekt eignen, um mit der Kamera hindurchzufliegen.", "DISCLAIMER": "Diese Visualisierung nutzt eine Technik namens 'Raymarching' (Sphere Tracing). Das bedeutet:", "DISCLAIMER_1": "Keine Polygone: Es gibt keine Dreiecke oder Gitter. Die Form wird rein mathematisch für jeden Pixel in Echtzeit berechnet.", "DISCLAIMER_2": "Distance Estimation: Der Algorithmus 'tastet' sich mit Lichtstrahlen voran, indem er berechnet, wie weit das nächste Objekt entfernt ist, ohne es sofort zu berühren.", "DISCLAIMER_3": "Unendliche Details: Da die Oberfläche mathematisch definiert ist, verpixelt sie nicht beim Zoom – es erscheinen immer neue Strukturen.", "DISCLAIMER_4": "Licht & Schatten: Um die Tiefe sichtbar zu machen, werden Lichtreflexionen und Schatten (Ambient Occlusion) basierend auf der Krümmung der Formel simuliert." } }, "PENDULUM": { "TITLE": "Doppel-Pendel", "TRAIL_DECAY_TIME": "Spurlänge", "DAMPING": "Dämpfung", "ATTRACTION": "Anziehungskraft", "L1_LENGTH": "Länge L1", "L2_LENGTH": "Länge L2", "M1_MASS": "Masse M1", "M2_MASS": "Masse M2", "POKE_M1": "Schubse M1", "POKE_M2": "Schubse M2", "RESET": "Neustarten", "EXPLANATION": { "TITLE": "Chaostheorie: Das Doppelpendel", "EXPLANATION": "Das Doppelpendel ist eines der bekanntesten und faszinierendsten Beispiele der Physik für ein dynamisches System, das 'deterministisches Chaos' erzeugt. Es besteht schlicht aus einem einfachen Pendel, an dessen unterem Ende ein zweites Pendel befestigt ist. Obwohl die zugrundeliegenden Bewegungsgesetze der klassischen Mechanik streng mathematisch definiert sind, ist das Verhalten des Doppelpendels auf lange Sicht absolut unvorhersehbar. Es gilt in der Physik als das klassische Vorzeigeobjekt für den sogenannten Schmetterlingseffekt.", "DISCLAIMER": "Diese WebGPU-Simulation berechnet die Bewegungs- und Beschleunigungsgleichungen des Pendels 60-mal pro Sekunde in Echtzeit. Dabei gelten folgende Besonderheiten:", "DISCLAIMER_1": "Extreme Sensitivität: Winzigste Änderungen in den Startbedingungen (z.B. ein Tausendstel Grad Abweichung im Startwinkel oder bei der Masse) führen schon nach kurzer Zeit zu einer völlig anderen, chaotischen Flugbahn.", "DISCLAIMER_2": "Deterministisches Chaos: Die Bewegung wirkt zwar völlig wild und zufällig, ist es aber nicht. Startest du die Simulation mit exakt denselben Werten neu, wird das Pendel zu 100 % denselben Weg fliegen.", "DISCLAIMER_3": "Numerische Integration: Da Computer Zeit nicht stufenlos, sondern in winzigen Schritten (dt) berechnen, entstehen bei jedem Frame winzige mathematische Rundungsfehler. Diese summieren sich auf und beeinflussen das Chaos zusätzlich.", "DISCLAIMER_4": "Energieerhaltung & Reibung: In einem perfekten physikalischen System ohne Widerstand würde das Pendel ewig weiterschwingen. Für eine natürliche Optik nutzt der Algorithmus einen künstlichen Dämpfungsfaktor, der Luftreibung simuliert und das System irgendwann beruhigt.", "DISCLAIMER_BOTTOM": "HINWEIS: Wenn zuviele Impulse in das System gegeben werden, wird die Simulation instabil. Dann hängt das Pendel nur noch runter und es muss neu gestartet werden." } }, "CLOTH": { "TITLE": "Stoffsimulation", "WIND_ON": "Wind Einschalten", "WIND_OFF": "Wind Ausschalten", "OUTLINE_ON": "Mesh anzeigen", "OUTLINE_OFF": "Mesh ausschalten", "STIFFNESS": "Steifigkeit", "ELONGATION": "Dehnung", "RESTART_SIMULATION": "Simulation neu starten", "EXPLANATION": { "TITLE": "Echtzeit-Stoffsimulation auf der GPU", "CLOTH_SIMULATION_EXPLANATION_TITLE": "Stoffsimulation", "XPBD_EXPLANATION_TITLE": "XPBD (Extended Position-Based Dynamics)", "GPU_PARALLELIZATION_EXPLANATION_TITLE": "GPU Parallelisierung", "DATA_STRUCTURES_EXPLANATION_TITLE": "Datenstrukturen", "CLOTH_SIMULATION_EXPLANATION": "Stoffsimulationen modellieren Textilien meist als ein Gitter aus Massepunkten (Vertices), die durch unsichtbare Verbindungen zusammengehalten werden. Ziel ist es, physikalische Einflüsse wie Schwerkraft, Wind und Kollisionen in Echtzeit darzustellen, ohne dass das Material zerreißt oder sich unnatürlich wie Gummi dehnt.", "XPBD_EXPLANATION": "XPBD (Extended Position-Based Dynamics) ist ein moderner Algorithmus, der statt Beschleunigungen direkt die Positionen der Punkte manipuliert, um Abstandsbedingungen (Constraints) zu erfüllen. Das 'Extended' bedeutet, dass echte physikalische Steifigkeit unabhängig von der Framerate simuliert wird. Vorteil: Absolut stabil, explodiert nicht und topologische Änderungen (wie das Zerschneiden von Stoff) sind trivial. Nachteil: Es ist ein iteratives Näherungsverfahren und physikalisch minimal weniger akkurat als komplexe Matrix-Löser.", "GPU_PARALLELIZATION_EXPLANATION": "Um zehntausende Punkte parallel auf der Grafikkarte zu berechnen, muss man 'Race Conditions' verhindern – also dass zwei Rechenkerne gleichzeitig denselben Knotenpunkt verschieben. Die Lösung nennt sich 'Independent Sets' (oder Graph Coloring): Die Verbindungen werden in isolierte Gruppen (z. B. 4 Phasen bei einem Gitter) unterteilt, in denen sich kein einziger Punkt überschneidet. So kann die GPU jede Gruppe blind und mit maximaler Geschwindigkeit abarbeiten.", "DATA_STRUCTURES_EXPLANATION": "Für maximale GPU-Performance müssen Daten speicherfreundlich ausgerichtet werden (16-Byte-Alignment). Anstatt viele einzelne Variablen zu nutzen, packt man Informationen clever in 4er-Blöcke (vec4). Ein Vertex speichert so z. B. [X, Y, Z, Inverse_Masse]. Hat ein Punkt die inverse Masse 0.0, wird er vom Algorithmus ignoriert und schwebt unbeweglich in der Luft – ein eleganter Trick für Aufhängungen ohne extra Wenn-Dann-Abfragen.", "DISCLAIMER": "XPBD vs. Masse-Feder-Systeme: In der physikalischen Simulation gibt es grundlegende Architektur-Unterschiede beim Lösen der Gleichungen:", "DISCLAIMER_1": "Klassische Masse-Feder-Systeme: Hier werden Kräfte (Hookesches Gesetz) berechnet, die zu Beschleunigungen und schließlich zu neuen Positionen führen. Es gibt zwei Wege, diese mathematisch in die Zukunft zu rechnen (Integration):", "DISCLAIMER_2": "Explizite Löser (z.B. Forward Euler): Sie berechnen den nächsten Schritt stur aus dem aktuellen Zustand. Sie sind leicht zu programmieren, aber bei steifen Stoffen extrem instabil. Die Kräfte schaukeln sich auf und die simulation 'explodiert', sofern man keine winzigen, sehr leistungsfressenden Zeitschritte wählt.", "DISCLAIMER_3": "Implizite Löser (z.B. Backward Euler): Sie berechnen den nächsten Schritt basierend auf dem zukünftigen Zustand. Das ist mathematisch enorm stabil, erfordert aber das Lösen riesiger globaler Matrix-Gleichungssysteme in jedem Frame. Dies ist auf der GPU schwerer zu parallelisieren und bricht zusammen, wenn sich die Struktur ändert (z. B. durch Zerschneiden des Stoffs).", "DISCLAIMER_4": "Der XPBD-Kompromiss: XPBD umgeht dieses komplexe Matrix-Problem völlig, indem es als lokaler Löser arbeitet. Es kombiniert die unbedingte Stabilität eines impliziten Lösers mit der enormen Geschwindigkeit, Parallelisierbarkeit und dynamischen Anpassungsfähigkeit eines expliziten Systems." } }, "GPU": { "WEBGL_FALLBACK": "WebGPU ist nicht verfügbar. WebGL wird als Fallback verwendet. Die Leistung kann eingeschränkt sein.", "NOT_SUPPORTED": "Weder WebGPU noch WebGL konnten initialisiert werden. GPU-Visualisierungen sind nicht verfügbar." }, "ALGORITHM": { "TITLE": "Algorithmen", "PATHFINDING": { "TITLE": "Wegfindung", "DESCRIPTION": "Vergleich von Dijkstra vs. A*." }, "SORTING": { "TITLE": "Sortierung", "DESCRIPTION": "Visualisierung verschiedener Sortieralgorithmen." }, "GOL": { "TITLE": "Conway's Game of Life", "DESCRIPTION": "Das 'Spiel des Lebens' ist ein vom Mathematiker John Horton Conway 1970 entworfenes Spiel." }, "LABYRINTH": { "TITLE": "Labyrinth-Erzeugung", "DESCRIPTION": "Visualisierung verschiedener Laybrinth-Erzeugungs-Algorithmen." }, "FOUR_COLOR": { "TITLE": "Vier-Farben-Satz", "DESCRIPTION": "Der Vier-Farben-Satz besagt, dass jede Landkarte in der Ebene mit maximal vier Farben so eingefärbt werden kann, dass keine zwei aneinandergrenzenden Gebiete dieselbe Farbe besitzen." }, "FOURIER": { "TITLE": "Fourier-Epizykel", "DESCRIPTION": "Jede geschlossene Form wird von einer Kette rotierender Kreise gezeichnet. Zeichne eine eigene Form und sieh zu, wie die Kreise sie mit einer diskreten Fourier-Transformation nachbauen." }, "FRACTAL": { "TITLE": "Fraktale", "DESCRIPTION": "Visualisierung von komplexe, geometrische Mustern, die sich selbst in immer kleineren Maßstäben ähneln (Selbstähnlichkeit)." }, "FRACTAL3D": { "TITLE": "Fraktale 3D", "DESCRIPTION": "3D-Visualisierung von komplexe, geometrische Mustern, die sich selbst in immer kleineren Maßstäben ähneln (Selbstähnlichkeit)." }, "PENDULUM": { "TITLE": "Doppel-Pendel", "DESCRIPTION": "Visualisierung einer chaotischen Doppel-Pendel-Simulation mit WebGPU." }, "CLOTH": { "TITLE": "Stoffsimulation", "DESCRIPTION": "Simulation on Stoff mit WebGPU." }, "NOTE": "HINWEIS", "GRID_HEIGHT": "Höhe", "GRID_WIDTH": "Beite" }, "FOUR_COLOR": { "TITLE": "Vier-Farben-Satz", "GENERATE": "Neue Karte generieren", "SOLVE": "Automatisch lösen", "CLEAR": "Farben zurücksetzen", "COLOR_1": "Farbe 1", "COLOR_2": "Farbe 2", "COLOR_3": "Farbe 3", "COLOR_4": "Farbe 4", "EXECUTION_TIME": "Ausführungszeit", "STATUS": { "LABEL": "Status", "INCOMPLETE": "Die Karte ist noch nicht vollständig eingefärbt.", "SOLVED": "Glückwunsch! Du hast die Karte korrekt gelöst!", "CONFLICTS": "Achtung: Benachbarte Regionen haben die gleiche Farbe!", "INVALID": "Karte ist vollständig, enthält aber Fehler." }, "EXPLANATION": { "TITLE": "Der Vier-Farben-Satz", "EXPLANATION": "Der Vier-Farben-Satz besagt, dass jede Landkarte in der Ebene mit maximal vier Farben so eingefärbt werden kann, dass keine zwei aneinandergrenzenden Gebiete dieselbe Farbe besitzen.", "DISCLAIMER": "Dieser Algorithmus verwendet Backtracking, um eine gültige Färbung für die generierten Regionen zu finden.", "DISCLAIMER_1": "Kartengenerierung: Regionen werden mittels eines zufälligen Seed-Wachstumsalgorithmus (Voronoi-ähnlich) auf einem Gitter erzeugt.", "DISCLAIMER_2": "Adjazenz: Zwei Regionen gelten als benachbart, wenn sie mindestens eine gemeinsame Kante im Gitter teilen.", "DISCLAIMER_3": "Backtracking: Der Löser probiert die Farben 1-4 für jede Region aus und macht Schritte rückgängig (Backtracking), wenn ein Konflikt auftritt.", "DISCLAIMER_4": "Interaktiv: Sie können auch auf Regionen klicken, um manuell durch die Farben zu wechseln.", "DISCLAIMER_BOTTOM": "Zum einfärben in die Zelle klicken. Durch erneutes klicken ändert sich die Farbe." }, "ALERT": { "NO_SOLUTION": "Keine Lösung gefunden (das sollte bei einer planaren Karte nicht passieren!)." } }, "FOURIER": { "TITLE": "Fourier-Epizykel", "DRAW_HINT": "Zeichne eine geschlossene Form auf die Fläche oder wähle eine Vorlage", "PRESET_HEART": "Herz", "PRESET_STAR": "Stern", "PRESET_SQUARE": "Quadrat", "PRESET_DOG": "Hund", "CIRCLES": "Kreise", "SPEED": "Geschwindigkeit", "SHOW_CIRCLES": "Kreise anzeigen", "HIDE_CIRCLES": "Kreise ausblenden", "EXPLANATION": { "TITLE": "Fourierreihe", "EXPLANATION": "Eine Fourierreihe besagt, dass jede periodische Kurve die Summe rotierender Kreise ist. Jeder Kreis hat einen eigenen Radius, eine eigene Geschwindigkeit und einen eigenen Startwinkel.", "DISCLAIMER": "Diese Demo geht in drei Schritten vor.", "DISCLAIMER_1": "Deine Form wird auf 256 gleichmäßig verteilte Punkte neu abgetastet.", "DISCLAIMER_2": "Jeder Punkt ist eine komplexe Zahl x + iy. Eine direkte diskrete Fourier-Transformation liefert einen Kreis pro Frequenz.", "DISCLAIMER_3": "Die Kreise werden nach Radius sortiert. Der Regler behält nur die größten N Kreise.", "DISCLAIMER_BOTTOM": "Weniger Kreise geben eine glattere Näherung. Mit allen Kreisen läuft die Kurve exakt durch alle 256 Punkte." } }, "STOPWATCH": { "TITLE": "Stoppuhr", "START": "Start", "PAUSE": "Pause", "STOP": "Stopp", "RESET": "Zurücksetzen", "INTERVAL_ENABLED": "Signalton bei Intervall", "FIRST_INTERVAL": "Erstes Intervall", "INTERVAL_MINUTES": "Minuten", "INTERVAL_SECONDS": "Sekunden", "SECOND_INTERVAL_ENABLED": "Zweites, abwechselndes Intervall hinzufügen", "INTERVAL_HINT": "Spielt einen kurzen Ton ab, sobald ein Intervall abgelaufen ist. Mit aktiviertem zweiten Intervall wechseln sich beide ab, das zweite startet direkt nach dem ersten.", "INTERVAL_SCHEDULE": "Nächste Signaltöne bei:" }, "NOT_FOUND": { "TITLE": "Seite nicht gefunden", "MESSAGE": "Die gesuchte Seite existiert nicht oder wurde verschoben.", "BACK_HOME": "Zurück zum Start" }, "SEO": { "DEFAULT": { "TITLE": "Andreas Dahm – Playground", "DESCRIPTION": "Portfolio und interaktiver Playground von Andreas Dahm, Senior Software Developer: Algorithmus-Visualisierungen, GPU-Simulationen und persönliche Projekte." }, "ABOUT": { "TITLE": "Über mich – Andreas Dahm", "DESCRIPTION": "Andreas Dahm, Senior Softwareentwickler und Architekt aus München: Erfahrung, Fähigkeiten und Projekte in Full-Stack-Entwicklung, 3D-Simulation, Algorithmen und KI-gestütztem Engineering." }, "PROJECTS": { "TITLE": "Projekte – Andreas Dahm", "DESCRIPTION": "Ausgewählte Projekte von Andreas Dahm: ein Steam-Release, selbst gehostete Infrastruktur, Game Jams und eine wissenschaftliche Diplomarbeit." }, "ALGORITHMS": { "TITLE": "Algorithmen – Andreas Dahm", "DESCRIPTION": "Interaktive Visualisierungen von Algorithmen und Simulationen: Pathfinding, Sortierung, Fraktale, Stoff- und Pendelphysik – direkt im Browser." }, "FOURIER": { "TITLE": "Fourier-Epizykel – Andreas Dahm", "DESCRIPTION": "Interaktive Fourier-Epizykel: Zeichne eine geschlossene Form und sieh zu, wie eine Kette rotierender Kreise sie mit einer diskreten Fourier-Transformation nachzeichnet." }, "IMPRINT": { "TITLE": "Impressum – Andreas Dahm", "DESCRIPTION": "Rechtliche Informationen und Kontaktdaten zur Website Andreas Dahm Playground." }, "PRIVACY": { "TITLE": "Datenschutzerklärung – Andreas Dahm", "DESCRIPTION": "Datenschutzerklärung zur Website Andreas Dahm Playground." }, "STOPWATCH": { "TITLE": "Stoppuhr – Andreas Dahm", "DESCRIPTION": "Eine einfache Online-Stoppuhr mit konfigurierbaren Intervall-Signaltönen." }, "NOT_FOUND": { "TITLE": "Seite nicht gefunden – Andreas Dahm", "DESCRIPTION": "Die angeforderte Seite konnte nicht gefunden werden." } } }