A nicely-spaced ASCII spiral

13

1

Consider this spiral

###########
#
# #######
# #     #
# # ### #
# # # # #
# # # # #
# #   # #
# ##### #
#       #
#########

Starting in the centre:

  • The first line (upwards) has 3 characters.
  • The second line has the same number of characters (3)
  • Next, we add two chars (5) for the next two sides.
  • This pattern continues, two sides the same length then increase the length by 2.

I want to generate this spiral for N lines.

  • Write in any language.
  • The input/argument, etc. is the number of lines in your spiral.
  • Each line starts with the ending character of the previous line in the direction 90 degrees clockwise of the previous line.
  • I don't care how much whitespace is before or after each line, as long as the elements of the spiral line up.
  • Output text to draw the spiral with any non-whitespace character you choose.
  • Attempt to do this in the smallest number of bytes.

Test cases (using a hash as output):

N = 1

#
#
#

N = 2

###
#
#

N = 3

###
# #
# #
  #
  #

N = 10

###########
#
# #######
# #     #
# # ### #
# # # # #
# # # # #
# #   # #
# ##### #
#       #
#########

AJFaraday

Posted 2018-06-26T10:59:21.377

Reputation: 10 466

@Shaggy "Output text to draw the spiral with any non-whitespace character you choose." Yep, whatever you like. – AJFaraday – 2018-06-26T11:09:11.207

3Related 1; Related 2. – Kevin Cruijssen – 2018-06-26T11:10:30.427

3The initial image has an * instead of #. Expected? – Wernisch – 2018-06-26T11:56:04.133

@Wernisch It was intended to be a helpful starting point, but I suppose it's misleading. You can use any character you like. – AJFaraday – 2018-06-26T12:36:28.930

Answers

11

05AB1E, 13 11 bytes

Code:

Thanks to Emigna for saving two bytes!

LDÈ-Ì'#3Ý·Λ

Uses the 05AB1E encoding. Try it online!

Explanation

The lengths of each individual edge on the spiral starts with length 3 and gradually increases every two steps by two:

$$ 3, 3, 5, 5, 7, 7, 9,\dots $$

For a spiral with \$n\$ edges, we just need to trim this list to size \$n\$. This is done with the following piece of code:

L                # Create a list from [1 .. input]
 DÈ              # Duplicate and check for each number if even
   -             # Subtract that from the first list
    Ì            # Add 2

This basically gives us the desired list of lengths.

     '#          # Push the '#' character
       0246S     # Push the array [0, 2, 4, 6]
            Λ    # Write to canvas

The canvas works as a function that pops three parameters (where the rightmost parameter is popped first): <length(s)>, <char(s)>, <direction(s)>. The directions parameter is in this case a list of numbers. The numbers that correspond to the directions are:

$$ \left[\begin{array}{r} 7 & 0 & 1 \\ 6 & \circ & 2 \\ 5 & 4 & 3 \end{array}\right] $$

In this case, [0, 2, 4, 6] corresponds to the directions list [↑, →, ↓, ←]. The canvas iterates over each length retrieved from the list of lengths, uses the '#' character and cyclically iterates over the directions list.

Adnan

Posted 2018-06-26T10:59:21.377

Reputation: 41 965

0246S = 3Ý· – Emigna – 2018-06-27T10:45:10.867

@Emigna Ah I did not think of that, thanks! – Adnan – 2018-06-27T14:06:34.550

6

Python 2, 176 170 165 161 157 bytes

g=lambda a,r:r and g(map(''.join,zip(*a))[::-1],r-1)or a
R=['#']
n=1
exec"R=g(['  '+l for l in g(R,n)][:-1]+[(n+2)*'#'],3*n);n+=1;"*input()
print'\n'.join(R)

Try it online!

Repeatedly: Uses g to rotate the nth iteration of the spiral into a 'canonical' position (similar to N=3 or N=7), adds a new segment by adding 2 spaces at the left of each existing row, then replacing the last row with all '#'s (resulting in a position comparable to N=4 or N=8), and finally using g again to rotate it back to the correct position. Lather, rinse, repeat.

Chas Brown

Posted 2018-06-26T10:59:21.377

Reputation: 8 959

4

