Espirales ASCII recursivas

21

Esta competencia ha terminado. Gracias por las interesantes entradas que no son esolang, y felicidades a Jakuje por su presentación ganadora de JavaScript.

En la gran tradición de los desafíos de arte ASCII en este sitio, aquí hay otro. Dada una entrada, dibuja una espiral.

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

Simple, si? Je, je, je ... Sí ...

(Inspirado en la publicación ASCII Dragons Curve y en las publicaciones ASCII Art of the Day de Optimizer )

Entrada

La entrada tendrá la forma de una serie de parámetros, tomados del STDIN / argumento de función / etc. Estas partes pueden ser cuatro argumentos separados, un cuádruple, una matriz de tamaño 4, etc. Por simplicidad y consistencia a lo largo del desafío, representaré la entrada como una sola palabra.

  • Un entero 2 ≤ x ≤ 20que especifica el tamaño de la espiral en términos de "cuadrados" con cada carácter impreso representando un "cuadrado" de tamaño. Teóricamente, esto podría tener un alcance enorme, pero dado que estamos dibujando arte ASCII, un límite superior seguro de esto será 20, de modo que se ajuste de manera algo decente en la pantalla.
  • Una sola letra de d u ro l, que indica el movimiento inicial desde el "cuadrado" inicial (abajo, arriba, derecha, izquierda).
  • Un opcional c, que indica "en sentido antihorario". Si cse omite el, asuma la rotación en sentido horario para la espiral.
  • Un entero final 1 ≤ y ≤ 10que especifica cuántas veces se repetirá el dibujo en espiral, utilizando el "cuadrado" final de la espiral anterior como el "cuadrado" inicial del nuevo. Estoy eligiendo un límite superior de 10 porque quiero que el dibujo termine en algún momento.
  • Algunas entradas de ejemplo: 20lc5 13d2 2rc1

De interés, tenga en cuenta que los valores impares para la entrada de tamaño resultarán en que @siempre sea el centro exacto de una espiral, pero los valores pares pueden tener el desplazamiento inicial "cuadrado" en cualquiera de las cuatro direcciones diagonales, dependiendo de la dirección inicial viaje. Esto puede dar lugar a algunos ... interesantes ... patrones. Vea los dos ejemplos pares a continuación.

La entrada que no sigue la especificación de entrada (p. Ej. 11q#s) No está definida y espero que el programa vomite adecuadamente. :)

Salida

La salida es una salida imprimible ASCII a través de STDOUT equivalente al idioma, con las siguientes especificaciones:

  • El "cuadrado" de inicio (de cada recursión) debe marcarse con un signo de signo @.
  • El "cuadrado" final debe estar marcado con un signo "&" &. En el caso de múltiples recursiones, solo se debe marcar el "cuadrado" final &.
  • Las esquinas del camino en espiral deben "apuntar" en la dirección de desplazamiento, utilizando < > v ^.
  • El recorrido vertical debe ser dibujado por tuberías |.
  • El recorrido horizontal debe dibujarse con guiones -.
  • Los "cuadrados" que se sobrescriben en las recursiones posteriores deben mostrar la dirección de viaje más reciente. Esto dará como resultado que las recursiones "más nuevas" aparezcan en capas sobre las recursiones "más antiguas". Vea el 4rc3ejemplo a continuación.
  • Una nueva línea final está bien, los espacios iniciales pueden ser obligatorios y, por lo tanto, están permitidos, pero los espacios finales no están permitidos.
  • No te acoplaré si usas secuencias de escape para dibujar el arte ASCII que va a STDOUT, pero estaré silenciosamente decepcionado de ti. (Seguirás siendo elegible para la recompensa si los usas)

Ejemplos

2d4 = diámetro de 2, comienza bajando, en sentido horario, 4 recursiones

&@@@@
^<<<<

En este ejemplo, el dibujo comienza en la esquina superior derecha @, baja uno, izquierda, uno arriba. En este punto, hemos terminado la 2dporción, y así comenzamos la segunda recursión, por lo que tenemos otro @, uno abajo, uno izquierdo, uno arriba; luego la tercera recursión; luego el 4 y finalmente nuestro &.

4rc3 = diámetro de 4, comienza yendo a la derecha, en sentido antihorario, 3 recursiones

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

En este ejemplo, el dibujo comienza en la parte inferior @, va hacia la derecha, hacia arriba, gira en espiral, hasta que llega al centro @y termina la 4rcporción. Esto luego se repite dos veces más para obtener las 3 recursiones completas solicitadas. Tenga en cuenta que 4rc1sería solo el bloque 4x4 superior izquierdo de este ejemplo.

7u1 = diámetro de 7, comienza subiendo, en sentido horario, 1 recursión (tenga en cuenta que es lo mismo que la introducción)

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

Ganadores y restricciones

Este es Code Golf, por lo que gana la respuesta más pequeña en bytes. Las presentaciones deben estar en la forma habitual de programa / función / CJam Code Block / etc. Se aplican restricciones estándar de lagunas. Conductor profesional en curso cerrado. Si la irritación persiste, suspenda su uso y consulte a su médico.

AdmBorkBork
fuente
3
Los detalles son bastante diferentes, pero solo como referencia, aquí hay un desafío de dibujo en espiral anterior: codegolf.stackexchange.com/questions/52494/… .
Reto Koradi
2
Buen desafío +1 para "Conductor profesional en curso cerrado"
jrich
3
Pide una respuesta> <>.
The_Basset_Hound
2
"Vamos, muchachos ... ¿van a dejar que gane Common Lisp? ;-)" Esa es la razón más graciosa de una recompensa que he visto. Gracias
coredump
1
Estoy sentado aquí riéndome de que Common Lisp y Lua son los dos idiomas que luchan por el primer puesto en una pregunta de código de golf. :)
AdmBorkBork

