Monday, November 24, 2025

10.500 YouTube-Videos visualisiert: Ein Experiment mit Gemini, UMAP und DBSCAN

Einleitung und Anfänge mit Sprachmodellen

Vor ungefähr zwei Jahren habe ich angefangen, Sprachmodelle zu benutzen – anfangs ChatGPT, später Gemini –, um Zusammenfassungen für YouTube-Videos auf deren Webseiten zu erstellen. Zu Beginn war der Kontext auf 4.000 oder 8.000 Token beschränkt. Zusammenfassungen von längeren Videos waren daher qualitativ nicht besonders gut. Ich musste die Videos in 10-Minuten-Blöcke unterteilen, diese einzeln zusammenfassen lassen und am Ende eine Gesamtzusammenfassung erstellen. Das hat leider nicht sehr gut funktioniert.

Der Durchbruch mit Gemini 1.5 Pro

Mit Gemini 1.5 Pro war der Kontext schließlich lang genug, um Videos von 30 bis 60 Minuten Länge in einem Stück zu verarbeiten. Die Kosten waren jedoch relativ hoch; eine Stunde Video zusammenzufassen kostete etwa 50 Cent. Aufgrund dieser Kosten begann ich, die Zusammenfassungen direkt als Kommentar unter die YouTube-Videos zu posten, immer mit dem Hinweis, dass sie mit Gemini 1.5 Pro erstellt wurden.

Gelegentlich erhielten diese Kommentare Likes. Wenn ich ein paar Mal vergaß zu erwähnen, dass eine KI den Text verfasst hat, bekam ich bis zu 60 Likes. Das zeigte mir, dass diese Zusammenfassungen tatsächlich nützlich sind. Doch nicht nur das Feedback von außen war entscheidend: Auch für mich selbst waren die Zusammenfassungen, die ich stets mit den Videos verglich, extrem hilfreich. Manchmal fand ich darin sogar Punkte, die ich beim bloßen Zuhören gar nicht aufgeschnappt hatte.

Motivation für die eigene Plattform und Datenbank

Da ich meine eigenen Kommentare auf YouTube schlecht wiederfinden konnte, wollte ich meine Anfragen inklusive Prompts und Ergebnissen speichern. Es gab jedoch noch einen tiefergehenden Grund für den Aufbau der Datenbank: Ich hatte gelernt, dass das Training (bzw. Fine-Tuning) eines Foundational LLM (Large Language Model) erstaunlich gut funktioniert und sogar in wenigen Tagen auf einer Consumer-Grafikkarte durchgeführt werden kann – vorausgesetzt, man hat ausreichend viele qualitativ hochwertige Beispiele. Um diesen Datensatz zu erstellen, baute ich ein Python-Skript mit einer Webseite.

Die Webseite unterstützt nach wie vor nur Google Gemini. Allerdings absolviere ich zurzeit einen Kurs von Jeremy Howard (fast.ai), in dem ich lerne, wie man mittels Python beliebige LLMs (zum Beispiel über Bibliotheken wie Lisette) nutzen kann. Das Ziel ist es, hier flexibler zu werden.

Visualisierung und Clustering (Embeddings & UMAP)

Nachdem ich etwa 4.000 Zusammenfassungen gesammelt hatte, erstellte ich daraus Embeddings. Google gibt hierfür Vektoren mit 3.072 Dimensionen zurück. Jedes Video entspricht also einem Punkt in diesem hochdimensionalen Vektorraum.

Ich nutzte einen Algorithmus namens UMAP (eine Weiterentwicklung von t-SNE), um diesen Raum auf 2D herunterzubrechen. Die Idee dabei ist, dass die Abstandscharakteristik erhalten bleibt: Punkte, die im hochdimensionalen Raum nah beieinander liegen, sind es auch im 2D-Raum. Das funktionierte gut, und thematische Cluster wurden sichtbar.



Abb. 1: Detailansicht der Cluster-Bildung. Hier ist schön zu sehen, wie medizinische Fachthemen wie Virologie und Parasitologie automatisch gruppiert wurden.

Um die Cluster besser zu trennen und farblich zu markieren, stellte ich fest, dass es effizienter ist, den Vektorraum mittels UMAP auf vier Dimensionen zu reduzieren und dort das Clustering (mit dem Algorithmus DBSCAN) durchzuführen, da es im 2D-Raum zu viele Überlappungen gab. Anschließend nahm ich einen Teil der Punkte eines Clusters, schickte deren Zusammenfassungen an die KI und ließ mir einen passenden Titel vorschlagen.



Abb. 2: Interaktive globale Projektion von über 4.000 YouTube-Video-Zusammenfassungen. Jeder Punkt repräsentiert ein Video, die Farben markieren thematische Zusammengehörigkeiten.

Aktueller Status und Kommerzialisierung

Mittlerweile haben wir etwa 10.500 Einträge auf der Webseite. Meine Idealvorstellung ist, dass jeder Nutzer beim Zusammenfassen eines Videos dessen Position auf der Karte sowie thematisch verwandte Videos in der Nähe sieht.

