Dibujar una curva de dragón

19

Tu tarea para hoy: ¡dibuja una curva de dragón!

En caso de que no sepas qué es Dragon Curve, aquí hay un video introductorio de ViHart (¡Realmente genial, mira!)

Su tarea: dibujar una curva de dragón, iterada al menos 9 veces. No tiene que mostrar las iteraciones 1 a 9, solo tiene que mostrar la curva final producida después de completar (al menos) 9 iteraciones. La curva debe dibujarse como líneas rectas que conectan los puntos en la curva; la salida debe coincidir con una de las imágenes a continuación que muestra 9 o más iteraciones (hasta reflexión, rotación, escala y variación en el ancho de línea, color de línea y color de fondo). Su salida debe ser lo suficientemente grande como para que las líneas individuales y los "cuadros" que forman puedan distinguirse entre sí; Si dos líneas no se cruzan en la curva, no deberían ocupar los mismos píxeles o adyacentes en la salida (debe haber al menos un píxel del fondo visible entre ellos). Puede mostrar la imagen en la pantalla o guardar la imagen en un archivo aceptado. La salida debe ser gráfica, no puede ser arte ASCII.

El código más corto en bytes gana, sin embargo , incluir directivas para bibliotecas no debe incluirse en el recuento de bytes, y puede usar bibliotecas de gráficos u otras bibliotecas escritas para el idioma de su elección si se escribieron antes de la publicación.

Incluya una imagen de la salida de su programa.

ingrese la descripción de la imagen aquí

Omita este párrafo si vio el video:Para aquellos de ustedes que decidieron no ver el video, a continuación se muestran las primeras 12 iteraciones de la curva del dragón. Para los propósitos de esta tarea, una curva de dragón es una curva generada por la siguiente regla: tomar el punto final de la curva actual, crear una segunda curva girada 90 grados alrededor de ese punto final para que el punto final del original curva es el punto de partida de la nueva curva, y une las dos curvas en una sola curva donde se encuentran. En las imágenes que se muestran a continuación, cada nueva iteración se genera girando la iteración anterior 90 grados en sentido horario alrededor del punto final de cada iteración. Cuando la curva se muestra en la pantalla, no es obvio qué extremo cuenta como el "punto final", sin embargo, cuando la curva se almacena como una matriz de puntos, es fácil definir el "punto final" como el último punto en la matriz

El arte ascii es apreciado, pero no aceptado: esta es una salida gráfica, no ascii-art.

J. Antonio Perez
fuente
3
¿Hay alguna especificación sobre tamaño, coloración, etc.? Como es, el resultado exacto es un poco confuso.
Rɪᴋᴇʀ
3
Relacionados .
Fatalize
66
Eliminé la etiqueta de la curva del dragón porque no parecía agregar nada
Azul
1
También relacionado.
Martin Ender
3
Esto no es un duplicado; Las técnicas de programación para resolverlo son bastante diferentes (excepto en quizás Charcoal). La mayoría de las respuestas usan bibliotecas de gráficos de tortugas, que no funcionarían en un contexto ASCII.

Respuestas:

2

x86, MSDOS, 16 bytes

Escribí esto hace un tiempo, que yo sepa, la rutina más pequeña para producir un dragón fractal. No utiliza iteraciones reales, sino que traza cada píxel discreto contenido dentro del fractal directamente, mostrando la imagen final . Se incluye con muchas otras pequeñas producciones en este paquete . La versión de 16 bytes fue el final de mi esfuerzo para lograr que el dragón fractal fuera lo más pequeño posible, comenzando 2014 con esta producción de 32 bytes .

Maleficio

14 10 19 CA D1 FA 10 DE 01 D1 CD 10 B4 0C EB F0

Código

S: 
adc al,0x10
sbb dx,cx       
sar dx,0x01 
adc dh,bl
add cx,dx
int 0x10
mov ah,0x0C
jmp short S

captura de pantalla

