Procesador ASCII L-system

16

Antecedentes

Un sistema L (o sistema Lindenmayer) es un sistema de reescritura paralelo que, entre otras cosas, puede usarse fácilmente para modelar fractales. Esta pregunta se refiere a sistemas L deterministas, sin contexto . Estos consisten en un alfabeto de símbolos, una cadena de axioma inicial y un conjunto de reglas de reescritura que asignan cada símbolo del alfabeto a una nueva cadena. Las reglas se aplican al axioma en paralelo, generando una nueva cadena. Este proceso luego se repite.

Por ejemplo, el sistema con el axioma "A" y las reglas A = ABA; B = BBB genera la secuencia de cadenas "ABA", "ABABBBABA", "ABABBBABABBBBBBBBBABABBBABA", etc. Para ser concisos, no mencionamos explícitamente el alfabeto al definir el sistema L. Además, se supone que cualquier símbolo sin una regla de reescritura explícita no cambia (es decir, la regla predeterminada para un símbolo A es A = A).

Los sistemas L se pueden visualizar utilizando una forma de gráficos de tortuga. Por convención, la tortuga comienza a mirar hacia la derecha. Luego se dibuja una cadena iterando sobre sus símbolos: una F significa "avanzar una unidad", una G significa "avanzar una unidad", una + significa "girar a la izquierda una unidad de ángulo" y una - significa "girar a la derecha un ángulo unidad". Todos los demás símbolos en la cadena se ignoran. A los efectos de esta pregunta, se supone que las unidades de ángulo siempre son 90 °.

Tarea

Dada una especificación de cualquier sistema L y una serie de iteraciones, su programa debe generar una representación ASCII de la cadena resultante (como se describe anteriormente) utilizando caracteres de dibujo de recuadro.

  • Los parámetros se pasan como una cadena separada por espacios que comprende el axioma, las reglas de reescritura (como una lista de ecuaciones separadas) y el número de iteraciones de reescritura. Por ejemplo, la entrada "FF = FGF; G = GGG 2" genera la cadena "FGFGGGFGF" y, por lo tanto, dibuja cuatro líneas con espacios apropiados.
  • Los símbolos utilizados por el sistema L pueden ser cualquier carácter ASCII aparte del espacio y el punto y coma. Hay como máximo una regla explícita especificada por símbolo (con la regla de reescritura predeterminada que es la asignación de identidad como se describe anteriormente).
  • Puede suponer que la salida siempre contendrá al menos una F.
  • La salida debe usar los siguientes caracteres de dibujo de recuadro UNICODE para representar la visualización: ─ (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) y ╷ (U + 2577). Ver abajo para ejemplos.
  • La salida no debe contener líneas vacías sobre el carácter del cuadro superior o debajo del inferior. Tampoco debe contener espacios a la izquierda del caracterizador de cuadro más a la izquierda ni a la derecha del caracter de extremo derecho. Se permiten líneas con espacios finales que no se extiendan más allá del cuadro de la derecha.

Puede escribir un programa o función, tomando datos a través de STDIN (o la alternativa más cercana), argumento de línea de comandos o argumento de función. Los resultados deben imprimirse en STDOUT (o la alternativa más cercana), guardarse en un archivo o devolverse como una cadena.

Ejemplos

# 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"
    ┌┐
   ┌┘└┐
  ┌┘  └┐
 ┌┼┐  ┌┼┐
╶┘└┘  └┘└╴

Otros ejemplos para probar su programa incluyen:

# 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"

Los primeros dos de los cuales se ven de la siguiente manera (producido usando la respuesta de @ edc65):

ingrese la descripción de la imagen aquí ingrese la descripción de la imagen aquí

Puede probar cualquiera de los sistemas en esta página .

Puntuación

El código más corto (en bytes) gana. Aplican reglas estándar.

Miscelánea

Este desafío fue inspirado por Draw a Random Walk with Slashes . De hecho, es posible representar una caminata aleatoria como un sistema L si ampliamos el sistema para permitir múltiples reglas por símbolo, con la expansión elegida de manera no determinista durante la reescritura. Una formulación es:

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

Otra extensión común, utilizada a menudo al modelar plantas, es interpretar los caracteres [y] como empujando y haciendo estallar la posición y el ángulo actuales. La mayoría de las plantas usan ángulos menores de 90 °, pero aquí hay un ejemplo que no:

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

Ninguno de estos ejemplos necesita ser apoyado en este desafío.

Este desafío también es similar a "Lo siento, joven, ¡pero son las tortugas hasta el fondo!" . Sin embargo, ese desafío utilizó el renderizado de líneas en lugar de ASCII y permitió una sintaxis más flexible.

Uri Granta
fuente

Respuestas:

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 golf

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')
}

Prueba Fragmento de código de prueba (en Firefox)

edc65
fuente
Gran (y rápida) respuesta. He agregado capturas de pantalla de las salidas de la curva de dragón y hilbert a la pregunta.
Uri Granta
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

Prueba de funcionamiento:

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

Cómo funciona:

  • reescritura (función g): analizo las reglas en una lista de asociación (letra -> cadena de reemplazo) y la mapeo repetidamente sobre el axioma.
  • la creación de la trayectoria (función ude un solo paso): No almacenar la ruta en una matriz pero en otra lista de asociación con (x, y) posiciones que las teclas y los patrones de bit de los 4 bloques básicos ( , , y ) como valores . En el camino mantengo un seguimiento de la posición y dirección actuales.
  • dibujando la ruta (función f): primero calculo las dimensiones max / min de la lista de rutas y luego itero sobre [max y -> min y] y [min x -> max x] y busco los bloques para dibujar.
nimi
fuente
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`
`
usuario74131
fuente