Da schon mehrere Leute die Webseite nutzen, wird das freie Kontingent von Google manchmal ausgeschöpft. Das ist problematisch, da ich dann keine Pro-Zugriffe mehr machen kann. Ich möchte das Projekt daher gerne so weit kommerzialisieren, dass Nutzer sich einloggen und gegen Bezahlung (z. B. durch Hinterlegen von 5 Dollar Guthaben) Zusammenfassungen erstellen können, ohne durch das kostenlose Kontingent limitiert zu werden. Ein wichtiger Aspekt dabei ist, dass diese Nutzer ihre Zusammenfassungen dann auf Wunsch auch privat halten können.

Das Preis-Leistungs-Verhältnis ist mittlerweile sehr gut: Ein zweistündiges Video lässt sich für zwei bis drei Cent mit einem Pro-Modell zusammenfassen.

Vergleich mit Konkurrenz und Anwendungsfälle

Youtube zeigt inzwischen selbst Zusammenfassungen an, die ich jedoch nicht vergleichbar finde mit dem, was das Script meiner Website erzeugt. Mein Ansatz generiert ein Abstract sowie eine detaillierte Zusammenfassung mit Zeitstempeln. Oft muss man das Video gar nicht mehr schauen oder kann gezielt zu interessanten Stellen springen.

Besonders hilfreich ist das für wissenschaftliche Inhalte. Ich schaue oft "MicrobeTV", wo Ärzte und Virologen Studien diskutieren ("Journal Club"). Das mündliche Gespräch ist oft verständlicher als die Artikel selbst, und durch die Zusammenfassung lassen sich die Informationen schnell einordnen.

Technische Limitationen und Ausblick (Parakeet)

Ein Problem bei YouTube-Transkripten ist die fehlende Sprechererkennung. Während das bei Firmen-Meetings (in Teams) durch getrennte Kanäle gut funktioniert, hat die KI bei Podcasts mit nur einem Audio Kanal mit mehreren Personen oft Schwierigkeiten, die Sprecher zuzuordnen. Eine vielversprechende Option, um YouTube-Summaries mit mehreren Sprechern massiv zu verbessern, wäre der Einsatz von Parakeet. Dabei handelt es sich um ein fortschrittliches Speech-to-Text-Modell, das über eine funktionierende Sprechererkennung (Speaker Diarization) verfügt.

Auch das Fehlen von Bilddaten ist ein Nachteil. Wenn in einem Video visuelle Prozesse gezeigt werden, hilft das reine Text-Transkript wenig. Ein interessanter nächster Schritt wäre daher, auch Bildausschnitte an die KI zu übergeben.

Zusammenfassend halte ich diese Art der Videokartierung und -zusammenfassung für sehr sinnvoll und plane, die Plattform weiter auszubauen.

Sunday, November 23, 2025

Zork I seziert: Eine Autopsie des Infocom-Quellcodes

Bemerkung: Ich habe diesen Text mit Hilfe von Gemini 3 Pro erstellt. Die Hauptarbeit bestand darin, den Gesamten Quellcode in den Context zu Laden und Fragen dazu zu stellen. Ich finde die Einsichten ganz interessant. Am Anfang wusste ich nicht mal, dass Zork in einem Lisp Dialekt entwickelt wurde. Ausserdem ist faszinierend, dass man den gesamten Spielablauf und die Raetsel mit Diagrammen darstellen lassen kann.

Einleitung: Jenseits der Nostalgie

Microsoft hat kürzlich das Repository von Zork I auf GitHub unter der MIT-Lizenz veröffentlicht. Für Historiker ist das nett. Für Entwickler ist es eine Offenbarung. Was wir hier vor uns haben, ist nicht einfach "Retro-Code", sondern eine archäologische Fundgrube, die zeigt, wie man komplexe Welten in einer Umgebung simuliert, die weniger Speicher hat als das Icon einer modernen Smartphone-App.

Der Quellcode bestätigt, was wir lange vermutet haben: Zork ist keine Ansammlung von IF-Statements. Es ist eine komplexe Simulation, die auf einer virtuellen Maschine läuft. Infocom löste das Problem der Portierbarkeit (Apple II, C64, PDP-10), indem sie nicht das Spiel portierten, sondern die CPU. Sie schufen die Z-Machine. Das Spiel selbst ist Bytecode. Das ist exakt das Prinzip der Java Virtual Machine (JVM), nur dass Infocom es 1979 tat, nicht 1995.

Der Code ist brilliant, aber lassen Sie uns ehrlich sein: Nach modernen Standards ist er grauenhaft. Er ist ein Monolith aus globalem Zustand, Seiteneffekten und Speicher-Hacks. Aber genau das macht ihn so interessant. Er zeigt Engineering unter extremen Zwängen.


Die Sprache: ZIL (Zork Implementation Language)

Um diesen Code zu lesen, müssen Sie verstehen, dass Sie hier kein Standard-LISP vor sich haben. Der Code ist in ZIL geschrieben, einer Sprache, die selbst LISP-Veteranen kurz stolpern lässt.

Der schismatische Ursprung: MDL vs. LISP

ZIL ist ein direkter Abkömmling von MDL (Muddle). MDL spaltete sich in den frühen 1970ern am MIT von der dominanten Maclisp-Linie ab. Während beide den ikonischen Klammer-Wald von Lisp 1.5 erbten, ging MDL einen anderen Weg: Es fokussierte sich auf starke Typisierung und komplexe Datenstrukturen, die Maclisp fehlten.

