4 Punkte von GN⁺ 2024-04-25 | 1 Kommentare | Auf WhatsApp teilen
  • Piet ist eine esoterische Programmiersprache, die so gestaltet wurde, dass Code wie abstrakte Kunst aussieht, und ist nach Piet Mondrian benannt, einem Pionier der geometrischen abstrakten Malerei
  • Programme sind Grafiken aus 20 erkennbaren Farben, und der Interpreter bewegt sich zwischen Farbblöcken und interpretiert die Veränderung der Farben als Befehle
  • Alle Daten existieren nur als Ganzzahlen und werden auf einem Stack gespeichert; die Größe eines Farbblocks ist zwar ein Wert, wird aber ohne einen push-Befehl nicht automatisch auf den Stack gelegt
  • Die Ablaufsteuerung wird durch Direction Pointer und Codel Chooser sowie Regeln für schwarze Blöcke, Ränder und weiße Blöcke bestimmt; einige Verhaltensweisen können je nach Implementierung unterschiedlich sein
  • Es gibt Beispiele und ein Ökosystem externer Tools, aber keinen maßgeblichen offiziellen Interpreter, und Fehlerbehandlung sowie die Interpretation nicht standardisierter Farben bleiben implementierungsabhängig

Piets Grundidee

  • Piet ist eine Programmiersprache, bei der der Programmcode wie abstrakte Kunst aussieht
  • Der Name stammt von Piet Mondrian, einem Pionier der geometrischen abstrakten Malerei
  • Eigentlich sollte der Name Mondrian verwendet werden, aber da es bereits eine Skriptsprache mit diesem Namen gab, wurde der Name Piet gewählt
  • Seit der Erstellung der Spezifikation ist eine kleine Community entstanden, die Programme, Interpreter, IDEs und Compiler entwickelt hat
  • Einen maßgeblichen offiziellen Interpreter gibt es nicht; verfügbare Implementierungen können die Spezifikation leicht unterschiedlich auslegen
  • Einige Erläuterungen zur Spezifikation wurden später ergänzt, aber manche bestehende Implementierungen folgen ihnen möglicherweise nicht

Farben und Code-Einheiten

  • Piet verwendet insgesamt 20 Farben
    • 18 Farben gehören zum Farbzyklus und zum Helligkeitszyklus
    • Weiß und Schwarz gehören zu keinem der beiden Zyklen
  • Der Farbzyklus verläuft in der Reihenfolge red -> yellow -> green -> cyan -> blue -> magenta -> red
  • Der Helligkeitszyklus verläuft in der Reihenfolge light -> normal -> dark -> light
    • light kann auch als eine Stufe dunkler als dark betrachtet werden, und umgekehrt gilt dasselbe
  • Nicht standardisierte Farben wie Orange oder Braun können verwendet werden, ihre Wirkung ist jedoch implementierungsabhängig
    • Im einfachsten Fall werden nicht standardisierte Farben wie Weiß behandelt
    • Eine andere Möglichkeit ist, sie wie Schwarz zu behandeln

Codels und Farbblöcke

  • Piet-Code ist eine Grafik aus erkennbaren Farben
  • Da einzelne Code-Pixel eine sprachliche Bedeutung haben, wird in zur besseren Sicht vergrößerten Programmen ein einzelnes Pixel im Code als Codel bezeichnet
  • Die grundlegende Ausführungseinheit ist der Farbblock
    • Ein Farbblock ist ein Bereich gleichfarbiger Codels, die horizontal oder vertikal zusammenhängen
    • Blöcke, die sich nur diagonal berühren, gelten nicht als verbunden
    • Ein Farbblock kann jede beliebige Form haben und im Inneren Löcher anderer Farben enthalten
    • Innere Löcher gehören nicht zu diesem Block

