Подтвердить что ты не робот

Code Golf: Пасхальная спираль

Что более подходящее, чем Спираль для пасхальных кодов для гольфа?
Ну, я думаю, почти все.

Задача

Самый короткий код по количеству символов, чтобы отобразить симпатичную ASCII-спираль, состоящую из звездочек ('*').

Ввод - это единственное число, R, которое будет размером x спирали. Другое измерение (y) всегда R-2. Программа может считать R всегда нечетной и >= 5.

Некоторые примеры:

Input
    7
Output

*******
*     *
* *** *
*   * *
***** *                   

Input
    9
Output

*********
*       *
* ***** *
* *   * *
* *** * *
*     * *
******* *

Input
   11
Output

***********
*         *
* ******* *
* *     * *
* * *** * *
* *   * * *
* ***** * *
*       * *
********* *

Количество кодов включает ввод/вывод (т.е. полную программу). Любой язык разрешен.

Мой легко битый 303-символьный длинный пример Python:

import sys;
d=int(sys.argv[1]);
a=[d*[' '] for i in range(d-2)];
r=[0,-1,0,1];
x=d-1;y=x-2;z=0;pz=d-2;v=2;
while d>2:
    while v>0:
        while pz>0:
            a[y][x]='*';
            pz-=1;
            if pz>0:
                x+=r[z];
                y+=r[(z+1)%4];
        z=(z+1)%4; pz=d; v-=1;
    v=2;d-=2;pz=d;
for w in a:
    print ''.join(w);

Теперь введите Spiral...

4b9b3361

Ответ 1

Python (2.6): 156 символов

r=input()
def p(r,s):x=(i+1)/2;print "* "*x+("*" if~i%2 else" ")*(r-4*x)+" *"*x+s
for i in range(r/2):p(r,"")
for i in range((r-1)/2-1)[::-1]:p(r-2," *")

Спасибо за комментарии. Я удалил посторонние пробелы и использовал input(). Я по-прежнему предпочитаю программу, которая принимает свой аргумент в командной строке, поэтому здесь версия все еще использует sys.argv на 176 символах:

import sys
r=int(sys.argv[1])
def p(r,s):x=(i+1)/2;print "* "*x+("*" if~i%2 else" ")*(r-4*x)+" *"*x+s
for i in range(r/2):p(r,"")
for i in range((r-1)/2-1)[::-1]:p(r-2," *")

Объяснение

Возьмите спираль и нарежьте ее в две почти равные части, сверху и снизу, верхняя одна строка больше нижней:

***********
*         *
* ******* *
* *     * *
* * *** * *

* *   * * *
* ***** * *
*       * *
********* *

Наблюдайте, как верхняя часть симпатична и симметрична. Обратите внимание на то, как нижняя часть имеет вертикальную линию с правой стороны, но в остальном похожа на верхнюю. Обратите внимание на рисунок в каждой второй строке вверху: все большее число звезд с каждой стороны. Обратите внимание, что каждый промежуточный ряд - это точно пила, как первая, за исключением того, что она заполняет среднюю область звездами.

Функция p (r, s) выводит i-ю строку верхней части спирали ширины r и прикрепляет суффикс s на конце. Обратите внимание, что я - глобальная переменная, хотя это может быть и не очевидно! Когда я даже он заполняет середину строки пробелами, в противном случае - звездами. (~ I% 2 был противным способом получить эффект i% 2 == 0, но на самом деле не нужен вообще, потому что я должен просто поменять "*" и "".) Сначала мы рисуем верх ряды спирали с ростом i, то мы рисуем нижние ряды с уменьшением i. Мы понижаем r на 2 и суффикс "*", чтобы получить столбец звезд справа.

Ответ 2

Java

328 символов

class S{
public static void main(String[]a){
int n=Integer.parseInt(a[0]),i=n*(n-2)/2-1,j=0,t=2,k;
char[]c=new char[n*n];
java.util.Arrays.fill(c,' ');
int[]d={1,n,-1,-n};
if(n/2%2==0){j=2;i+=1+n;}
c[i]='*';
while(t<n){
for(k=0;k<t;k++)c[i+=d[j]]='*';
j=(j+1)%4;
if(j%2==0)t+=2;
}
for(i=0;i<n-2;i++)System.out.println(new String(c,i*n,n));
}
}

Похоже, что на 1/6 больше, чем Python, не так уж плохо;)

Здесь то же самое с правильным отступом:

