Nicht lesbar , 2199 2145 2134 2104 2087 2084 Bytes
Unterstützt sowohl k
/ j
als auch ▲
/ ▼
Syntax.
In guter unleserlicher Tradition ist hier das Programm in proportionaler Schrift formatiert, um die Unterscheidung zwischen Apostrophen und doppelten Anführungszeichen zu verschleiern:
"" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" " "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" """" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" " "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" ""'" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" " "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" " "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" """ "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" " "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" """" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" " "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" """ "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" " "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" """" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" " "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" """ "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" " "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" """" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" """" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" " "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" """" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" " "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" " "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" """" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" """" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" " "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" """'" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" " "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" " "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" """" "" "" "" "" "
Dies war eine erstaunliche Herausforderung. Vielen Dank für die Veröffentlichung!
Erläuterung
Um ein Gefühl dafür zu bekommen, was Unreadable kann und was nicht, stellen Sie sich Brainfuck mit einem unendlichen Band in beide Richtungen vor. Statt eines Speicherzeigers, der jeweils eine Zelle bewegt, können Sie auf eine beliebige Speicherzelle zugreifen, indem Sie einen Zeiger dereferenzieren. Dies ist in dieser Lösung sehr praktisch, obwohl andere arithmetische Operationen - einschließlich Modulo - von Hand ausgeführt werden müssen.
Hier ist das Programm als Pseudocode mit Regiekommentar:
// Initialize memory pointer. Why 5 will be explained at the very end!
ptr = 5
// FIRST PASS:
// Read all characters from stdin, store them in memory, and also keep track of the
// current line number at each character.
// We need the +1 here so that EOF, which is -1, ends the loop. We increment ptr by 2
// because we use two memory cells for each input character: one contains the actual
// character (which we store here); the other will contain the line number at which the
// character occurs (updated at the end of this loop body).
while ch = (*(ptr += 2) = read) + 1:
// At this point, ch will be one more than the actual value.
// However, the most code-economical way for the following loop is to
// decrement inside the while condition. This way we get one fewer
// iteration than the value of ch. Thus, the +1 comes in handy.
// We are now going to calculate modulo 4 and 5. Why? Because
// the mod 4 and 5 values of the desired input characters are:
//
// ch %5 %4
// ^ 1
// v 2
// k 3
// j 4
// ▲ 0 2
// ▼ 0 0
//
// As you can see, %5 allows us to differentiate all of them except ▲/▼,
// so we use %4 to differentiate between those two.
mod4 = 0 // read Update 2 to find out why mod5 = 0 is missing
while --ch:
mod5 = mod5 ? mod5 + 1 : -4
mod4 = mod4 ? mod4 + 1 : -3
// At the end of this loop, the value of mod5 is ch % 5, except that it
// uses negative numbers: -4 instead of 1, -3 instead of 2, etc. up to 0.
// Similarly, mod4 is ch % 4 with negative numbers.
// How many lines do we need to go up or down?
// We deliberately store a value 1 higher here, which serves two purposes.
// One, as already stated, while loops are shorter in code if the decrement
// happens inside the while condition. Secondly, the number 1 ('""") is
// much shorter than 0 ('""""""""'""").
up = (mod5 ? mod5+1 ? mod5+3 ? 1 : 3 : 2 : mod4 ? 3 : 1)
dn = (mod5 ? mod5+2 ? mod5+4 ? 1 : 3 : 2 : mod4 ? 1 : 3)
// As an aside, here’s the reason I made the modulos negative. The -1 instruction
// is much longer than the +1 instruction. In the above while loop, we only have
// two negative numbers (-3 and -4). If they were positive, then the conditions in
// the above ternaries, such as mod5+3, would have to be mod5-3 etc. instead. There
// are many more of those, so the code would be longer.
// Update the line numbers. The variables updated here are:
// curLine = current line number (initially 0)
// minLine = smallest linenum so far, relative to curLine (always non-positive)
// maxLine = highest linenum so far, relative to curLine (always non-negative)
// This way, we will know the vertical extent of our foray at the end.
while --up:
curLine--
minLine ? minLine++ : no-op
maxLine++
while --dn:
curLine++
minLine--
maxLine ? maxLine-- : no-op
// Store the current line number in memory, but +1 (for a later while loop)
*(ptr + 1) = curLine + 1
// At the end of this, minLine and maxLine are still relative to curLine.
// The real minimum line number is curLine + minLine.
// The real maximum line number is curLine + maxLine.
// The total number of lines to output is maxLine - minLine.
// Calculate the number of lines (into maxLine) and the real minimum
// line number (into curLine) in a single loop. Note that maxLine is
// now off by 1 because it started at 0 and thus the very line in which
// everything began was never counted.
while (++minLine) - 1:
curLine--
maxLine++
// Make all the row numbers in memory positive by adding curLine to all of them.
while (++curLine) - 1:
ptr2 = ptr + 1
while (ptr2 -= 2) - 2: // Why -2? Read until end!
*ptr2++
// Finally, output line by line. At each line, we go through the memory, output the
// characters whose the line number is 0, and decrement that line number. This way,
// characters “come into view” in each line by passing across the line number 0.
while (--maxLine) + 2: // +2 because maxLine is off by 1
ptr3 = 5
while (ptr -= 2) - 5:
print (*((ptr3 += 2) + 1) = *(ptr3 + 1) - 1) ? 32 : *ptr3 // 32 = space
ptr = ptr3 + 2
print 10 // newline
Soviel zur Programmlogik. Jetzt müssen wir dies in Unreadable übersetzen und ein paar weitere interessante Golf-Tricks anwenden .
Variablen werden in Unreadable immer dereferenziert (zB a = 1
wird so etwas *(1) = 1
). Einige numerische Literale sind länger als andere; die kürzeste ist 1, gefolgt von 2 usw. Um zu zeigen, wie viel länger negative Zahlen sind, sind hier die Zahlen von -1 bis 7:
-1 '""""""""'""""""""'""" 22
0 '""""""""'""" 13
1 '""" 4
2 '""'""" 7
3 '""'""'""" 10
4 '""'""'""'""" 13
5 '""'""'""'""'""" 16
6 '""'""'""'""'""'""" 19
7 '""'""'""'""'""'""'""" 22
Natürlich möchten wir die Variable # 1 derjenigen zuordnen, die im Code am häufigsten vorkommt . In der ersten while-Schleife ist dies definitiv mod5
die 10-fache. Wir brauchen aber mod5
nach der ersten while-Schleife nichts mehr, sodass wir den gleichen Speicherplatz anderen Variablen zuweisen können, die wir später verwenden. Das sind ptr2
und ptr3
. Jetzt wird die Variable insgesamt 21 Mal referenziert. (Wenn Sie versuchen, die Anzahl der Vorkommen selbst zu zählen, denken Sie daran, a++
zweimal zu zählen , einmal, um den Wert abzurufen, und einmal, um ihn festzulegen.)
Es gibt nur eine andere Variable, die wir wiederverwenden können. nachdem wir die Modulo-Werte berechnet haben, ch
wird es nicht mehr benötigt. up
und dn
kommen die gleiche Anzahl von Malen, so ist entweder in Ordnung. Lassen Sie uns merge ch
mit up
.
Somit bleiben insgesamt 8 eindeutige Variablen übrig. Wir könnten die Variablen 0 bis 7 zuweisen und dann den Speicherblock (der die Zeichen und Zeilennummern enthält) bei 8 starten. Aber! Da 7 im Code die gleiche Länge hat wie −1, können wir auch die Variablen −1 bis 6 verwenden und den Speicherblock bei 7 starten. Auf diese Weise ist jeder Verweis auf die Startposition des Speicherblocks im Code etwas kürzer! Dies lässt uns mit folgenden Aufgaben:
-1 dn
0 ← ptr or minLine?
1 mod5, ptr2, ptr3
2 curLine
3 maxLine
4 ← ptr or minLine?
5 ch, up
6 mod4
7... [data block]
Nun dies erklärt die Initialisierung an der Spitze: es 5 ist , weil es 7 (der Anfang des Speicherblockes) minus 2 (der obligatorische Schritt in dem ersten , während Bedingung). Gleiches gilt für die beiden anderen Vorkommen von 5 in der letzten Schleife.
Beachten Sie, dass 0 und 4 im Code dieselbe Länge haben ptr
und minLine
umgekehrt zugewiesen werden können. ... Oder könnten sie?
Was ist mit der mysteriösen 2 in der vorletzten while-Schleife? Sollte das nicht eine 6 sein? Wir wollen nur die Zahlen im Datenblock dekrementieren, oder? Sobald wir 6 erreichen, sind wir außerhalb des Datenblocks und wir sollten aufhören! Es wäre eine Sicherheitslücke, die durch einen Pufferüberlauffehler ausgelöst werden könnte!
Denken Sie darüber nach, was passiert, wenn wir nicht aufhören. Wir dekrementieren die Variablen 6 und 4. Die Variable 6 ist mod4
. Das wird nur in der ersten while-Schleife verwendet und hier nicht mehr benötigt, also kein Schaden angerichtet. Was ist mit Variable 4? Was denkst du, sollte Variable 4 sein ptr
oder sollte es sein minLine
? Das stimmt, minLine
wird an dieser Stelle auch nicht mehr verwendet! Somit ist die Variable # 4 minLine
und wir können sie sicher dekrementieren und keinen Schaden anrichten!
UPDATE 1! Golf von 2199 bis 2145 Bytes durch Realisierung, dn
die auch mit zusammengeführt werden können mod5
, obwohl mod5
noch in der Berechnung des Wertes für verwendet wird dn
! Neue Variablenbelegung ist jetzt:
0 ptr
1 mod5, dn, ptr2, ptr3
2 curLine
3 maxLine
4 minLine
5 ch, up
6 mod4
7... [data block]
UPDATE 2! Wurde zwischen 2145 und 2134 Byte verarbeitet, indem erkannt wurde, dass mod5
die Variable dn
, die jetzt in einer while-Schleife bis 0 gezählt wird, mod5
nicht mehr explizit auf 0 initialisiert werden muss.
UPDATE 3! Golf von 2134 bis 2104 Bytes durch die Realisierung von zwei Dingen. Erstens, obwohl sich die Idee des „negativen Moduls“ gelohnt hat mod5
, gilt die gleiche Argumentation nicht, mod4
da wir nie mit mod4+2
usw. testen . Daher führt die Änderung mod4 ? mod4+1 : -3
von mod4 ? mod4-1 : 3
zu 2110 Byte. Zweitens mod4
können wir , da immer 0 oder 2 ist, mod4
auf 2 anstelle von 0 initialisieren und die beiden Ternären ( mod4 ? 3 : 1
anstelle von mod4 ? 1 : 3
) umkehren .
UPDATE 4! Wird zwischen 2104 und 2087 Byte verarbeitet, indem erkannt wird, dass die while-Schleife, die die Modulo-Werte berechnet, immer mindestens einmal ausgeführt wird. In diesem Fall können Sie mit Unreadable den Wert der letzten Anweisung in einem anderen Ausdruck wiederverwenden. Anstelle von while --ch: [...]; up = (mod5 ? mod5+1 ? [...]
jetzt haben wir also up = ((while --ch: [...]) ? mod5+1 ? [...]
(und innerhalb dieser while-Schleife berechnen wir mod4
zuerst, das mod5
ist also die letzte Anweisung).
UPDATE 5! Golf von 2087 bis 2084 Bytes durch die Erkenntnis, dass ich anstelle des Ausschreibens der Konstanten 32
und 10
(Leerzeichen und Zeilenvorschub) die Nummer 10 in der (jetzt nicht verwendeten) Variablen # 2 speichern kann (nennen wir es ten
). Anstatt zu ptr3 = 5
schreiben ten = (ptr3 = 5) + 5
, 32
wird ten+22
und print 10
wird print ten
.