Stack und Wertdarstellung

  • Piet speichert alle Datenwerte auf einem Stack
  • Datenwerte existieren nur als Ganzzahlen
    • Je nach Befehl können sie als Unicode-Zeichenwerte ein- oder ausgegeben werden
  • Der Stack ist konzeptionell unendlich tief, Implementierungen können jedoch eine endliche maximale Stack-Größe haben
  • Tritt bei einem endlichen Stack ein Overflow auf, ist das ein Laufzeitfehler; die Behandlung ist implementierungsabhängig
  • Farbblöcke außer schwarzen und weißen repräsentieren einen Ganzzahlwert, der der Anzahl der Codels in diesem Block entspricht
    • Nicht positive Ganzzahlen lassen sich nicht direkt darstellen
    • Sie können jedoch durch Operatoren erzeugt werden
    • Der Wert eines Farbblocks wird nicht automatisch auf den Stack gepusht; dafür ist ein expliziter push-Befehl nötig
  • Auch die Größe von Ganzzahlen ist konzeptionell unbegrenzt, Implementierungen können aber eine endliche maximale Ganzzahlgröße festlegen
    • Ganzzahl-Overflow ist ein Laufzeitfehler, dessen Behandlung implementierungsabhängig ist

Ausführungsfluss

  • Der Interpreter beginnt die Ausführung im Farbblock, der den Codel links oben des Programms enthält
  • Während der Ausführung werden zwei Zustände beibehalten
    • Direction Pointer (DP): zeigt anfangs nach rechts und kann nach rechts, links, unten oder oben zeigen
    • Codel Chooser (CC): zeigt anfangs nach links und kann links oder rechts anzeigen
  • Das nächste Bewegungsziel wird durch den Rand des aktuellen Farbblocks und die Kombination aus DP und CC bestimmt
    • Zuerst wird die Kante des aktuellen Farbblocks gesucht, die in DP-Richtung am weitesten liegt
    • Auf dieser Kante wird, relativ zur DP-Richtung, der in CC-Richtung am weitesten entfernte Codel gewählt
    • Von diesem Codel aus wird in DP-Richtung in den Farbblock gewechselt, zu dem der direkt nächste Codel gehört
  • Dieser Vorgang wiederholt sich, bis eine Abbruchbedingung erreicht wird; dann endet das Programm

Schwarze Blöcke, Ränder und weiße Blöcke

  • Schwarze Farbblöcke und der Rand des Programms wirken als Barrieren, die den Ausführungsfluss blockieren
  • Wenn der Interpreter in einen schwarzen Block wechseln oder über den Rand hinauslaufen würde, stoppt er und schaltet den CC um
  • Scheitert auch der zweite Versuch, wird der DP um eine Position im Uhrzeigersinn gedreht
  • Wenn nach insgesamt 8 Versuchen mit abwechselndem Umschalten von CC und Drehen von DP der aktuelle Farbblock immer noch nicht verlassen werden kann, endet das Programm
  • Bewegung durch weiße Blöcke

    • Weiße Farbblöcke sind freie Bereiche, die der Interpreter ungehindert durchquert
    • Wechselt der Interpreter aus einem Farbblock in einen weißen Bereich, bewegt er sich in DP-Richtung geradlinig weiter, bis er einen nicht weißen Farbblock erreicht
    • Beim Wechsel in eine neue Farbe über einen weißen Block wird kein Befehl ausgeführt
    • Dadurch können weiße Blöcke die aktuelle Farbe ändern, ohne einen Befehl auszuführen, was sie nützlich für das Schreiben von Schleifen macht
    • Bei der Bewegung durch weiße Blöcke wird nicht das Verfahren zur Wahl eines Ausgangs aus einem nicht weißen Farbblock verwendet, sondern nur eine geradlinige Bewegung
  • Wenn ein weißer Block blockiert wird

    • Trifft die geradlinige Bewegung durch einen weißen Block auf einen schwarzen Block oder den Rand, gilt dies als Behinderung
    • In diesem Fall wird der CC umgeschaltet, aber da sich das Ziel dadurch nicht ändert, wird der DP sofort um eine Position im Uhrzeigersinn gedreht
    • Danach setzt der Interpreter die geradlinige Bewegung vom aktuellen weißen Codel in der neuen DP-Richtung fort
    • Jedes Mal, wenn innerhalb eines weißen Blocks eine Behinderung auftritt, werden CC-Umschaltung und DP-Drehung wiederholt
    • Wird ein Farbblock betreten, läuft die Ausführung weiter; beginnt der Pfad innerhalb des weißen Blocks zurückzuverfolgen, gibt es keinen Ausweg mehr und die Ausführung endet

