Espirais ASCII recursivas

21

Esta competição acabou. Obrigado pelas entradas interessantes que não são esolang e parabéns a Jakuje pelo envio vencedor do JavaScript.

Na grande tradição dos Desafios de Arte ASCII neste site, aqui está outro. Dada uma entrada, desenhe uma espiral.

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

Simples, sim? Heh, heh, heh ... Sim ...

(Inspirado na postagem ASCII Dragons Curve e na postagem ASCII Art of the Day do Optimizer )

Entrada

A entrada será na forma de uma série de parâmetros, extraídos do argumento STDIN / função usual / etc., Qualquer que seja seu idioma equivalente, composto por quatro partes. Essas partes podem ser quatro argumentos separados, um quádruplo, uma matriz de tamanho 4, etc. Por simplicidade e consistência ao longo do desafio, representarei a entrada como uma única palavra.

  • Um número inteiro 2 ≤ x ≤ 20que especifica o tamanho da espiral em termos de "quadrados", com cada caractere impresso representando um "quadrado" em tamanho. Teoricamente, isso pode ter um escopo enorme, mas, como estamos desenhando a arte ASCII, um limite superior seguro será 20, para que caiba de maneira um tanto decente na tela.
  • Uma única letra de d u rou l, indicando o movimento inicial do "quadrado" inicial (para baixo, para cima, direita, esquerda).
  • Um opcional c, indicando "anti-horário". Se comitido, assuma a rotação no sentido horário da espiral.
  • Um número inteiro final 1 ≤ y ≤ 10que especifica quantas vezes repetir o desenho em espiral, usando o "quadrado" final da espiral anterior como o "quadrado" inicial da nova. Estou escolhendo um limite superior de 10 porque quero que o desenho termine em algum momento.
  • Alguns exemplos de entradas: 20lc5 13d2 2rc1

De interesse, observe que valores ímpares para a entrada de tamanho resultarão no centro @sempre exato de uma espiral, mas valores pares podem ter o deslocamento "quadrado" inicial em qualquer uma das quatro direções diagonais, dependendo da direção da inicial. viagem. Isso pode resultar em alguns ... padrões interessantes. Veja os dois exemplos pares abaixo.

A entrada que não segue a especificação de entrada (por exemplo, 11q#s) é indefinida e espero totalmente que o programa vomite adequadamente. :)

Saída

Saída é uma saída imprimível ASCII via STDOUT equivalente ao idioma, com as seguintes especificações:

  • O "quadrado" inicial (de cada recursão) deve ser marcado com um sinal de arroba @.
  • O "quadrado" final deve ser marcado com um e comercial &. No caso de várias recursões, apenas o "quadrado" final deve ser marcado &.
  • Os cantos do caminho em espiral precisam "apontar" na direção da viagem, usando < > v ^.
  • O deslocamento vertical precisa ser desenhado por tubos |.
  • O deslocamento horizontal precisa ser desenhado com traços -.
  • Os "quadrados" substituídos por recursões posteriores devem exibir a direção da viagem mais recente. Isso resultará em recursões "mais recentes" que parecem estar em camadas sobre as recursões "mais antigas". Veja o 4rc3exemplo abaixo.
  • Uma nova linha final à direita está OK, os espaços à esquerda podem ser uma obrigação e, portanto, são permitidos, mas os espaços à direita não são permitidos.
  • Não vou atracar você se você usar seqüências de escape para desenhar a arte ASCII para STDOUT, mas ficarei silenciosamente desapontado com você. (Você ainda será elegível para a recompensa se usá-las)

Exemplos

2d4 = diâmetro de 2, começa descendo, no sentido horário, 4 recursões

&@@@@
^<<<<

Neste exemplo, o desenho começa no canto superior direito @, desce um, deixa um, sobe um. Nesse ponto, terminamos a 2dparte e, portanto, iniciamos a 2ª recursão, para que tenhamos outra @, descendo uma, deixada uma, subindo uma; depois a 3ª recursão; depois a quarta e finalmente a nossa &.

4rc3 = diâmetro de 4, começa indo à direita, no sentido anti-horário, 3 recursões

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

Neste exemplo, o desenho começa no fundo @, sobe um, sobe um, gira em espiral, até chegar ao meio @e terminar a 4rcparte. Isso então se repete mais duas vezes para obter as 3 recursões completas solicitadas. Observe que 4rc1seria apenas o bloco 4x4 superior esquerdo deste exemplo.

7u1 = diâmetro de 7, começa subindo, no sentido horário, 1 recursão (observe que é o mesmo que a introdução)

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

Vencimento e restrições

Este é o Code Golf, então a menor resposta em bytes vence. As submissões devem estar na forma usual de programa / função / CJam Code Block / etc. Aplicam-se restrições de brecha padrão. Motorista profissional em curso fechado. Se a irritação persistir, interrompa o uso e consulte seu médico.

AdmBorkBork
fonte
3
As especificidades são bem diferentes, mas apenas para referência, eis um desafio anterior de desenho em espiral: codegolf.stackexchange.com/questions/52494/… .
Reto Koradi 01/09/2015
2
Bom desafio. +1 para "Motorista profissional em curso fechado"
jrich 01/09/2015
3
Ele pede uma resposta> <>.
The_Basset_Hound
2
"Vamos lá, pessoal ... você vai deixar o Common Lisp vencer? ;-)" Essa é a razão mais hilária para uma recompensa que eu já vi. Obrigado
coredump
11
Estou sentado aqui rindo que Common Lisp e Lua são as duas línguas que lutam pelo primeiro lugar em uma questão de código-golfe. :)
AdmBorkBork

