Rekurencyjne spirale ASCII

21

Ta konkurencja się skończyła. Dzięki za interesujące wpisy nie esolang i gratulacje dla Jakuje za jego zwycięskie zgłoszenie JavaScript.

W wielkiej tradycji ASCII Art Challenges na tej stronie, oto kolejna. Biorąc pod uwagę wkład, narysuj spiralę.

&>----v
||>--v|
|||>v||
|||@|||
||^-<||
|^---<|
^-----<

Proste, tak? Heh, heh, heh ... Tak ...

(Zainspirowany postem ASCII Dragons Curve i postami Optymalizatora ASCII Art of the Day )

Wkład

Dane wejściowe będą miały postać szeregu parametrów, zaczerpniętych ze zwykłego argumentu STDIN / function / itd., Niezależnie od języka, który składa się z czterech części. Częściami tymi mogą być cztery oddzielne argumenty, poczwórny, tablica wielkości 4 itd. Dla uproszczenia i spójności podczas wyzwania przedstawię dane wejściowe jako pojedyncze słowo.

  • Liczba całkowita 2 ≤ x ≤ 20określająca rozmiar spirali w kategoriach „kwadratów”, przy czym każdy drukowany znak reprezentuje jeden „kwadrat”. Teoretycznie może to być ogromny zasięg, ale biorąc pod uwagę, że rysujemy grafikę ASCII, bezpiecznym górnym limitem będzie 20, aby pasowała dość przyzwoicie na ekranie.
  • Pojedyncza litera d u rlub l, wskazująca początkowy ruch od początkowego „kwadratu” (w dół, w górę, w prawo, w lewo).
  • Opcjonalny c, oznaczający „przeciwnie do ruchu wskazówek zegara”. Jeśli cpominięto, załóż, że spirala obraca się zgodnie z ruchem wskazówek zegara.
  • Końcowa liczba całkowita, 1 ≤ y ≤ 10która określa liczbę powtórzeń rysunku spirali, wykorzystując wykończenie „kwadratu” poprzedniej spirali jako „kwadrat” początkowy nowej. Wybieram górną granicę 10, ponieważ chcę, aby rysunek zakończył się w pewnym momencie.
  • Kilka przykładowych danych wejściowych: 20lc5 13d2 2rc1

Co ciekawe, należy pamiętać, że nieparzyste wartości dla wielkości wejściowej powodują, że @zawsze będzie to dokładny środek spirali, ale nawet wartości mogą mieć początkowe przesunięcie „kwadratowe” w dowolnym z czterech przekątnych kierunków, w zależności od kierunku początkowego podróżować. Może to skutkować ... interesującymi ... wzorami. Zobacz dwa parzyste przykłady poniżej.

