Mechanizm renderujący ASCII L-system

16

tło

System L (lub system Lindenmayer) to równoległy system przepisywania, który między innymi można łatwo wykorzystać do modelowania fraktali. To pytanie dotyczy deterministyczne, kontekst wolna L-systemy . Składają się one z alfabetu symboli, początkowego ciągu aksjomatów i zestawu reguł przepisywania mapujących każdy symbol alfabetu na nowy ciąg. Reguły są stosowane do aksjomatu równolegle, generując nowy ciąg. Ten proces jest następnie powtarzany.

Na przykład system z aksjomatem „A” i regułami A = ABA; B = BBB generuje ciąg ciągów „ABA”, „ABABBBABA”, „ABABBBABABBBBBBBBABABBBABA” itp. Dla zwięzłości nie wspominamy wyraźnie o alfabet podczas definiowania systemu L. Ponadto zakłada się, że każdy symbol bez wyraźnej reguły przepisywania pozostaje niezmieniony (tj. Domyślna reguła dla symbolu A to A = A).

Systemy L można wizualizować za pomocą formy żółwia. Zgodnie z konwencją żółw zaczyna być skierowany w prawo. Ciąg jest następnie rysowany przez iterację po jego symbolach: F oznacza „pociągnij o jedną jednostkę do przodu”, G oznacza „przesuń o jedną jednostkę do przodu”, a + oznacza „skręć w lewo o jeden kąt”, a a - „skręć w prawo o jeden kąt” jednostka". Wszystkie pozostałe symbole w ciągu są ignorowane. Na potrzeby tego pytania przyjmuje się, że jednostki kąta zawsze wynoszą 90 °.

Zadanie

Biorąc pod uwagę specyfikację dowolnego systemu L i szereg iteracji, twój program powinien wypisać renderowanie ASCII wynikowego łańcucha (jak opisano powyżej) przy użyciu znaków rysujących w ramce.

  • Parametry są przekazywane jako ciąg oddzielony spacjami, zawierający aksjomat, reguły przepisywania (jako oddzielną listę równań) i liczbę powtórzeń przepisywania. Na przykład, wejście „FF = FGF; G = GGG 2” generuje ciąg „FGFGGGFGF”, a zatem rysuje cztery linie z odpowiednimi przerwami.
  • Symbole używane przez system L mogą być dowolnymi znakami ASCII oprócz spacji i średników. Istnieje co najwyżej jedna wyraźna reguła dla każdego symbolu (domyślną regułą przepisywania jest odwzorowanie tożsamości, jak opisano powyżej).
  • Możesz założyć, że wyjście zawsze będzie zawierało co najmniej jeden F.
  • Dane wyjściowe powinny wykorzystywać następujące znaki rysunkowe UNICODE do przedstawienia wizualizacji: ─ (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) i ╷ (U + 2577). Przykłady poniżej.
  • Dane wyjściowe nie powinny zawierać pustych wierszy powyżej znaku najwyższego pola lub poniżej najniższego znaku. Nie powinien także zawierać spacji po lewej stronie znaku skrajnego lewego pola lub po prawej stronie skrajnego prawego znaku. Dozwolone są linie ze spacjami końcowymi, które nie wykraczają poza prawą ramkę.

Możesz napisać program lub funkcję, przyjmując dane wejściowe przez STDIN (lub najbliższą alternatywę), argument wiersza poleceń lub argument funkcji. Wyniki powinny zostać wydrukowane do STDOUT (lub najbliższej alternatywy), zapisane w pliku lub zwrócone jako ciąg.

Przykłady

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

Inne przykłady testowania programu obejmują:

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

Pierwsze dwa z nich wyglądają następująco (wyprodukowane przy użyciu odpowiedzi @ edc65):

wprowadź opis zdjęcia tutaj wprowadź opis zdjęcia tutaj

Na tej stronie możesz przetestować dowolny system .

Punktacja

Najkrótszy kod (w bajtach) wygrywa. Obowiązują standardowe zasady.

Miscellania

Wyzwanie to zostało zainspirowane Draw a Random Walk with Slashes . W rzeczywistości możliwe jest przedstawienie losowego przejścia jako system L, jeśli rozszerzymy system, aby umożliwić stosowanie wielu reguł na symbol, z rozszerzeniem wybieranym nie deterministycznie podczas przepisywania. Jedna formuła to:

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

Innym powszechnym rozszerzeniem, często używanym podczas modelowania roślin, jest interpretowanie znaków [i] jako popychanie i wyskakiwanie bieżącej pozycji i kąta. Większość roślin używa kątów mniejszych niż 90 °, ale oto jeden przykład, który nie:

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

Żaden z tych przykładów nie wymaga wsparcia w tym wyzwaniu.

To wyzwanie jest również podobne do „Przepraszam młody człowieku, ale to żółwie aż do samego końca!” . Jednak wyzwanie to wykorzystywało renderowanie linii zamiast ASCII i pozwoliło na bardziej elastyczną składnię.

Uri Granta
źródło

Odpowiedzi:

7

JavaScript (ES6), 440 bajtów (410 znaków)

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

Mniej golfa

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

Test Fragment kodu do testu (Firefox)

edc65
źródło
Świetna (i szybka!) Odpowiedź. Do pytania dodałem zrzuty ekranu przedstawiające wyniki krzywej smoka i Hilberta.
Uri Granta,
6

Haskell, 568 bajtów

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

Testowe uruchomienie:

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

Jak to działa:

  • przepisywanie (funkcja g ): Analizuję reguły w listę asocjacji (litera -> ciąg zastępujący) i wielokrotnie mapuję ją na aksjomat.
  • tworząc ścieżkę (funkcja uw jednym etapie): nie przechowywać ścieżkę w matrycy, ale w innej listy asocjacji z (x, y) pozycjach, ponieważ klucze i wzorami bitowymi z 4 bloków podstawowych ( , , i ), a wartości . Po drodze śledzę aktualną pozycję i kierunek.
  • rysowanie ścieżki (funkcja f): najpierw obliczam maksymalne / minimalne wymiary z listy ścieżek, a następnie iteruję [max y -> min y] i [min x -> max x] i szukam bloków do narysowania.
nimi
źródło
0

ES7, 394 znaki, 424 bajty

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`
`
użytkownik74131
źródło