Hacer un poco de nieve!

18

Su tarea: generar un copo de nieve Koch hasta la enésima profundidad. No necesita hacer un copo de nieve Koch completo, solo un lado del triángulo inicial. Wikipedia sobre copos de Koch: https://en.wikipedia.org/wiki/Koch_snowflake .

Reglas:

  • El programa debe generar un lado del copo de nieve de Koch hasta la enésima profundidad.
  • La salida debe ser ASCII.
  • Usted puede generar todo el copo de nieve; Esto no es obligatorio.
  • Se aplican reglas estándar para entrada / salida y lagunas y demás.
  • El espacio en blanco no importa, siempre y cuando todos los personajes estén en el lugar correcto entre sí.
  • ¡El código más corto gana!

Casos de prueba:

n = 0:

__

n = 1:

__/\__

n = 2:

      __/\__
      \    /
__/\__/    \__/\__

n = 3:

                        __/\__
                        \    /
                  __/\__/    \__/\__
                  \                /
                  /_              _\
                    \            /
      __/\__      __/            \__      __/\__
      \    /      \                /      \    /
__/\__/    \__/\__/                \__/\__/    \__/\__

Espero que esto tenga sentido. Observe que en cada caso de prueba, el fractal se puede dividir en tres partes de igual longitud. Observe también que el ancho de cada copo de nieve es tres veces el ancho de la generación anterior del copo de nieve.

Camarada SparklePony
fuente
Para su información, se acordó que esto no es un engaño de esto .
Camarada SparklePony
No creo que haya definido adecuadamente cuál es la representación ASCII adecuada de la enésima curva de Koch.
orlp
No estoy seguro de que las proporciones tengan sentido. El no duplicado se usa __/\__con dos subrayados, lo que hace que cada iteración sea 3 veces más grande que la anterior. El uso de un solo subrayado parece dar contradicciones que comienzan a ponerse realmente incómodas en n = 3 Por ejemplo, las partes exteriores tienen anchura 12 mientras que la parte media tiene solamente la anchura 10, como consecuencia de la /_y _\ que son demasiado estrechos. E incluso antes de eso, se ha _expandido al doble del ancho de /y \ .
Ørjan Johansen
Creo que /_y _\ son la única parte realmente fatal: los guiones bajos deben desaparecer, porque deben estar en la misma posición que el /y \ . Una vez hecho esto, las cosas pueden expandirse 3 veces desde n = 1 en adelante (pero n = 0 no encaja)
Ørjan Johansen
Por desgracia, no, la parte central todavía tiene un ancho que no coincide con las partes externas, como lo demuestra n = 3 que tiene un ancho de 52 en lugar de 54 = 2 * 3 ^ 3. Prueba uno de estos . Incluí versiones al revés con partes que solo aparecen desde n = 4 o n = 5; difieren de las hacia arriba en las que se eliminan los guiones bajos.
Ørjan Johansen

Respuestas:

10

Haskell , 308 300 299 bytes

Ediciones:

  • -4 Bytes: Cambiar zipWith(+)a zipWith(-)y ajuste de codificaciones y las compensaciones se deshizo de toda señal de negación.
  • -1 byte: Ajustar aún más la codificación permitió #que se eliminaran varios nombres de variables mediante el uso en r=reverselugar de la coincidencia directa de patrones.
  • -2 bytes: Usar un operador en lugar de alfanumérico para zipWith(-).
  • -1 byte: definición o=[0,0]para acortar las constantes de la lista.
  • -1 byte: Fusionando dos ramas de ?.