HellMood
fuente
1
Esto es ... increíble, por decir lo menos. ¿Cómo haría para ejecutarlo?
J. Antonio Perez
La forma más rápida sería un DosBox en línea, twt86.co?c=FBAZytH6EN4B0c0QtAzr8A%3D%3D Puede copiar la fuente aquí y compilarla usted mismo allí. La forma clásica es descargar DosBox (0.74) usted mismo y ejecutarlo allí. La forma más real es obtener un MSDos o FreeDos Bootstick (Rufus), y ejecutarlo de verdad #noemu;)
HellMood
9

Python 2/3, 169 167 150 111 98 78 Bytes

Tenga en cuenta que la importación no está incluida en el recuento de bytes, de acuerdo con las especificaciones de desafío.

Gracias a @AlexHall por guardar 39 (!) Bytes y @ nedla2004 por otros 13

from turtle import*
o=[90]
for z in o*9:o+=[90]+[-x for x in o[::-1]]
fd(5)
for i in o:rt(i);fd(5)

Comienza generando una lista o giros a la derecha (90) y a la izquierda (-90), luego pasa por la lista y mueve la tortuga.

Salida generada: ingrese la descripción de la imagen aquí

EDITAR: Si esto es demasiado aburrido, mire, agregue speed(0)justo antes del primero fd(5). Funcionará igual, excepto que la tortuga se moverá mucho más rápido.

Theo
fuente
Una foto estaría bien :)
Kritixi Lithos
¿Podría publicar una imagen o captura de pantalla de la salida? Ni siquiera está claro que este código imprima algo en la pantalla
J. Antonio Perez
Tu imagen se cortó
J. Antonio Perez
Debería arreglarse ahora :)
Theo
@AlexHall ¡Gracias! Sabía que debía haber una manera de hacer que ese bucle fuera mucho más corto :)
Theo
8

Logo, 43 bytes

for[i 1 512][fd 9 lt :i/(bitand :i -:i)*90]

Pruebe con el intérprete en http://www.calormen.com/jslogo/#

Esto utiliza el mismo principio que mi respuesta anterior de arte ASCII y la fórmula en wikipedia, excepto que invertí la dirección para que coincida con la imagen en la pregunta:

Primero, exprese n en la forma k*(2^m)donde k es un número impar. La dirección del enésimo giro está determinada por k mod 4, es decir, el resto hacia la izquierda cuando k se divide por 4. Si k mod 4 es 1, entonces el enésimo giro es R L; si k mod 4 es 3, entonces el enésimo turno es L R

bitand :i -:iencuentra el bit menos significativo de i. Dividimos ipor esto a la iderecha la cantidad requerida, dando el número impar requerido k. No hay necesidad de distinguir entre giros a la izquierda y a la derecha; simplemente giramos a la izquierda por k*90grados y confiamos en el hecho de que la rotación es una operación de módulo 360 para realizar el módulo por nosotros.

Salida

use htpara ocultar tortugas si es necesario.

ingrese la descripción de la imagen aquí

Salida (modificada)

A continuación se muestra cómo la curva es un solo filamento.

bk 6 for[i 1 512][fd 6 rt :i/(bitand :i -:i)%4*45-90 fd 3 rt :i/(bitand :i -:i)%4*45-90]

ingrese la descripción de la imagen aquí

Level River St
fuente
4

LindenMASM , 51 Bytes

LindenMASM fue un lenguaje que creé para un desafío hace un tiempo que vivirá para siempre en el Sandbox. Utiliza el concepto de sistemas Lindenmayer para dibujar cosas como curvas de dragón, plantas fractales, triángulos de Sierpinski, etc.

El código fuente es el siguiente:

STT
AXI FX
INC 9
SET F 0
RPL X X+YF+
RPL Y -FX-Y
END

Para configurar esto, n = 6por ejemplo:

STT
AXI FX
INC 6
SET F 0
RPL X X+YF+
RPL Y -FX-Y
END

Esto produce la siguiente imagen a través de Python 3 turtle:

6 generaciones

Puede haber una ligera diferencia de numeración para las iteraciones, ya que en el sistema Lindenmayer la primera iteración es una sola línea. Esto es lo que parece n = 10:

10 generaciones

Solo por diversión, así es como se ve con 15 generaciones (con una instrucción adicional MOV 2para hacerlo un poco más pequeño):

15 generaciones