Dane wejściowe, które nie są zgodne ze specyfikacją wejściową (np. 11q#s), Są niezdefiniowane i w pełni oczekuję, że program odpowiednio odczyta kod. :)

Wydajność

Dane wyjściowe to dane wyjściowe do wydruku w formacie ASCII za pośrednictwem STDOUT odpowiadającego językowi, z następującymi specyfikacjami:

  • Początkowy „kwadrat” (każdej rekurencji) musi być oznaczony znakiem at @.
  • Końcowy „kwadrat” musi być oznaczony znakiem ampersand &. W przypadku wielokrotnych rekurencji należy zaznaczyć tylko ostatni „kwadrat” &.
  • Rogi spiralnej ścieżki muszą „wskazywać” w kierunku jazdy, używając < > v ^.
  • Przesuw pionowy należy narysować za pomocą rur |.
  • Podróż w poziomie musi być rysowana za pomocą myślników -.
  • „Kwadraty”, które zostaną zastąpione późniejszymi rekurencjami, powinny wyświetlać najnowszy kierunek podróży. Spowoduje to, że „nowsze” rekurencje będą się nakładać na „starsze” rekurencje. Zobacz 4rc3przykład poniżej.
  • Końcowa nowa linia jest OK, początkowe spacje mogą być koniecznością i dlatego są dozwolone, ale końcowe spacje nie są dozwolone.
  • Nie zadokuję cię, jeśli użyjesz sekwencji ucieczki, aby narysować sztukę ASCII zmierzającą do STDOUT, ale będę cię cicho rozczarowany. (Nadal będziesz uprawniony do nagrody, jeśli z nich skorzystasz)

Przykłady

2d4 = średnica 2, zaczyna się od zejścia w dół, zgodnie z ruchem wskazówek zegara, 4 rekurencje

&@@@@
^<<<<

W tym przykładzie rysunek zaczyna się w prawym górnym rogu @, idzie w dół o jeden, w lewo, o jeden w górę. W tym momencie zakończyliśmy 2dczęść, więc zacznij drugą rekurencję, więc mamy następną @, niższą, lewą, wyższą; następnie 3. rekursja; potem czwarty i wreszcie nasz &.

4rc3 = średnica 4, zaczyna się w prawo, w lewo, 3 rekurencje

&--<
v-<|
|@^|<
>--^|
 |@^|<
 >--^|
  |@^|
  >--^

W tym przykładzie rysunek zaczyna się od dołu @, idzie w prawo, w górę, kręci się wokół, aż osiąga środek @i kończy 4rcczęść. To następnie powtarza się jeszcze dwa razy, aby uzyskać pełne 3 żądane rekurencje. Zauważ, że 4rc1byłby to tylko lewy górny blok 4x4 w tym przykładzie.

7u1 = średnica 7, zaczyna się od przejścia w górę, zgodnie z ruchem wskazówek zegara, 1 rekurencja (zwróć uwagę, że to samo co wprowadzenie)

&>----v
||>--v|
|||>v||
|||@|||
||^-<||
|^---<|
^-----<

Zwycięstwo i ograniczenia

To jest Code Golf, więc najmniejsza odpowiedź w bajtach wygrywa. Zgłoszenia powinny mieć zwykłą formę programu / funkcji / bloku kodu CJam / itp. Obowiązują standardowe ograniczenia dotyczące luk. Profesjonalny kierowca na zamkniętym kursie. Jeśli podrażnienie nie ustępuje, przerwij stosowanie i skonsultuj się z lekarzem.

AdmBorkBork
źródło
3
Specyfika są całkiem różne, ale tylko w celach informacyjnych, oto wcześniej spirala wyzwanie rysunek: codegolf.stackexchange.com/questions/52494/... .
Reto Koradi,
2
Niezłe wyzwanie. +1 za „Zawodowego kierowcę na zamkniętym kursie”
jrich
3
Prosi o odpowiedź> <>.
The_Basset_Hound
2
„No dalej, chłopaki ... pozwolicie wygrać Common Lisp? ;-)” To najbardziej zabawny powód, dla którego dostałem nagrodę. Dzięki
coredump
1
Siedzę tutaj, chichocząc, że Common Lisp i Lua to dwa języki walczące o pierwsze miejsce w kwestii golfa. :)
AdmBorkBork

Odpowiedzi:

6

JavaScript, 578, 575, 553, 478, 377 bajtów

Po pobiciu Lua zmieniłem język na bardziej zwarty i przeniosłem konkurencję na Javascript:

s=function(w,d,c,r){d="dlur".indexOf(d)
j=i=G=H=I=J=w*r;o=[];for(x=0;x<J*2;x++){o[x]=[]
for(y=0;y<J*2;)o[x][y++]=' '}for(;r--;){a=d;m=l=z=1;o[i][j]="@"
for(k=w*w-1;k--;){G=G<i?G:i;H=H>i?H:i;I=I<j?I:j;J=J>j?J:j
o[i+=(1-a)%2][j+=a?a-2:0]=l++==m?(a+=c=="c"?3:1,m+=z=!z,l=1,"v<^>"[a%=4]):k?"|-"[a%2]:"&"}}for(i=G;i<=H;)console.log(o[i++].slice(I,J+1).join("").replace(/\s+$/g,''))}

Algorytm jest taki sam, ale napisany w bardziej zwartym języku, więc udało mi się pokonać złe Lisp :)

