"Desculpe, jovem, mas são tartarugas até o fim!"

21

Executar um sistema Lindenmayer

Um sistema Lindenmayer (ou sistema L) está relacionado aos sistemas Thue e Post e é usado na modelagem botânica e geração de fractal .

Um sistema L é descrito por reescrita de seqüência de caracteres, onde um símbolo do alfabeto é mapeado para uma sequência de substituição de símbolos. Uma coleção desses mapeamentos constitui o sistema L propriamente dito.

O método de saída gráfica desenvolvido por Prusinkiewicz interpreta a sequência resultante após os mapeamentos terem sido aplicados a uma sequência inicial para um número especificado de iterações , como comandos de Desenho de Tartarugas: frente, trás, esquerda, direita, esse tipo de coisa. Isso pode exigir código extra para controlar a escala do desenho, pois contagens de iterações diferentes podem produzir imagens de tamanhos drasticamente diferentes.

Sua tarefa é executar um sistema L com o menor número de caracteres. Seu programa deve ser capaz de renderizar tanto a Curva do Dragão quanto as Hastes de Ramificação da página da Wikipedia, fornecendo a entrada apropriada (arquivo, linha de comando, mas externo à fonte, por favor).

Hastes de ramificação Dragon Curve

Isso é código de golfe.

Edit: Aqui estão alguns exemplos que eu publiquei na cidade. responda a SO / gire para o norte { Onde descobri o sistema L pela primeira vez } , responda a SO / como programar um fractal , responda a SO / recursão em postscript , discussão comp.lang.postscript / considerando , coleção postscript do sistema l , codegolf.SE/draw-a-sierpinski-triangle {origem da competição entre mim e thomasW} .

luser droog
fonte
Ignorou a caixa de areia. Isso parece relativamente simples e deve ser divertido.
Luser droog
BTW, alguém sabe a origem da citação acima? Eu ouvi William James e ouvi Faraday.
Luser droog
11
A Wikipedia diz que a origem está em disputa, o melhor palpite é Bertrand Russel.
Ugoren
ITYM Bertrand Russell .
Paul R
11
Existem limites para o tamanho do alfabeto, número de regras, número de rodadas ou possíveis regras (visualização) (desenhe uma linha reta, empurre / posicione / incline o ângulo, gire quantos graus etc.) Se precisarmos desenhar apenas aqueles dois, então precisaríamos pressionar e pular, desenhar linhas retas e poder girar 45 graus em ambas as direções, apenas duas regras e um alfabeto de tamanho 4. #
shiona

Respostas:

31

Mathematica 200 198 188 171 168

Espaços adicionados para maior clareza:

