Renderizador de sistema ASCII L

16

fundo

Um sistema L (ou sistema Lindenmayer) é um sistema de reescrita paralelo que, entre outras coisas, pode ser facilmente usado para modelar fractais. Esta questão diz respeito a sistemas L determinísticos e livres de contexto . Eles consistem em um alfabeto de símbolos, uma sequência de axiomas inicial e um conjunto de regras de reescrita que mapeiam cada símbolo do alfabeto para uma nova sequência. As regras são aplicadas ao axioma em paralelo, gerando uma nova sequência. Este processo é então repetido.

Por exemplo, o sistema com o axioma "A" e as regras A = ABA; B = BBB gera a sequência de cadeias "ABA", "ABABBBABA", "ABABBBABABBBBBBBBBABABBBABA", etc. Por questões de concisão, não mencionamos explicitamente o alfabeto ao definir o sistema L. Além disso, qualquer símbolo sem uma regra de reescrita explícita é assumido como inalterado (ou seja, a regra padrão para um símbolo A é A = A).

Os sistemas L podem ser visualizados usando uma forma de gráficos de tartarugas. Por convenção, a tartaruga começa a ficar voltada para a direita. Uma corda é então desenhada iterando sobre seus símbolos: um F significa "avançar uma unidade", um G significa "avançar uma unidade", um + significa "virar à esquerda uma unidade de ângulo" e a - significa "virar à direita um ângulo unidade". Todos os outros símbolos da string são ignorados. Para o propósito desta questão, assume-se que as unidades angulares sejam sempre 90 °.

Tarefa

Dada uma especificação de qualquer sistema L e várias iterações, seu programa deve gerar uma renderização ASCII da string resultante (conforme descrito acima) usando caracteres de desenho de caixa.

  • Os parâmetros são passados ​​como uma sequência separada por espaço, que compreende o axioma, regras de reescrita (como uma lista separada de equações) e número de iterações de reescrita. Por exemplo, a entrada "FF = FGF; G = GGG 2" gera a sequência "FGFGGGFGF" e, portanto, desenha quatro linhas com intervalos apropriados.
  • Os símbolos usados ​​pelo sistema L podem ser qualquer caractere ASCII além do espaço e ponto e vírgula. Há no máximo uma regra explícita especificada por símbolo (com a regra de reescrita padrão sendo o mapeamento de identidade conforme descrito acima).
  • Você pode assumir que a saída sempre conterá pelo menos um F.
  • A saída deve usar os seguintes caracteres de desenho de caixa UNICODE para representar a visualização: ─ (U + 2500), │ (U + 2502), ┌ (U + 250C), ┐ (U + 2510), └ (U + 2514) , ┘ (U + 2518), ├ (U + 251C), ┤ (U + 2524), ┬ (U + 252C), ┴ (U + 2534), ┼ (U + 253C), ╴ (U + 2574), ╵ (U + 2575), ╶ (U + 2576) e ╷ (U + 2577). Veja abaixo exemplos.
  • A saída não deve conter linhas vazias acima do caractere da caixa superior ou abaixo do caractere inferior. Também não deve conter espaços à esquerda do traçador da caixa mais à esquerda ou à direita do traço à direita. Linhas com espaços à direita que não ultrapassam o caractere da caixa mais à direita são permitidas.

Você pode escrever um programa ou função, recebendo informações via STDIN (ou alternativa mais próxima), argumento de linha de comando ou argumento de função. Os resultados devem ser impressos em STDOUT (ou alternativa mais próxima), salvos em um arquivo ou retornados como uma sequência.

Exemplos

# Cantor dust
>> "F F=FGF;G=GGG 0"
╶╴
>> "F F=FGF;G=GGG 1"
╶╴╶╴
>> "F F=FGF;G=GGG 2"
╶╴╶╴  ╶╴╶╴
>> "F F=FGF;G=GGG 3"
╶╴╶╴  ╶╴╶╴        ╶╴╶╴  ╶╴╶╴