Infocom nutzte MDL nicht, weil es hübsch war, sondern weil es mächtig war. ZIL ist im Grunde ein kastriertes MDL. Man nahm die Sprache, schrieb das Spiel und nutzte dann einen Compiler (ZILCH), um die gesamte schwere LISP-Laufzeitumgebung (Garbage Collection, EVAL) wegzuschneiden. Übrig blieb nackter, kompakter Bytecode für die Z-Machine.


Diagram mit der Einordnung von MDL und ZIL in die Entwicklungsgeschichte von Lisp Dialekten

Interessant ist, was MDL hinterlassen hat. MDL mag den evolutionären Krieg gegen Maclisp (der Vorfahre von Common Lisp) und Scheme verloren haben, aber es starb nicht spurlos. Das berüchtigte, extrem gesprächige LOOP-Makro in modernem Common Lisp? Das stammt direkt aus MDL. Ebenso das Konzept eines robusten Condition-Handling-Systems. Wenn wir ZIL lesen, lesen wir die letzte kommerzielle Festung eines "toten" LISP-Zweigs, der die Industrie mehr geprägt hat, als viele wissen.

Syntax und "Alien Technology"

Wer LISP kennt, erwartet (funktion args). ZIL macht das anders. Es nutzt eine visuelle Unterscheidung, die den Code für das ungeübte Auge wie einen XML-Unfall aussehen lässt, aber einen strikten Zweck erfüllt:

  • < > (Spitze Klammern): Bezeichnen einen Funktionsaufruf oder eine Formauswertung.
    Beispiel: <SETG LIT T> (Setze globale Variable LIT auf True).
  • ( ) (Runde Klammern): Bezeichnen Listen und Datenstrukturen.
    Beispiel: (FLAGS TAKEBIT BURNBIT) ist keine Funktion, sondern eine Liste von Flags.

Diese Unterscheidung macht den Code beim Parsen im Kopf etwas schneller, sobald man sich daran gewöhnt hat.

Makros als DSL

Da Speicherplatz auf Disketten (ca. 160 KB) die härteste Währung war, nutzt ZIL exzessive Meta-Programmierung. Dateien wie gmacros.zil zeigen, dass ZIL oft eher als Domain Specific Language (DSL) fungiert.

Ein Befehl wie <TELL "Hello"> ist kein Funktionsaufruf zur Laufzeit. Es ist ein Makro, das zur Kompilierzeit in eine optimierte Serie von Z-Machine-Opcodes (wie PRINTI) expandiert wird. Der Entwickler schreibt High-Level-Code, der Compiler erzeugt Low-Level-Assembler.



Darstellung der Code Pipeline in Zork.

Diese Pipeline ist der Grund, warum der Code so aussieht, wie er aussieht. Es ist keine Sprache für Puristen. Es ist eine Sprache für Ingenieure, die ein Universum in eine Streichholzschachtel pressen müssen.


Die Architektur: Ein geniales Chaos

Wer Spaghetti-Code erwartet, wird überrascht sein. Die Architektur von Zork ist modular, durchdacht und – für 1980 – fast schon modern. Infocom hat nicht einfach ein Spiel geschrieben; sie haben eine Engine gebaut.

Der Code teilt sich strikt in zwei Welten:

  1. Die generische Engine (g*.zil): Dateien wie gverbs.zil oder gparser.zil definieren die Physik der Welt. Was bedeutet "Nehmen"? Was bedeutet "Öffnen"? Diese Dateien wurden für Zork II, Zork III und andere Spiele wiederverwendet.
  2. Die spezifische Datenbank (1*.zil): Dateien wie 1dungeon.zil und 1actions.zil enthalten den eigentlichen Inhalt von Zork I.

Diese Trennung war der Schlüssel zu Infocoms Produktivität. Sie bauten eine Fabrik für Textadventures.

Das Objekt-Modell: Alles ist ein Ding

In ZIL gibt es keine Trennung zwischen "Räumen", "Gegenständen" oder "Akteuren". Alles ist ein <OBJECT>. Das Datenmodell ist eine strikte Hierarchie (Containment Tree). Der Spieler ist ein Objekt in einem Raum-Objekt, und das Schwert ist ein Objekt im Spieler-Objekt.



Darstellung der in Zork verwendeten Objekte.

Ein Blick in 1dungeon.zil zeigt, wie effizient diese Definitionen sind. Nehmen wir den Startpunkt des Spiels:

<ROOM WEST-OF-HOUSE
      (IN ROOMS)
      (DESC "West of House")
      (NORTH TO NORTH-OF-HOUSE)
      (EAST "The door is boarded and you can't remove the boards.")
      (ACTION WEST-HOUSE)
      (FLAGS RLANDBIT ONBIT SACREDBIT)
      (GLOBAL WHITE-HOUSE BOARD FOREST)>