class S {
    public static void main(String[] a) {
        int n = Integer.parseInt(a[0]), i = n * (n - 2) / 2 - 1, j = 0, t = 2, k;
        char[] c = new char[n * n];
        java.util.Arrays.fill(c, ' ');
        int[] d = { 1, n, -1, -n };
        if (n / 2 % 2 == 0) {
            j = 2;
            i += 1 + n;
        }
        c[i] = '*';
        while (t < n) {
            for (k = 0; k < t; k++)
                c[i += d[j]] = '*';
            j = (j + 1) % 4;
            if (j % 2 == 0)
                t += 2;
        }
        for (i = 0; i < n - 2; i++)
            System.out.println(new String(c, i * n, n));
    }
}

Ответ 3

F #, 267 символов

Много ответов начинаются с пробелов и добавления * s, но я думаю, что может быть проще начать с звездочки и добавить пробелы.

let n=int(System.Console.ReadLine())-2
let mutable x,y,d,A=n,n,[|1;0;-1;0|],
 Array.init(n)(fun _->System.Text.StringBuilder(String.replicate(n+2)"*"))
for i=1 to n do for j=1 to(n-i+1)-i%2 do x<-x+d.[i%4];y<-y+d.[(i+1)%4];A.[y].[x]<-' '
Seq.iter(printfn"%O")A

Для тех, кто ищет представление о том, как я играю в гольф, мне удалось сэкономить много прогресса на этом пути, который я представляю здесь с комментариями. Не каждая программа совершенно правильная, но все они оттачивают более короткое решение.

Во-первых, я искал образец рисования белого:

********* 
*       * 
* ***** * 
* *   * * 
* *** * * 
*     * * 
******* * 

********* 
*6543216* 
*1*****5* 
*2*212*4* 
*3***1*3* 
*41234*2* 
*******1* 

*********** 
*         * 
* ******* * 
* *     * * 
* * *** * * 
* *   * * * 
* ***** * * 
*       * * 
********* *

*********** 
*876543218* 
*1*******7* 
*2*43214*6* 
*3*1***3*5* 
*4*212*2*4* 
*5*****1*3* 
*6123456*2* 
*********1*

Хорошо, я это вижу. Первая программа:

let Main() =
    let n=int(System.Console.ReadLine())
    let A=Array2D.create(n-2)n '*'
    let mutable x,y,z,i=n-2,n-2,0,n-2
    let d=[|0,-1;-1,0;0,1;1,0|]  // TODO
    while i>0 do
     for j in 1..i-(if i%2=1 then 1 else 0)do
      x<-x+fst d.[z]
      y<-y+snd d.[z]
      A.[y,x]<-'0'+char j
     z<-(z+1)%4
     i<-i-1
    printfn"%A"A
Main()

Я знаю, что d, кортеж-массив (x, y) -diffs-modulo-4 позже может быть уменьшен на x и y, индексируя их в разные части одного и того же int-массива, следовательно, TODO. Остальное прямолинейно основано на визуальном понимании "белой живописи". Я печатаю 2D-массив, который неправильный, нужен массив строк, поэтому:

let n=int(System.Console.ReadLine())
let s=String.replicate n "*"
let A=Array.init(n-2)(fun _->System.Text.StringBuilder(s))
let mutable x,y,z,i=n-2,n-2,0,n-2
let d=[|0,-1;-1,0;0,1;1,0|]
while i>0 do
 for j in 1..i-(if i%2=1 then 1 else 0)do
  x<-x+fst d.[z]
  y<-y+snd d.[z]
  A.[y].[x]<-' '
 z<-(z+1)%4
 i<-i-1
for i in 0..n-3 do
 printfn"%O"A.[i]

Итак, теперь изменим массив кортежей на массив из int:

let n=int(System.Console.ReadLine())-2
let mutable x,y,z,i,d=n,n,0,n,[|0;-1;0;1;0|]
let A=Array.init(n)(fun _->System.Text.StringBuilder(String.replicate(n+2)"*"))
while i>0 do
 for j in 1..i-i%2 do x<-x+d.[z];y<-y+d.[z+1];A.[y].[x]<-' '
 z<-(z+1)%4;i<-i-1
A|>Seq.iter(printfn"%O")

let для A может быть частью предыдущей строки. И z и i в основном избыточны, я могу вычислить один в терминах другого.

let n=int(System.Console.ReadLine())-2
let mutable x,y,d,A=n,n,[|0;-1;0;1|],
 Array.init(n)(fun _->System.Text.StringBuilder(String.replicate(n+2)"*"))
for i=n downto 1 do for j in 1..i-i%2 do x<-x+d.[(n-i)%4];y<-y+d.[(n-i+1)%4];A.[y].[x]<-' '
Seq.iter(printfn"%O")A

downto длинный, повторите математику, чтобы я мог (вверх) to в цикле.

