Rekursive ASCII-Spiralen

21

Dieser Wettbewerb ist vorbei. Vielen Dank für die interessanten Nicht-Esolang-Einträge und herzlichen Glückwunsch an Jakuje für seine erfolgreiche JavaScript-Einsendung.

In der großen Tradition der ASCII Art Challenges auf dieser Site finden Sie hier eine weitere. Zeichnen Sie bei einer Eingabe eine Spirale.

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

Einfach, ja? Heh, heh, heh ... Ja ...

(Inspiriert vom ASCII Dragons Curve- Post und den ASCII Art of the Day- Posts des Optimizers )

Eingang

Die Eingabe erfolgt in Form einer Reihe von Parametern, die aus dem üblichen STDIN / Funktionsargument / usw. Stammen und aus vier Teilen bestehen. Diese Teile können aus vier separaten Argumenten, einem Vierfachen, einem Array der Größe 4 usw. bestehen. Der Einfachheit und Konsistenz halber werde ich die Eingabe als ein einzelnes Wort darstellen.

  • Eine Ganzzahl 2 ≤ x ≤ 20, die die Größe der Spirale in Form von "Quadraten" angibt, wobei jedes gedruckte Zeichen ein "Quadrat" darstellt. Theoretisch könnte dies einen enormen Umfang haben, aber angesichts der Tatsache, dass wir ASCII-Kunst zeichnen, wird eine sichere Obergrenze von 20 festgelegt, damit sie auf den Bildschirm passt.
  • Ein einzelner Buchstabe von d u roder l, der die anfängliche Bewegung vom Startquadrat (abwärts, aufwärts, rechts, links) anzeigt.
  • Ein optionales Symbol c, das "gegen den Uhrzeigersinn" anzeigt. Wenn das cweggelassen wird, nehmen Sie eine Drehung der Spirale im Uhrzeigersinn an.
  • Eine letzte Ganzzahl 1 ≤ y ≤ 10, die angibt, wie oft die Spiralzeichnung wiederholt werden soll, wobei das Endquadrat der vorherigen Spirale als Startquadrat des neuen verwendet wird. Ich wähle eine Obergrenze von 10, weil ich möchte, dass die Zeichnung irgendwann fertig wird.
  • Einige Beispieleingaben: 20lc5 13d2 2rc1

Es ist von Interesse, dass ungerade Werte für die Größeneingabe dazu führen, dass @immer der exakte Mittelpunkt einer Spirale ist. Gerade Werte können jedoch in Abhängigkeit von der Anfangsrichtung in jeder der vier diagonalen Richtungen den Startquadratversatz aufweisen Reise. Dies kann zu einigen ... interessanten ... Mustern führen. Siehe die zwei geraden Beispiele unten.

Eingaben, die nicht der Eingabespezifikation entsprechen (z. B. 11q#s), sind undefiniert, und ich erwarte, dass das Programm entsprechend gesperrt wird. :)

Ausgabe

Bei der Ausgabe handelt es sich um eine druckbare ASCII-Ausgabe über sprachäquivalentes STDOUT mit den folgenden Spezifikationen:

  • Das Startquadrat (jeder Rekursion) muss mit einem At-Zeichen gekennzeichnet sein @.
  • Das letzte "Quadrat" muss mit einem kaufmännischen Und markiert werden &. Bei mehreren Rekursionen sollte nur das letzte "Quadrat" markiert werden &.
  • Ecken der Spiralbahn müssen mit in Fahrtrichtung "zeigen" < > v ^.
  • Die vertikale Bewegung muss durch Rohre erfolgen |.
  • Die horizontale Bewegung muss mit Bindestrichen gezeichnet werden -.
  • "Quadrate", die durch spätere Rekursionen überschrieben werden, sollten die aktuellste Fahrtrichtung anzeigen. Dies führt dazu, dass "neuere" Rekursionen scheinbar über die "älteren" Rekursionen gelegt werden. Siehe das folgende 4rc3Beispiel.
  • Ein abschließender Zeilenumbruch ist in Ordnung, führende Leerzeichen können ein Muss sein und sind daher zulässig, abschließende Leerzeichen sind jedoch nicht zulässig.
  • Ich werde Sie nicht andocken, wenn Sie Escapesequenzen verwenden, um die ASCII-Grafik für STDOUT zu zeichnen, aber ich werde stumm von Ihnen enttäuscht sein. (Sie sind weiterhin berechtigt, die Prämie zu erhalten, wenn Sie sie verwenden.)

Beispiele

2d4 = Durchmesser von 2, beginnt im Uhrzeigersinn, 4 Rekursionen

&@@@@
^<<<<

In diesem Beispiel beginnt die Zeichnung oben rechts @, geht eine nach unten, eine nach links und eine nach oben. An diesem Punkt haben wir den 2dTeil beendet und beginnen die 2. Rekursion, so dass wir einen weiteren haben, einen nach @unten, einen nach links, einen nach oben; dann die 3. Rekursion; dann der 4. und endlich unser &.

4rc3 = Durchmesser von 4, rechts beginnend, gegen den Uhrzeigersinn, 3 Rekursionen

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

In diesem Beispiel beginnt die Zeichnung unten @, verläuft eine nach oben und spiralförmig nach rechts, bis sie die Mitte erreicht @und den 4rcAbschnitt beendet. Dies wird dann noch zweimal wiederholt, um die vollen 3 angeforderten Rekursionen zu erhalten. Beachten Sie, dass 4rc1dies nur der obere linke 4x4-Block in diesem Beispiel ist.

7u1 = Durchmesser von 7, beginnt im Uhrzeigersinn, 1 Rekursion (beachten Sie, dass dies dasselbe ist wie das Intro)

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

Gewinnen & Einschränkungen

Das ist Code Golf, also gewinnt die kleinste Antwort in Bytes. Die Einreichung sollte in der üblichen Form von Programm / Funktion / CJam Code Block / etc. Erfolgen. Es gelten die üblichen Lückenbeschränkungen. Berufskraftfahrer auf geschlossenem Kurs. Wenn die Reizung anhält, stellen Sie die Anwendung ein und konsultieren Sie Ihren Arzt.

AdmBorkBork
quelle
3
Die Besonderheiten sind sehr unterschiedlich, aber nur als Referenz, hier ist eine frühere Herausforderung beim Spiralzeichnen : codegolf.stackexchange.com/questions/52494/… .
Reto Koradi
2
Schöne Herausforderung. +1 für "Berufskraftfahrer auf geschlossenem Kurs"
jrich
3
Es fragt nach einer> <> Antwort.
The_Basset_Hound
2
"Komm schon, Leute ... wirst du Common Lisp gewinnen lassen? ;-)" Das ist der lustigste Grund für ein Kopfgeld, das ich je gesehen habe. Vielen Dank
coredump
1
Ich sitze hier und kichere, dass Common Lisp und Lua die beiden Sprachen sind, die bei einer Code-Golf-Frage um den ersten Platz kämpfen. :)
AdmBorkBork