Hier sehen wir die extreme Optimierung:

  • Flags: RLANDBIT (Land, kein Wasser) oder ONBIT (beleuchtet) sind einzelne Bits. Das spart Speicherplatz gegenüber Booleschen Variablen.
  • Globale Referenzen: (GLOBAL ...) verlinkt Objekte, die hier "sichtbar" sein sollen, ohne physisch im Raum zu liegen (wie das Haus oder der Wald).
  • Action Hooks: (ACTION WEST-HOUSE) ist der entscheidende Hook für die Spiellogik.

Der Game Loop: Das "Intercept"-Muster

Die genialste Designentscheidung in Zork ist die Art und Weise, wie Aktionen verarbeitet werden. Es ist ein Kaskaden-System, das "Spezifisch vor Generisch" priorisiert.

Wenn der Spieler "Öffne Tür" tippt, passiert folgendes:

  1. Der Parser identifiziert das Verb (V?OPEN) und das Objekt (WOODEN-DOOR).
  2. Die Engine prüft: Hat die Tür eine eigene ACTION-Funktion (FRONT-DOOR-FCN)?
  3. JA: Führe diese aus. Wenn diese Funktion "Handled" zurückgibt, stoppt die Engine.
  4. NEIN: Führe die Standard-Physik aus (V-OPEN in gverbs.zil), die prüft, ob das Objekt überhaupt geöffnet werden kann.


Zustandsdiagram der zentralen Game Loop.

Das erlaubt es den Designern, die physikalischen Regeln jederzeit zu brechen. Die Standard-Logik sagt: "Man kann Türen öffnen." Die Logik der WOODEN-DOOR sagt aber: "Diese Tür ist zugenagelt."

Ein Beispiel aus 1actions.zil:

<ROUTINE FRONT-DOOR-FCN ()
     <COND (<VERB? OPEN>
            <TELL "The door cannot be opened." CR>)
           (<VERB? BURN>
            <TELL "You cannot burn this door." CR>)>>

Hier fängt das Skript den OPEN und BURN Befehl ab, bevor die Standard-Engine versuchen kann, den Status der Tür zu ändern.

Global State: Die Register der Z-Machine

Während die Struktur sauber wirkt, ist die Datenhaltung "schmutzig". ZIL vermeidet lokale Variablen und Parameterübergabe, um den Stack klein zu halten. Stattdessen nutzt das Spiel globale Variablen, die fast wie CPU-Register fungieren.

Die wichtigsten "Register", die wir im Code gefunden haben:

  • PRSA (Parse Action): Das aktuelle Verb.
  • PRSO / PRSI: Das direkte und indirekte Objekt (z.B. "Schlage TROLL mit SCHWERT").
  • HERE: Ein Pointer auf den aktuellen Raum.
  • WINNER: Der Akteur. Meistens der Spieler, aber wenn Sie dem Roboter Befehle geben, wird der Roboter kurzzeitig zum WINNER.

Jede Funktion kann jeden Zustand ändern. Eine Kampf-Routine (I-FIGHT) greift direkt auf die globale Variable LOAD-ALLOWED (Traglast) zu, um den Spieler zu schwächen, wenn er verwundet wird. Das ist klassischer Spaghetti-Zustand – hochgradig effizient, aber ein Albtraum für das Debugging. Ein Fehler in einer Routine kann theoretisch den Zustand eines völlig unbeteiligten Objekts am anderen Ende der Karte zerstören.


Der Parser: Die Illusion von Intelligenz

Für Spieler in den 1980ern wirkte Zork wie Magie. Man konnte tippen: "Nimm das Schwert und töte den Troll damit", und das Spiel verstand es. Wer den Quellcode liest, stellt fest: Es gibt keine echte KI. Es gibt nur einen extrem ausgeklügelten Mustererkennungs-Algorithmus, der darauf trainiert ist, Unklarheiten aggressiv zu beseitigen.

Die Datei gparser.zil ist das schwerste Stück Code im ganzen Repository. Sie verwandelt chaotische menschliche Sprache in saubere Maschinenbefehle.

Die Anatomie eines Befehls

Der Parser arbeitet nicht Wort für Wort, sondern sucht nach grammatikalischen Schablonen. In gsyntax.zil sind diese Schablonen definiert. Ein Blick darauf zerstört die Illusion der Sprachbegabung sofort:

<SYNTAX ATTACK OBJECT (FIND ACTORBIT) (ON-GROUND IN-ROOM)
        WITH OBJECT (FIND WEAPONBIT) (HELD CARRIED HAVE) = V-ATTACK>

Diese Zeile verrät alles über die "Intelligenz" des Spiels:

  1. Synonyme: Egal ob der Spieler KILL, MURDER, SLAY oder HIT tippt – für die Engine ist es alles derselbe interne ID-Code (V?ATTACK).
  2. Constraints (Einschränkungen): Der Parser prüft vor der Ausführung, ob die Objekte logisch sind.
    • Das Ziel muss ein ACTORBIT haben (man kann keinen Stein "ermorden").
    • Die Waffe muss ein WEAPONBIT haben (man kann niemanden mit einer Wurst "töten").
    • Die Waffe muss HELD sein (im Inventar).

Wenn der Spieler "Töte Troll mit Wurst" tippt, scheitert nicht die Kampf-Logik, sondern der Parser bricht ab, weil die Wurst das WEAPONBIT nicht besitzt. Die Fehlermeldung ist generisch, wirkt aber kontextsensitiv.

