Ilegible , 2199 2145 2134 2104 2087 2084 bytes
Soporta tanto k/ jcomo ▲/▼ sintaxis.
En una buena tradición ilegible, aquí está el programa formateado en fuente proporcional, para ofuscar la distinción entre apóstrofes y comillas dobles:
'"" "" ""' "" "" "" "" '"" "" "" "" "" "'" "" "" '"" "" "'" "" "" "' "" '""' "" '""' "" "'" "'" "" "" "" "" "" "" "" "" "" "'" ""' "" '" "'" "" "" ""' "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" '"" ""'"" ""' "" "" '"" "" ""' "" '""' "" '""' "" '"" "" "" "" "'" "" "" " '""' "" '""' "" '"" "'" "" "" "" "" "" "" "" "" "" "" "" '""' "" '" "'" ""' "" "" "" "" '"" "" "" "" "" ""' "" "" "" "" "" "" "" "" """'" "'" "'" "'" "'" "'" "" '"" "" "" ""' "" "" "" "" "" "" "" "'" " '""' "" "'" "" "" "" "'" "" "" "'" "'" "'" "'" "'" "'" "" '""' "" ' "" "'" "" "" "'" "" '"" "" "" ""' "" "" "" "" "" "" "" "" "" "" "'"" "'" "" "" "" "" "" "" "" "'" "" "" "" '"" "" "" "" "" "" "" ""' " "" '"" "" "" "" "" ""' "" "" "" "" "" '"" "" "" "" "" "" "" "" "" "" "" "" '"" "'" "" '""' "" "" "" "" "" "" "" "" "" "" "" "" "" "" "'""' "" '""' "" '""' "" "'" ""' "" '""' "" "'" "" "" "" "" "'" "" "" "" "" '"" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" '""' "" '""' "" '"" "" "" "" ""' "" "'" "'" "'" ""' "" '"""'" "" "" "" ""' "" "" "" "" "" '"" ""' "" '""' "" "" "" "" "" "" '" "" '"" "" "'" "" "" "'" "" "" "" "" "" "" "" "" "" "" ""' " "'" "'" "'" "'" "" '"" "" "" "" "" "" "" "" ""' "" "" "" '""' ""'""' "" "'" "" "" "'" "'" "'" "'" ""' "" '"" "" "" "" "" ""' "" '"" "'" ""' "" "" "" '""' "" '"" "'" "'" "" "" "" "" "" "" "" "" "" "" " '""' "" "'" "" "" "" "'" "" "" "'" "'" "" '"" "" "" "" "" "'" "" '"" "" "" "" '"" "" "" "'" "" "" "" "" "" "'" "" "" "" "" ""' "" "" "" '"" "" "" ""' "" "" "" "'" "'" "'" "" "" "" "" "" "" "" "" "" "" "'" "'" "'" "" '"" "" ""' "" '""' "" "'" "" "" "" "" "" "" "" "" "" "'"" "'" "" '"" "" ""' "" '"" "'" "'" "" "" "" "" "" "" "" "" "" "" "" '"" "" "" "'" "" "" "" "'" "" "" "" "" "" "" "" "" "" "" "" "" "" "" " "" "" '"" "" ""' "" '""' "" '"" "'" "'" "" "" "" "" "" "'" "'" ""'"" ""' "" "" "" "" "'" "" "" "" "" ""' "" "" "" "" "" "" "'" "" "" " '""' "" '"" "'" "'" "" "" ""' "" '""' "" "'" "" "" "" "" "" "" "" "" "" "'" "'" "" '""' "" "" "" "" "" "" "'" "" "'" "" "" "" "" "'" "'"" "" "" "'" "" "" "" "" ""' "" "" "" "" "" "" "" "" "" "" "'" "" "" "'" ""' "" "" "" "" '"" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" '"" "'" "'" "" "" ""' "" "" "" "" "" '"" "" "" "" "" "" "" "" "'"" '""' "" "'" "" "" "" "" "" "" ""' "" '"" "" "" "" ""' "" "" "" "" "'" "" "" "'" "'" ""' "" '""' "" '""' "" '"" "" "" "" "'" "'" "'" " '""' "" "'" "" "" "" "" "" ""' "" "" "" "" "" "" "" "" "" "" "" """'" "" "" "" "'" "" "" "'" "" "" "" "" ""' "" "" "" "" "" "" "" "" '" "" "" "" '"" "" "" ""' "" "" "" "" "" "" "" "" "" "" "" "" "" "" "'" "" '""' "" '"" "" "" "'" "" "" "" "" "" '"" "" "" "" "" "" "" """'" ""' "" '""' "" '""' "" '""' "" '""' "" '""' "" '""' "" '""' "" '""' "" '""' "" '""' "" '""' "" "" "" "" "" "" "" "" "" "" '"" "" " "" '"" "'" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" """" "" "'" "'" ""
Este fue un desafío increíble. ¡Gracias por tu publicación!
Explicación
Para tener una idea de lo que ilegible puede y no puede hacer, imagine Brainfuck con una cinta infinita en ambas direcciones, pero en lugar de un puntero de memoria moviendo una celda a la vez, puede acceder a cualquier celda de memoria desreferenciando un puntero. Esto resulta bastante útil en esta solución, aunque otras operaciones aritméticas, incluido el módulo, deben realizarse a mano.
Aquí está el programa como pseudocódigo con comentarios del director:
// 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
Esto en cuanto a la lógica del programa. Ahora tenemos que traducir esto a ilegible y usar algunos trucos de golf más interesantes.
Las variables siempre se desreferencian numéricamente en No legible (por ejemplo, se a = 1convierte en algo parecido *(1) = 1). Algunos literales numéricos son más largos que otros; el más corto es 1, seguido de 2, etc. Para mostrar cuánto más largos son los números negativos, aquí están los números de -1 a 7:
-1 '""""""""'""""""""'""" 22
0 '""""""""'""" 13
1 '""" 4
2 '""'""" 7
3 '""'""'""" 10
4 '""'""'""'""" 13
5 '""'""'""'""'""" 16
6 '""'""'""'""'""'""" 19
7 '""'""'""'""'""'""'""" 22
Claramente, queremos asignar la variable # 1 a la que ocurre con mayor frecuencia en el código. En el primer ciclo while, esto es definitivamente mod5, que aparece 10 veces. Pero no necesitamos mod5más después del primer ciclo while, por lo que podemos reasignar la misma ubicación de memoria a otras variables que usaremos más adelante. Estos son ptr2y ptr3. Ahora se hace referencia a la variable 21 veces en total. (Si está tratando de contar el número de ocurrencias usted mismo, recuerde contar algo comoa++ dos veces, una para obtener el valor y otra para establecerlo).
Solo hay otra variable que podemos reutilizar; después de calcular los valores del módulo, chya no es necesario. upy dnaparece el mismo número de veces, así que cualquiera está bien. Fusionémonos chcon up.
Esto deja un total de 8 variables únicas. Podríamos asignar las variables 0 a 7 y luego iniciar el bloque de memoria (que contiene los caracteres y los números de línea) en 8. ¡Pero! Como 7 tiene la misma longitud en el código que −1, también podríamos usar las variables −1 a 6 e iniciar el bloque de memoria en 7. ¡De esta manera, cada referencia a la posición de inicio del bloque de memoria es ligeramente más corta en el código! Esto nos deja con las siguientes tareas:
-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]
Ahora esto explica la inicialización en la parte superior: es 5 porque es 7 (el comienzo del bloque de memoria) menos 2 (el incremento obligatorio en la primera condición while). Lo mismo ocurre con las otras dos ocurrencias de 5 en el último bucle.
Tenga en cuenta que, dado que 0 y 4 tienen la misma longitud en el código, ptry minLinepodrían asignarse de cualquier manera. ... ¿O podrían?
¿Qué pasa con el misterioso 2 en el penúltimo bucle while? ¿No debería ser este un 6? Solo queremos disminuir los números en el bloque de datos, ¿verdad? Una vez que llegamos a 6, estamos fuera del bloque de datos y debemos detenernos. ¡Sería una vulnerabilidad de seguridad de error de error de desbordamiento de búfer!
Bueno, piensa en lo que pasa si no nos detenemos. Decrementamos las variables 6 y 4. La variable 6 es mod4. Eso solo se usa en el primer ciclo while y ya no es necesario aquí, por lo que no se hace daño. ¿Qué pasa con la variable 4? ¿Qué crees que debería ser ptro debería ser la variable 4 minLine? Así es, ¡ minLineya no se usa en este momento! Por lo tanto, la variable # 4 es minLiney podemos disminuirla de manera segura y no hacer daño.
ACTUALIZACIÓN 1! Golfed desde 2199 a 2145 bytes por darse cuenta de que dnpuede también fusionarse con mod5, a pesar de que mod5todavía se utiliza en el cálculo del valor de dn! La nueva asignación de variables es ahora:
0 ptr
1 mod5, dn, ptr2, ptr3
2 curLine
3 maxLine
4 minLine
5 ch, up
6 mod4
7... [data block]
ACTUALIZACIÓN 2! Golfó de 2145 a 2134 bytes al darse cuenta de que, dado mod5que ahora está en la misma variable que dn, que se cuenta a 0 en un ciclo while, mod5ya no necesita inicializarse explícitamente a 0.
ACTUALIZACIÓN 3! Golfó de 2134 a 2104 bytes al darse cuenta de dos cosas. Primero, aunque la idea del "módulo negativo" valió la pena mod5, el mismo razonamiento no se aplica mod4porque nunca probamos, mod4+2etc. Por lo tanto, cambiar mod4 ? mod4+1 : -3a mod4 ? mod4-1 : 3nos lleva a 2110 bytes. En segundo lugar, dado mod4que siempre es 0 o 2, podemos inicializar mod4a 2 en lugar de 0 e invertir los dos ternarios (en mod4 ? 3 : 1lugar de mod4 ? 1 : 3).
ACTUALIZACIÓN 4! Jugó desde 2104 hasta 2087 bytes al darse cuenta de que el ciclo while que calcula los valores del módulo siempre se ejecuta al menos una vez, y en tal caso, Ilegible le permite reutilizar el valor de la última declaración en otra expresión. Por lo tanto, en lugar de while --ch: [...]; up = (mod5 ? mod5+1 ? [...]ahora tenemos up = ((while --ch: [...]) ? mod5+1 ? [...](y dentro de ese ciclo while, calculamos mod4primero, así que esa mod5es la última declaración).
ACTUALIZACIÓN 5! Golfé de 2087 a 2084 bytes al darme cuenta de que en lugar de escribir las constantes 32y 10(espacio y nueva línea), puedo almacenar el número 10 en la variable (ahora no utilizada) # 2 (llamémosla ten). En lugar de ptr3 = 5escribir ten = (ptr3 = 5) + 5, luego se 32vuelve ten+22y se print 10vuelve print ten.