# Koch curve
>> "F F=F+F−F−F+F 1"
 ┌┐
╶┘└╴
>> "F F=F+F-F-F+F 2"
    ┌┐
   ┌┘└┐
  ┌┘  └┐
 ┌┼┐  ┌┼┐
╶┘└┘  └┘└╴

Outros exemplos para testar seu programa incluem:

# Dragon curve
>> "FX X=X+YF+;Y=-FX-Y n"

# Hilbert curve
>> "A A=-BF+AFA+FB-;B=+AF-BFB-FA+ n"

# Sierpinski carpet
>> "F F=F+F-F-F-G+F+F+F-F;G=GGG n"

Os dois primeiros têm a seguinte aparência (produzida usando a resposta de @ edc65):

insira a descrição da imagem aqui insira a descrição da imagem aqui

Você pode testar qualquer um dos sistemas nesta página .

Pontuação

O código mais curto (em bytes) vence. Aplicam-se regras padrão.

Miscellania

Esse desafio foi inspirado no Draw a Random Walk with Slashes . De fato, é possível representar um passeio aleatório como um sistema L se estendermos o sistema para permitir várias regras por símbolo, com a expansão escolhida de maneira não-determinística durante a reescrita. Uma formulação é:

"F F=FF;F=F+F;F=F++F;F=F+++F"

Outra extensão comum, geralmente usada na modelagem de plantas, é interpretar os caracteres [e] como empurrar e estourar a posição e o ângulo atuais. A maioria das plantas usa ângulos menores que 90 °, mas aqui está um exemplo que não:

"FAX X=[-FAX][FAX][+FAX];A=AFB;B=A"

Nenhum desses exemplos precisa ser apoiado neste desafio.

Esse desafio também é semelhante a "Desculpe, jovem, mas são tartarugas até o fim!" . No entanto, esse desafio usou a renderização de linha em vez do ASCII e permitiu uma sintaxe mais flexível.

Uri Granta
fonte

Respostas:

7

JavaScript (ES6), 440 bytes (410 caracteres)

F=p=>([a,r,n]=p.split(' '),t=>{r.split(';').map(x=>r[x[0]]=x.slice(2),r={});for(;n--;)a=[...a].map(c=>r[c]||c).join('');u=x=y=0,g=[];for(c of a)c=='+'?[t,u]=[u,-t]:c=='-'?[u,t]=[t,-u]:c<'F'|c>'G'?0:((y+=u)<0?(g=[[],...g],++y):g[y]=g[y]||[],(x+=t)<0?(g=g.map(r=>[,...r]),++x):0,c>'F'?0:g[g[f=t?0.5:2,y][x]|=(3+t-u)*f,y-u][x-t]|=(3+u-t)*f)})(1)||g.map(r=>[for(c of r)' ╶╴─╵└┘┴╷┌┐┬│├┤┼'[~~c]].join('')).join('\n')

Menos golfe

F=p=>{
  [a,r,n]=p.split(' '),
  r.split(';').map(x=>r[x[0]]=x.slice(2),r={}); // set rules
  for(;n--;)a=[...a].map(c=>r[c]||c).join(''); // build string
  t=1,u=x=y=0, // start pos 0,0 start direction 1,0
  g=[[]]; // rendering in bitmap g
  for(c of a)
    c=='+'?[t,u]=[u,-t] // left turn
    :c=='-'?[u,t]=[t,-u] // right turn
    :c=='F'|c=='G'?(     // move or draw
      (y+=u)<0?(g=[[],...g],++y):g[y]=g[y]||[], // move vertical, enlarge grid if needed
      (x+=t)<0?(g=g.map(r=>[,...r]),++x):0, // move horizontal, enlarge grid if needed
      c=='F'&&( // draw: set bits
        f=t?0.5:2,
        g[y][x]|=(3+t-u)*f,
        g[y-u][x-t]|=(3+u-t)*f
      )
    ):0;
  // render bits as box characters
  return g.map(r=>[' ╶╴─╵└┘┴╷┌┐┬│├┤┼'[~~c]for(c of r)].join('')).join('\n')
}

