ASCII L系统渲染器


16

背景

一个L-系统(或系统Lindenmayer)是平行重写系统,除其他外,可以很容易地用于模型分形。这个问题涉及确定性,上下文无关的L系统。它们由符号字母,初始公理字符串和将每个字母符号映射到新字符串的一组重写规则组成。规则并行应用于公理,生成一个新字符串。然后重复此过程。

例如,具有公理“ A”和规则A = ABA; B = BBB的系统生成字符串“ ABA”,“ ABABBBABA”,“ ABABBBABABBBBBBBBBBBBBABABBBABA”等字符串的序列。为简洁起见,我们没有明确提及定义L系统时的字母。此外,假定没有显式重写规则的任何符号都是不变的(即,符号A的默认规则为A = A)。

L系统可以使用乌龟图形的形式进行可视化。按照惯例,乌龟开始朝右。然后,通过重复其符号来绘制字符串:F表示“向前拉一个单位”,G表示“向前拉一个单位”,+表示“向左旋转一个角度单位”,而-表示“向右旋转一个角度单位”。单元”。字符串中的所有其他符号将被忽略。出于这个问题的目的,假定角度单位始终为90°。

任务

给定任何L系统的规范和多次迭代后,您的程序应使用箱形绘图字符输出结果字符串的ASCII渲染(如上所述)。

  • 参数以空格分隔的字符串形式传递,该字符串包括公理,重写规则(作为等式的列表)和重写迭代次数。例如,输入“ FF = FGF; G = GGG 2”生成字符串“ FGFGGGFGF”,因此以适当的间距绘制四行。
  • L系统使用的符号可以是除空格和分号之外的任何ASCII字符。每个符号最多指定一个显式规则(默认重写规则是如上所述的身份映射)。
  • 您可以假定输出将始终包含至少一个F。
  • 输出应使用以下UNICODE框形图字符表示可视化效果:─(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)和╷(U + 2577)。请参见下面的示例。
  • 输出中不应在最上面的方框字符上方或最下面的方框字符下方包含空行。它也不应在最左边的框字符的左侧或最右边的框字符的右侧包含任何空格。允许行尾空格不超过最右边的方框字符的行。

您可以编写程序或函数,通过STDIN(或最接近的替代方案),命令行参数或函数参数获取输入。结果应打印到STDOUT(或最接近的替代品),保存到文件或作为字符串返回。

例子

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

其他测试程序的示例包括:

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

前两个如下所示(使用@ edc65的答案生成):

在此处输入图片说明 在此处输入图片说明

您可以在此页面上测试任何系统。

计分

最短的代码(以字节为单位)获胜。适用标准规则。

杂记

此挑战的灵感来自“ 用斜线绘制随机游走”。实际上,如果我们扩展系统以允许每个符号有多个规则,并且可以在重写期间不确定地选择扩展,则可以将随机游动表示为L系统。一种公式是:

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

在对植物进行建模时经常使用的另一个常见扩展是将[和]字符解释为推动和弹出当前位置和角度。大多数植物使用小于90°的角度,但这是一个不这样的示例:

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

在这个挑战中,这些例子都不需要得到支持。

这个挑战也类似于“对不起,年轻人,但是乌龟一直降下来!” 。但是,该挑战使用行渲染而不是ASCII,并允许使用更灵活的语法。

Answers:


7

JavaScript(ES6),440字节(410个字符)

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

少打高尔夫球

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

测试代码段进行测试(在Firefox中)


好极了(而且很快!)。我已经将Dragon和hilbert曲线输出的屏幕截图添加到了问题中。
乌里·格兰塔

6

Haskell,568个字节

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

测试运行:

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

怎么运行的:

  • 重写(功能 g):我将规则解析为关联列表(字母->替换字符串),并在公理上反复映射。
  • 创建路径(函数u用于单一步骤):我不路径存储在一个矩阵,而是与(X,Y)的位置作为4个基本块(的键和位模式另一个关联列表)作为值。一路上,我会跟踪当前的位置和方向。
  • 绘制路径(函数f):首先,我从路径列表中计算最大/最小尺寸,然后迭代[max y-> min y]和[min x-> max x]并查找要绘制的块。

0

ES7,394个字符,424个字节

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`
`
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.