let n=int(System.Console.ReadLine())-2
let mutable x,y,d,A=n,n,[|1;0;-1;0|],
 Array.init(n)(fun _->System.Text.StringBuilder(String.replicate(n+2)"*"))
for i=1 to n do for j in 1..(n-i+1)-i%2 do x<-x+d.[i%4];y<-y+d.[(i+1)%4];A.[y].[x]<-' '
Seq.iter(printfn"%O")A

Немного больше затягивания дает окончательное решение.

Ответ 4

Python: 238 - 221 - 209 символов

Все комментарии приветствуются:

d=input();r=range
a=[[' ']*d for i in r(d-2)]
x=y=d/4*2
s=d%4-2
for e in r(3,d+1,2):
 for j in r(y,y+s*e-s,s):a[x][j]='*';y+=s
 for j in r(x,x+s*e-(e==d)-s,s):a[j][y]='*';x+=s
 s=-s
for l in a:print''.join(l)

Ответ 5

Groovy, 373 295 257 243 символа

Пробовал рекурсивный подход, который создает квадраты, начиная с самого внешнего, входящего внутрь. Я использовал Groovy.

*********
*********
*********
*********
*********
*********
******* *

*********
*       *
*       *
*       *
*       *
*     * *
******* *

*********
*       *
* ***** *
* ***** *
* *** * *
*     * *
******* *

*********
*       *
* ***** *
* *   * *
* *** * *
*     * *
******* *

и т.д.

r=args[0] as int;o=r+1;c='*'
t=new StringBuffer('\n'*(r*r-r-2))
e(r,0)
def y(){c=c==' '?'*':' '}
def e(s,p){if (s==3)t[o*p+p..o*p+p+2]=c*s else{l=o*(p+s-3)+p+s-2;(p+0..<p+s-2).each{t[o*it+p..<o*it+p+s]=c*s};y();t[l..l]=c;e(s-2,p+1)}}
println t

читаемый:

r=args[0] as int;o=r+1;c='*'
t=new StringBuffer('\n'*(r*r-r-2))
e(r,0)
def y(){c=c==' '?'*':' '}
def e(s,p){
 if (s==3)
  t[o*p+p..o*p+p+2]=c*s 
 else{
  l=o*(p+s-3)+p+s-2
  (p+0..<p+s-2).each{
   t[o*it+p..<o*it+p+s]=c*s}
   y()
   t[l..l]=c
   e(s-2,p+1)      
  }   
}
println t

EDIT: улучшено путем заполнения квадратов и последующего их переопределения (проверьте новый пример): поэтому я избегал заполнять только край прямоугольника, но весь.

Ответ 6

Ruby, 237 символов

Я новичок в кодовом гольф-поле, поэтому я нахожусь в стороне, но я решил, что сделаю это.

x=ARGV[0].to_i
y=x-2
s,h,j,g=' ',x-1,y-1,Array.new(y){Array.new(x,'*')}
(1..x/2+2).step(2){|d|(d..y-d).each{|i|g[i][h-d]=s}
(d..h-d).each{|i|g[d][i]=s}
(d..j-d).each{|i|g[i][d]=s}
(d..h-d-2).each{|i|g[j-d][i]=s}}
g.each{|r|print r;puts}

Длинная версия

Ответ 7

Java, 265 250 245 240 символов

Вместо того, чтобы предварительно распределять прямоугольный буфер и заполнять его, я просто перехожу через координаты x/y и вывожу '*' или '' для текущей позиции. Для этого нам нужен алгоритм, который может оценивать произвольные точки, независимо от того, находятся ли они на спирали. Используемый алгоритм основан на наблюдении, что спираль эквивалентна набору концентрических квадратов, за исключением набора положений, которые все происходят по определенной диагонали; эти позиции требуют коррекции (они должны быть инвертированы).

Несколько читаемая версия:

public class Spr2 {

  public static void main(String[] args) {
    int n = Integer.parseInt(args[0]);
    int cy = (n - 5) / 4 * 2 + 1;
    int cx = cy + 2;
    for (int y = n - 3; y >= 0; y--) {
      for (int x = 0; x < n; x++) {
        int dx = cx - x;
        int dy = cy - y;
        int adx = Math.abs(dx);
        int ady = Math.abs(dy);
        boolean c = (dx > 0 && dx == dy + 1);
        boolean b = ((adx % 2 == 1 && ady <= adx) || (ady % 2 == 1 && adx <= ady)) ^ c;
        System.out.print(b ? '*' : ' ');
      }
      System.out.println();
    }
  }

}

Краткое объяснение вышеизложенного:

cx,cy = center
dx,dy = delta from center
adx,ady = abs(delta from center)
c = correction factor (whether to invert)
b = the evaluation

