ASCII L-system renderer


16

พื้นหลัง

L-ระบบ (หรือระบบ Lindenmayer) เป็นระบบการเขียนแบบขนานที่เหนือสิ่งอื่น ๆ ที่สามารถใช้งานได้ง่ายเพื่อ fractals รุ่น คำถามนี้เกี่ยวข้องกับการกำหนดบริบทฟรี L-ระบบ สิ่งเหล่านี้ประกอบด้วยตัวอักษรของสัญลักษณ์สตริงสัจพจน์เริ่มต้นและชุดของกฎการเขียนใหม่การแมปสัญลักษณ์ตัวอักษรแต่ละตัวกับสายอักขระใหม่ กฎถูกนำไปใช้กับสัจพจน์ในแบบคู่ขนานสร้างสตริงใหม่ กระบวนการนี้ซ้ำแล้วซ้ำอีก

ตัวอย่างเช่นระบบที่มีสัจพจน์ "A" และกฎ A = ABA; B = BBB สร้างลำดับของสตริง "ABA", "ABABBBABA", "ABABBBABABBBBBBBBBABABBBABA" อย่างชัดเจนเราไม่ได้กล่าวถึง ตัวอักษรเมื่อกำหนดระบบ L นอกจากนี้สัญลักษณ์ใด ๆ ที่ไม่มีกฎการเขียนที่ชัดเจนจะถือว่าไม่มีการเปลี่ยนแปลง (เช่นกฎเริ่มต้นสำหรับสัญลักษณ์ A คือ A = A)

ระบบ L สามารถมองเห็นได้โดยใช้รูปแบบของกราฟิกเต่า โดยการประชุมเต่าเริ่มหันหน้าไปทางขวา สตริงจะถูกดึงโดยการวนซ้ำสัญลักษณ์: F หมายถึง "ดึงไปข้างหน้าหนึ่งหน่วย", G หมายถึง "เคลื่อนที่ไปข้างหน้าหนึ่งยูนิต", + หมายถึง "เลี้ยวซ้ายหนึ่งมุมหน่วย" และ a - หมายถึง "เลี้ยวขวาหนึ่งมุม หน่วย" สัญลักษณ์อื่นทั้งหมดในสตริงจะถูกละเว้น สำหรับจุดประสงค์ของคำถามนี้หน่วยมุมจะถือว่าเป็น 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):

ป้อนคำอธิบายรูปภาพที่นี่ ป้อนคำอธิบายรูปภาพที่นี่

คุณสามารถทดสอบระบบใด ๆ ในหน้านี้

เกณฑ์การให้คะแนน

รหัสที่สั้นที่สุด (เป็นไบต์) ชนะ ใช้กฎมาตรฐาน

Miscellania

ความท้าทายนี้เป็นแรงบันดาลใจวาดเดินสุ่มกับ Slashes ในความเป็นจริงมันเป็นไปได้ที่จะเป็นตัวแทนของการเดินสุ่มเป็น L-ระบบถ้าเราขยายระบบเพื่อให้กฎหลายต่อสัญลักษณ์มีการขยายตัวที่เลือกไม่ใช่ determisticallyในระหว่างการเขียนใหม่ สูตรหนึ่งคือ:

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

ส่วนขยายที่ใช้ร่วมกันอื่นซึ่งมักใช้เมื่อสร้างแบบจำลองพืชคือการตีความอักขระ [และ] เป็นการกดและ popping ตำแหน่งและมุมปัจจุบัน พืชส่วนใหญ่ใช้มุมที่เล็กกว่า 90 ° แต่นี่คือตัวอย่างหนึ่งที่ไม่:

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

ตัวอย่างเหล่านี้ไม่จำเป็นต้องได้รับการสนับสนุนในการท้าทายนี้

ความท้าทายนี้ก็คล้ายกับ"ขอโทษที่ชายหนุ่ม แต่เต่าลงมา!" . อย่างไรก็ตามความท้าทายนั้นใช้การเรนเดอร์บรรทัดมากกว่า ASCII และอนุญาตให้ใช้ไวยากรณ์ที่ยืดหยุ่นมากขึ้น

คำตอบ:


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

น้อย golfed

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)


คำตอบที่ดี (และรวดเร็ว!) ฉันได้เพิ่มสกรีนช็อตของมังกรและฮิลแบร์ตกราฟท์เอาท์พุทให้กับคำถาม
Uri Granta

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 chars, 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`
`
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.