GWIM: "Get What I Mean"

Die vielleicht wichtigste Routine in gparser.zil ist GWIM (Get What I Mean). Sie ist der Grund, warum sich Zork modern anfühlt.

Wenn der Spieler nur "Öffnen" tippt, fehlt das Objekt. Ein dummer Parser würde "Was öffnen?" fragen. Zork startet GWIM. Die Routine scannt den aktuellen Raum (HERE) nach Objekten, die zum Verb passen. OPEN verlangt ein Objekt mit dem Flag DOORBIT oder CONTBIT (Container).

  • Findet GWIM genau ein passendes Objekt (z.B. eine Tür), führt es den Befehl automatisch aus: "(the door)".
  • Findet es kein Objekt, gibt es einen Fehler aus.
  • Findet es zwei (z.B. eine Tür und eine Kiste), muss es nachfragen: "Welches meinst du, die Tür oder die Kiste?"

Diese Heuristik spart dem Spieler tausende von Tastenanschlägen und erzeugt die Illusion, dass das Spiel "mitdenkt".



Ablaufdiagramm fuer den heuristischen Kommando Parser.

Die Grenzen des Wortschatzes

Ein Blick in die Vokabular-Listen zeigt kuriose Artefakte. Um Speicher zu sparen, schneidet der Parser Wörter oft nach 6 Buchstaben ab. LANTERN ist für das Spiel identisch mit LANTER.

Noch interessanter sind die "Buzzwords" in gsyntax.zil:

<BUZZ A AN THE IS AND OF THEN ALL ONE BUT EXCEPT>

Diese Wörter werden vom Parser einfach ignoriert (außer ALL und EXCEPT in speziellen Kontexten). Der Satz "Nimm das Schwert" wird intern zu "Nimm Schwert". Das Spiel versteht keine Grammatik; es filtert Rauschen heraus, bis nur noch Schlüsselwörter übrig sind.

Der "Orphan"-Status

Was passiert, wenn der Spieler einen Satz abbricht?

Spieler: "Töte"
Spiel: "Wen möchtest du töten?"
Spieler: "Den Troll"

Das ist für einen Computer extrem schwer, da der Kontext ("Töten") im zweiten Schritt verloren ist. gparser.zil löst das durch den "Orphan"-Status (P-OFLAG). Wenn ein Satz unvollständig ist, speichert der Parser das Verb (V?ATTACK) in einem globalen Puffer und setzt das Orphan-Flag. Die nächste Eingabe wird dann nicht als neuer Befehl, sondern als Ergänzung zum gepufferten Verb interpretiert.

Zusammenfassung der Technik: Der Parser ist ein Meisterwerk der Täuschung. Er versteht keine Sprache, er versteht Zustände. Er mappt Eingaben auf eine Matrix aus erlaubten Bit-Flags (WEAPONBIT, DOORBIT, ACTORBIT). Alles, was nicht in diese Matrix passt, wird mit generischen Fehlermeldungen abgelehnt.


Die Spielmechanik: Simulation statt Skript

Wenn man Zork spielt, fühlt es sich oft so an, als würde man ein Buch lesen, dessen Seiten man selbst umblättern darf. Der Code enthüllt jedoch etwas völlig anderes: Unter der Haube läuft eine komplexe Zustands-Simulation, die Elemente aus Rollenspielen, Stealth-Games und Physik-Puzzles kombiniert.

Es gibt keine "Cutscenes" oder fest geskripteten Abläufe. Alles ist Emergenz – das Ergebnis von Systemen, die in Echtzeit interagieren.

1. Der Dieb: Ein KI-Agent in 1980

Der Dieb ist nicht einfach ein Monster, das in einem Raum wartet. Er ist ein autonomer Agent, gesteuert von einer Finite State Machine (FSM) in der Routine I-THIEF.

Jede Runde würfelt das Spiel. Der Dieb kann sich bewegen, stehlen oder den Spieler angreifen. Der Code in 1actions.zil beweist, wie detailliert sein Verhalten ist:

<ROUTINE ROB-MAZE (RM "AUX" X N)
     <SET X <FIRST? .RM>>
     <COND (<AND <FSET? .X ,TAKEBIT>
                 <PROB 40>>
            <TELL "You hear, off in the distance, someone saying
                   \"My, I wonder what this fine " D .X " is doing here.\"" CR>
            <MOVE .X ,THIEF>)>

Das ist faszinierend: Der Dieb scannt tatsächlich den Inhalt von Räumen (<FIRST? .RM>). Wenn er ein wertvolles Item findet (TAKEBIT), besteht eine 40% Chance, dass er es nimmt. Er "teleportiert" es dann in sein eigenes Inventar (<MOVE .X ,THIEF>). Der Spieler verliert Gegenstände nicht durch ein Skript, sondern weil ein NPC sie physisch aufhebt und wegträgt.

Mehr noch: Der Dieb nutzt die Beute. Wenn er das "Juwelenbesetzte Ei" stiehlt, öffnet er es in seinem Versteck (TREASURE-ROOM), weil er – anders als der Spieler – die Geschicklichkeit dazu hat. Dies ist kein Bug, sondern ein essenzieller Teil des Puzzles: Man muss sich bestehlen lassen, um an den Inhalt des Eies zu kommen.