f[i_, b_, h_, j_, r_, n_] :=
 (a = h; p = j; s = k = {}; t = Flatten;
  (Switch[#,
      6, s = {a, p, s},
      8, {a, p, s} = s,
      _C, k = {k, Line@{p, p += {Cos@a, Sin@a}}}];
     If[# < 9, a += I^# b ]) & /@ t@Nest[# /. r &, i, n];
  Graphics@t@k)

Onde:

i: estado inicial;
b: ângulo de rotação
h: ângulo inicial
j: posição inicial
r: regras de produção
n: iterações

Gramática das regras de produção:

2 = Vire à esquerda (-);
4 = Vire à direita (+);
6 = Empurre e vire à esquerda ("[");
8 = Pop e vire à direita ("]");
C [i] = Desenhar (Qualquer número de símbolos)
Qualquer outro símbolo = Não fazer nada, basta usá-lo na produção do próximo estado (Qualquer número de símbolos)

A sequência {2,4,6,8} está lá porque eu estou usando I^n( I= unidade imaginária) para fazer curvas.

Exemplos:

f[{C[1], X}, Pi/2, 0, {0, 0}, {X -> {X, 4, Y, C[1]}, Y -> {C[1], X, 2, Y}}, 10]

Gráficos do Mathematica

f[{C@1}, Pi/2, 0, {0,0}, {C@1->{C@1, 2, C@1, 4, C@1, 4, C@1, 2, C@1}}, 6]

Gráficos do Mathematica

f[{C[1]}, Pi/4, Pi/2, {0, 0}, {C[2] -> {C[2], C[2]}, C[1] -> {C[2], 6, C[1], 8, C[1]}}, 10]

Gráficos do Mathematica

f[{C[1]}, Pi/3, 0, {0, 0}, {C@1 -> {C@2, 4, C@1, 4, C@2}, C@2 -> {C@1, 2, C@2, 2, C@1}}, 10]

Gráficos do Mathematica

f[{X},5/36 Pi, Pi/3, {0,0},{X->{C@1, 4, 6, 6, X, 8, 2, X, 8, 2, C@1, 6, 2, C@1, X, 8, 4, X},
                            C@1->{C@1, C@1}}, 6]

Gráficos do Mathematica

Dr. belisarius
fonte
Apenas modifique Graphics@kpor Graphics@Flatten@kse você planeja usar muitas iterações. Caso contrário, um limite de recursão o morderá e sua sessão de Mma será cancelada.
perfil completo de belisarius
Meu método de macroexpansão teve um problema semelhante com iterações mais altas. As cordas se tornam enormes. Mas a solução não havia não para achatar. :)
luser droog
2
+1 muito bom mesmo;) Poderia ser uma demonstração legal. Você os envia?
Vitaliy Kaurov
@VitaliyKaurov Não, mas fique à vontade para usá-lo se você acha que vale a pena o esforço
Dr. belisarius
3
@belisarius demonstrations.wolfram.com/GraphicalLSystems
Vitaliy Kaurov
9

Python, 369 294

Não sou um vencedor, mas vou postar o que tentei de qualquer maneira.

from turtle import*
I=open('l').read().split()
s,S,p=I[0],'',[]
for j in range(8):
    for i in s:S+=eval('{'+I[1]+'}')[i]
    s,S=S,''
for k in s:
    if k=='[':p+=[heading(),pos()];continue
    if k==']':pu();goto(p.pop());seth(p.pop());pd();continue
    try:{'f':fd,'F':fd,'+':rt,'-':lt}[k](5)
    except:pass

Não é bom no golfe de Python ...... Talvez alguém possa fazer isso.

Entrada

A entrada é de um arquivo externo chamado "l" (sem extensão), com o seguinte formato:
Linha 1 : Estado inicial (Axioma)
Linha 2 : Regras separadas por vírgula

Símbolos
f e F: Desenhe para a frente
+: Vire à direita 5 graus
-: Vire à esquerda 5 graus
[: Salve a posição e o cabeçalho
]: Posição pop e o cabeçote
Outros símbolos são ignorados pela função de desenho.

Regras
Uma regra está no formato "predecessor":"successor(s)"
Observe que as aspas são necessárias, sejam simples ou duplas.
predecessordeve ser um único caractere.
Além disso, não há constantes implícitas: você deve especificar explicitamente uma regra de não alteração para elas.

Exemplos

Hastes de ramificação

------------------f
'-':'-','+':'+','[':'[',']':']','F':'FF','f':'F[+++++++++f]---------f'

Saída

Observe que a fonte é modificada para liberar isso APENAS PARA ESCALAR O GRÁFICO NA ÁREA VISÍVEL. O console também é usado para esconder a "tartaruga".

Curva do dragão

fx
'-':'-','+':'+','[':'[',']':']','f':'f','x':'x++++++++++++++++++yf','y':'fx------------------y'

Saída

Novamente, o console é usado para ocultar a "tartaruga".

Triângulo de Sierpinski

f------------------------F------------------------F
'-':'-','+':'+','[':'[',']':']','f':'f------------------------F++++++++++++++++++++++++f++++++++++++++++++++++++F------------------------f','F':'FF'