Befehlssystem

  • Piet-Befehle werden durch die Farbänderung bestimmt, wenn von einem Farbblock in den nächsten gewechselt wird
  • Wie viele Schritte im Farbzyklus und im Helligkeitszyklus weitergegangen wird, legt den Befehl fest
  • Bei Farbwechseln über weiße Blöcke wird kein Befehl ausgeführt
  • Stack- und Arithmetik-Befehle

    • push: legt den Wert des Farbblocks, der gerade verlassen wurde, auf den Stack
    • pop: entfernt den obersten Wert vom Stack und verwirft ihn
    • add: addiert die beiden obersten Werte und legt das Ergebnis zurück auf den Stack
    • subtract: legt das Ergebnis aus zweitem Wert minus oberstem Wert auf den Stack
    • multiply: multipliziert die beiden obersten Werte
    • divide: teilt den zweiten Wert per Ganzzahldivision durch den obersten Wert
    • Division durch 0 ist ein implementierungsabhängiger Fehler; empfohlen wird, den Befehl zu ignorieren
    • mod: legt den Rest aus der Division des zweiten Werts durch den obersten Wert auf den Stack
    • Das Ergebnis hat dasselbe Vorzeichen wie der Divisor, also der oberste Stack-Wert
    • Ist der oberste Wert 0, ist das ein Division-durch-0-Fehler; empfohlen wird, den Befehl zu ignorieren
    • mod bei negativem Dividenden entspricht der floored division, wie sie in Wikipedias modulus operation beschrieben wird
  • Vergleichs-, Pointer- und Ein-/Ausgabe-Befehle

    • not: macht aus einem vom Stack genommenen Wert 0, wenn er ungleich 0 ist, und 1, wenn er 0 ist
    • greater: legt 1 auf den Stack, wenn der zweite Wert größer als der oberste Wert ist, sonst 0
    • pointer: nimmt den obersten Stack-Wert und dreht den DP um diese Anzahl Schritte im Uhrzeigersinn
    • Ist der Wert negativ, erfolgt die Drehung gegen den Uhrzeigersinn
    • switch: nimmt den obersten Stack-Wert und schaltet den CC so oft um
    • Ist der Wert negativ, wird entsprechend seinem Absolutwert umgeschaltet
    • duplicate: legt eine Kopie des obersten Stack-Werts auf den Stack
    • roll: nimmt die beiden obersten Werte und rotiert einen Teil des verbleibenden Stacks um angegebene Tiefe und Anzahl von Schritten
    • Ist die Tiefe negativ, liegt ein Fehler vor und der Befehl wird ignoriert
    • Ein roll, das die implementierungsabhängige maximale Stack-Tiefe überschreitet, ist ein implementierungsabhängiger Fehler; empfohlen wird, den Befehl zu ignorieren
    • in: liest eine Zahl oder ein Zeichen von STDIN und legt sie auf den Stack
    • Gibt es keine Eingabe oder wird bei einer Ganzzahleingabe keine Ganzzahl empfangen, liegt ein Fehler vor und der Befehl wird ignoriert
    • out: gibt den obersten Stack-Wert als Zahl oder Zeichen auf STDOUT aus
    • Operationen, die wegen zu weniger Stack-Werte nicht ausgeführt werden können, werden ignoriert und der nächste Befehl wird verarbeitet

Beispiele und Tools

