Desenhar uma curva do dragão

19

Sua tarefa para hoje: desenhe uma curva de dragão!

Caso você não saiba o que é uma curva do dragão, aqui está um vídeo introdutório do ViHart (muito legal, por favor, assista!)

Sua tarefa: desenhe uma curva de dragão, iterada pelo menos 9 vezes. Você não precisa mostrar as iterações de 1 a 9, apenas a curva final produzida após concluir (pelo menos) 9 iterações. A curva deve ser desenhada como linhas retas conectando os pontos na curva; a saída deve corresponder a uma das imagens abaixo que mostra 9 ou mais iterações (até reflexão, rotação, escala e variação na largura da linha, cor da linha e cor do plano de fundo). Sua saída deve ser grande o suficiente para que as linhas individuais e as "caixas" que elas formam possam ser distinguidas uma da outra; se duas linhas não se cruzam na curva, elas não devem ocupar os mesmos pixels ou pixels adjacentes na saída (deve haver pelo menos um pixel do plano de fundo visível entre elas). Você pode exibir a imagem na tela ou salvar a imagem em um arquivo. A saída deve ser gráfica - não pode ser arte ASCII.

O código mais curto em bytes ganha, no entanto , as diretivas para bibliotecas não devem ser incluídas na contagem de bytes, e você pode usar bibliotecas gráficas ou outras bibliotecas escritas para o idioma de sua escolha se elas foram escritas antes da publicação.

Por favor, inclua uma imagem da saída do seu programa.

insira a descrição da imagem aqui

Pule este parágrafo se você assistiu ao vídeo:Para aqueles que decidiram não assistir ao vídeo, as 12 primeiras iterações da curva do dragão são mostradas abaixo. Para os fins desta tarefa, uma curva de dragão é uma curva gerada pela seguinte regra: pegue o ponto final da curva atual, crie uma segunda curva girada 90 graus em torno desse ponto final, para que o ponto final do original curva é o ponto inicial da nova curva e une as duas curvas em uma única curva onde elas se encontram. Nas imagens mostradas abaixo, cada nova iteração é gerada girando a iteração anterior 90 graus no sentido horário em torno do ponto final de cada iteração. Quando a curva é exibida na tela, não é óbvio qual extremidade conta como o "ponto final"; no entanto, quando a curva é armazenada como uma matriz de pontos, é fácil definir o "ponto final" como o último ponto em a matriz.

Ascii art é apreciada, mas não aceita: Esta é uma saída gráfica, não ascii-art.

J. Antonio Perez
fonte
3
Existem especificações sobre dimensionamento, coloração etc.? Como é a saída exata, é um pouco incerto.
Rɪᴋᴇʀ
3
Relacionado .
Fatalize
6
Eu removi a marcação da curva do dragão porque ela não parecia adicionar nada #
Blue
11
Também relacionado.
22816 Martin Ender19:
3
Isso não é uma duplicata; as técnicas de programação para resolvê-lo são bem diferentes (exceto talvez no carvão). A maioria das respostas usa bibliotecas de gráficos de tartarugas, que não funcionariam em um contexto ASCII.

Respostas:

2

x86, MSDOS, 16 bytes

Escrevi isso há um tempo atrás, até onde sei, a menor rotina para produzir um fractal de dragão. Ele não usa iterações reais, mas plota todos os pixels discretos contidos diretamente no fractal, mostrando a imagem final . Está incluído em muitas outras pequenas produções neste pacote . A versão de 16 bytes foi o fim do meu esforço para obter o fractal do dragão o menor possível, a partir de 2014 com esta produção de 32 bytes .

Hex

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 tela

HellMood
fonte
11
Isso é ... incrível, para dizer o mínimo. Como eu iria executá-lo?
J. Antonio Perez
A maneira mais rápida seria um DosBox on-line, twt86.co?c=FBAZytH6EN4B0c0QtAzr8A%3D%3D Você pode copiar a fonte aqui e compilá-la lá. A maneira clássica é fazer o download do DosBox (0,74) e executá-lo lá. A maneira mais real é obter um MSDOS ou FreeDos Bootstick (Rufus), e executá-lo para #noemu real;)
HellMood
9

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