Edycja: Potrzebne były pewne zmiany strukturalne, aby ponownie sięgnąć pod Lisp i wyeliminować końcowe białe spacje. Ale znów tu jesteśmy.

Edycja2: Niektóre abstrakcje wzięto pod uwagę, aby uzyskać mniej niż 500. Mam nadzieję, że to wystarczy :)

Edit3: Dzięki @Timwi, kod jest o 100 znaków cieńszy. Nie zaktualizowałem jeszcze wyjaśnienia.

Testy ( wersja online , testowana w Chrome):

----| 2d4 |---
s.js:9 &@@@@
s.js:9 ^<<<<
ss.html:7 ----| 4rc3 |---
s.js:9 &--<
s.js:9 v-<|
s.js:9 |@^|<
s.js:9 >--^|
s.js:9  |@^|<
s.js:9  >--^|
s.js:9   |@^|
s.js:9   >--^
ss.html:9 ----| 7u1 |---
s.js:9 &>----v
s.js:9 ||>--v|
s.js:9 |||>v||
s.js:9 |||@|||
s.js:9 ||^-<||
s.js:9 |^---<|
s.js:9 ^-----<
ss.html:11 ----| 8r3 |---
s.js:9       >------v
s.js:9       |>----v|
s.js:9       ||>--v||
s.js:9       |||@v|||
s.js:9    >------v|||
s.js:9    |>----v|<||
s.js:9    ||>--v||-<|
s.js:9    |||@v|||--<
s.js:9 >------v|||
s.js:9 |>----v|<||
s.js:9 ||>--v||-<|
s.js:9 |||@v|||--<
s.js:9 ||^-<|||
s.js:9 |^---<||
s.js:9 ^-----<|
s.js:9 &------<
ss.html:13 ----| 8rc3 |---
s.js:9 &------<
s.js:9 v-----<|
s.js:9 |v---<||
s.js:9 ||v-<|||
s.js:9 |||@^|||--<
s.js:9 ||>--^||-<|
s.js:9 |>----^|<||
s.js:9 >------^|||
s.js:9    |||@^|||--<
s.js:9    ||>--^||-<|
s.js:9    |>----^|<||
s.js:9    >------^|||
s.js:9       |||@^|||
s.js:9       ||>--^||
s.js:9       |>----^|
s.js:9       >------^

I szczerze mówiąc, istnieje uczciwe wyjaśnienie:

s = function(w, d, c, r) {
    // width, direction, "c" as counter-clockwise and number of repetition
    // transfer direction to internal numerical representation
    d=d=="d"?0:d=="u"?2:d=="l"?1:3;
    // output strings
    x="v<^>"
    y="|-"
    // this is size of our canvas. Could be smaller, but this is shorter
    M = w * r * 2;
    // fill canvas with spaces to have something to build upon
    o = [];
    for (i = 0; i < M; i++) {
        o[i] = [];
        for (j = 0; j < M; j++)
            o[i][j] = ' '
    }
    // i,j is starting position
    // G,H,I,J are current boundaries (maximum and minimum values of i and j during the time)
    j = i = G = H = I = J = M / 2
    for (q = 0; q < r; q++) { // number of repeats
        a = d; // reset original direction
        // m is the expected length of side
        // l counts the of current side length
        m = l = 1;
        z = 0; // counts occurrences of the length
        o[i][j] = "@" // write initial character
        for (k = w * w; k > 1; k--) { // cycle over the whole spiral
            // update boundaries
            G = G < i ? G : i;
            H = H > i ? H : i;
            I = I < j ? I : j;
            J = J > j ? J : j;
            // move to the next position according to direction
            i+=a<3?1-a:0;
            j+=a>0?a-2:0
            if (k == 2) // we reached the end
                o[i][j] = "&"
            else if (l == m) {
                // we reached the corner so we need to count next direction
                a=(c=="c"?a+3:a+1)%4;
                // and write according sign
                o[i][j]=x[a]
                // first occurrence of this length
                if (z == 0)
                    z = 1; // wait for finish of the other
                else {
                    m++; // increase length of side
                    z = 0 // wait again for the first one
                }
                l = 1 // start the side counter over
            } else {
                l++ // another part of this side
                // according side character
                o[i][j] = y[a%2]
            }
        }
    }
    // blow it all out
    for (i = G; i <= H; i++)
        console.log(o[i].slice(I, J + 1).join("").replace(/\s+$/g, ''))
}
Jakuje
źródło
Bardzo dobrze. Zgodnie z zasadami i podążając za twoim przykładem, postanowiłem usunąć &optionalsłowo kluczowe (i spację), aby zaoszczędzić 10 bajtów, co daje 576 ... zły śmiech (cóż, powiedziałeś, że możesz trochę zagrać w golfa, więc to nie powinno być trudne do pokonania; dopóki ktoś nie napisze 60-bajtowej odpowiedzi w Pyth, oczywiście).
coredump
@coredump Wyzwanie przyjęte :) Jest trudniejsze niż się spodziewałem, ale nadal jest możliwe :) Wierzę, że możesz to zrobić w pyth, ale nikt go nigdy nie zrozumie, więc wierzę, że złożoność jest ponad możliwościami takiego języka.
Jakuje
3
Jeśli i=M/2;j=i;G=i;H=i;I=i;J=i;i=j=G=H=I=J=M/2;m=1;l=1;m=l=1;
połączysz
2
To rozwiązanie jest dość sprytne. Znalazłem jednak kilka innych miejsc, w które można grać w golfa: 377 bajtów
Timwi
1
@Jakuje Wierzę, że intencją Timwi było wzięcie 377-bajtowej wersji i edytowanie odpowiedzi, aby z niej skorzystać. ;) (W przeciwnym razie opublikowałby osobną odpowiedź.)
Martin Ender,
7