1 Kommentare

 
GN⁺ 2024-04-25
Hacker-News-Kommentare
  • Das letzte Programm auf der Beispielseite ist wirklich erstaunlich: Jemand namens Piet sah ein Kunstwerk, das an die Sprache Piet erinnerte, und probierte aus, es auszuführen.
    Es lief, und es könnte vielleicht der erste Fall in der Geschichte sein, in dem ein Grafikkünstler zufällig ein funktionierendes Computerprogramm gemalt hat.
    https://www.dangermouse.net/esoteric/piet/samples.html
    https://gitlab.fabcity.hamburg/hofalab/piet-get-together

    • Wenn man die Bedingung „funktionierend“ ausreichend locker fasst, hat sich schon gezeigt, dass die meisten Farbspritzer bereits gültige Perl-Programme sind.
      https://www.mcmillen.dev/sigbovik/
    • Piet J. sah in einer kleinen Galerie ein Kunstwerk und hatte das Gefühl, es sehe wie ein Piet-Programm aus; der Künstler sagte, er kenne die Sprache überhaupt nicht.
      Piet machte ein Foto des Werks, wandelte es in eine Bilddatei um, deren Farben er auf die Piet-Palette annäherte, und führte es dann aus. Tatsächlich lief es, und der Code war eine Endlosschleife, die ASCII-Zeichen las und deren ASCII-Zahlenwert ausgab.
      Das ist wirklich kaum zu glauben.
    • Auch das Beispiel zur Berechnung von π war gut.
      Die Erklärung „Natürlich erhält man mit einem größeren Programm einen genaueren Wert“ wirkte auf mich wie eine Art Witz, den ich so noch nie gesehen hatte.
    • Leider hängt das von einem Unterschied zwischen npiet und der aktuellen Piet-Spezifikation ab.
      Laut Spezifikation sollte der Interpreter beim aktuellen weißen Codel beginnen, in der neuen DP-Richtung zu gleiten, und weiterlaufen, bis er in einen Farbblock eintritt oder auf eine andere Begrenzung stößt.
      Der npiet-Interpreter schaut jedoch in den Leerraum hinein und springt dann zur Position des letzten farbigen Codels zurück. Irgendwann möchte ich dieses Verhalten als Option in den Lexer meines Piet-Compilers einbauen, bin aber noch nicht dazu gekommen.
      Nach Spezifikation wird dieses Programm zu einer einfachen nicht terminierenden Schleife, weil die äußersten Ecken fast aller Blöcke an Weiß angrenzen. Komplexe Piet-Programme zu schreiben, die mit mehreren Interpretern und Compilern funktionieren, ist ziemlich schwierig; alle haben subtil unterschiedliche, nicht dokumentierte Auslegungen.
      Ich glaube, die Ausgabe meines Piet-Backends ist im Allgemeinen weniger interpreterabhängig, aber genauer untersucht habe ich nur drei oder vier andere Interpreter.
      https://github.com/boothby/repiet/
    • Ich frage mich, wie hoch die Wahrscheinlichkeit ist, dass so ein schlichtes Bild – also ein paar große rechteckige Blöcke – ein gültiges Programm ist.
      Beim Überfliegen der Dokumentation scheint es wegen der Bedingung „Operationen, die nicht ausgeführt werden können, etwa weil zu wenige Werte auf dem Stack liegen, um etwas zu entfernen, werden einfach ignoriert und die Ausführung fährt mit dem nächsten Befehl fort“ möglich, dass all diese Bilder ohne Fehler laufen.
      Eine andere Frage ist allerdings, wie viele solcher zufälligen Bilder tatsächlich etwas „Sinnvolles“ tun.
  • Piet ist unter den esoterischen Programmiersprachen ein Meilenstein-Experiment, aber ich finde, es verfehlt das Ziel, Programme wie Mondrian-Gemälde aussehen zu lassen, sofern der Entwickler das nicht wirklich beabsichtigt.
    Es wäre schön, wenn die Sprachstruktur selbst so gestaltet wäre, dass alles, was man „schreibt“, wie ein Mondrian-Gemälde aussieht.

    • Stimmt schon, allerdings verwendete Mondrian im Grunde vor allem Primärfarben, daher wäre das ziemlich eingeschränkt gewesen.
  • Mir kommt immer wieder diese Frage in den Sinn: Wie sieht ein Algorithmus aus?
    Lässt sich in der Realität etwas erschaffen, das dem ähnelt, was in Herman Hesses Roman Das Glasperlenspiel vorkommt? Der Originaltitel lautet Magister Ludi
    Als visuell orientierter Mensch möchte ich glauben, dass es möglich ist, und ich habe solche Werkzeuge tatsächlich auch ausprobiert
    https://community.carbide3d.com/uploads/default/original/3X/5/b/5b0872a5666fec9b7bb6fd623c431de03263372d.jpeg
    Aber ohne eine klare Antwort auf die obige Frage laufen solche Werkzeuge immer Gefahr, am Ende so auszusehen:
    https://blueprintsfromhell.tumblr.com/
    https://scriptsofanotherdimension.tumblr.com/
    Auch die Balance zwischen visueller Ausdruckskraft und Modularität ist schwierig; wenn man Modularität zu stark vorantreibt, landet man allzu leicht wieder bei der Textbarriere, die man eigentlich vermeiden wollte

    • Piet von Hand zu schreiben, macht Spaß, weil man dabei erkundet, „wie ein Algorithmus aussieht“
      Sergei Lewis und ich haben jeweils Werkzeuge gebaut, die Piet-Code erzeugen. Sergeis Assembler produziert deutlich hübscher aussehenden Code als mein Piet-Backend
      Was man in der Ausgabe meines Compilers tatsächlich sieht, ist vor allem, dass ich ziemlich faul Trampoline verwendet habe
      http://www.toothycat.net/wiki/wiki.pl?MoonShadow/Piet
      https://github.com/boothby/repiet/
      https://en.wikipedia.org/wiki/Trampoline_(computing)
    • Ich glaube, dass es für jeden Algorithmus, ja sogar für jedes geistige Konzept, eine 1:1-Beziehung zu einer visuellen Darstellung gibt
      Der Gedanke kam mir beim Lesen eines Buchs von Steven Pinker: Abstrakte Wörter lassen sich in einfachere Wörter zerlegen und beschreiben am Ende irgendeine räumliche Beziehung. „rekindle“ kann man zum Beispiel als „zwei Dinge wieder zusammenbringen“ sehen
      Ähnlich ist eine for-Schleife das geistige Konzept, dass „eines über mehrere andere hinweggeht“, und das hat eine visuelle Darstellung wie „100“ -> „010“ -> „001“
      Daher frage ich mich, ob man eine Sprache schaffen könnte, die solche Bausteine als reine visuelle Transformationen definiert
    • Bei einfachen Programmen könnte man sich auch vorstellen, eine Turingmaschine zu implementieren, deren Symbole Farben sind
  • So etwas würde gut in einen Krimi-Thriller passen: Es taucht auf, blockiert den Protagonisten oder die Ermittler, bis jemand erkennt, dass es Code ist
    Dabei dachte ich, nur QR-Codes seien nützlich

  • Jemand hat in Piet eine Quine gebaut: http://mamememo.blogspot.com/2009/10/piet-quine.html?m=1
    Die Bilder in dem Beitrag sind kaputt, aber eine Kopie gibt es hier: https://codegolf.stackexchange.com/a/23255/103045

  • Der Moment, in dem man Piet entdeckt, ist ein besonderer Moment aus Ehrfurcht, Verwirrung und Staunen
    In meinem Fall ist er in diesem Gespräch der Informatik-Podcastfolge „The CS Primer Show“ mit meinem Freund Oz festgehalten: https://show.csprimer.com/episodes/e2-dont-let-a-gpt-have-all-the-fun

  • An der Uni gab es einen kleinen Kurs über esoterische Programmiersprachen
    Jede Person sollte sich eine Sprache wie Brainfuck oder Piet aussuchen und damit herumspielen; ich entschied mich für Piet und hatte ziemlich viel Spaß damit
    Ehrlich gesagt war die kleine Beispiel-App, die ich gebaut habe, ästhetisch nicht besonders gelungen, und um mit Piet Kunst zu machen, muss man wohl Piet-Experte werden

  • Die Beispielseite ist großartig
    Man kann sehen, wie sich die Leinwand nach und nach zu etwas immer Ausgefeilterem und Schönerem entwickelt
    https://www.dangermouse.net/esoteric/piet/samples.html

  • Dass „light“ als eine Stufe dunkler als „dark“ gilt, ist ziemlich tiefgründig

  • Es wäre cool, wenn man einen Autoencoder bauen könnte, der lernt, Code in Python oder anderen weniger esoterischen Sprachen zu nehmen und als Piet auszugeben
    Dann könnte man vielleicht ähnlich wie mit Stable Diffusion zufällige Algorithmen erzeugen