Spirales ASCII récursives

21

Cette compétition est terminée. Merci pour les entrées intéressantes non-esolang, et félicitations à Jakuje pour sa soumission JavaScript gagnante.

Dans la grande tradition des défis artistiques ASCII sur ce site, en voici un autre. Étant donné une entrée, dessinez une spirale.

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

C'est simple, non? Hé, hé, hé ... Ouais ...

(Inspiré du post ASCII Dragons Curve et du post ASCII Art of the Day d' Optimizer )

Contribution

L'entrée se fera sous la forme d'une série de paramètres, extraits de l'argument STDIN / fonction habituel / etc., Quel que soit votre équivalent linguistique, composé de quatre parties. Ces parties peuvent être quatre arguments distincts, un quadruple, un tableau de taille 4, etc. Pour plus de simplicité et de cohérence tout au long du défi, je représenterai l'entrée en un seul mot.

  • Un entier 2 ≤ x ≤ 20qui spécifie la taille de la spirale en termes de "carrés", chaque caractère imprimé représentant un "carré". Théoriquement, cela pourrait avoir une portée énorme, mais étant donné que nous dessinons de l'art ASCII, une limite supérieure sûre à ce sujet sera de 20 afin qu'il s'adapte assez décemment à l'écran.
  • Une seule lettre de d u rou l, indiquant le mouvement initial à partir du "carré" de départ (bas, haut, droite, gauche).
  • Facultatif c, indiquant "dans le sens antihoraire". Si le cest omis, supposez une rotation dans le sens horaire pour la spirale.
  • Un entier final 1 ≤ y ≤ 10qui spécifie le nombre de répétitions du dessin en spirale, en utilisant le "carré" de fin de la spirale précédente comme "carré" de départ de la nouvelle. Je choisis une limite supérieure de 10 parce que je veux que le dessin se termine à un moment donné.
  • Quelques exemples d'entrées: 20lc5 13d2 2rc1

Il est intéressant de noter que les valeurs impaires pour l'entrée de taille auront pour résultat d' @être toujours le centre exact d'une spirale, mais les valeurs paires peuvent avoir le "carré" de départ décalé dans l'une des quatre directions diagonales, en fonction de la direction de l'initiale Voyage. Cela peut entraîner des motifs ... intéressants. Voir les deux exemples pairs ci-dessous.

L'entrée qui ne suit pas la spécification d'entrée (par exemple, 11q#s) n'est pas définie et je m'attends pleinement à ce que le programme barf de manière appropriée. :)

Production

La sortie est une sortie imprimable ASCII via STDOUT équivalent à la langue, avec les spécifications suivantes:

  • Le "carré" de départ (de chaque récursivité) doit être marqué d'un signe at @.
  • Le "carré" final doit être marqué d'une esperluette &. Dans le cas de récursions multiples, seul le "carré" très final doit être marqué &.
  • Les coins du chemin en spirale doivent "pointer" dans le sens de la marche, en utilisant < > v ^.
  • Les déplacements verticaux doivent être tirés par des tuyaux |.
  • Le déplacement horizontal doit être tracé avec des tirets -.
  • Les "carrés" qui sont remplacés par des récursions ultérieures devraient afficher le sens de déplacement le plus récent. Il en résultera des récursions "plus récentes" semblant se superposer aux récursions "plus anciennes". Voir l' 4rc3exemple ci-dessous.
  • Un retour à la ligne final final est OK, les espaces de début peuvent être un must et sont donc autorisés, mais les espaces de fin ne sont pas autorisés.
  • Je ne vous ancrerai pas si vous utilisez des séquences d'échappement pour dessiner l'art ASCII allant à STDOUT, mais je serai silencieusement déçu de vous. (Vous serez toujours éligible à la prime si vous les utilisez)

Exemples

2d4 = diamètre de 2, commence par descendre, dans le sens horaire, 4 récursions

&@@@@
^<<<<

Dans cet exemple, le dessin commence en haut à droite @, descend un, gauche un, haut un. À ce stade, nous avons terminé la 2dpartie, et donc commencer la 2e récursivité, nous avons donc un autre @, en bas un, gauche un, en haut un; puis la 3ème récursivité; puis le 4 et enfin le nôtre &.

4rc3 = diamètre de 4, commence par aller à droite, dans le sens antihoraire, 3 récursions

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

Dans cet exemple, le dessin commence en bas @, va à droite, en haut, en spirale, jusqu'à ce qu'il atteigne le milieu @et termine la 4rcportion. Cela se répète ensuite deux fois de plus pour obtenir les 3 récursions demandées. Notez que ce 4rc1serait juste le bloc 4x4 supérieur gauche de cet exemple.

7u1 = diamètre de 7, commence par monter, dans le sens horaire, 1 récursion (notez que c'est la même chose que l'intro)

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

Gain et restrictions

C'est Code Golf, donc la plus petite réponse en octets l'emporte. Les soumissions doivent être sous la forme habituelle de programme / fonction / bloc de code CJam / etc. Des restrictions standard sur les échappatoires s'appliquent. Chauffeur professionnel sur circuit fermé. Si l'irritation persiste, arrêtez l'utilisation et consultez votre médecin.

AdmBorkBork
la source
3
Les détails sont assez différents, mais juste pour référence, voici un défi de dessin en spirale antérieur: codegolf.stackexchange.com/questions/52494/… .
Reto Koradi
2
Beau défi. +1 pour "Pilote professionnel sur
circuit
3
Il demande une réponse> <>.
The_Basset_Hound
2
"Allez, les gars ... allez-vous laisser Common Lisp l'emporter? ;-)" C'est la raison la plus hilarante d'une prime que j'ai jamais vue. Merci
coredump
1
Je suis assis ici en riant que Common Lisp et Lua sont les deux langues qui se battent pour la première place sur une question de code-golf. :)
AdmBorkBork

Réponses:

6

Javascript, 578, 575, 553, 478, 377 octets

Après avoir battu Lua, je suis passé à un langage plus compact et j'ai déplacé la concurrence vers 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,''))}

L'algorithme est le même, mais écrit dans un langage plus compact, j'ai donc réussi à battre le mal Lisp :)