Charcoal, 16 15 14 bytes

↶FN«¶×#⁺³⊗÷ι²↷

-2 bytes thanks to @Neil.

Try it online (verbose) or Try it online (pure).

Explanation:

Printing direction is to the right by default, and we want to start upwards, so we start by rotating 45 degrees counterclockwise:

PivotLeft();
↶

Then loop i in the range [0, input):

For(InputNumber()){ ... }
FN« ...

Print a new-line to mimic the effect of moving back one position:

Print("\n");
¶

Print "#" x amount of times in the current direction:

Print(Times("#", ... ));
×# ...

Where x is: 3 + i // 2 * 2:

Add(3,Doubled(IntegerDivide(i,2))
⁺³⊗÷ι²

And then rotate 45 degrees clockwise for the next iteration of the loop:

PivotRight();
↷

Kevin Cruijssen

Posted 2018-06-26T10:59:21.377

Reputation: 67 575

I assume there's no command in Charcoal for moving backwards? – Emigna – 2018-06-26T12:18:11.397

@Emigna Not sure, but I couldn't find it either. Was indeed looking for one myself when I was writing this answer. There is a Move with a given direction, as well as a Jump with given coordinates, but not a MoveBack unfortunately. – Kevin Cruijssen – 2018-06-26T12:22:46.253

1⊗÷ι² is a byte shorter than ⁻ι﹪ι². Also, you can get the effect of moving back by printing a \n before the #s, which will allow you to remove the »# for an additional overall byte saving. – Neil – 2018-06-26T14:25:42.643

@Neil Thanks for the ⊗÷ι², but what would the changes be for printing a \n before the #s? The spiral is incorrect if I add the Print("\n").

– Kevin Cruijssen – 2018-06-26T14:40:52.863

Because the arms now overlap you need to make them an extra # longer. – Neil – 2018-06-26T14:43:06.960

3

Python 2, 179 178 bytes

thanks to Kevin Cruijssen for -1 byte.

n=input()
S,H=' #'
m=[S*n]*(n%2-~n)
x=n/4*2
y=0--n/4*2
k=2
m[y]=S*x+H+S*n
M=[1,0,-1,0]*n
exec'exec k/2*2*"x+=M[~k];y+=M[k];m[y]=m[y][:x]+H+m[y][x+1:];";k+=1;'*n
print'\n'.join(m)

Try it online!


Python 2, 179 bytes

In this approach formulas are used for x and y deltas instead of a lookup list.

n=input()
S,H=' #'
m=[S*n]*(n%2-~n)
x=n/4*2
y=0--n/4*2
k=2
m[y]=S*x+H+S*n
exec'exec k/2*2*"x+=k%-2+k%4/3*2;y-=(k%2or k%4)-1;m[y]=m[y][:x]+H+m[y][x+1:];";k+=1;'*n
print'\n'.join(m)

Try it online!

ovs

Posted 2018-06-26T10:59:21.377

Reputation: 21 408

n+1+n%2 to n%2-~n for -1 byte. And I need to remember 0--n/4*2 being 1 shorter than -(-n/4*2). Nice answer, +1 from me. – Kevin Cruijssen – 2018-06-26T15:17:56.513

1

JavaScript (ES6), 185 bytes

Sure this can be golfed more, maybe with currying, but here's my very humble attempt. Line breaks added for readability except penultimate character

r=(a,n=1)=>n?r(a.reduce((_,c)=>c).map((_,i)=>a.map(e=>e[i])).reverse(),n-1):a,
s=n=>n?r(s(n-1)).map((r,i)=>[...r,w,w].map(x=>i?x:'#')):[[w=' ']],
d=n=>r(s(n),1-i).map(r=>r.join``).join`
`

Usage: d(10) returns a string as per the N=10 challenge example.

Defines a function r(a,n) to rotate an array a by n turns; a function s(n) to generate a 2-dimensional array representing a spiral of size n by recursively rotating and adding spacing and lines (not rotated back to starting position); and a function d(n) to draw a spiral of size n, rotated consistently as per the challenge, and rendered as a returned string.

This was a really fun challenge :¬)

Chris M

Posted 2018-06-26T10:59:21.377

Reputation: 191