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


21

Этот конкурс окончен. Спасибо за интересные записи, не относящиеся к esolang, и поздравляю Jakuje за его успешное представление JavaScript.

В великой традиции ASCII Art Challenges на этом сайте, вот еще один. Учитывая вход, нарисуйте спираль.

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

Просто, да? Хе-хе-хе ... Да ...

(Вдохновлено постом ASCII Dragons Curve и постами Оптимизатора ASCII Art of the Day )

вход

Ввод будет выполнен в виде последовательности параметров, взятых из обычного 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пример ниже.
  • Последний завершающий перевод строки в порядке, начальные пробелы могут быть обязательными, и поэтому допускаются, но завершающие пробелы не допускаются.
  • Я не буду пристыковывать вас, если вы используете escape-последовательности, чтобы нарисовать ASCII-арт, идущий в STDOUT, но я буду молча разочарован в вас. (Вы все равно будете иметь право на получение награды, если будете ее использовать)

Примеры

2d4 = диаметр 2, начинается с спуска, по часовой стрелке, 4 рекурсии

&@@@@
^<<<<

В этом примере рисунок начинается в правом верхнем углу @, идет вниз, слева, вверх, вверх. К этому моменту мы закончили 2dчасть и, таким образом, начинаем 2-ю рекурсию, так что у нас есть еще один @, вниз один, слева один, вверх один; затем 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
«Да ладно, ребята ... вы собираетесь позволить Common Lisp победить? ;-)» Это самая веселая причина для награды, которую я когда-либо видел. Спасибо
coredump

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

Ответы:


6

Мусор, 578, 575, 553, 478377 байт

После побежденного Lua я переключился на более компактный язык и перешел на 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)))))

Все тесты еще проходят. Функция ungolfed также была обновлена, чтобы отразить изменения, как и комментарии. Я наконец избавился от него 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(pastebin).

Ungolfed

Здесь нет никакой рекурсии, просто базовый графический подход Turtle с циклами:

  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

Lua 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, но Lua, вероятно, не лучший вариант. Я трачу на это слишком много времени, перепроектировал некоторые детали, чтобы закончить с этим уродливым, но работающим решением. Возможно, позже я опробую другой язык, чтобы обыграть Лисп, так как есть около 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.