AS-системи рендеринга ASCII


16

Фон

L-система (або система Лінденмайер) являє собою систему паралельних перезапису , що, крім іншого, може бути легко використана для модельних фракталів. Це питання стосується детермінованих, без контексту 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):

введіть тут опис зображення введіть тут опис зображення

Ви можете протестувати будь-яку із систем на цій сторінці .

Оцінка балів

Найкоротший код (у байтах) виграє. Діють стандартні правила.

Різне

Цей виклик був надихнутий Намалюйте випадкову прогулянку з косими рисами . Насправді, можна уявити випадкову прогулянку як L-систему, якщо розширити систему, щоб дозволити кілька правил на символ, при цьому розширення вибрано недетермістично під час переписування. Один з препаратів:

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

Ще одне поширене розширення, яке часто використовується при моделюванні рослин, - інтерпретувати символи [і] як штовхання та вискакування поточного положення та кута. Більшість рослин використовують кути, менші за 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')

Менше гольфу

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)


Чудова (і швидка!) Відповідь. Я додав скріншоти з результатів кривої дракона та гільберта.
Урі Гранта

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 для одного кроку): Я не зберігати шлях в матриці , але в іншому списку асоціації з (х, у) позиції в якості ключів і бітових комбінацій з 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`
`
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.