Teste Código trecho de teste (no Firefox)

edc65
fonte
Ótima (e rápida!) Resposta. Adicionei capturas de tela dos resultados da curva do dragão e de hilbert à pergunta.
precisa saber é o seguinte
6

Haskell, 568 bytes

import Data.List.Split
p=splitOn
l=lookup
m=maximum
n=minimum
o[h,x]=(h,x)
Just x#_=x
_#x=x
g[s,r,i]=iterate((\c->lookup[c](map(o.p"=")(p";"r))#[c])=<<)s!!read i
u v@(a,x,y,d,e)c|c=='+'=(a,x,y,-e,d)|c=='-'=(a,x,y,e,-d)|c=='G'=(a,x+d,y+e,d,e)|c=='F'=(s(x,y)(d%e)a:s(x+d,y+e)(d?e)a:a,x+d,y+e,d,e)|1<2=v
s p n a=(p,n+(l p a)#0)
1%0=2;0%1=8;-1%0=1;0%(-1)=4
1?0=1;0?1=4;-1?0=2;0?(-1)=8
f z=unlines[[" ╴╶─╷┐┌┬╵┘└┴│┤├┼"!!(l(x,y)q#0)|x<-[n a..m a]]|y<-[m b,m b-1..n b]]where a=map(fst.fst)q;b=map(snd.fst)q;(q,_,_,_,_)=foldl u([],0,0,1,0)$g$p" "z

Execução de teste:

*Main> putStr $ f "F F=F-F+F+F-F 3"
╶┐┌┐  ┌┐┌┐        ┌┐┌┐  ┌┐┌╴
 └┼┘  └┼┼┘        └┼┼┘  └┼┘
  └┐  ┌┼┼┐        ┌┼┼┐  ┌┘
   └┐┌┼┘└┘        └┘└┼┐┌┘
    └┼┘              └┼┘   
     └┐              ┌┘
      └┐┌┐        ┌┐┌┘
       └┼┘        └┼┘
        └┐        ┌┘
         └┐┌┐  ┌┐┌┘
          └┼┘  └┼┘
           └┐  ┌┘
            └┐┌┘
             └┘

Como funciona:

  • reescrita (função g): analiso as regras em uma lista de associação (letra -> sequência de substituição) e mapeio-a repetidamente sobre o axioma.
  • criando o caminho (função upara um único passo): Não armazenar o caminho em uma matriz, mas em outra lista associação com (x, y) as posições como as chaves e os padrões de bits dos 4 blocos básicos ( , , e ) na forma de valores . Ao longo do caminho, acompanho a posição e a direção atuais.
  • desenhando o caminho (função f): primeiro eu calculo as dimensões max / min da lista de caminhos e depois itero sobre [max y -> min y] e [min x -> max x] e procuro os blocos a serem desenhados.
nimi
fonte
0

ES7, 394 caracteres, 424 bytes

F=p=>([a,r,n]=p.split` `,t=>{r.split`;`.map(x=>r[x[0]]=x.slice(2),r={});for(;n--;)a=[...a].map(c=>r[c]||c).join``;u=x=y=0,g=[];for(c of a)c=='+'||c=='-'?[t,u]=[u,-t]:c<'F'|c>'G'?0:((y+=u)<0?(g=[[],...g],++y):g[y]=g[y]||[],(x+=t)<0?(g=g.map(r=>[,...r]),++x):0,c>'F'?0:g[g[f=t?0.5:2,y][x]|=(3+t-u)*f,y-u][x-t]|=(3+u-t)*f)})(1)||g.map(r=>[for(c of r)'╶╴─╵└┘┴╷┌┐┬│├┤┼'[~~c]].join``).join`
`
user74131
fonte