Respostas:

6

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

Após a derrotada Lua, mudei para uma linguagem mais compacta e mudei a concorrência para o 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,''))}

O algoritmo é o mesmo, mas escrito em uma linguagem mais compacta, então eu consegui vencer o mal Lisp :)

Edit: Algumas mudanças estruturais foram necessárias para alcançar o Lisp novamente e eliminar os espaços em branco à direita. Mas estamos aqui novamente.

Edit2: Algumas abstrações levadas em conta para ficar abaixo de 500. Espero que seja o suficiente :)

Edit3: Obrigado @Timwi, o código é mais 100 caracteres mais fino. Ainda não atualizei a explicação.

Testes ( versão online , testada no 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       >------^

E para ser justo, há uma explicação 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
fonte
Muito agradável. De acordo com as regras e seguindo o seu exemplo, decidi remover a &optionalpalavra - chave (e um espaço) para economizar 10 bytes, o que dá 576 ... risadas ruins (bem, você disse que poderia jogar um pouco mais, então isso não deve ser difícil de derrotar; até que alguém escreva uma resposta de 60 bytes em Pyth, é claro).
Coredump
@coredump O desafio foi aceito :) É mais difícil do que eu esperava, mas ainda é possível :) Eu acredito que você pode fazer isso em pyth, mas ninguém nunca vai entender, então eu acredito que a complexidade está sobre as possibilidades dessa linguagem.
Jakuje 9/09/15
3
Se você encadear as atribuições i=M/2;j=i;G=i;H=i;I=i;J=i;em i=j=G=H=I=J=M/2;e m=1;l=1;em m=l=1;que você pode economizar 12 bytes
SLuck49
2
Esta solução é bastante inteligente. No entanto, descobri vários outros lugares que podem ser golfed: 377 bytes
Timwi
11
@Jakuje Eu acredito que era a intenção de Timwi para você pegar a versão de 377 bytes e editar sua resposta para usá-la. ;) (Caso contrário, ele teria só postou uma resposta em separado).
Martin Ender
7

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

Todos os testes ainda passam. A função ungolfed também foi atualizada para refletir as alterações, assim como os comentários. Finalmente me livrei remove-duplicatespara encurtar o código, mas agora não sei onde encontrar mais bytes. Bem feito Jakuje.

Exemplos

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

&@@@@
^<<<<

Veja também 20lc10(pastebin).

Ungolfed

Não há recursão envolvida aqui, apenas uma abordagem básica de gráficos Turtle com loops:

  1. Desenhe as espirais na memória armazenando (x y char)triplos em uma pilha.
  2. Elementos de classificação estável de acordo com yex
  3. Itere sobre essa lista, evitando duplicatas (traços anteriores) e imprima da parte superior esquerda para a parte inferior direita.
(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
fonte
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

Eu pensei que seria divertido tentar implementar algum algoritmo para vencer o Lisp, mas Lua provavelmente não é a melhor opção. Eu gasto muito tempo nisso, desenvolvendo demais algumas peças para terminar com essa solução feia, mas funcional. Provavelmente tentarei uma linguagem diferente mais tarde para vencer o Lisp, pois existem cerca de 90 caracteres que não consigo tirar desse algoritmo.

Testando saídas:

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

PHP, 524 bytes

Cheguei tarde a esta festa. Minha solução PHP não é a menor nem a mais inteligente. Isso simplesmente 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";}

Como executá-lo:

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

A versão detalhada com testes, explicações e outras vantagens pode ser encontrada no github .

axiac
fonte