Gerações de saída reduzidas para 5 aqui.

TwiNight
fonte
3
Você pode obter uma economia decente (eu torná-lo 32 caracteres), removendo as funções f, r, l; adicionando um parâmetro fictício a oe c; e, em seguida, mudar o interruptor pseudo para{'f':fd,'F':fd,'+':rt,'-':lt,'[':o,']':c}[k](5)
Peter Taylor
Pode também em linha g, e eu acho oe cvalem a pena eliminando com embutidos ifdeclarações (mais barata do que a globaldeclaração)
Peter Taylor
@PeterTaylor bom trabalho. Eu tinha uma intuição de que algumas dessas funções poderiam ser incorporadas, mas não conhecia Python suficiente para sugerir isso articularmente.
Luser droog
11
@luserdroog, também não conheço Python: fiz apenas tentativa e erro para ver o que funcionava - ou seja, algumas das coisas que tentei (por exemplo, usar lambdas para oe cdiretamente no pseudo-interruptor) deram erros de sintaxe, mas outras não ' t.
Peter Taylor
Dicas para golfe adicional: 1. Altere o formato de entrada: exija chaves ao redor das regras e separe o axioma das regras por um espaço (não uma nova linha). 2. Leia a partir de stdin: s,R,*p=input().split(). 3. Gere o valor final de spor exec('s="".join(map(eval(R).get,s));'*8). 4. Deixe de fora continue. 5. Recue apenas 1 espaço. 6. Economize espaço após a iftroca das laterais do teste. 7. Coloque k:inta dict(primeira entrada) e então você não precisa except: try:. (Recebo 215 caracteres.)
Reinstate Monica
7

Javascript (179 bytes)

Não tenho certeza absoluta de que isso se qualifica, pois o objeto de regras faz todo o desenho real.

Demo (Dragon, animado):
- Expandido: http://jsfiddle.net/SVkMR/9/show/light
- Com código: http://jsfiddle.net/SVkMR/9/

Minificado:

function L(c,r,n){o=(function g(d,s,o,i){for(o=i='';a=d&&s[i++];)o+=r.r[a]?g(d-1,r.r[a]):a;return o||s;})(n,r.r[r.s]);(function z(i){r[s=o[i]]&&r[s](c)||setTimeout(z,10,i+1)})(0)}

Legível (ish):

function L(c,r,n){
    o=(function g(d,s,o,i){
        for(o=i='';a=d&&s[i++];)o+=r.r[a]?g(d-1,r.r[a]):o+=a
        return o||s
    })(n,r.r[r.s]);

    (function p(i){
        r[s=o[i]]&&r[s](c)||setTimeout(p,10,i+1)
    })(0)
}

Entrada:

var sierspinski = {
    r:{'A':'B-A-B','B':'A+B+A'},
    '+':function(c){c.rotate(-this.a);c.rotate(this.a-=Math.PI/3)},
    '-':function(c){c.rotate(-this.a);c.rotate(this.a+=Math.PI/3)},
    'A':function(c){c.beginPath();c.moveTo(0,0);c.translate(this.m,0);c.lineTo(0,0);c.stroke()},
    'B':function(c){this['A'](c)},
    s:'A',
    a:0,
    m:1
};

var koch = {
    r: {'F':'F+F-F-F+F'},
    '+':function(c){c.rotate(-this.a);c.rotate(this.a-=Math.PI/2)},
    '-':function(c){c.rotate(-this.a);c.rotate(this.a+=Math.PI/2)},
    'F':function(c){c.beginPath();c.moveTo(0,0);c.translate(this.m,0);c.lineTo(0,0);c.stroke()},
    s:'F',
    a:0,
    m:2
};
var dragon = {
    r: {'X':'X+YF','Y':'FX-Y'},
    '+':function(c){c.rotate(-this.a);c.rotate(this.a-=Math.PI/2)},
    '-':function(c){c.rotate(-this.a);c.rotate(this.a+=Math.PI/2)},
    'F':function(c){c.beginPath();c.moveTo(0,0);c.translate(this.m,0);c.lineTo(0,0);c.stroke()},
    s:'X',
    a:0,
    m:5
};

