Рекурсивні спіралі ASCII


21

Цей конкурс закінчився. Дякуємо за цікаві записи, що не належать до esolang, та вітаю Якову за виграшне подання JavaScript.

У чудовій традиції ASCII Art Challenges на цьому сайті, ось ще одна. Давши вхід, намалюйте спіраль.

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

Просто, так? Хе-хе-хе ... Ага ...

(Натхненний публікацією " Крива драконів ASCII" та повідомленнями " Арт- оптимізатор" АССІІ "Мистецтво дня" )

Вхідні дані

Введення буде у вигляді серії параметрів, узятих із звичайного STDIN / аргументу функції / тощо, незалежно від мовного еквівалента, що складається з чотирьох частин. Ці частини можуть бути чотирма окремими аргументами, четверним, масивом розміром 4 і т. Д. Для простоти та послідовності протягом виклику я буду представляти введення як одне слово.

  • Ціле число, 2 ≤ x ≤ 20яке визначає розмір спіралі в термінах "квадрати" з кожним друкованим символом, що представляє один "квадрат" за розміром. Теоретично це може бути величезним за обсягом, але, враховуючи, що ми малюємо мистецтво ASCII, безпечним верхньою межею для цього буде 20, щоб воно дещо пристойно розмістилося на екрані.
  • Одина літера d u rабо l, що вказує на початковий рух від початкового "квадрата" (вниз, вгору, вправо, вліво).
  • Необов’язково із cзазначенням "проти годинникової стрілки". Якщо cопущено, припустимо обертання спіралі за годинниковою стрілкою.
  • Кінцеве ціле число, 1 ≤ y ≤ 10яке визначає, скільки разів повторити малюнок спіралі, використовуючи фінішний "квадрат" попередньої спіралі як початковий "квадрат" нової. Я підбираю верхню межу 10, оскільки хочу, щоб малюнок закінчувався в якийсь момент.
  • Кілька прикладів входів: 20lc5 13d2 2rc1

Цікаво, зауважте, що непарні значення для введення розміру призведуть до того, що @завжди буде точний центр спіралі, але навіть значення можуть мати початковий "квадратний" зсув у будь-якому з чотирьох діагональних напрямків, залежно від напрямку початкового подорожі. Це може призвести до деяких ... цікавих ... моделей. Дивіться два рівні приклади нижче.

Введення, яке не відповідає специфікації введення (наприклад, 11q#s), не визначене, і я повністю очікую, що програма належним чином переправиться. :)

Вихідні дані

Вихід - вихід ASCII для друку через еквівалентну мові STDOUT із такими специфікаціями:

  • Початковий "квадрат" (кожної рекурсії) повинен бути позначений знаком "at" @.
  • Остаточний "квадрат" повинен бути позначений символом "" &. У разі багаторазових рекурсій слід позначати лише самий остаточний "квадрат" &.
  • Кути спіральної доріжки потрібно «вказувати» у напрямку руху, використовуючи < > v ^.
  • Вертикальні ходи потрібно провести трубами |.
  • Горизонтальну подорож потрібно намалювати тире -.
  • "Квадрати", перезаписані пізнішими рекурсіями, повинні відображати самий останній напрямок руху. Це призведе до того, що «новіші» рекурсії, схоже, будуть шаруватими поверх «старих» рекурсій. Дивіться 4rc3приклад нижче.
  • Остаточний новий рядок є нормальним, провідні пробіли можуть бути обов'язковими, і так дозволено, але пробіли не допускаються.
  • Я не буду стикувати вас, якщо ви використовуєте послідовності втечі, щоб намалювати мистецтво ASCII, що йде до STDOUT, але я буду мовчки розчарований у вас. (Ви все одно отримаєте право на винагороду, якщо будете використовувати їх)

Приклади

2d4 = діаметр 2, починається зі спуску, за годинниковою стрілкою, 4 рекурсії

&@@@@
^<<<<

У цьому прикладі малюнок починається вгорі праворуч @, йде вниз по одному, вліво, вгору. На цьому етапі ми закінчили 2dпорцію, і так розпочинаємо другу рекурсію, тож у нас є ще одна @, внизу одна, ліва, вгору одна; потім 3-та рекурсія; потім 4-й і нарешті наш &.

4rc3 = діаметр 4, починається з руху вправо, проти годинникової стрілки, 3 рекурсії

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

У цьому прикладі креслення починається знизу @, йде праворуч, вгору, спіраль навколо, поки не досягне середини @і не закінчить 4rcчастину. Потім це повторюється ще два рази, щоб отримати всі 3 запитуваних рекурсії. Зауважте, що це 4rc1був би лише верхній лівий блок 4x4 цього прикладу.

7u1 = діаметр 7, починається з підйому вгору, за годинниковою стрілкою, 1 рекурсія (зверніть увагу на те, що це введення)

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

Перемога та обмеження

Це Code Golf, тому найменша відповідь у байтах виграє. Подання повинні бути у звичайній формі програми / функції / Блок коду CJam / тощо. Застосовуються стандартні обмеження на прорізи. Професійний водій на закритому курсі. Якщо роздратування не зникає, припиніть використання та проконсультуйтеся з лікарем.


3
Специфікація зовсім інша, але лише для довідки, ось попередній виклик спірального малювання: codegolf.stackexchange.com/questions/52494/… .
Рето Коради

2
Приємний виклик. +1 за "Професійний водій на закритому курсі"
jrich

3
Він запитує відповідь> <>.
The_Basset_Hound

2
"Давай, хлопці ... ви збираєтесь дозволити звичайному Лісп виграти? ;-)" Це найсмішніша причина баунті, яку я коли-небудь бачив. Дякуємо
coredump

1
Я сиджу тут, посміхаючись, що Common Lisp та Lua - це дві мови, які борються за перше місце у кодово-гольф-питанні. :)
AdmBorkBork