Observe que a importação não está incluída na contagem de bytes, de acordo com as especificações do desafio.

Agradecemos a @AlexHall por salvar 39 (!) Bytes e a @ nedla2004 por mais 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)

Começa gerando uma lista ou gire à direita (90) e à esquerda (-90), depois percorre a lista e move a tartaruga.

Saída gerada: insira a descrição da imagem aqui

EDIT: Se isso é muito chato demais assistir, adicione speed(0)logo antes do primeiro fd(5). Funcionará da mesma forma, exceto que a tartaruga se moverá muito mais rápido.

Theo
fonte
A imagem seria bom :)
Kritixi Lithos
Você poderia postar uma imagem ou captura de tela da saída? Não é ainda claro que este código imprime nada para a tela
J. Antonio Perez
Sua imagem foi cortada
J. Antonio Perez
Deve ser corrigido agora :)
Theo
@AlexHall Thanks! Eu sabia que devia haver uma maneira de tornar esse loop mais curto :)
Theo
8

Logotipo, 43 bytes

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

Tente com o intérprete em http://www.calormen.com/jslogo/#

Isso usa o mesmo princípio que minha resposta de arte ASCII anterior e a fórmula na wikipedia, exceto que eu invertei a direção para corresponder à imagem na pergunta:

Primeiro, expresse n na forma em k*(2^m)que k é um número ímpar. A direção da enésima volta é determinada por k mod 4, isto é, o restante restante quando k é dividido por 4. Se k mod 4 for 1, a enésima volta será RL ; se k mod 4 for 3, a enésima volta será L R

bitand :i -:iencontra o bit menos significativo de i. Dividimos ipor isso shitft idireito a quantidade necessária, dando ao número ímpar necessário k. Não há necessidade de distinguir entre curvas à esquerda e à direita; nós apenas vire à esquerda pork*90 graus e confiamos no fato de que a rotação é uma operação do módulo 360 para executar o módulo para nós.

Resultado

usar ht para esconder a tartaruga, se necessário.

insira a descrição da imagem aqui

Saída (modificada)

A seguir, mostramos como a curva é um único fio.

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]

insira a descrição da imagem aqui

Level River St
fonte
4

LindenMASM , 51 Bytes

O LindenMASM foi um idioma que eu criei para um desafio há algum tempo e que viverá para sempre na Sandbox. Utiliza o conceito de sistemas Lindenmayer para desenhar coisas como curvas de dragão, plantas fractais, triângulos de Sierpinski, etc.

O código fonte é o seguinte:

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

Para configurar isso, n = 6por exemplo:

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

Isso produz a seguinte imagem via Python 3 turtle:

6 gerações

Pode haver uma pequena diferença de numeração para as iterações, pois no sistema Lindenmayer a primeira iteração é uma única linha. Aqui está o que parece n = 10:

10 gerações

Apenas por diversão, aqui está a aparência de 15 gerações (com instruções adicionais MOV 2para torná-lo um pouco menor):

15 gerações

Depois de chegar a 20 gerações (com MOV 0.5), você não consegue mais ver as linhas, e são necessárias muitas etapas para criar (pares de pares +-e -+não são otimizados). Aqui está o que você recebe:

20 gerações

Observe que o intérprete atual pode apresentar problemas gráficos por pequenas quantidades de gerações, ou seja, possivelmente não atraindo a tela. Infelizmente, quando esse intérprete foi criado, não houve problemas, uma possível alteração no Python 3 poderia ter causado isso ou poderia ser apenas o meu sistema

Kade
fonte
4

Subcarga, 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

Eu pensei que poderia ser interessante tentar esse desafio em um esolang de baixa potência; O Underload se sai bem em um idioma com um número tão baixo de comandos.