Zustandsdiagram fuer den Dieb in Zork.

2. Fluiddynamik: Das Staudamm-Rätsel

Das komplexeste physische Puzzle im Spiel ist der Staudamm. Es ist ein Paradebeispiel dafür, wie ZIL globale Zustände nutzt, um die Geografie der Welt zur Laufzeit umzuschreiben.

Das System basiert auf zwei globalen Variablen: GATES-OPEN (Schleusen offen) und LOW-TIDE (Niedrigwasser). Wenn der Spieler den Bolzen dreht, startet eine Simulation (I-MAINT-ROOM), die den Wasserstand Runde für Runde verändert.

<ROUTINE I-RFILL ()
     <FSET ,RESERVOIR ,NONLANDBIT>
     <FCLEAR ,RESERVOIR ,RLANDBIT>
     <COND (<EQUAL? ,HERE ,RESERVOIR>
            <JIGS-UP "You are lifted up by the rising river! ...">)>

Hier sehen wir die Map-Mutation live im Code:

  • FSET ,RESERVOIR ,NONLANDBIT: Der Raum "Reservoir" wird offiziell zu Wasser.
  • FCLEAR ,RESERVOIR ,RLANDBIT: Er verliert die Eigenschaft "Land".

Wer sich in diesem Moment im Reservoir befindet, stirbt nicht durch ein Skript, sondern durch die Physik-Engine: Der Raum ist nun "Nicht-Land", und der Spieler hat kein Boot. Die Konsequenz (JIGS-UP) ist unausweichlich.

3. Das Kampfsystem: Mathematik statt Zufall

Kämpfe in Zork wirken zufällig, folgen aber einer harten mathematischen Logik. Die Routine DO-FIGHT greift auf Tabellen zurück, die wie Matrizen in einem Pen-&-Paper-Rollenspiel funktionieren.

Besonders spannend ist die Waffenspezialisierung. Die Tabelle VILLAINS definiert für jeden Gegner eine "beste Waffe":

<GLOBAL VILLAINS
    <LTABLE <TABLE TROLL SWORD 1 0 TROLL-MELEE>
            <TABLE THIEF KNIFE 1 0 THIEF-MELEE> ... >>

Der Code besagt: Gegen den Troll gibt das Schwert (SWORD) einen Bonus von +1. Gegen den Dieb ist das Messer (KNIFE) effektiver. Wer den Dieb mit dem Schwert angreift, kämpft mit einem mathematischen Nachteil (VILLAIN-STRENGTH Routine), ohne es zu wissen. Das Spiel kommuniziert dies nie explizit, es ist eine versteckte Mechanik.

4. Der "Critical Path": Ein unsichtbares Netz aus Abhängigkeiten

Wenn man all diese Systeme zusammennimmt – den Dieb, die Physik, die Rätsel – entsteht ein Graph aus Abhängigkeiten, der den einzigen Weg zum Sieg diktiert. Der Code erzwingt eine nicht-lineare Lösung.

Ein Blick auf 1actions.zil enthüllt harte Logik-Gatter ("Gates"), die den Fortschritt blockieren:

  1. Das Troll-Gate: Man kann das Dungeon nicht betreten, ohne den Troll zu töten (TROLL-FLAG).
  2. Das Staudamm-Gate: Man muss den Damm schließen, um an die Truhe zu kommen, aber man muss ihn wieder öffnen, um den Fluss mit dem Boot zu befahren.
  3. Das Dieb-Gate: Man kann das Ei nicht selbst öffnen (der Code zerstört es sonst: BAD-EGG). Man ist gezwungen, die KI des Diebes gegen ihn zu verwenden.


Ablaufdiagram der wesentlichen Story-Elemente im Spiel Zork.

Das Faszinierende an diesem Code ist nicht, wie alt er ist, sondern wie systemisch er denkt. Infocom hat nicht einfach eine Geschichte geschrieben; sie haben eine Welt simuliert und den Spieler darin ausgesetzt. Die Geschichte ist nur das, was passiert, wenn der Spieler versucht, diese Simulation zu brechen.


Code-Archäologie: Die schmutzige Wahrheit

Wer diesen Code heute liest und moderne Clean-Code-Standards erwartet, wird schreiend davonlaufen. Zork ist ein Produkt seiner Zeit. Speicherplatz war teurer als Gold, und CPU-Zyklen waren rar. Das führte zu Programmierpraktiken, für die man heute in jedem Code-Review gefeuert würde. Doch genau diese "Sünden" machten das Spiel erst möglich.

1. Bit-Packing bis zum Exzess

In einer modernen Sprache würden wir für den Zustand einer Tür (Offen? Verschlossen? Unsichtbar?) einzelne Boolesche Variablen oder ein State-Enum nutzen. In ZIL wäre das Platzverschwendung.

Stattdessen werden Eigenschaften in Bitmasken gepresst. Ein Blick in 1dungeon.zil zeigt Zeilen wie diese:

<OBJECT GRATE
      (FLAGS DOORBIT NDESCBIT INVISIBLE)
      (ACTION GRATE-FUNCTION)>

