Spiral out the Alphabet!

13

Look at the following string. Notice a pattern?

ABEFNOPEFGH
DC G  Q   I
M  H  R   J
LKJI  S   K
D     T   L
C     U   M
BAZYXWV   N
E         O
D         P
C         Q
BAZYXWVUTSR

As some might have noticed, it's basically a spiral of the alphabet, where the distances between rows / columns are gradually increasing by 1 space / newline.

Rigorous definition

  • Let's have a counter c, which is initially 0.
  • We write out the first c + 1 letters of the alphabet from left to right: A.
  • Then, from top to bottom the next (c + 1)(c + 2)/2 letters (add B): AB.

  • From left to right, the next (c + 1)(c + 2)/2 (add C):

    AB
     C
    
  • And from bottom to top, the next c + 1 letters (add D):

    AB
    DC
    
  • Reached the end of the cycle. Hence, let's increment c (which becomes 1). Then, it starts back from the first step, the only difference being that instead of using the first c + 1 letters of the alphabet, we use the next c + 1 letters, starting from the last element of this cycle (D in this case, so we continue with EFG...). When Z is reached, it cycles back from A.

Task

Given an integer N (which is positive for 1-indexing or non-negative for 0-indexing), output the first N cycles of the spiral.

Rules

  • You can either use the lowercase or the uppercase alphabet, but your choice must be consistent (only use one of them, mixing is not allowed).

  • You can take input and provide output through any of the standard methods, in any programming language, while noting that these loopholes are forbidden by default.

  • Acceptable output formats: multiline string, a list of strings representing lines, a list containing multiple lists of characters, each representing one line, or anything else you find suitable. In case you don't choose the first format, it would be nice to include a pretty-print version of your code.

  • This is , so the shortest code in bytes (in each language) which fulfils the requirements wins!


Test cases

The input integer will be separated by its corresponding output through a newline, and the tests will be separated using dashes. Note that these are 1-indexed.

1

AB
DC

--------

2

ABEF
DC G
M  H
LKJI 

--------

3

ABEFNOP
DC G  Q
M  H  R
LKJI  S
D     T
C     U
BAZYXWV

-------

4

ABEFNOPEFGH
DC G  Q   I
M  H  R   J
LKJI  S   K
D     T   L
C     U   M
BAZYXWV   N
E         O
D         P
C         Q
BAZYXWVUTSR

-------
5

ABEFNOPEFGHFGHIJ
DC G  Q   I    K
M  H  R   J    L
LKJI  S   K    M
D     T   L    N
C     U   M    O
BAZYXWV   N    P
E         O    Q
D         P    R
C         Q    S
BAZYXWVUTSR    T
R              U
Q              V
P              W
O              X
NMLKJIHGFEDCBAZY

------

6

ABEFNOPEFGHFGHIJSTUVWX
DC G  Q   I    K     Y
M  H  R   J    L     Z
LKJI  S   K    M     A
D     T   L    N     B
C     U   M    O     C
BAZYXWV   N    P     D
E         O    Q     E
D         P    R     F
C         Q    S     G
BAZYXWVUTSR    T     H
R              U     I
Q              V     J
P              W     K
O              X     L
NMLKJIHGFEDCBAZY     M
S                    N
R                    O
Q                    P
P                    Q
O                    R 
NMLKJIHGFEDCBAZYXWVUTS

Mr. Xcoder

Posted 2017-12-01T09:05:52.940

Reputation: 39 774

The testcases should be n=1,2,3,5,6, I think. – TFeld – 2017-12-01T10:22:38.810

Answers

9

Charcoal, 31 bytes

F⮌…⁰NB⁺²⊘×ι⁺³ι⭆α§α⁺λ÷×ι⊕×ι⁺⁹⊗ι⁶

Try it online! Link is to verbose version of code. Note: The deverbosifier outputs a trailing separator for some reason. Explanation:

F⮌…⁰NB

Draw boxes in reverse order of size (largest to smallest).

⁺²⊘×ι⁺³ι

Calculate the size of the box.

⭆α§α⁺λ

Draw the border of the box using a rotated alphabet.

÷×ι⊕×ι⁺⁹⊗ι⁶

Calculate the letter that would appear at the top left of the box (0-indexed).

Neil

Posted 2017-12-01T09:05:52.940

Reputation: 95 035

6

Python 2, 176 bytes

n=input()
k=n*-~n/2+1
a=eval(`[[' ']*k]*k`)
x=y=z=0
for s in range(4*n+4):exec s/4*(s/4+1)/2*"if'!'>a[y][x]:a[y][x]=chr(z%26+65);z+=1\nx+=abs(2-s%4)-1;y+=s%2-s%4/3*2\n"
print a

Try it online!

Explanation

We construct an empty array of spaces of the right size, then move over it like this, starting in the top-left corner:

  • 1 step →, 1 step ↓, 1 step ←, 1 step ↑

  • 3 steps →, 3 steps ↓, 3 steps ←, 3 steps ↑

  • 6 steps →, 6 steps ↓, 6 steps ←, 6 steps ↑

  • 10 steps →, 10 steps ↓, 10 steps ←, 10 steps ↑