A saída é um arquivo SVG com tags muito aninhadas e alguns atalhos de golfe. Até agora, não encontrei um navegador que possa exibi-lo (o Firefox trava por vários minutos tentando carregá-lo, e o Firefox e o Chromium exibem uma tela em branco). A maioria dos programas de processamento de imagens também não pode carregá-lo (dificultando a conversão para outro formato), mas consegui carregá-lo no visualizador de imagens Eye of Gnome (que faz parte da instalação padrão no Ubuntu). Então, tirei uma captura de tela da imagem para que você possa vê-la (a imagem real tem um plano de fundo transparente, mas você não pode capturar uma tela transparente):

Captura de tela de uma curva de dragão no Underload

Precisamos especificar o tamanho da imagem explicitamente. Escolher uma orientação apropriada para a imagem, desenhar tudo no tamanho mínimo legal e fazer o número mínimo de iterações especificadas pelo desafio, nos dá uma imagem que cabe apenas em 99 pixels de largura, economizando um byte. É bom quando as coisas funcionam assim.

O algoritmo geral usado para desenhar a imagem é manter duas variáveis (não subcarga não nomear variáveis, mas eu pensei neles como x e y ), ambos inicialmente vazio. Em seguida, substituímos repetidamente ( x , y ) por ( x , vire à esquerda e avance, y ) e ( x , vire à direita e avance, y ). Após dez iterações, x e y mantêm uma curva de dragão de nove iterações.

Também existem algumas micro-otimizações e truques específicos para o Underload. A fim de evitar o excesso de brincar com o topo da pilha, cada iteração do loop, começamos combinando x e y para a função "retornar a string criado pela concatenação: x , uma instrução por sua vez, o argumento da função, um movi- instrução para a frente e y ". Essa função ocupa apenas um espaço na pilha, para que possamos duplicá-la, chamá-la -90como argumento, trocar o valor de retorno pela duplicata e chamá-la 90como argumento para obter novos valores para x e ysem precisar tocar mais do que os dois principais elementos da pilha (que são de longe os mais acessíveis). Esta função é gerada por código em tempo de execução. O gerador em si também é gerado por código em tempo de execução, para permitir a reutilização da string <g transform="translateque também é usada para definir a origem da imagem. Primeiro, geramos todas as tags abertas e, em seguida, porque todas as tags de fechamento são justas </g>, podemos produzir 1024 tags de fechamento simplesmente repetindo a string, sem nos preocupar em correspondê-las às tags abertas. (Escrever números eficientemente em Underload é um problema interessante por si só; (:*)::*:**:*provavelmente é a maneira mais eficiente de escrever 1024, traduzindo "2 para a potência de (1 + 2 × 2) × 2").

O Underload não possui bibliotecas gráficas, por isso produzo SVG usando uma combinação de linhas de desenho em uma posição fixa e girando a imagem em torno de um determinado ponto; em vez de girar a caneta, giramos o papel. A idéia é que, desenhando uma linha, girando a imagem inteira, desenhando outra linha, girando a imagem novamente etc., podemos simular efetivamente gráficos de tartarugas sem ter que fazer nenhuma aritmética ou usar bibliotecas de gráficos, pois todas as linhas são desenhadas no mesmo local. Obviamente, isso significa que temos algumas tags de rotação da imagem muito aninhadas, o que confunde muitos visualizadores SVG.

O estilo da imagem contaria contra a contagem de bytes, portanto, eu precisava fornecer o estilo mínimo necessário para exibir a imagem. Isso acaba sendo stroke="#", que mais ou menos se traduz como "a linha precisa ter alguma cor"; isso parece ser expandido para desenhá-lo em preto. (Normalmente, você especificaria a cor como, por exemplo, "# 000".) O plano de fundo é transparente por padrão. Não especificamos uma largura de traçado, mas a escolha escolhida pelo Olho do Gnomo deixa tudo visível.

Muitos intérpretes do Underload lutam com esse programa, por exemplo, o do Try It Online falha, porque gera algumas seqüências muito grandes internamente. O intérprete online Underload original funciona, no entanto. (Interessante, o primeiro intérprete estava on-line, portanto o idioma era utilizável on-line antes de ser usado off-line.)