Antworten:

6

Javascript, 578, 575, 553, 478377 Bytes

Nach der geschlagenen Lua habe ich auf eine kompaktere Sprache umgestellt und die Konkurrenz auf Javascript umgestellt:

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

Der Algorithmus ist derselbe, aber in einer kompakteren Sprache geschrieben, also habe ich es geschafft, den bösen Lisp zu besiegen :)

Bearbeiten: Einige strukturelle Änderungen waren erforderlich, um wieder unter Lisp zu gelangen und nachfolgende Leerzeichen zu entfernen. Aber wir sind wieder da.

Edit2: Einige Abstraktionen berücksichtigt, um unter 500 zu bekommen. Hoffe, es wird genug sein :)

Edit3: Danke @Timwi, der Code ist noch 100 Zeichen schlanker. Ich habe die Erklärung noch nicht aktualisiert.

Tests ( Online-Version , getestet in 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       >------^

Und um fair zu sein, gibt es eine faire Erklärung:

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
quelle
Sehr schön. Mit entsprechend den Regeln und nach Ihrem Beispiel habe ich beschlossen , das entfernen &optionalStichwort (und ein Leerzeichen), um 10 Bytes zu speichern, die 576 ... gibt böse Lachen (na ja, Sie sagten , Sie Golf könnte ein wenig mehr, so dass diese sollte nicht schwer zu schlagen sein; bis jemand eine 60-Byte-Antwort in Pyth schreibt, natürlich).
Coredump
@coredump Challenge angenommen :) Es ist schwieriger als erwartet, aber immer noch möglich :) Ich glaube, Sie können es in Pyth tun, aber niemand wird es jemals verstehen, also glaube ich, dass die Komplexität über den Möglichkeiten einer solchen Sprache liegt.
Jakuje,
3
Wenn Sie die Zuweisungen i=M/2;j=i;G=i;H=i;I=i;J=i;in i=j=G=H=I=J=M/2;und m=1;l=1;in m=l=1;
verketten,
2
Diese Lösung ist ziemlich clever. Allerdings habe ich noch einige Plätze gefunden, an denen man Golf spielen kann: 377 Bytes
Timwi
1
@ Jakuje Ich glaube, es war Timwis Absicht, dass Sie die 377-Byte-Version nehmen und Ihre Antwort bearbeiten, um sie zu verwenden. ;) (Andernfalls hätte er gerade eine separate Antwort gepostet.)
Martin Ender
7

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

Alle Tests bestehen weiterhin. Die Funktion "Ungolfed" wurde ebenfalls aktualisiert, um die Änderungen sowie die Kommentare wiederzugeben. Ich habe es endlich remove-duplicatesgeschafft, den Code zu verkürzen, aber jetzt weiß ich nicht, wo ich mehr Bytes finden kann. Gut gemacht, Jakuje.

Beispiele

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

&@@@@
^<<<<

Siehe auch 20lc10(Pastebin).

Ungolfed

Hier handelt es sich nicht um eine Rekursion, sondern nur um einen einfachen Turtle-Grafikansatz mit Schleifen:

  1. Zeichnen Sie die Spiralen in Erinnerung, indem Sie die (x y char)Tripel in einem Stapel speichern .
  2. Stabil sortierte Elemente nach yundx
  3. Durchlaufen Sie diese Liste unter Vermeidung von Duplikaten (vorherige Spuren) und drucken Sie von links oben nach rechts unten.
(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)))
Core-Dump
quelle
4

Lua 5.2, 740 Bytes

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

Ich denke, dass es Spaß machen würde, einen Algorithmus zu implementieren, um Lisp zu schlagen, aber Lua ist wahrscheinlich nicht die beste Option. Ich verbringe zu viel Zeit damit, einige Teile zu überarbeiten, um mit dieser ugglen, aber funktionierenden Lösung fertig zu werden. Wahrscheinlich werde ich später eine andere Sprache ausprobieren, um Lisp zu schlagen, da es ungefähr 90 Zeichen gibt, die ich von diesem Algorithmus nicht wegnehmen kann.

Ausgänge testen:

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
quelle
2

PHP, 524 Bytes

Ich bin spät zu dieser Party gekommen. Meine PHP-Lösung ist weder die kleinste noch die intelligenteste. Es funktioniert einfach

$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";}

Wie man es ausführt:

$ 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||
|||@|||
||^-<||
|^---<|
^-----<

Die ausführliche Version mit Tests, Erklärungen und anderen Extras finden Sie auf Github .

Axiac
quelle