Common Lisp, 649 617 605 586 576 565 554 527 518

(lambda(r v z c &aux(m 0)s(c(if c 1 -1))o p(x 0)i(y 0)j u)(#8=dotimes(_ z)(#7=setf p(aref"ruld"v)i(1-(abs(- p 2)))j(- 1(abs(1- p)))s'@)(#8#($(1- r))#5=(#7#m(min m x)o(cons`(,x,y,s)o)s(aref"-|-|"p)x(+ x i)y(+ y j))#2=(#7#s(*(- c)j)j(* i c)i s p(mod(+ p c)4)s(aref">^<v"p)u(#8#(_(1+ $))#5#))#2#))(#7#s'& u #5#o(#4=stable-sort(#4#o'< :key'car)'> :key'cadr)y(cadar o)x m)(dolist(k o)(do()((>=(cadr k)y))(#7#y(1- y)x m)(terpri))(do()((<=(car k)x))#9=(incf x)(princ" "))(and(=(cadr k)y)(=(car k)x)#9#(princ(caddr k)))))

Wszystkie testy wciąż przechodzą. Ulepszona funkcja również została zaktualizowana, aby odzwierciedlić zmiany, podobnie jak komentarze. W końcu pozbyłem się remove-duplicates, aby skrócić kod, ale teraz nie wiem, gdzie znaleźć więcej bajtów. Dobra robota Jakuje.

Przykłady

(funcall *fun* 8 #\r 3 nil)

      >------v
      |>----v|
      ||>--v||
      |||@v|||
   >------v|||
   |>----v|<||
   ||>--v||-<|
   |||@v|||--<
>------v|||
|>----v|<||
||>--v||-<|
|||@v|||--<
||^-<|||
|^---<||
^-----<|
&------<

(funcall *fun* 8 #\r 3 t) ;; counter-clockwise

&------<
v-----<|
|v---<||
||v-<|||
|||@^|||--<
||>--^||-<|
|>----^|<||
>------^|||
   |||@^|||--<
   ||>--^||-<|
   |>----^|<||
   >------^|||
      |||@^|||
      ||>--^||
      |>----^|
      >------^

(funcall *fun* 7 #\u 1 nil)

&>----v
||>--v|
|||>v||
|||@|||
||^-<||
|^---<|
^-----<

(funcall *fun* 2 #\d 4 nil)

&@@@@
^<<<<

Zobacz także 20lc10(pastebin).

Nie golfił

Nie ma tu żadnej rekurencji, tylko podstawowe podejście graficzne Turtle z pętlami:

  1. Narysuj spirale w pamięci, przechowując (x y char)potrójne na stosie.
  2. Elementy stabilnego sortowania według yix
  3. Iteruj po tej liście, unikając duplikatów (poprzednie ślady) i drukuj od lewego górnego do prawego dolnego rogu.
(lambda
    (r v z c
     &aux
       (m 0)       ; minimal x
       s           ; symbol to print (a character)
       (c          ; 1 if clockwise, -1 otherwise
        (if c
            1
            -1))
       o           ; stack of (x y char) traces
       p           ; position of s in ">^<v"
       i           ; move direction of x
       j           ; move direction of y
       (x 0)       ; position in x
       (y 0)       ; position in y
       u           ; auxiliary variable
       )
  ;; repeat for each recursive step
  (dotimes (_ z)
    ;; initialize spiral parameters
    (setf s '@            ; start spiral with @
          p (position v"ruld") ; initialize p according to input V

          ;; Set initial direction in I and J.
          i (1-(abs(- p 2))) ; i(0..3) = ( 1, 0, -1, 0 )
          j (- 1(abs(1- p))) ; j(0..3) = ( 0, 1, 0, -1 )

    ;; Iterate with increasing diameter $. For each iteration, draw a
    ;; "L"-shape that extends previous shape. Here is roughly what
    ;; happens at each step:
    ;;
    ;;   3334
    ;;   3124
    ;;   3224
    ;;   4444
    ;;
    (dotimes($(1- r))

      ;;
      ;; Assign the form to the reader variable #1# in order to
      ;; copy-paste later. This is like declaring a local function,
      ;; but shorter: push trace into list O and move forward.
      ;;
      #1=(setf m (min m x)
               o (cons `(,x ,y ,s) o)
               s (aref "-|-|" p)
               x (+ x i)
               y (+ y j))

      ;;
      ;; Helper function #2#: rotate and draw a line of length $.
      ;;

      #2=(setf u (* (- c) j) ; rotation as a vectorial                   
               j (* i c)     ; product of (i j 0) with (0 0 c).
               u i           ; At first I used PSETF, but now I reuse
                             ; the existing SETF with an auxiliary 
                             ; variable U to shorten the code and get
                             ; rid of PROGN. That's also why I affect
                             ; the result of DOTIMES to U (no need
                             ; for two forms and a PROGN, just SETF).

               p (mod(+ p c)4)   ; ">^<v" is sorted counter-clockwise, which 
               s (aref ">^<v" p) ; means that adding P and C (modulo 4) gives 
                                 ; the next symbol after rotation.

               ;; trace a line by repeatedly invoking code snippet #1#
               u (dotimes(_(1+ $)) #1#))
      ;; 
      ;; Rotate and draw a second line, hence drawing a "L"-shape.
      ;;
      #2#))

  ;; Finally, draw the final symbol &
  (setf s '&)
  #1#

  (setf o

        ;; From inside-out:
        ;;
        ;; - stable sort O according to X
        ;;   (from lowest (left) to highest (right))
        ;;
        ;; - stable sort the result according to Y
        ;;   (from highest (top) to lowest (bottom))
        ;;
        (stable-sort (stable-sort o '< :key 'car) '> :key 'cadr)

        ;; initialize Y with the first Y in O, which is also the
        ;; minimum of all Y.
        y (cadar o)

        ;; initialize X with the minimum of all X
        x m) 

  ;; For each trace in O
  (dolist (k o)

    ;; Add as many lines as needed to advance Y to current trace's Y.
    (do ()
      ((<= y (cadr k)))
      (setf y (1- y)
            x m)
      (terpri))

    ;; Add as many spaces as needed to advance X to current trace's X.
    (do () ((>= x (car k))) (incf x) (princ " "))

    ;; Then, print current trace's character and increment X.
    ;; This happens only when we have a "new" trace, not another
    ;; trace at the same point (which was being overwritten).
    (and(=(car k)x)(=(cadr k)y)(incf x)(princ(caddr k)))
rdzeń rdzeniowy
źródło
4

Lua 5.2, 740 bajtów

s=io.read W=io.write Z=math.max A=math.min
D="d"U="u"L="l"R="r"function n()G=A(G,i)H=Z(H,i)I=A(I,j)J=Z(J,j)i=(a==D and i+1 or a==U and i-1 or i)j=(a==R and j+1 or a==L and j-1 or j)end
w=s"*n"d=s(1)c=s(1)r=(c=="c")and s"*n"or c
c=c=="c"M=w*(r+1)o={}for i=1,M do o[i]={}for j=1,M do o[i][j]=" "end end
i=M/2 j=i G=i H=i I=i J=i
for q=1,r do a=d m=1 l=1 z=0
o[i][j]="@"for k=3,w^2 do
n()if l==m then
a=c and(a==D and R or a==U and L or a==L and D or a==R and U)or(a==D and L or a==U and R or a==L and U or a==R and D)o[i][j]=(a==D and"v"or a==U and"^"or a==L and"<"or a==R and">")
if z==0 then z=1 else m=m+1;z=0 end
l=1
else
l=l+1
o[i][j]=(a==D or a==U)and"|"or"-"end end
n()o[i][j]="&"end
for i=G,H do for j=I,J do
W(o[i][j])end W("\n")end

Pomyślałem, że fajnie byłoby spróbować zaimplementować jakiś algorytm, aby pokonać Lisp, ale Lua prawdopodobnie nie jest najlepszą opcją. Spędzam za dużo czasu, przerobiłem niektóre części, aby skończyć z tym brzydkim, ale działającym rozwiązaniem. Prawdopodobnie spróbuję później innego języka, aby pokonać Lisp, ponieważ istnieje około 90 znaków, których nie mogę usunąć z tego algorytmu.

Testowanie wyników:

jakuje@E6430:/tmp$ echo "2d4" | lua s.lua 
&@@@@
^<<<<
jakuje@E6430:/tmp$ echo "4rc3" | lua s.lua 
&--<  
v-<|  
|@^|< 
>--^| 
 |@^|<
 >--^|
  |@^|
  >--^
jakuje@E6430:/tmp$ echo "7u1" | lua s.lua 
&>----v
||>--v|
|||>v||
|||@|||
||^-<||
|^---<|
^-----<
Jakuje
źródło
2

PHP, 524 bajtów

Przyjechałem późno na to przyjęcie. Moje rozwiązanie PHP nie jest ani najmniejsze, ani najmądrzejsze. To po prostu działa.

$a=$argv;
$b=[['|','^',0,-1],['-','>',1,0],['|',v,0,1],['-','<',-1,$x=$y=$o=$p=$q=$r=0]];
for($t=$a[4];$t;$t--){$d=strpos(urdl,$a[2]);$c=$b[$d];$m[$y][$x]='@';
for($s=0;++$s<$a[1];){for($k=3;--$k;){for($i=$s;--$i;)
$m[$y+=$c[3]][$x+=$c[2]]=$c[0];$x+=$c[2];$y+=$c[3];$c=$b[$d=($d+($a[3]==c?3:1))%4];
$m[$y][$x]=$c[1];}$o=min($x,$o);$p=max($p,$x);$q=min($y,$q);$r=max($r,$y);}
for($i=$s;--$i;)$m[$y+=$c[3]][$x+=$c[2]]=$c[0];$m[$y][$x]='&';}
for($y=$q;$y<=$r;$y++){$l='';for($x=$o;$x<=$p;$x++)$l.=$m[$y][$x]?:' ';
echo rtrim($l)."\n";}

Jak to uruchomić:

$ php -d error_reporting=0 recursive-ascii-spirals.php 4 r c 3
&--<
v-<|
|@^|<
>--^|
 |@^|<
 >--^|
  |@^|
  >--^
$ php -d error_reporting=0 recursive-ascii-spirals.php 7 u '' 1
&>----v
||>--v|
|||>v||
|||@|||
||^-<||
|^---<|
^-----<

Szczegółową wersję z testami, objaśnieniami i innymi dodatkami można znaleźć na github .

aksjomat
źródło