BrainF ** k, 396 391 bytes
>+>>++++[-<++++++++>]->,----------[++++++++++.>>++++++++[-<++++<------>>]<.,----------]-<+[-<+]->>+[-<<<<<++++++++++.[-]>[-<+>>.<]<[->+<]>+>>>[[->+]->>+<<<+[-<+]->]>+[-<->[[->+]->+>>+<<<<+[-<+]->]<+>->+[->+]->>[->+<]>+>++++++++++>>-<<[-<-[>>]<]<->>>+[-<<<+>>>[-<->]<+++++++++>>>+]++++++++[-<++++<++++++>>]<<<[-<<<<+[-<+]-<+>>+[->+]->>>>+<]>.>.[-]<[-]<<<[->+<]<<+[-<+]>+]>>[-]<<<-<+[-<+]->>+]
No pude resistir la tentación de hacer esto. Al menos el triángulo tiene el lado puntiagudo hacia abajo.
La entrada viene como una cadena de caracteres numéricos seguidos de una nueva línea nueva.
La salida contendrá un solo espacio final en cada línea.
Ejemplos:
$ bf sd.bf
010
0 1 0
1 1
2
$ bf sd.bf
123456
1 2 3 4 5 6
3 5 7 9 1
8 2 6 0
0 8 6
8 4
2
$ bf sd.bf
9245322
9 2 4 5 3 2 2
1 6 9 8 5 4
7 5 7 3 9
2 2 0 2
4 2 2
6 4
0
Explicación
Dado que es bastante difícil explicar el código desde una perspectiva funcional, podemos verlo desde la perspectiva del estado de la cinta en varios momentos. La idea central aquí es que el triángulo que generamos se inicializa como una matriz muy compacta (para BF, de todos modos) que se reduce en tamaño en 1 cada iteración de un bucle. Otro pensamiento importante es que usamos 255
para indicar un "marcador de posición" que podemos buscar en la cinta.
Inicialización
Este es el paso más fácil. Al comienzo del programa, ejecutamos lo siguiente:
>+>>++++[-<++++++++>]->
Esto fuerza a la cinta al siguiente estado (donde >N<
indica la ubicación del puntero en la cinta)
[ 0 1 32 255 >0< 0 0 ...]
El primer número aquí es una ubicación de "búfer". No lo vamos a utilizar a largo plazo, pero es útil para simplificar las pequeñas operaciones y para copiar datos.
El segundo número es el número de espacios que generaremos al comienzo de cada línea, comenzando después de la primera línea. La primera línea no tendrá espacios iniciales.
El tercer número es el carácter de espacio que mostramos.
El cuarto número es un marcador de posición 255, por lo que podemos volver a esta posición con relativa facilidad.
Entrada
Desde esta posición, leeremos todos los personajes. Al final de este paso, esperamos estar en la siguiente situación:
[ 0 1 32 255 a b c d e f ... >255< 0 0 ... ]
Donde a b c d e f ...
indica la cadena de caracteres numéricos que se ingresó (no la nueva línea).
Logramos esto con lo siguiente:
,----------[++++++++++.>>++++++++[-<++++<------>>]<.,----------]-
Hay algunos matices en esto. En primer lugar, mostraremos cada carácter a medida que los obtengamos, y luego mostraremos un espacio después. En segundo lugar, no queremos copiar el valor ASCII en la cinta, queremos copiar el dígito numérico real. Tercero, queremos detenernos cuando lleguemos a una nueva línea y dejarnos en un buen lugar en ese momento.
Digamos que nuestra entrada es 6723
. Luego, al leer el primero 6
, nuestra cinta se ve así:
[ 0 1 32 255 >54< 0 0 ...]
Verificamos que este valor no sea igual a 10
(una nueva línea ASCII) con ,----------[++++++++++
. Luego imprimimos el valor y continuamos restando simultáneamente 48 del valor de entrada y sumando 32 al valor al lado ( >>++++++++[-<++++<------>>]<
), dejándonos aquí:
[ 0 1 32 255 6 >32< 0 ...]
Observe cómo a lo largo de este proceso podemos suponer que todos los dígitos a la derecha de nuestra entrada son 0; esto significa que no estamos en peligro de arruinar ningún estado anterior si usamos valores a la derecha para calcular 6 * 8
y 4 * 8
.
Ahora sacamos el carácter de espacio que acabamos de generar y tomamos una nueva entrada, eliminando el espacio que calculamos allí. Eventualmente, la entrada será terminada por una nueva línea y el ciclo saldrá, dejando un lugar 255
donde la nueva línea habría estado ( ,----------]-
). Este es el segundo carácter de marcador de posición que utilizaremos para navegar por la cinta. En este punto de nuestro escenario, nuestra cinta es exactamente esto:
[ 0 1 32 255 6 7 2 3 >255< 0 0 ... ]
Cálculo
La forma en que esto funciona es que la lista de dígitos entre nuestros 255
marcadores de posición se reducirá en uno cada iteración del bucle. Cuando solo le queda 1 dígito, hemos terminado y deberíamos detenernos de inmediato (tenga en cuenta que, en este punto, cada dígito de esa lista ya se ha generado, por lo que no tenemos que preocuparnos por volver a generarlo).
Ahora utilizamos este truco para navegar hasta el primer 255
marcador de posición: <+[-<+]-
. Esto efectivamente busca en la cinta a la izquierda un 255
, sin cambiar nada en el medio. Ahora que hemos movido el puntero, podemos verificar nuestra condición de salida: si solo hay un dígito en la lista, entonces se mantendrán los dos espacios de la celda a la derecha 255
. Por lo tanto, verificamos eso y comenzamos un ciclo:>>+[-<<
El primer paso en nuestro bucle es generar una nueva línea. Entonces nos movemos a la primera celda (nuestra celda de búfer), le agregamos 10 y salimos. El siguiente paso es generar todos los caracteres de espacio iniciales. Después de generarlos, incrementamos nuestra cuenta para el número de espacios iniciales. Estos pasos se logran de la siguiente manera:
-<<<<<++++++++++.[-]>[-<+>>.<]<[->+<]>+>>>
Lo que nos deja en este estado:
[ 0 2 32 255 >6< 7 2 3 255 0 0 0 0 0 0 ]
Nuestro siguiente paso es copiar el primer valor de la lista, más allá del segundo marcador de posición 255
:
[[->+]->>+<<<+[-<+]->]
Esencialmente hacemos esto saltando de un lado a otro entre nuestros marcadores de posición 255
, dejándonos aquí:
[ 0 2 32 255 >0< 7 2 3 255 0 6 0 0 ... ]
Ahora comenzamos un ciclo, iterando por el resto de la lista, deteniéndonos cuando tocamos 255
:>+[-<
En este punto, el dígito a nuestra izquierda inmediata siempre es 0. Entonces, como los amamos, colocamos un marcador 255
de posición allí para que podamos volver a nuestro lugar en la lista. El siguiente paso es mover el segundo lugar de la lista a las ubicaciones que rodean a donde movimos el primer lugar, más allá del segundo marcador de posición 255
. Estos pasos se logran de la siguiente manera:
->
[[->+]->+>>+<<<<+[-<+]->]
Dejándonos aquí: [ 0 2 32 255 255 >0< 2 3 255 7 6 7 0 ]
ahora, tanto el 6
y 7
se han movido a una ubicación donde puede ocurrir el cálculo. Necesitamos dos copias del 7
porque el siguiente número de la lista también lo necesitará. El 7
inmediatamente posterior al 255
sirve para este propósito, mientras que el otro 7
será consumido por el cálculo.
Primero, agregamos los dos dígitos:
<+>->+[->+]->>
[->+<]>
Dejándonos aquí:
[ 0 2 32 255 0 255 2 3 255 7 0 >13< 0 ]
La siguiente combinación de pasos es la más complicada. Necesitamos ver si el número al que apuntamos es mayor que 10, y si es así, restamos 10
. En realidad, lo que hacemos es restarle 10 y ver si llega 0
a algún punto de la resta. Si es así, agregamos 10
nuevamente más tarde. Al final de esto, deberíamos tener la suma módulo 10.
Prepare a 10 to the right
+>++++++++++
Leave yet another 255 for a loop condition later
>>-<<
If the number is greater than 10 end up one space to the left
else one space to the right
[-<-[>>]<]<->
Check if the previous 255 is two spaces to the right and if it is
add 10 back to our sum--we've subtracted too much
>>+[-<<<+>>>[-<->]<+++++++++>>>+]
En este punto, hemos logrado el objetivo. ¡Tenemos la suma módulo 10! Además, ya sea que el número sea mayor que 10, terminaremos aquí:
[ 0 2 32 255 0 255 2 3 255 7 0 3 0 0 >0< ]
Nuestros próximos objetivos son generar esta nueva suma, seguirla con un espacio e inyectarla nuevamente en nuestra lista. Hacemos todo esto con nuestras técnicas anteriores de 255
salto y adición 48
a nuestra suma, por lo que no lo cubriré en detalle.
++++++++[-<++++<++++++>>]
<<<[-<<<<+[-<+]-<+>>+[->+]->>>>+<]
>.>.
Y estamos aquí: [ 0 2 32 255 3 255 2 3 255 7 0 0 51 >32< ]
observe cómo colocamos un 255
marcador de posición adicional después de nuestro recién inyectado 3
para que no perdamos lugar en la lista. En este punto, hemos generado nuestra suma y su espacio, por lo que debemos limpiar y volver a un estado en el que la próxima iteración de este bucle vaya a funcionar. Necesitamos limpiar nuestro 51
y32
celdas , mover la 7
una vez a la derecha y navegar a nuestro marcador de posición de lista para que podamos comenzar de nuevo.
[-]<[-]<<<[->+<]<<+[-<+]
Ahora estamos aquí: [ 0 2 32 255 3 >0< 2 3 255 0 7 0 ... ]
que es exactamente donde queremos estar para nuestra próxima iteración. ¡Así que verifique 255 y siga adelante! ( >+]
)
Cuando nos retiremos del ciclo, tendremos una lista completamente nueva, compuesta de las sumas de la lista anterior. La primera vez, se verá así:
[ 0 2 32 255 3 9 5 0 >0< ]
Ahora queremos repetir todo el proceso en nuestra nueva lista, ¡así que dejamos 255
caer a la izquierda y comenzamos de nuevo! Necesitamos hacer un poco de limpieza >>[-]<<
y luego quitar nuestro marcador de posición <-
. Después de eso, estamos exactamente en el mismo lugar donde estábamos después de la entrada, por lo que podemos hacer las mismas verificaciones: ¡ <+[-<+]->>+
y boom! ¡Tenemos nuestro circuito completo! Todo lo que necesitamos es el corchete de cierre, y cuando termina todo lo que ya hemos de salida, por lo que hemos terminado: ]
.