Respuestas:

6

Javascript, 578, 575, 553, 478, 377 bytes

Después de la vencida Lua, cambié a un lenguaje más compacto y moví la competencia a 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,''))}

El algoritmo es el mismo, pero escrito en un lenguaje más compacto, así que logré vencer al malvado Lisp :)

Editar: se necesitaron algunos cambios estructurales para llegar a Lisp nuevamente y eliminar los espacios en blanco finales. Pero estamos aquí de nuevo.

Edit2: Algunas abstracciones tomadas en cuenta para llegar a menos de 500. Espero que sea suficiente :)

Edit3: Gracias @Timwi, el código es otro 100 caracteres más delgado. No actualicé la explicación todavía.

Pruebas ( versión en línea , probada en 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       >------^

Y para ser justos, hay una explicación justa:

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
fuente
Muy agradable. De acuerdo con las reglas y siguiendo su ejemplo, decidí eliminar la &optionalpalabra clave (y un espacio) para ahorrar 10 bytes, lo que da 576 ... risas malignas (bueno, usted dijo que podía jugar al golf un poco más, así que esto no debería ser difícil de superar; hasta que alguien escriba una respuesta de 60 bytes en Pyth, por supuesto).
coredump
@coredump Challenge aceptado :) Es más difícil de lo que esperaba, pero aún es posible :) Creo que puedes hacerlo en pyth, pero nadie lo entenderá, así que creo que la complejidad supera las posibilidades de ese lenguaje.
Jakuje
3
Si se encadenan las asignaciones i=M/2;j=i;G=i;H=i;I=i;J=i;dentro i=j=G=H=I=J=M/2;y m=1;l=1;en m=l=1;que puede ahorrar 12 bytes
SLuck49
2
Esta solución es bastante inteligente. Sin embargo, encontré varios lugares más que se pueden jugar al golf: 377 bytes
Timwi
1
@Jakuje Creo que fue la intención de Timwi que tomaras la versión de 377 bytes y editaras tu respuesta para usarla. ;) (De lo contrario, habría publicado una respuesta por separado.)
Martin Ender
7

Lisp común, 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)))))

Todas las pruebas aún pasan. La función no oculta también se actualizó para reflejar los cambios, al igual que los comentarios. Finalmente me deshice remove-duplicatespara acortar el código, pero ahora no sé dónde encontrar más bytes. Bien hecho Jakuje.

Ejemplos

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

&@@@@
^<<<<

Ver también 20lc10(pastebin).

Sin golf

No hay una recursión involucrada aquí, solo un enfoque básico de gráficos Turtle con bucles:

  1. Dibuja las espirales en la memoria almacenando (x y char)triples en una pila.
  2. Elementos de clasificación estable según yyx
  3. Itere sobre esa lista evitando duplicados (trazas anteriores) e imprima de arriba a la izquierda a abajo a la derecha.
(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)))
volcado de memoria
fuente
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

Pensé que sería divertido intentar implementar algún algoritmo para vencer a Lisp, pero Lua probablemente no sea la mejor opción. Dedico demasiado tiempo a ello, diseñé en exceso algunas partes para terminar con esta fea, pero la solución funciona. Probablemente intentaré un lenguaje diferente más tarde para vencer a Lisp, ya que hay unos 90 caracteres que no puedo eliminar de este algoritmo.

Pruebas de salidas:

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

PHP, 524 bytes

Llegué tarde a esta fiesta. Mi solución PHP no es la más pequeña ni la más inteligente. Simplemente funciona

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

Cómo ejecutarlo:

$ 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 versión detallada con pruebas, explicaciones y otras ventajas se puede encontrar en github .

axiac
fuente