Every time we find a blank cell, we put a letter there and cycle to the next letter in the alphabet.

In the code, s%4 is the direction (→↓←↑), and we step this many times:

TriangularNumber(s/4) = s/4*(s/4+1)/2.

Golf opportunities

  • Is there a shorter way to map s%4 to 1,0,-1,0 than abs(2-s%4)-1?

  • Is there a shorter way to map s%4 to 0,1,0,-1 than s%2-s%4/3*2?

Credits

  • Mr. Xcoder saved a byte.

Lynn

Posted 2017-12-01T09:05:52.940

Reputation: 55 648

2

+1 Wow, this is just brilliant. Took me a while to figure out how it works. I found a shorthand for 21/(s%4+3)%3-1: s%2-2*(s%4>2) (179 bytes). It might still be golfable though

– Mr. Xcoder – 2017-12-01T15:39:24.360

4

C,  305  281 bytes

Thanks to @Mr. Xcoder for saving four bytes!

#define L(x,y)l[x][y]=a++%26+65;
#define F for(
c,i,j,a,p;f(n){char**l=calloc(8,i=a=n*n*4);F;i--;memset(l[i],32,a))l[i]=malloc(a);F c=a=p=i=0;c<n;++c){F i=p;i<c+p+!c;)L(j=0,c+i++)F p=i;j<=-~c*(c+2)/2;)L(j++,c+i)F;c+i-1;)L(j-1,c+--i)F i=0;i<=c;)L(j+~i++,0)}F i=0;i<j;)puts(l[i++]);}

Try it online!

Steadybox

Posted 2017-12-01T09:05:52.940

Reputation: 15 798

1First time I see a #define for for( (that actually saves bytes). +1 from me. :) – Kevin Cruijssen – 2017-12-01T15:01:58.613

2

Python 2, 262 260 254 245 bytes

lambda n:[[[' ',chr(65+(4*(i<j)+sum((i<j)*8+2+I*[5,9][i<j]+sum(2*R(I))for I in R(S(i,j)-1))+[i+j,-i-j][i<j])%26)][max(i,j)==S(i,j)*-~S(i,j)/2or i*j<1]for i in R(1+n*-~n/2)]for j in R(1+n*-~n/2)]
R=range
S=lambda x,y:int((8*max(x,y)+1)**.5+.99)/2

Try it online!

New method with more maths!

Returns a list of char-lists.


Old version:

Python 2, 322 321 308 298 bytes

R=range
n=input()
w=1+n*-~n/2;r=[w*[' ']for _ in R(w)];r[0][0]='A';l=R(1,w*w*9);c=lambda:chr(65+l.pop(0)%26)
for i in R(n+1):
 w=1+i*-~i/2;W=w-i
 for x in R(W,w):r[0][x]=c()
 for y in R(1,w):r[y][w-1]=c()
 for x in R(1,w):r[w-1][w+~x]=c()
 for x in R(1,w-W):r[w+~x][0]=c()
for l in r:print`l`[2::5]

Try it online!

TFeld

Posted 2017-12-01T09:05:52.940

Reputation: 19 246

@Mr.Xcoder Isn't just .49 sufficient enough in that case, or does it fail for very big inputs? – Kevin Cruijssen – 2017-12-01T14:40:40.130

1

@KevinCruijssen Not sure, but this works for sure, 260 bytes.

– Mr. Xcoder – 2017-12-01T14:42:50.567

I don't know Python that well, but is it possible to make an inline-variable for i<j, since you are using it four times? – Kevin Cruijssen – 2017-12-01T14:48:30.673

1245 bytes by golfing +1==1+ from my previous method – Mr. Xcoder – 2017-12-01T14:56:10.663

1

Perl 5, 177 +2 (-nl) = 179 bytes

2 bytes saved thanks to Xcali

sub n{chr 65+$c++%26}@O=(n);while($,++<$_){$_.=$"x$,for@O;push@O,($"x length$O[0])x$,;$O[0]=~s/ /n/eg;s/ $/n/e for@O;1while$O[-1]=~s/.*\K /n/e;s/^ /n/e for reverse@O}print for@O

Try it online

Nahuel Fouilleul

Posted 2017-12-01T09:05:52.940

Reputation: 5 582

You can save a byte by using -1 instead of $#O. Also using $, instead of $n will let you remove the space before for in $_.=$"x$n for@O – Xcali – 2017-12-01T16:34:19.527

thank you, i was looking for other improvements but could not find for the moment – Nahuel Fouilleul – 2017-12-01T16:45:12.497

saved 2 more bytes changing regex s/ (?=\S)/n/e to s/.*\K /n/e – Nahuel Fouilleul – 2017-12-01T16:49:41.977

0

APL (Dyalog Classic), 47 bytes

(⎕a⍴⍨≢i)@i⍴∘''⊃⌈/i←∪+\↓1⍪⊃⍪⌿(+\⍳⎕)⌿¨⊂⍪∘-⍨∘.≠⍨⍳2

Try it online!

ngn

Posted 2017-12-01T09:05:52.940

Reputation: 11 449