Ich präsentiere Ihnen die ersten 3% eines Hexagony-Selbstinterpreten ...
|./...\..._..>}{<$}=<;>'<..../;<_'\{*46\..8._~;/;{{;<..|M..'{.>{{=.<.).|.."~....._.>(=</.\=\'$/}{<}.\../>../..._>../_....@/{$|....>...</..~\.>,<$/'";{}({/>-'(<\=&\><${~-"~<$)<....'.>=&'*){=&')&}\'\'2"'23}}_}&<_3.>.'*)'-<>{=/{\*={(&)'){\$<....={\>}}}\&32'-<=._.)}=)+'_+'&<
Probieren Sie es online! Sie können es auch selbst ausführen, es dauert jedoch ca. 5-10 Sekunden.
Im Prinzip könnte dies in die Seitenlänge 9 passen (für eine Punktzahl von 217 oder weniger), da hier nur 201 Befehle verwendet werden und die ungolfed-Version, die ich zuerst geschrieben habe (auf Seitenlänge 30), nur 178 Befehle benötigte. Ich bin mir jedoch ziemlich sicher, dass es ewig dauern würde, bis wirklich alles passt. Ich bin mir also nicht sicher, ob ich es tatsächlich versuchen werde.
Es sollte auch möglich sein, dies ein bisschen in Größe 10 zu spielen, indem die Verwendung der letzten ein oder zwei Reihen vermieden wird, so dass die nachgestellten No-Ops weggelassen werden können, aber dies würde ein wesentliches Umschreiben als einer der ersten Pfade erfordern joins nutzt die linke untere Ecke.
Erläuterung
Beginnen wir damit, den Code zu entfalten und die Kontrollflusspfade zu kommentieren:
Das ist immer noch ziemlich chaotisch, also hier das gleiche Diagramm für den "ungolfed" Code, den ich zuerst geschrieben habe (tatsächlich ist das die Seitenlänge 20 und ursprünglich habe ich den Code auf Seitenlänge 30 geschrieben, aber das war so spärlich, dass es nicht so wäre verbessert die Lesbarkeit überhaupt nicht, deshalb habe ich sie ein wenig komprimiert, um die Größe ein wenig vernünftiger zu machen):
Klicken Sie für eine größere Version.
Die Farben sind bis auf ein paar sehr kleine Details genau gleich, die Befehle ohne Kontrollfluss sind auch genau gleich. Ich werde Ihnen also anhand der ungolfed Version erklären, wie das funktioniert. Wenn Sie wirklich wissen möchten, wie das golfed funktioniert, können Sie überprüfen, welche Teile dort welchen im größeren Sechseck entsprechen. (Der einzige Haken ist, dass der Golf-Code mit einem Spiegel beginnt, sodass der eigentliche Code in der rechten Ecke nach links beginnt.)
Der grundlegende Algorithmus ist fast identisch mit meiner CJam-Antwort . Es gibt zwei Unterschiede:
- Anstatt die zentrierte hexagonale Zahlengleichung zu lösen, berechne ich einfach aufeinanderfolgende zentrierte hexagonale Zahlen, bis eine gleich oder größer als die Länge der Eingabe ist. Dies liegt daran, dass Hexagony keine einfache Methode zum Berechnen einer Quadratwurzel bietet.
- Anstatt die Eingabe sofort mit No-Ops aufzufüllen, überprüfe ich später, ob die Befehle in der Eingabe bereits erschöpft sind, und drucke
.
stattdessen eine aus, wenn vorhanden.
Das heißt, die Grundidee läuft auf Folgendes hinaus:
- Liest und speichert die Eingabezeichenfolge, während die Länge berechnet wird.
- Suchen Sie die kleinste Seitenlänge
N
(und die entsprechende zentrierte hexagonale Zahl hex(N)
), die die gesamte Eingabe aufnehmen kann.
- Berechnen Sie den Durchmesser
2N-1
.
- Berechnen Sie für jede Zeile den Einzug und die Anzahl der Zellen (deren Summe
2N-1
). Einzug drucken, Zellen drucken ( .
wenn die Eingabe bereits erschöpft ist), Zeilenvorschub drucken.
Beachten Sie, dass es nur No-Ops gibt, so dass der eigentliche Code in der linken Ecke beginnt (der $
, der über den springt >
, damit wir wirklich auf ,
dem dunkelgrauen Pfad beginnen).
Hier ist das anfängliche Speicherraster:
Der Speicherzeiger beginnt also an der mit input bezeichneten Kante und zeigt nach Norden. ,
liest ein Byte von STDIN oder a, -1
wenn wir EOF in diese Kante getroffen haben. Daher ist das <
Recht danach eine Bedingung dafür, ob wir alle Eingaben gelesen haben. Bleiben wir vorerst in der Eingabeschleife. Der nächste Code, den wir ausführen, ist
{&32'-
Dies schreibt eine 32 in den Rand mit der Bezeichnung " Raum" und subtrahiert sie dann vom Eingabewert in dem Rand mit der Bezeichnung " Diff" . Beachten Sie, dass dies niemals negativ sein kann, da garantiert wird, dass die Eingabe nur druckbares ASCII enthält. Es wird Null sein, wenn die Eingabe ein Leerzeichen war. (Timwi weist darauf hin, dass dies weiterhin funktionieren würde, wenn die Eingabe Zeilenvorschübe oder Tabulatoren enthalten könnte, aber auch alle anderen nicht druckbaren Zeichen mit Zeichencodes unter 32 entfernen würde.) In diesem Fall wird der <
verbleibende Anweisungszeiger (IP) abgelenkt und der hellgraue Weg wird genommen. Dieser Pfad setzt einfach die Position des MP mit zurück {=
und liest dann das nächste Zeichen - daher werden Leerzeichen übersprungen. Wenn das Zeichen kein Leerzeichen war, führen wir es aus
=}}})&'+'+)=}
Dieser bewegt sich zunächst um das Sechseck durch die Länge Kante bis zu seiner gegenüber dem diff Kante mit =}}}
. Dann kopiert den Wert aus gegenüber der Länge Kante in die Länge Kante und inkrementiert sie mit )&'+'+)
. Wir werden gleich sehen, warum dies Sinn macht. Zum Schluss verschieben wir die Kante mit =}
:
(Die jeweiligen Kantenwerte stammen aus dem letzten in der Challenge angegebenen Testfall.) Zu diesem Zeitpunkt wiederholt sich die Schleife, wobei sich jedoch alles um ein Sechseck nach Nordosten verschoben hat. Nachdem wir ein anderes Zeichen gelesen haben, erhalten wir Folgendes:
Jetzt können Sie sehen, dass wir die Eingabe (Minuszeichen) schrittweise entlang der Nordostdiagonale schreiben, wobei die Zeichen an jeder anderen Kante und die Länge bis zu diesem Zeichen parallel zur mit Länge bezeichneten Kante gespeichert werden .
Wenn wir mit der Eingabeschleife fertig sind, sieht der Speicher folgendermaßen aus (wo ich bereits ein paar neue Kanten für den nächsten Teil markiert habe):
Dies %
ist das letzte Zeichen, das wir lesen, und die 29
Anzahl der Zeichen, die keine Leerzeichen sind. Nun wollen wir die Seitenlänge des Sechsecks ermitteln. Erstens gibt es im dunkelgrünen / grauen Pfad einen linearen Initialisierungscode:
=&''3{
Hier wird =&
die Länge (in unserem Beispiel 29) in die mit Länge bezeichnete Kante kopiert . Bewegt ''3
sich dann zu der mit 3 bezeichneten Kante und setzt ihren Wert auf 3
(den wir nur als Konstante in der Berechnung benötigen). Bewegt {
sich schließlich zu der mit N (N-1) bezeichneten Kante .
Nun betreten wir die blaue Schleife. Diese Schleifeninkremente N
(in der mit N bezeichneten Zelle gespeichert ) berechnen dann ihre zentrierte hexagonale Zahl und subtrahieren sie von der eingegebenen Länge. Der lineare Code, der das macht, ist:
{)')&({=*'*)'-
Hier {)
bewegt sich auf und erhöht N . ')&(
bewegt sich zu der mit N-1 bezeichneten Kante , kopiert N
dort und dekrementiert sie. {=*
berechnet ihr Produkt in N (N-1) . '*)
multipliziert dies mit der Konstanten 3
und erhöht das Ergebnis in der mit hex (N) bezeichneten Kante . Wie erwartet ist dies die N-te zentrierte hexagonale Zahl. Zum Schluss wird '-
die Differenz zwischen dieser und der eingegebenen Länge berechnet. Wenn das Ergebnis positiv ist, ist die Seitenlänge noch nicht groß genug, und die Schleife wird wiederholt (wobei }}
der MP zurück zu der mit N (N-1) bezeichneten Kante bewegt wird ).
Sobald die Seitenlänge groß genug ist, ist die Differenz null oder negativ und wir erhalten Folgendes:
Als erstes gibt es jetzt den sehr langen linearen grünen Pfad, der einige notwendige Initialisierungen für die Ausgangsschleife vornimmt:
{=&}}}32'"2'=&'*){=&')&}}
Die {=&
beginnt mit dem Kopieren des Ergebnisses in der diff Kante in die Länge Rand, weil wir später etwas kraft- es brauchen. }}}32
Schreibt eine 32 in den Rand mit der Bezeichnung Leerzeichen . '"2
Schreibt eine Konstante 2 in die unbeschriftete Kante über diff . '=&
kopiert N-1
in die zweite Kante mit dem gleichen Etikett. '*)
multipliziert es mit 2 und inkrementiert es, so dass wir den korrekten Wert in der mit 2N-1 bezeichneten Kante oben erhalten. Dies ist der Durchmesser des Sechsecks. {=&')&
kopiert den Durchmesser in die andere Kante mit der Bezeichnung 2N-1 . Anschließend }}
kehren Sie zu der mit 2N-1 gekennzeichneten Kante oben zurück.
Beschriften wir die Kanten neu:
Die Kante, auf der wir uns gerade befinden (die immer noch den Durchmesser des Sechsecks enthält), wird verwendet, um über die Linien der Ausgabe zu iterieren. Die mit Einzug bezeichnete Kante berechnet, wie viele Leerzeichen in der aktuellen Zeile benötigt werden. Die randbeschrifteten Zellen werden verwendet, um die Anzahl der Zellen in der aktuellen Zeile zu durchlaufen.
Wir befinden uns jetzt auf dem rosa Pfad, der den Einzug berechnet . ('-
dekrementiert den Linien Iterator und subtrahiert es von N-1 (in den Gedankenstrich Flanke). Der kurze blau / graue Zweig im Code berechnet einfach den Modul des Ergebnisses ( ~
negiert den Wert, wenn er negativ oder null ist, und nichts passiert, wenn er positiv ist). Der Rest des rosafarbenen Pfads "-~{
subtrahiert den Einzug vom Durchmesser in die Zellenkante und bewegt sich dann zurück zur Einzugskante .
Der schmutzige gelbe Pfad druckt jetzt den Einzug. Die Schleifeninhalte sind wirklich gerecht
'";{}(
Wo '"
bewegt sich zum Rand des Raums , ;
druckt es, {}
bewegt sich zurück zum Einzug und (
dekrementiert es.
Wenn wir damit fertig sind, sucht der (zweite) dunkelgraue Pfad nach dem nächsten zu druckenden Zeichen. Der =}
bewegt sich in Position (dh auf den Zellenrand , zeigt nach Süden). Dann haben wir eine sehr enge Schleife, {}
die sich einfach zwei Kanten in Richtung Südwesten nach unten bewegt, bis wir das Ende der gespeicherten Zeichenfolge erreichen:
Beachten Sie, dass ich dort eine Kante EOF neu etikettiert habe ? . Sobald wir dieses Zeichen verarbeitet haben, machen wir diese Kante negativ, sodass die {}
Schleife hier anstelle der nächsten Iteration endet:
Im Code befinden wir uns am Ende des dunkelgrauen Pfads, wo wir '
einen Schritt zurück zum eingegebenen Zeichen gehen. Handelt es sich bei der Situation um eines der letzten beiden Diagramme (dh es gibt immer noch ein Zeichen aus der Eingabe, die wir noch nicht gedruckt haben), nehmen wir den grünen Pfad (den unteren für Leute, die mit Grün und nicht gut umgehen können) Blau). Das ist ziemlich einfach: ;
druckt das Zeichen selbst. '
bewegt sich zu der entsprechenden Raum Kante , die immer noch ein 32 aus früheren und hält ;
Druck , den Raum. Dann {~
macht unser EOF? Negativ für die nächste Iteration, '
bewegt sich einen Schritt zurück, damit wir mit einer weiteren engen }{
Schleife zum nordwestlichen Ende der Saite zurückkehren können . Was auf der Länge endetZelle (die nicht-positiv unter hex (N) . Schließlich }
bewegt sich zurück in die Zellen Kante.
Wenn wir die Eingabe jedoch bereits erschöpft haben, sucht die Schleife nach EOF? wird hier tatsächlich enden:
In diesem Fall bewegen wir uns '
auf die Länge der Zelle und nehmen stattdessen den hellblauen (oberen) Pfad, der ein No-Op ausgibt. Der Code in diesem Zweig ist linear:
{*46;{{;{{=
Der {*46;
schreibt eine 46 in die Kante mit der Bezeichnung no-op und druckt sie aus (dh einen Punkt). Bewegt {{;
sich dann zum Rand des Raums und druckt diesen aus. Die {{=
Bewegungen zurück in den Zellen für die nächste Iteration umranden.
Zu diesem Zeitpunkt verbinden sich die Pfade wieder und (
dekrementieren die Zellenkante . Wenn der Iterator noch nicht Null ist, nehmen wir den hellgrauen Pfad, der einfach die Richtung des MP umkehrt =
und dann nach dem nächsten zu druckenden Zeichen sucht.
Ansonsten haben wir das Ende der aktuellen Zeile erreicht und die IP nimmt stattdessen den violetten Pfad. So sieht das Speicherraster an diesem Punkt aus:
Der violette Pfad enthält Folgendes:
=M8;~'"=
Das =
kehrt die Richtung des MP wieder um. M8
Setzt den Wert des Sets auf 778
(da der Zeichencode von M
is 77
und die Ziffern an den aktuellen Wert angehängt werden). Das ist zufällig 10 (mod 256)
so. Wenn wir es mit drucken ;
, erhalten wir einen Zeilenvorschub. Dann ~
macht die Flanke wieder negativ, '"
bewegt sich zurück zur Linienkante und =
kehrt die MP noch einmal um.
Wenn die Linienkante Null ist, sind wir fertig. Die IP nimmt den (sehr kurzen) roten Pfad, auf dem @
das Programm beendet wird. Andernfalls fahren wir auf dem violetten Pfad fort, der sich in den rosafarbenen Pfad zurückzieht, um eine weitere Zeile zu drucken.
Kontrollflussdiagramme, die mit dem HexagonyColorer von Timwi erstellt wurden . Speicherdiagramme, die mit dem visuellen Debugger in seiner Esoteric IDE erstellt wurden .
abc`defg
würde eigentlich pastebin.com/ZrdJmHiR werden