Відповіді:


6

Javascript, 578, 575, 553, 478, 377 байт

Після побитого Луа я перейшов на більш компактну мову і перемістив конкуренцію на 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,''))}

Алгоритм такий же, але написаний більш компактною мовою, тому мені вдалося перемогти злого Лиса :)

Редагувати: Деякі структурні зміни були потрібні, щоб знову дістатись під Ліспом та усунути відсталі пробіли. Але ми знову тут.

Edit2: Деякі абстракції враховуються, щоб отримати менше 500. Сподіваюся, цього буде достатньо :)

Edit3: Спасибі @Timwi, код ще на 100 символів тонший. Я ще не оновлював пояснення.

Тести ( онлайн-версія , протестована в 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       >------^

Для справедливості є справедливе пояснення:

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

Дуже хороша. Відповідно до правил і, слідуючи вашому прикладу, я вирішив видалити &optionalключове слово (і пробіл), щоб зберегти 10 байт, що дає 576 ... злий сміх (ну, ви сказали, що можете трохи більше покатати, так що це не важко перемогти; доки, звичайно, хтось не напише 60-байтну відповідь у Pyth).
coredump

@coredump Challenge прийнято :) Це складніше, ніж я очікував, але все ж можливий :) Я вірю, що ти можеш це зробити в pyth, але ніхто його ніколи не зрозуміє, тому я вважаю, що складність полягає в можливостях такої мови.
Jakuje

3
Якщо ви 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;
з'єднаєте

2
Це рішення досить розумне. Однак я знайшов ще кілька місць, де можна пограти в гольф: 377 байт
Тімві

1
@Jakuje Я вважаю, що Тимві мав намір взяти версію 377 байт і відредагувати свою відповідь, щоб використовувати її. ;) (Інакше він би щойно опублікував окрему відповідь.)
Мартін Ендер,

7

Лисп звичайний, 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)))))

Всі тести все-таки проходять. Функція без вогків також була оновлена ​​для відображення змін, як і коментарі. Нарешті я позбувся remove-duplicates, щоб скоротити код, але тепер не знаю, де знайти більше байтів. Молодці Якує.

Приклади

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

&@@@@
^<<<<

Дивись також 20lc10 (пастебін).

Безумовно

Тут немає жодної рекурсії, просто базовий графічний підхід Черепахи з петлями:

  1. Намалюйте спіралі в пам'яті, зберігаючи (x y char) трійки в стеку.
  2. Стабільні сортування елементів відповідно до yтаx
  3. Повторіть цей список, уникаючи дублікатів (попередніх слідів) та друкуйте зверху вліво-внизу праворуч.
(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)))

4

Луа 5.2, 740 байт

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

Я хоч, що було б весело спробувати реалізувати якийсь алгоритм, щоб перемогти Лісп, але Луа, мабуть, не найкращий варіант. Я витрачаю на це занадто багато часу, переробляючи деякі частини, щоб закінчити це некрасиве, але робоче рішення. Можливо, пізніше я спробую іншою мовою, щоб перемогти Lisp, оскільки є 90 символів, які я не можу забрати з цього алгоритму.

Тестові результати:

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

2

PHP, 524 байти

Я пізно приїхав на цю вечірку. Моє рішення PHP не є ні найменшим, ні найрозумнішим. Це просто працює.

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

Як це запустити:

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

Детальну версію з тестами, поясненнями та іншими смакотами можна знайти на github .

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.