Algo que me incomoda um pouco é que parece haver apenas 1023 segmentos de linha aqui, e esperamos 1024. Pode ser que um dos segmentos no final não esteja sendo desenhado com esse algoritmo (seria desenhada na próxima iteração). Se isso é desqualificante, pode ser possível adaptar o programa, mas pode acabar bem mais longo. (Não é como se esse desafio vencesse a competição de qualquer maneira; já existem várias inscrições mais curtas.)


fonte
4

MATL , 26 bytes

0J1_h9:"tPJ*h]hYsXG15Y01ZG

Se diferentes escalas nos dois eixos forem aceitas, o código poderá ser reduzido para 19 bytes:

0J1_h9:"tPJ*h]hYsXG

As figuras abaixo correspondem à versão em escala igual (26 bytes).

O código acima produz a 9a-iteração (com base em 0), ou seja, a décima imagem no desafio:

insira a descrição da imagem aqui

Para outros valores, altere o 9código ou substitua-o por ipara obter o número como entrada do usuário. Por exemplo, o resultado para 13é:

insira a descrição da imagem aqui

Explicação

Isso usa um loop para construir gradualmente uma matriz das etapas seguidas pela curva no plano complexo. Por exemplo, os dois primeiros passos são 1j(para cima) e -1(para a esquerda).

Em cada iteração, a matriz de etapas até agora é copiada. A cópia da matriz é revertida , multiplicada por 1j(para girar 90 graus) e concatenada ao original.

Após o loop, uma soma acumulada das etapas fornece os pontos reais, que são plotados no plano complexo.

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
fonte
Sua resposta é impressionante :) você se importa em fornecer uma explicação do código?
J. Antonio Perez
@Jorge Obrigado! Feito
Luis Mendo
As versões de "19 bytes" e "26 bytes" fornecidas são idênticas. Presumo que haja um erro de copiar e colar aqui?
@ ais523 De fato! Corrigido agora, obrigado por perceber. BTW ele pode ser visto em ação aqui (compilador experimental; pode necessitar de atualizar a 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]