Оптимизировано. 265 символов:

public class S{
public static void main(String[]a){
int n=Integer.parseInt(a[0]),c=(n-5)/4*2+1,d=c+2,e,f,g,h,x,y;
for(y=0;y<n-2;y++){
for(x=0;x<=n;x++){
e=d-x;f=c-y;g=e>0?e:-e;h=f>0?f:-f;
System.out.print(x==n?'\n':(g%2==1&&h<=g||h%2==1&&g<=h)^(e>0&&e==f+1)?'*':' ');
}}}}

Обновление. Теперь до 250 символов:

class S{
public static void main(String[]a){
int n=Integer.parseInt(a[0]),c=(n-5)/4*2+1,d=c+2,g,h,x,y;
for(y=-c;y<n-2-c;y++){
for(x=-d;x<=n-d;x++){
g=x>0?x:-x;h=y>0?y:-y;
System.out.print(x==n-d?'\n':(g%2==1&&h<=g||h%2==1&&g<=h)^(x<0&&x==y-1)?'*':' ');
}}}}

Выбрил еще несколько персонажей. 245 символов:

class S{
public static void main(String[]a){
int n=Integer.parseInt(a[0]),c=(n-5)/4*2+1,d=c+2,g,h,x,y=-c;
for(;y<n-2-c;y++){
for(x=-d;x<=n-d;x++){
g=x>0?x:-x;h=y>0?y:-y;
System.out.print(x==n-d?'\n':(g%2==1&h<=g|h%2==1&g<=h)^(x<0&x==y-1)?'*':' ');
}}}}

Выбрил еще несколько персонажей. 240 символов:

class S{
public static void main(String[]a){
int n=Byte.decode(a[0]),c=(n-5)/4*2+1,d=c+2,g,h,x,y=-c;
for(;y<n-2-c;y++){
for(x=-d;x<=n-d;x++){
g=x>0?x:-x;h=y>0?y:-y;
System.out.print(x==n-d?'\n':(g%2==1&h<=g|h%2==1&g<=h)^(x<0&x==y-1)?'*':' ');
}}}}

Ответ 8

OCaml, 299 символов


Вот решение в OCaml, не кратчайшее, но я считаю вполне читаемым.

Он использует только строковые манипуляции, используя тот факт, что вы можете построить спираль, зеркалируя предыдущую.

Скажем, вы начинаете с n = 5:

55555
5   5
555 5

Теперь, когда n = 7:

7777777
7     7
5 555 7
5   5 7
55555 7

Вы видели, куда пошли все 5?

Вот непрофессиональный код, используя только ограниченную библиотеку, поставляемую с OCaml:

(* The standard library lacks a function to reverse a string *)
let rev s =
  let n = String.length s - 1 in
  let r = String.create (n + 1) in
    for i = 0 to n do
      r.[i] <- s.[n - i]
    done;
    r
;;

let rec f n =
  if n = 5 then
    [
      "*****";
      "*   *";
      "*** *"
    ]
  else
    [
      String.make n '*';
      "*" ^ (String.make (n - 2) ' ') ^ "*"
    ] @ ( 
      List.rev_map (fun s -> (rev s) ^ " *") (f (n - 2))
    )
;;

let p n =
  List.iter print_endline (f n)
;;

let () = p (read_int ());;

Вот зафуфированная версия длиной 299 символов:

open String
let rev s=
  let n=length s-1 in
  let r=create(n+1)in
    for i=0 to n do r.[i]<-s.[n-i]done;r
let rec f n=
  if n=5 then["*****";"*   *";"*** *"]else
    [make n '*';"*"^(make (n-2) ' ')^"*"]
    @(List.rev_map(fun s->(rev s)^" *")(f(n-2)));;
List.iter print_endline (f(read_int ()))

Ответ 9

С#, 292 262 255 символов

Простой подход: потяните спиральную линию за строкой извне.

using C=System.Console;class P{static void Main(string[]a){int A=
1,d=1,X=int.Parse(a[0]),Y=X-2,l=X,t=0,i,z;while(l>2){d*=A=-A;l=l<
4?4:l;for(i=1;i<(A<0?l-2:l);i++){C.SetCursorPosition(X,Y);C.Write
("*");z=A<0?Y+=d:X+=d;}if(t++>1||l<5){l-=2;t=1;}}C.Read();}}

Ответ 10

Ruby (1.9.2) - 126

f=->s{s<0?[]:(z=?**s;[" "*s]+(s<2?[]:[z]+f[s-4]<<?*.rjust(s))).map{|i|"* #{i} *"}<<z+"** *"}
s=gets.to_i;puts [?**s]+f[s-4]

Perl, где ты? )