var algae = {
    r: {'A':'B[A]A','B':'BB'},
    '[':function(c){c.save();c.rotate(Math.PI/4);},  // save/restore will push/pop current state of context. 
    ']':function(c){c.restore();c.rotate(-Math.PI/4);},
    'A':function(c){c.beginPath();c.moveTo(0,0);c.translate(this.m,0);c.lineTo(0,0);c.stroke()},
    'B':function(c){this['A'](c);},
    s:'A',
    a:-Math.PI/2,
    m:1
};

var tree = {
    r:{'X':'F-[[X]+X]+F[+FX]-X','F':'FF'},
    '+':function(c){c.rotate(-this.a);c.rotate(this.a+=Math.PI/180*25)},
    '-':function(c){c.rotate(-this.a);c.rotate(this.a-=Math.PI/180*25)},
    '[':function(c){c.save();},
    ']':function(c){c.restore();},
    'F':function(c){c.beginPath();c.moveTo(0,0);c.translate(this.m,0);c.lineTo(0,0);c.stroke()},
    s:'X',
    a:-Math.PI/180*25,
    m:5
};

Uso:

var ctx = document.getElementById('l').getContext('2d'); // grab context
ctx.translate(299.5,199.5); // start drawing from center, fractional pixels because the canvas draws lines centered on the x/y coord specified
L(ctx, dragon, 8); // pass in context, rules object, and recursion cap

Bônus: Espiral Dourada http://jsfiddle.net/SVkMR/35/show/light/

var golden = {
    r:{'A':'FT[TT]A','T':'+F'},
    'F':function(c){c.beginPath();c.moveTo(0,0);c.translate(this.m,0);c.lineTo(0,0);c.stroke()},
    '[':function(c){c.save();},
    ']':function(c){
        c.restore();

        c.beginPath();
        c.arc(0,-this.m,this.m,Math.PI/2,Math.PI);
        c.stroke();

        this.m+=this.d;this.d=this.m-this.d
    },
    '+':function(c){c.rotate(-Math.PI/2);},
    s:'A',
    a:-Math.PI/2,
    m:1,
    d:0
};
Shmiddty
fonte
Eu acho que a animação mais do que compensa quaisquer liberdades com as regras. Bom trabalho! +1
luser droog 30/01
:) Coisas divertidas! .
Shmiddty
5

Postscript 264 298 295 255

Aqui está minha tentativa de fazê-lo de maneira diferente. Em vez da macroexpansão que eu costumo usar, esta verifica o tamanho da pilha de execução para vincular a recursão. Se o limite for excedido, ele pára de examinar recursivamente o procedimento e tenta interpretar os comandos da tartaruga (e descarta o pop popcontrário). Uma vantagem desse método é que ele não requer enormes quantidades de memória. Uma desvantagem é que o controle de recursão é bastante desajeitado, pois o tamanho da pilha cresce mais do que apenas 1 de um nível de recursão para o próximo.

Editar: +34 caracteres para ramificação.
Edit: -3 caracteres. Redesenhado para usar a pilha de operandos para controle de recursão. Isso torna o sistema básico muito mais simples. Como os colchetes precisam de uma pilha independente, coloquei a posição salva na pilha do dicionário e quase paguei todas as economias.

Além disso, redesenhado para usar seqüências de caracteres e números inteiros em vez de matrizes e nomes.

Editar: -40 caracteres. Foram adicionados dois procedimentos para chamar nomes de sistemas por número (não consigo fazer com que os tokens binários brutos funcionem. Mas esse idioma funciona para mim.)