Como funciona: {1,-1}Saídas {1,-1}. Basicamente "empurra para a pilha". Este valor pode ser recuperado com %. r=Reversebasicamente apenas renomeia a função Reverse porque eu a uso duas vezes no código. O Graphics@Line@apenas pega uma lista de pontos e desenha uma linha conectando-os. A carne real do problema acontece neste segmento de código: Nest[Join[l=Last@#;h=#-l&/@#,r[r@#%&/@h]]&,{{0,0},%},9]. Deixa eu te contar - esse segmento é complicado pra caralho. Aqui está o que Nestfaz: Nest[f,x,9]gera o resultado da chamada f[f[f[f[f[f[f[f[f[x]]]]]]]]].

No meu código, esse primeiro argumento fé :, Join[l=Last@#;h=#-l&/@#,r[r@#%&/@h]]&o segundo argumento xé {{0,0},%}(que é avaliado como {{0,0},{1,-1}}) e o terceiro argumento é n, que é apenas 9 (que apenas aplicará o primeiro argumento ao segundo argumento 9 vezes).

A parte mais complexa de tudo é esse primeiro argumento:, Join[l=Last@#;h=#-l&/@#,r[r@#%&/@h]]&que é uma bagunça gigante de açúcar sintático quase puro. Eu estava realmente abusando do açúcar sintático do mathematica por este. Essa linha de código representa a versão do mathematica de uma função anônima, exceto para encurtar as coisas que realmente defini duas funções anônimas separadas dentro dessa função anônima. Sim, isso é legal, pessoal. Vamos dividir.

Joinleva dois argumentos. O primeiro é l=Last@#;h=#-l&/@#e o segundo é r[r@#%&/@h].

O primeiro argumento de Join: Dentro da função anônima "principal", #é uma lista de todos os pontos da iteração atual na curva. Então l=Last@#;significa "Pegue o ponto na lista de pontos que você recebeu como entrada e atribua esse ponto à variável l. O próximo segmento,, h=#-l&/@#é um pouco mais complexo. Significa" Você tem uma função. Essa função pega um ponto como entrada, subtrai le retorna o resultado. Agora, aplique essa função a todos os elementos da lista de pontos que você recebeu como entrada para gerar uma lista de pontos deslocados e atribua essa nova lista à variável h.

O segundo argumento de Join: r[r@#%&/@h] tem literalmente a sintaxe mais complexa que já escrevi. Não acredito que qualquer segmento de código possa conter algo como @#%&/@- parece que estou xingando como um personagem de desenho animado no meio de um programa! Mas é possível decompô-lo. Lembrar - r[x]pega uma lista de pontos e retorna essa lista em ordem inversa. r@#%&é uma função anônima que inverte sua entrada e a multiplica pelo valor armazenado em %(que é {1,-1}) e retorna o resultado. Basicamente, ele gira sua entrada em 90 graus, mas no código o mais curto que eu poderia escrever. Então r@#%&/@hsignifica "Crie uma nova lista com todos os pontos hgirados em 90 graus".

Tão geral, Join[l=Last@#;h=#-l&/@#,r[r@#*%&/@h]]&é uma função que pega uma lista de pontos como entrada e adiciona a mesma lista de pontos girados 90 graus para obter a próxima iteração da curva. Isso é repetido 9 vezes para obter a curva do dragão. Em seguida, a lista de pontos resultante é desenhada na tela como uma linha. E a saída:

insira a descrição da imagem aqui

J. Antonio Perez
fonte
3
Acabei de encontrar o truque mais estranho para escrever um vetor nulo: 0{,}... funciona porque 0 xé 0para quase todo xe {,}é açúcar sintático para {Null,Null}.
Martin Ender
3

Python 2, 43 bytes

Esta resposta tem 43 bytes, sem incluir a declaração de importação, e é amplamente baseada na resposta do logotipo da Level River St e seu uso i/(i&-i)no código. Experimente online em trinket.io

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

Aqui está uma imagem da saída.

insira a descrição da imagem aqui

Sherlock9
fonte
Tanto quanto eu sei, você precisa incluir a contagem de bytes da instrução de importação na sua contagem total de bytes.
Theo
11
@Theo, apenas citando a especificação do desafio: #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]]

insira a descrição da imagem aqui

Explicação: OEIS A034947

Apenas por diversão, aqui está uma versão colorida da 19a iteração.

insira a descrição da imagem aqui

alefalpha
fonte
2

Mathematica, 63 bytes

Usando AnglePath

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

Nove iterações

A Simmons
fonte
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
fonte
0

Haskell + diagramas, 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)

A saída é um arquivo svg de 99 pixels de largura e fundo transparente (uma imagem de 9 pixels de largura teria um traçado muito espesso para recuperar qualquer coisa). Aqui é redimensionado e composto sobre um fundo branco:

Dragão número nove

Angs
fonte
0

tosh , 518 bytes

tosh é zero , mas com texto em vez de blocos. Com 518 bytes, essa resposta é provavelmente ainda pior que Java.

Esta resposta usa a mesma lógica que a resposta Python de @ Theo , mas com seqüências de caracteres de "L" e "R" em vez de números, pois os recursos da lista de Scratch (e, portanto, de Tosh) são terríveis.

Você pode executá-lo como um projeto do zero aqui . (tosh compila em projetos do 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  

Explicação:

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 primeira parte faz com que o programa seja executado quando a bandeira verde é clicada ( when flag clicked), define a variável do caminho como "R" e coloca o sprite e o estágio no estado adequado para estar pronto para desenhar.

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

Agora chegamos ao código de geração de caminho. Ele usa a mesma lógica da resposta Python de @ Theo , exceto com as seqüências de caracteres de "R" e "L" em vez de números, e usamos loops aninhados em vez de compreensão de lista.

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  

Por fim, desenhamos o caminho percorrendo cada letra da variável do caminho e girando para a esquerda ou direita, dependendo da letra.

BookOwl
fonte