Edit: Certains changements structurels ont été nécessaires pour atteindre à nouveau sous Lisp et pour éliminer les espaces vides. Mais nous revoilà.

Edit2: Quelques abstractions prises en compte pour descendre en dessous de 500. J'espère que ça suffira :)

Edit3: Merci @Timwi, le code est encore plus mince de 100 caractères. Je n'ai pas encore mis à jour l'explication.

Tests ( version en ligne , testé dans 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       >------^

Et pour être juste, il y a une explication juste:

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
la source
Très agréable. Conformément aux règles et suivant votre exemple, j'ai décidé de supprimer le &optionalmot - clé (et un espace) afin d'économiser 10 octets, ce qui donne 576 ... rires diaboliques (enfin, vous avez dit que vous pouviez jouer un peu plus au golf, donc ce ne devrait pas être difficile à battre; jusqu'à ce que quelqu'un écrive une réponse de 60 octets en Pyth, bien sûr).
coredump
@coredump Challenge accepté :) C'est plus difficile que ce à quoi je m'attendais, mais toujours possible :) Je crois que vous pouvez le faire en pyth, mais personne ne le comprendra jamais, donc je crois que la complexité est au-dessus des possibilités d'un tel langage.
Jakuje
3
Si vous enchaînez les affectations i=M/2;j=i;G=i;H=i;I=i;J=i;dans i=j=G=H=I=J=M/2;et m=1;l=1;en, m=l=1;vous pouvez économiser 12 octets
SLuck49
2
Cette solution est assez intelligente. Cependant, j'ai trouvé plusieurs autres endroits qui peuvent être
joués au golf
1
@Jakuje Je pense que Timwi avait l'intention de prendre la version 377 octets et de modifier votre réponse pour l'utiliser. ;) (Sinon, il aurait juste posté une réponse séparée.)
Martin Ender
7

Lisp commun, 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)))))

Tous les tests réussissent toujours. La fonction non golfée a également été mise à jour pour refléter les changements, tout comme les commentaires. Je me suis finalement débarrassé de remove-duplicatespour raccourcir le code, mais maintenant je ne sais plus où trouver plus d'octets. Bravo Jakuje.

Exemples

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

&@@@@
^<<<<

Voir aussi 20lc10(pastebin).

Non golfé

Aucune récursivité n'est impliquée ici, juste une approche graphique de base de Turtle avec des boucles:

  1. Dessinez les spirales en mémoire en stockant des (x y char)triplets dans une pile.
  2. Éléments de tri stables selon yetx
  3. Parcourez cette liste tout en évitant les doublons (traces précédentes) et imprimez de haut en bas à bas à droite.
(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)))
coredump
la source
4

Lua 5.2, 740 octets

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

Je pensais que ce serait amusant d'essayer d'implémenter un algorithme pour battre Lisp, mais Lua n'est probablement pas la meilleure option. J'y consacre trop de temps, j'ai sur-conçu certaines pièces pour en finir avec cette solution laide mais fonctionnelle. Je vais probablement essayer un langage différent plus tard pour battre Lisp car il y a environ 90 caractères que je ne peux pas retirer de cet algorithme.

Tester les sorties:

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
la source
2

PHP, 524 octets

Je suis arrivé en retard à cette fête. Ma solution PHP n'est ni la plus petite, ni la plus intelligente. Ça marche juste.

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

Comment l'exécuter:

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

La version détaillée avec des tests, des explications et d'autres goodies peut être trouvée sur github .

axiaque
la source