FLAGS ist hier keine Liste, sondern ein einziges Maschinenwort (16 Bit).

  • DOORBIT: Das Objekt verhält sich wie eine Tür.
  • INVISIBLE: Der Parser ignoriert es, bis es "entdeckt" wird.
  • NDESCBIT: Es wird in der Raumbeschreibung ("No Description") nicht automatisch aufgelistet.

Die Engine prüft diese Bits mit extrem schnellen bitweisen Operationen (BTST, FSET). Das ist maximal effizient, aber wehe dem Entwickler, der vergisst, ein Bit zu löschen. Ein INVISIBLE-Objekt, das man aufheben kann (TAKEBIT), führt zu Geister-Items im Inventar.

2. Toter Code und Phantomschmerzen

Das Repository enthält Artefakte, die zeigen, dass Zork I nicht sauber geplant, sondern organisch gewachsen (und geschrumpft) ist. Die Datei zork1.errors (ein Log des Compilers) listet gnadenlos auf:

Symbols unused:
UNTIE-FROM
BREATHE
CYCLOPS-MELEE
...

Im Quellcode selbst finden wir die Leichen dieser Funktionen. In 1actions.zil existiert eine Routine BREATHE:

<ROUTINE BREATHE ()
     <PERFORM ,V?INFLATE ,PRSO ,LUNGS>>

Der Code ist da, aber er wird nie aufgerufen. Vermutlich gab es einmal ein Puzzle, bei dem man das Boot mit dem Mund aufblasen musste (daher LUNGS). Im finalen Spiel braucht man zwingend die Pumpe (PUMP). Der Code wurde nie gelöscht, nur "abgeklemmt" – digitaler Müll, der Speicherplatz auf der Entwicklungsmaschine belegte, aber dank des Compilers nicht auf der Diskette landete.

3. Die "Copy-Paste"-Architektur

Infocom entwickelte nicht nur ein Spiel, sondern eine Trilogie. Der Quellcode zeigt, dass sie kein sauberes Versionskontrollsystem (wie Git Branches) hatten, sondern oft denselben Code für alle drei Spiele nutzten und mit IF-Abfragen umschalteten.

Ein Blick in gverbs.zil offenbart diesen Wartungs-Albtraum:

<ROUTINE V-SWIM ()
     <COND (<OR <==? ,ZORK-NUMBER 1>
                <==? ,ZORK-NUMBER 2>>
            <TELL "Swimming isn't usually allowed in the dungeon.">)
           (T
            <TELL "Go jump in a lake!" CR>)>>

Diese Routine prüft zur Kompilierzeit (<COND ...>), welches Spiel gerade gebaut wird. Das bedeutet: Der Code für Zork II und Zork III geistert in den Quelldateien von Zork I herum. Wenn man einen Bug in der Schwimm-Logik von Teil 1 fixte, riskierte man, Teil 3 kaputtzumachen.

4. Der 6-Buchstaben-Hack

Um das Vokabular effizient zu speichern, schnitt der Z-Machine-Standard Wörter oft nach 6 Buchstaben ab. Im Code sehen wir das nicht direkt, aber es erklärt seltsame Kommentare in zork1.errors. Für den Spieler bedeutete das: LANTERN und LANTER waren identisch. Das führte zu dem kuriosen Effekt, dass man oft Tippfehler machen konnte, solange die ersten 6 Buchstaben stimmten, und das Spiel einen trotzdem verstand.


Fazit: Ein Triumph des Engineerings

Der offene Quellcode von Zork I ist entzaubernd und faszinierend zugleich. Er zeigt, dass die legendäre "KI" des Parsers nur ein cleverer Taschenspielertrick aus Bitmasken und Heuristiken ist. Er zeigt, dass die Welt nicht aus Magie besteht, sondern aus globalen Variablen und Zustandsautomaten.

Aber vor allem zeigt er eines: Pragmatismus. Die Entwickler bei Infocom haben keinen "schönen" Code geschrieben. Sie haben Code geschrieben, der funktionierte – unter Bedingungen, die uns heute unmöglich erscheinen (64 KB RAM, langsame Prozessoren). Sie nutzten jeden Trick, jeden Hack und jede Abkürzung.

Zork ist kein Lehrbuchbeispiel für Softwarearchitektur. Es ist ein Lehrbuchbeispiel dafür, wie man ein Produkt ausliefert, egal wie hart die technischen Limits sind. Und das ist eine Lektion, die auch 45 Jahre später noch relevant ist.




Thursday, November 6, 2025

Beyond the Textbook: Fascinating Finds in Modern Biology

Ever had a conversation where your curiosity outpaced your knowledge? I recently found myself in that exact spot. After diving into podcasts on the MicrobeTV YouTube channel, I had the chance to speak with a biologist, only to realize how quickly the fascinating details I'd learned had faded. To cement my understanding and share these incredible findings, I've summarized some of the most thought-provoking topics I came across.

Blood Pressure Regulation via Oral Microbes

  • Key finding: After eating nitrate-rich vegetables, salivary glands concentrate nitrate from blood into saliva (up to 20x higher) for several hours. This gives oral bacteria (Veillonella, Actinomyces) extended time to convert it into nitrite, which the body uses to produce blood-pressure-lowering nitric oxide. Antiseptic mouthwash disrupts this beneficial process.
  • Source (2017): https://pubmed.ncbi.nlm.nih.gov/28353075/