Una vez que obtienes hasta 20 generaciones (con MOV 0.5), ya no puedes ver las líneas, y se necesitan MUCHOS pasos para crear (pares de +-y -+no están optimizados). Esto es lo que obtienes:

20 generaciones

Tenga en cuenta que el intérprete actual puede presentar problemas gráficos para pequeñas cantidades de generaciones, es decir, posiblemente no dibujar en la pantalla. Desafortunadamente, cuando se creó este intérprete, no hubo problemas, un posible cambio en Python 3 podría haber causado esto o podría ser mi sistema

Kade
fuente
4

Baja carga, 196 bytes

()()(<svg width="99" height="147">)S(<g transform="translate):S((33,33)">)S((3,0)rotate)*a(*a(~*)*~("><path d="M0h3" stroke="#"/>)~*a(*)**:(-90)a~^~(90)a~^)*::*:**:*^S(</g>)(:*)::*:**:*^S(</svg>)S

Pensé que podría ser interesante probar este desafío en un esolang de baja potencia; Underload funciona bastante bien para un lenguaje con un número tan bajo de comandos.

El resultado es un archivo SVG con etiquetas muy anidadas y algunos atajos de golf. Hasta ahora, no he encontrado un navegador que pueda mostrarlo (Firefox se cuelga durante varios minutos tratando de cargarlo, y tanto Firefox como Chromium muestran una pantalla en blanco). La mayoría de los programas de procesamiento de imágenes tampoco pueden cargarlo (lo que dificulta la conversión a otro formato), pero logré cargarlo en el visor de imágenes Eye of Gnome (que es parte de la instalación predeterminada en Ubuntu). Así que tomé una captura de pantalla de la imagen para que pueda verla (la imagen real tiene un fondo transparente, pero en realidad no puede capturar la pantalla transparente):

Captura de pantalla de una curva de dragón en Underload

Necesitamos especificar el tamaño de la imagen explícitamente. Recogiendo una orientación adecuada para la imagen, dibujo todo a la talla mínima legal, y haciendo el mínimo número de iteraciones especificados por el desafío, nos da una imagen que sólo encaja en 99 píxeles de ancho, el ahorro de un byte. Es agradable cuando las cosas funcionan así.

El algoritmo general utilizado para dibujar la imagen es mantener dos variables (Underload no nombra las variables, pero pensé en ellas como x e y ), ambas inicialmente vacías. Luego, reemplazamos repetidamente ( x , y ) con ( x , gire a la izquierda y avance, y ) y ( x , gire a la derecha y avance, y ). Después de diez iteraciones, tanto x como y mantienen una curva de dragón de nueve iteraciones.

También hay algunas micro optimizaciones y trucos específicos de subcarga. Para evitar perder el tiempo con la parte superior de la pila, cada iteración del bucle, comenzamos combinando x e y en la función "devolver la cadena creada concatenando: x , una instrucción de giro, el argumento de la función, un movimiento- instrucción hacia adelante, y y ". Esta función solo ocupa un espacio en la pila, por lo que podemos duplicarlo, llamarlo -90como argumento, intercambiar el valor de retorno debajo del duplicado y llamarlo 90como argumento, para obtener nuevos valores para x e ysin necesidad de tocar más que los dos elementos superiores de la pila (que son, con mucho, los más accesibles). Esta función es generada por código en tiempo de ejecución. El generador en sí también se genera con código en tiempo de ejecución, para permitirle reutilizar la cadena <g transform="translateque también se usa para establecer el origen de la imagen. Primero generamos todas las etiquetas abiertas, y luego, dado que todas las etiquetas de cierre son justas </g>, podemos generar 1024 etiquetas de cierre simplemente repitiendo la cadena, sin preocuparnos de hacer coincidirlas con las etiquetas abiertas. (Escribir números de manera eficiente en Underload es un problema interesante por derecho propio; (:*)::*:**:*sin embargo, es probablemente la forma más eficiente de escribir 1024, traduciéndose en "2 a la potencia de (1 + 2 × 2) × 2".

Underload no tiene ninguna biblioteca de gráficos, por lo que produzco SVG usando una combinación de líneas de dibujo en una posición fija y girando la imagen alrededor de un punto dado; en lugar de girar la pluma, giramos el papel. La idea es que dibujando una línea, girando la imagen completa, dibujando otra línea, girando la imagen nuevamente, etc., podemos simular efectivamente gráficos de tortuga sin tener que hacer ningún cálculo ni usar ninguna biblioteca de gráficos, ya que todas las líneas se dibujan en el mismo lugar Por supuesto, eso significa que tenemos algunas etiquetas de rotación de imagen muy anidadas, lo que confunde a muchos espectadores SVG.

Diseñar la imagen contaría contra el recuento de bytes, por lo que necesitaba dar el estilo mínimo necesario para mostrar la imagen. Esto resulta ser stroke="#", que se traduce más o menos como "la línea debe ser de algún color"; esto parece expandirse para dibujarlo en negro. (Normalmente, especificaría el color como, por ejemplo, "# 000".) El fondo es transparente de forma predeterminada. No especificamos un ancho de trazo, pero la elección elegida por Eye of Gnome deja todo visible.

Muchos intérpretes de Underload luchan con este programa, por ejemplo, el de Try It Online se bloquea, porque genera algunas cadenas muy grandes internamente. Sin embargo, el intérprete original de Underload en línea funciona. (Curiosamente, el primer intérprete estaba en línea, por lo que el idioma se podía usar en línea antes de que se pudiera usar sin conexión).

Algo que me inquieta un poco es que solo parece haber 1023 segmentos de línea aquí, y esperaríamos 1024. Podría ser que uno de los segmentos al final no se dibuje con este algoritmo (sería dibujado en la siguiente iteración). Si eso es descalificador, puede ser posible adaptar el programa, pero podría terminar considerablemente más tiempo. (No es como si este desafío fuera a ganar la competencia de todos modos; ya hay varias entradas más cortas).


fuente
4

MATL , 26 bytes

0J1_h9:"tPJ*h]hYsXG15Y01ZG

Si se aceptan diferentes escalas en los dos ejes, el código se puede reducir a 19 bytes:

0J1_h9:"tPJ*h]hYsXG

Las siguientes figuras corresponden a la versión de igual escala (26 bytes).

El código anterior produce la novena iteración (basada en 0), es decir, la décima imagen en el desafío:

ingrese la descripción de la imagen aquí

Para otros valores, cambie el 9código o reemplácelo ipara tomar el número como entrada del usuario. Por ejemplo, el resultado para 13es:

ingrese la descripción de la imagen aquí

Explicación

Esto usa un bucle para construir gradualmente una matriz de los pasos seguidos por la curva en el plano complejo. Por ejemplo, los dos primeros pasos son 1j(arriba) y -1(izquierda).

En cada iteración, la matriz de pasos hasta el momento se copia. La copia de la matriz se invierte , se multiplica por 1j(para rotar 90 grados) y se concatena al original.

Después del ciclo, una suma acumulativa de los pasos da los puntos reales, que luego se trazan en el plano complejo.

0                          % Push 0
 J1_h                      % Push array [1j, -1]. This defines the first two steps
     9:                    % Push [1, 2, ..., 9]
       "                   % For each
        t                  %   Duplicate the array of steps so far
         P                 %   Reverse
          J*               %   Multiply by 1j
            h              %   Concatenate horizontally to previous steps
             ]             % End
              h            % Concatenate with the initial 0
               Ys          % Cumulative sum
                 XG        % Plot. Complex numbers are plotted with real and imag as x and y
                   15Y0    % Push string 'equal'
                       1ZG % Set equal scale in the two axes
Luis Mendo
fuente
Su respuesta es impresionante :) ¿Le importaría dar una explicación del código?
J. Antonio Perez
@Jorge ¡Gracias! Hecho
Luis Mendo
Las versiones de "19 bytes" y "26 bytes" que proporciona son idénticas. ¿Asumo que hay un error de copiar y pegar aquí?
@ ais523 De hecho! Corregido ahora, gracias por notarlo. Por cierto, se puede ver en acción aquí (compilador experimental; puede requerir actualizar la página)
Luis Mendo
3