import Data.List
k n=0?sort(o#(f=<<scanl1(+)(iterate(>>=(:[1,4,1]))[6]!!n)))
x?l@(([_,w],c):r)|x>w='\n':0?l|0<1=([2..w-x]>>" ")++[c|w>x]++w?r
_?_=""
w#((c,l):m)=(l&w,c):r l&(l&w)#m
_#_=[]
f x=zip"_/\\_/\\"([id,r]<*>[0:1:o,[0,1,0,1],o++[1,1]])!!mod x 6<$[1,3..gcd 3x]
(&)=zipWith(-)
r=reverse
o=[0,0]

Pruébalo en línea! (Lamentablemente, cualquier cosa mayor que n = 3 queda horriblemente envuelta e ilegible, pero puede copiarla en otro programa para verla).

Variaciones

Cómo funciona

  • kes la función principal, toma un Int ny devuelve a String.
  • iterate(>>=(:[1,4,1]))[6]genera una lista infinita que contiene, para cada n, los giros entre líneas consecutivas en esa iteración de curva, estilo de gráficos de tortuga, como números nominalmente entre 0y 5. Cada iteración es solo la anterior con los giros 1,4,1intercalados. La única razón por las listas secundarias comienzan con 6en lugar de 0es hacer el gcdtruco en fel trabajo evitando f 0.
  • scanl1(+)convierte los giros en direcciones "absolutas", hasta el módulo 6. A 0significa hacia la derecha, luego cada número más alto es 60 grados en sentido antihorario desde el anterior. (Bueno, sería 60 grados si este fuera un dibujo adecuado en lugar de ASCII).
  • f convierte una dirección absoluta en una lista de pares (codificación de caracteres, desplazamiento) que codifica qué caracteres agregar a la curva (para direcciones horizontales genera dos pares, de lo contrario, uno) y cómo cambia la posición relativa.
  • El #operador recorre la lista anterior de pares (caracteres, codificación offset), generando pares reales (coordenadas, caracteres).
  • Principios de codificación:
    • Un carácter de _/\nominalmente representa una línea dibujada desde una esquina inicial a través de una celda rectangular hasta una esquina final diferente.
    • Las coordenadas de celda son de la forma [y,x], de arriba a abajo, de izquierda a derecha, de modo que se ordenan en el orden en que queremos imprimirlas. Las columnas están basadas en 1. Se utilizan listas en lugar de tuplas para aritmética vectorial más corta con (&)=zipWith(-).
    • Una esquina se denota con las mismas coordenadas [y,x]que la celda en su esquina superior izquierda. Esto garantiza que todos los desplazamientos desde una esquina a sus celdas vecinas no sean negativos, evitando constantes negativas.
    • Sin embargo, las coordenadas de las esquinas se pasan negadas para permitir que todas las operaciones vectoriales sean sustracciones en lugar de sumas, lo que evita todos los demás signos explícitos.
    • Una lista de codificación de desplazamiento es [y1,x1,x2,y2]donde [y1,x1]está el desplazamiento de coordenadas desde la esquina inicial a la celda de caracteres y [y2,x2]es el desplazamiento desde la esquina final a la celda de caracteres. Esto significa:
      • Las listas de codificación para las direcciones 3... 5son solo el reverso de las listas para 0... 2, lo que permite que se generen con [id,r]<*>.
      • Toda la aritmética vectorial necesaria se puede hacer usando (&)=zipWith(-)una lista de codificación o su reverso.
  • Después de ordenar la lista de pares (coordenadas, caracteres), se pasan a ellos ?, lo que genera el final a Stringpartir de ellos.
    • In x?l@(([_,w],c):r) xes la coordenada x del carácter anterior que se muestra en esta línea, o 0si está al comienzo de una línea; les la lista actual completa, wes la coordenada x del siguiente carácter que se va a agregar, ces el carácter y res la lista restante.
    • En esta etapa, las coordenadas y ya no son necesarias. Debido a que cada línea contiene caracteres, y el primer carácter de cada línea está bien a la izquierda del final de la anterior, el inicio de nuevas líneas se detecta al verificar si la coordenada x ha disminuido.
    • El subrayado tiene un valor ASCII mayor que \y /, por lo que se ordena en último lugar si se superpone con otro carácter en la misma posición. Por lo tanto, se detecta un guión bajo redundante comprobando que se ha repetido una coordenada x.
Ørjan Johansen
fuente
¡Agradable! Aceptaré esto si no hay más actividad sobre esta pregunta hoy.
Camarada SparklePony