/*{<920>dup 1 4 3 roll put cvx exec}def/${//* 73
*}def[/T[48{}49{}43{A<88>$}45{A<6e88>$}70{R
0<85>$}91{<1e39>$[/.[<286827>$]cvx>><0d0d>$}93{.<9c6b1e39390d>$}>>/S{dup
B eq{T<0d3e>${<643f>$}<4939>$}{exch{<643e>$ 1 add S}73 *}85 * 1 sub}>><0d6b>$
0 S<a7>$

Binário semi-comentado.

/*{<920>dup 1 4 3 roll put cvx exec}def/${//* 73 *}def %73=forall
[/T[70{R 0<85>$}48{}49{} %<85>=rlineto
43{A<88>$}45{A<6e88>$} %<88>=rotate <6e>=neg
91{<1e39>$ %<1e>=currentdict <39>=end
    [/.[<286827>$]cvx>> %<28>=currentpoint <68>=matrix <27>=currentmatrix
        <0d0d>$} %<0d>=begin
93{.<9c6b1e39390d>$}>> %<9c>=setmatrix <6b>=moveto
/S{dup B eq{T<0d3e>${<643f>$}<4939>$} %<3e>=exch <64>=load <3f>=exec <49>=forall
{exch{<643e>$ 1 add S}73 *}85 * 1 sub}>>
<0d6b>$ 0 S<a7>$  % 85=ifelse <a7>=stroke

Un- "binário".

[/T[70{R 0 rlineto}48{}49{}43{A rotate}45{A neg rotate}91{currentdict
end[/.[currentpoint matrix currentmatrix]cvx>>begin begin}93{. setmatrix
moveto currentdict end end begin}>>/S{dup B eq{T begin exch{load exec}forall
end}{exch{load exch 1 add S}forall}ifelse 1 sub }>>begin moveto 0 S stroke

Ele exige que o sistema L seja definido em um dicionário na pilha de pistas, com a sequência inicial e a posição inicial da tartaruga na pilha de operandos (anexada à fonte, por exemplo gs dragon.sys lsys.ps).

Curva do dragão.

%!
[                     %begin dictionary construction
    % productions are described by integer-key/string-value pairs
    48(0+1F) %0       %ascii code for '0' defined as the string "0+1F"
    49(F0-1) %1       %  "     "   "  '1'   "     "   "    "    "F0-1"
    43(+) %+          %  "     "   "  '+' defined as itself
    45(-) %-          %  "     "   "  '-'   "     "   "
    70(F) %F          %  "     "   "  'F'   "     "   "
    % parameters
    /A 90 %angle
    /R 2  %radius
    /B 10 %maximum recursion-level
>>begin  % L-system dictionary on top of dictstack
(F0)     % initial string on stack
300 400  % starting position on stack

Hastes de ramificação.

[
    48(F[+0]-0) %0
    49(F0-1) %1
    43(+) %+
    45(-) %-
    70(FF) %F
    91([) %[
    93(]) %]
    /A 45 %angle
    /R 5  %radius
    /B 3 %recursion
>>begin
(++0)     % initial string
300 400  % starting position

Ungolfed e comentou.

[                                 % begin dictionary construction
    /T[                           % /T is the Turtle dictionary containing
                                  % integer-key/procedure-value pairs
                                  % keyed to ascii values
        70{R 0 rlineto}        %F  
        48{}                   %0
        49{}                   %1  
        43{A rotate}           %+  
        45{A neg rotate}       %-  

          % For brackets, create a dictionary containing a single procedure '.' (dot)
          % which holds a saved matrix (orientation+size) and currentpoint.
          % Since this procedure is called while the Turtle dict is on top of the
          % dictstack, the temporary dictionary is placed just under the top.
        91{currentdict end[/.[currentpoint matrix currentmatrix]cvx>>begin begin} %[
          % Right bracket: reset position and orientation,
          % pop the dict just under the top.
        93{. setmatrix moveto currentdict end end begin}    %]  
    >>  
    /S{ % stack contains: string recursion-level
        dup B eq{ % hit recursion bound, interpret string as turtle commands
            T begin
                exch % level string
                %dup =
                {                      % iterate through string
                    load exec          % execute turtle command by integer code
                } forall % level       % string has been consumed
            end
            %(B)= pstack
        }{ % recurse
            %(A)= pstack
            exch % level string
            { % level char                   iterate through string
                load exch % string level   process production:load string by int code
                1 add S   % string level+1   increase level and call self
            } forall                       % string has been consumed
        }ifelse
        1 sub            % return level-1
        %(C)= pstack
    }
>>begin
moveto 0 S stroke

Para executá-lo, esses 3 blocos podem ser salvos como 3 arquivos: dragon.ps, stems.ps, lsys.ps (qualquer um dos blocos de programas acima funcionará de forma idêntica). Em seguida, execute com gs: gs dragon.ps lsys.psou gs stems.ps lsys.ps. Eles também podem ser concatenados primeiro, se desejado: cat dragon.ps lsys.ps | gs -ou cat stems.ps lsys.ps | gs -.

curva do dragão

Nenhuma imagem de hastes. Não fica mais interessante em profundidades mais altas.

luser droog
fonte
4

Mathematica 290

Essa implementação básica concentra-se na saída e não no processamento. Ele não usa regras de produção. Portanto, pode não ser uma resposta adequada ao desafio.

Hastes ramificadas adaptadas da demonstração de Theo Gray .

Código

f@{a_, b_} := {{a, #}, {b, #}} &[a + (b - a)/2 + {{0, 1/2}, {-1/2, 0}}.(b - a)]; w = Flatten;
h[s_, g_] :=Graphics[If[s == 0,Line /@ Nest[w[f /@ #, 1] &, {{{0, 0}, {1, 0}}}, g], 
 MapIndexed[Line@# &, NestList[w[Map[{{#[[2]], #[[2]] + m.(#[[2]] - #[[1]])}, {#[[2]], 
 #[[2]] + ({{1, -1}, {-1,1}} m).(#[[2]] - #[[1]])}} &, #], 1] &, {{{0, -1}, {0, 0}}}, g]]]]

Uso

O primeiro parâmetro determina se a Curva do Dragão ou Hastes de Ramificação serão exibidas. O segundo termo refere-se à geração.

h[0, 5]
h[1, 5]

segunda foto


Mais exemplos

GraphicsGrid@Partition[Flatten[Table[h[j, k], {j, 0, 1}, {k, 10}]], 5]

fractal3

DavidC
fonte
11
Muito bonito. Mas não economizaria alguns bytes para passar a regra como argumento?
Luser droog
Se essa fosse uma solução geral, talvez alguém pudesse passar uma regra em vez de parâmetros. Eu teria que ter mais conhecimento sobre a Lindenmayer Systems do que atualmente tenho.
DavidC
Eu não leio mathematica. Eu deveria ir aprender um pouco. (adicione-o à pilha :) Mas você pode interpretar que isso significa que "o que constitui a descrição da imagem, diferente do mecanismo que a move", pode ser levado em consideração. Se você pode modificar os dados para controlar algum recurso da imagem, independentemente de tocar no mecanismo adequado ; Eu consideraria isso "funcionalmente equivalente" a um sistema L. [ Isso deve lhe dar muitas brechas para se trabalhar;) ]. +1 de qualquer maneira, porque é tão bonito.
Luser droog
11
@dude Eu acho que é porque os requisitos gráficos não são adequados para eles
Dr. belisarius
11
Finalmente descobri o l-sistema para sua árvore: A->F[+A][-A]onde Fé movimento, +é rodar para a esquerda 30, -é certo girar 30, e [/ ]são push / pop
Shmiddty