Mathematica 86 bytes

{1,-1}
r=Reverse;Graphics@Line@Nest[Join[l=Last@#;h=#-l&/@#,r[r@#%&/@h]]&,{{0,0},%},9]

Cómo funciona: {1,-1}salidas {1,-1}. Básicamente "lo empuja a la pila". Este valor se puede recuperar con %. r=Reversebásicamente solo cambia el nombre de la función Reverse porque la uso dos veces en el código. El Graphics@Line@solo toma una lista de puntos y dibuja una línea que los conecta. El verdadero meollo del problema que ocurre en este segmento de código: Nest[Join[l=Last@#;h=#-l&/@#,r[r@#%&/@h]]&,{{0,0},%},9]. Déjame decirte que ese segmento es complicado como f ****** ck. NestEsto es lo que hace: Nest[f,x,9]genera el resultado de la llamada f[f[f[f[f[f[f[f[f[x]]]]]]]]].

En mi código, este primer argumento fes:, Join[l=Last@#;h=#-l&/@#,r[r@#%&/@h]]&el segundo argumento xes {{0,0},%}(que se evalúa como {{0,0},{1,-1}}), y el tercer argumento es n, que es solo 9 (que solo aplicará el primer argumento al segundo argumento 9 veces).

La parte más compleja de todas es este primer argumento: Join[l=Last@#;h=#-l&/@#,r[r@#%&/@h]]&que es un desorden gigante de azúcar sintáctico casi puro. Yo estaba realmente abusando de azúcar sintáctico de Mathematica para éste. Esa línea de código representa la versión matemática de una función anónima, excepto para acortar cosas, en realidad definí dos funciones anónimas separadas dentro de esa función anónima. Sí, eso es legal, amigos. Vamos a desglosarlo.

JoinToma dos argumentos. El primero es l=Last@#;h=#-l&/@#, y el segundo es r[r@#%&/@h].

El primer argumento de Join: dentro de la función anónima "principal", #hay una lista de todos los puntos en la iteración actual en la curva. Entonces l=Last@#;significa "Toma el punto en la lista de puntos que recibiste como entrada y asigna ese punto a la variable l. El siguiente segmento h=#-l&/@#, es un poco más complejo. Significa" Tienes una función. Esta función toma un punto como entrada, lo resta ly devuelve el resultado. Ahora, aplique esa función a cada elemento en la lista de puntos que recibió como entrada para generar una lista de puntos desplazados, y asigne esa nueva lista a la variable h.

El segundo argumento de Join: r[r@#%&/@h] tiene literalmente la sintaxis más compleja que he escrito. No puedo creer que ningún segmento de código pueda contener algo como @#%&/@: ¡parece que estoy maldiciendo como un personaje de dibujos animados en medio de un programa! Pero es posible desglosarlo. Recuerde: r[x]toma una lista de puntos y la devuelve en orden inverso. r@#%&es una función anónima que invierte su entrada, luego la multiplica por el valor almacenado en %(que es {1,-1}) y devuelve el resultado. Básicamente gira su entrada 90 grados, pero en un código tan corto como podría escribir. Luego r@#%&/@hsignifica "Mostrar una nueva lista que es cada punto hgirado 90 grados".

Entonces, en general, Join[l=Last@#;h=#-l&/@#,r[r@#*%&/@h]]&es una función que toma una lista de puntos como entrada y agrega esa misma lista de puntos rotados 90 grados para obtener la siguiente iteración de la curva. Esto se repite 9 veces para obtener la curva del dragón. Luego, la lista resultante de puntos se dibuja en la pantalla como una línea. Y la salida:

ingrese la descripción de la imagen aquí

J. Antonio Perez
fuente
3
Acabo de encontrar el truco más extraño para escribir un vector nulo: 0{,}... funciona porque 0 xes 0para casi cualquier xy {,}es azúcar sintáctico para {Null,Null}.
Martin Ender
3

Python 2, 43 bytes

Esta respuesta es de 43 bytes sin incluir la declaración de importación y se basa en gran medida en la respuesta del logotipo de Level River St y su uso i/(i&-i)en su código. Pruébelo en línea en trinket.io

from turtle import*
for i in range(1,513):fd(9);rt(90*i/(i&-i))

Aquí hay una imagen de la salida.

ingrese la descripción de la imagen aquí

Sherlock9
fuente
Hasta donde sé, debe incluir el recuento de bytes de la declaración de importación en su recuento total de bytes.
Theo
1
@Theo, solo citando la especificación del desafío:The shortest code in bytes wins, however include directives for libraries shouldn't be included in the byte count, and you may use graphics libraries or other libraries written for your language of choice if they were written before the posting.
Sherlock9
3

Mathematica, 56 55 bytes

Graphics@Line@AnglePath[Pi/2JacobiSymbol[-1,Range@512]]

ingrese la descripción de la imagen aquí

Explicación: OEIS A034947

Solo por diversión, aquí hay una versión en color de la 19ª iteración.

ingrese la descripción de la imagen aquí

alephalpha
fuente
2

Mathematica, 63 bytes

Utilizando AnglePath

Graphics@Line@AnglePath[Pi/2Nest[Join[#,{1},-Reverse@#]&,{},9]]

Nueve iteraciones

Un simmons
fuente
1

HTML + JavaScript, 182

<canvas id=C></canvas><script>c=C.getContext("2d")
C.width=C.height=400
s=n=9
x=y=200
for(i=d=0;i<=1<<n;d+=++i/(i&-i))
c.lineTo(x,y),
d&1?y+=d&2?s:-s:x+=d&2?-s:s
c.stroke()</script>

edc65
fuente
0

Diagramas Haskell +, 179 bytes

import Diagrams.Prelude
import Diagrams.Backend.SVG
d 1=hrule 1<>vrule 1
d n=d(n-1)<>d(n-1)#reverseTrail#rotateBy(1/4)
main=renderSVG"d"(mkWidth 99)$strokeT(d 9::Trail V2 Double)

La salida es un archivo svg de 99 píxeles de ancho con fondo transparente (una imagen de 9 píxeles de ancho tendría un trazo demasiado grueso para reconocer algo). Aquí se vuelve a escalar y se compone sobre un fondo blanco:

Dragón número nueve

Angs
fuente
0

tosh , 518 bytes

tosh es Scratch , pero con texto en lugar de bloques. Con 518 bytes, esta respuesta es probablemente incluso peor que Java.

Esta respuesta utiliza la misma lógica que la respuesta de Python de @ Theo , pero con cadenas de "L" y "R" en lugar de números, ya que las capacidades de la lista de Scratch (y por lo tanto tosh) son horribles.

Puede ejecutarlo como un proyecto Scratch aquí . (Tosh compila proyectos Scratch)

when flag clicked
set path to "R"
go to x: -50 y: 100
point in direction 90
pen down
set pen size to 2
clear
repeat 9
    set path copy to path
    set path to join (path) "R"
    set i to length of path copy
    repeat length of path copy
        if letter i of path copy = "R" then
            set path to join (path) "L"
        else
            set path to join (path) "R"
        end
        change i by -1
    end
end
set i to 0
repeat length of path
    change i by 1
    if letter i of path = "R" then
         turn cw 90 degrees
    else
         turn ccw 90 degrees
    end
    move 7 steps
end  

Explicación:

when flag clicked
set path to "R"
go to x: -50 y: 100
point in direction 90
pen down
set pen size to 2
clear

Esta primera parte hace que el programa se ejecute cuando se hace clic en la bandera verde ( when flag clicked), establece la variable de ruta en "R" y obtiene el sprite y la etapa en el estado adecuado para que estén listos para dibujar.

repeat 9
    set path copy to path
    set path to join (path) "R"
    set i to length of path copy
    repeat length of path copy
        if letter i of path copy = "R" then
            set path to join (path) "L"
        else
            set path to join (path) "R"
        end
        change i by -1
    end
end

Ahora llegamos al código de generación de ruta. Utiliza la misma lógica que la respuesta de Python de @ Theo , excepto con cadenas de "R" y "L" en lugar de números, y usamos bucles anidados en lugar de listas de comprensión.

set i to 0
repeat length of path
    change i by 1
    if letter i of path = "R" then
         turn cw 90 degrees
    else
         turn ccw 90 degrees
    end
    move 7 steps
end  

Finalmente, dibujamos la ruta pasando por cada letra de la variable de ruta y girando hacia la izquierda o hacia la derecha según la letra.

BookOwl
fuente