mRNA Vaccines Enhance Cancer Immunotherapy

  • Key finding: Cancer patients receiving an mRNA COVID vaccine within 100 days of starting immune checkpoint inhibitor (ICI) therapy showed significantly improved survival (median increased from ~20 to 37 months). The mRNA-lipid nanoparticle platform triggers a type I interferon surge that "warms up" immunologically "cold" tumors by increasing their PD-L1 expression, making them susceptible to ICI treatment.
  • Source: https://www.youtube.com/watch?v=zEwzqVhuTzk

mRNA Vaccines Enhance Cancer Immunotherapy

  • Key finding: Cancer patients receiving an mRNA COVID vaccine within 100 days of starting immune checkpoint inhibitor (ICI) therapy showed significantly improved survival (median increased from ~20 to 37 months). The vaccine's lipid nanoparticle formulation causes single-stranded RNA to form complex secondary structures that are recognized by the intracellular sensor MDA5 as viral double-stranded RNA, triggering a type I interferon surge (notably IFNα) that sensitizes tumors to ICI treatment.

  • Details: The mechanism relies on innate immune sensing, not antigen specificity—vaccines encoding different antigens (SARS-CoV-2 spike or CMV pp65) showed similar anti-tumor effects. The LNP encapsulation is crucial: when the ssRNA is packaged in LNPs, it forms higher-order secondary structures that activate MDA5. Anionic lipoplexes (a different lipid carrier) did not produce anti-tumor effects, confirming the LNP's critical role. The researchers ruled out dsRNA contamination (only 0.011% present, and removal didn't affect efficacy) and RIG-I involvement. Human studies confirmed a dramatic 280-fold increase in plasma IFNα 24 hours post-vaccination, part of a broader but short-lived inflammatory response.

  • Sources:


Cancer's Mitochondrial Sabotage of T-Cells

  • Key finding: Cancer cells engage in a dual mitochondrial manipulation strategy: they steal healthy mitochondria from T-cells to fuel their own growth, while simultaneously donating their own defective, mutation-laden mitochondria back to T-cells. These damaged mitochondria resist normal degradation (mitophagy) due to proteins like USP30, accumulate in T-cells, and cause T-cell exhaustion and dysfunction—effectively crippling the anti-tumor immune response.

  • Details: The transfer occurs through two mechanisms: (1) Tunneling nanotubes (TNTs)—thin, membrane-bound intercellular conduits composed of F-actin filaments (and sometimes microtubules) that hover above the substratum and create temporary bridges between cells for direct transfer; and (2) Extracellular vesicles (EVs)—cancer cells package mitochondria into small EVs that are released and fuse with T-cell membranes to deliver their cargo. The cells do not permanently share membranes or fuse into a hybrid entity; TNTs are temporary connections, and EV fusion occurs only at the vesicle scale.

  • Sources:


Bacterial Survival Without Cell Walls (L-Forms)

  • Key finding: Bacteria can lose their protective peptidoglycan cell walls and survive as L-forms—a transformation triggered by stress conditions like high osmotic pressure (e.g., 0.5 molar sucrose) or antibiotic exposure. These fragile, wall-deficient cells require high osmolyte environments to prevent bursting and can persist indefinitely or revert to walled forms, enabling resistance to both antibiotics and bacteriophages while contributing to persistent infections like tuberculosis and recurrent UTIs.

  • Source: https://www.youtube.com/watch?v=NIrmcitpBp8


Bacterial "Dead Man Switch" Defense (Panoptes System)

  • Key finding: The Panoptes system is a two-gene bacterial defense mechanism where one protein (OptS) constantly produces a cyclic nucleotide "all-clear" signal that inactivates a potent membrane-disrupting toxin (OptE). When a phage infects and its "sponge" proteins (like ACB2) absorb this signal to disable other bacterial defenses, they inadvertently trigger the toxin, killing the bacterium in an "abortive infection" that stops phage replication and protects the bacterial population.

  • Source: https://www.youtube.com/watch?v=9lHeeBWZnk0


Active vs. Passive Reflux in Distillation

  • Key finding: Passive reflux (e.g., Vigreux column) uses surface area to cause repeated condensation-revaporization cycles for gradual separation. Active reflux uses a dephlegmator (active reflux condenser, a cooled pipe that causes the condensate to flow back into the column) that precisely controls cooling to force vapor condensation back down the column. The column consists of several layers that each have a valve that lets gas pass through towards the top and liquid drip down, allowing for interaction between gas and liquid. This allows control over final purity from moderate separation (if dephlegmator and column is heated) to nearly pure alcohol in a single pass (if dephlegmator is cooled a lot causing most of the condensate to drip back into the column). Note: The reflux mechanism is analogous to feedback in digital/analog filter design—adjusting the amount of recycled output (like a feedback parameter) controls the trade-off between purity/sharpness and throughput/speed.

  • Source: https://youtu.be/oBHIc6LwH6o



These summaries were compiled as part of an assignment for the "How to solve it with code" course, where I used AI to help distill and clarify the original research. You can explore the full AI-powered research process https://share.solve.it.com/d/2fb1825450db52465c69ce52a2d15c4a