There are 4 regular polygons that we can construct using ASCII art:

                              . . .
                  . . .      . . . .
  .     . . .    . . . .    . . . . .
 . .    . . .   . . . . .   . . . . .
. . .   . . .    . . . .    . . . . .
                  . . .      . . . .
                              . . .

That is, a triangle, a square, a hexagon and an octagon.

Your task is to take two inputs and produce the corresponding ASCII art polygon.


The two inputs will be:

  • A string/list of printable ASCII characters (i.e. with code points in the range \$33\$ (!) to \$126\$ (~)
  • An indicator as to which polygon you should output. This indicator can be any \$4\$ distinct and consistent values (e.g. 1234, ABCD, etc.), but each must be only one character long, to avoid any exploitation of this input.

The string will be a perfect fit for the polygon, meaning that, if the shape to be outputted is:

The string may have repeated characters, but will always fit the appropriate lengths for the given shape/sequence, so there is no need to worry about too many/few characters.


You should output a shape, indicated by the second input, in the format shown at the top of the challenge with the . replaced with the characters in the inputted string. Each character must be separated by a single space, and leading/trailing whitespace is acceptable, so long as the shape is preserved. The characters may be used in any order, but each character in the string must only appear once. For example, for a hexagon using ABCDABC, the following are valid:

 A B     B A
C D A   A B C
 B C     D C

as each character from the string is used once. However,

 A A    A B    AD
A A A  A B C   ABC
 A A    C A    BC

are not valid (the first only has A, the second doesn't have a D and the third doesn't have spaces between characters).

This is so the shortest code in bytes wins

Test cases

These test cases use T, S, H and O to indicate which shape the output should be in. The string of characters is on the first line and the indicator on the second


 i 1
+ K 8
{ ^ w
 s W


   , r 1 ~
  v i 4 l {
 W @ + r < v
z P ; m q > :
 8 g J c g g
  $ j ` $ R
   F h D V


0 J
+ +


 n [
V y j


# 5
~ m


  ' * g
 ( A U F
V ~ O u 2
n = 1 7 2
s ' | S 1
 1 q & j
  = + #

  • 2
    \$\begingroup\$ The current input spec could be abused. I could specify that a triangle should be specified as [[0,0,1,0],[0,1,0,1],[1,0,1,0,1]] for example, and so avoid the need to encode the triangle within code. Or is that ok? \$\endgroup\$ Oct 26, 2019 at 7:55
  • 1
    \$\begingroup\$ Actually, scratch that, I found a way more abusive solution: eval. The four distinct inputs just define functions which format the four shapes. @cairdcoinheringaahing, I recommend scoring as length(code)+length(indicators); or restrict indicator to one character. \$\endgroup\$ Oct 26, 2019 at 12:53
  • 9
    \$\begingroup\$ However, I'd expect the community to downvote answers which violate the spirit of the challenge, such as an answer that uses that "loophole", and I certainly would downvote such a boring answer I really do not think that we should downvote an answer to a code golf question that is both golfed and fits the defined spec! \$\endgroup\$ Oct 26, 2019 at 13:29
  • 3
    \$\begingroup\$ It’s unclear, and subjective, to refer to the “spirit” of the challenge. I think it’s better to have a clear spec as to what’s allowed and what’s not \$\endgroup\$
    – Luis Mendo
    Oct 26, 2019 at 14:41
  • 1
    \$\begingroup\$ @Arnauld The < and > characters didn't work with the <pre> tags. Updated to fix that \$\endgroup\$ Oct 27, 2019 at 0:54

5 Answers 5


Ruby, 139 138 bytes

->s,n{w=(((s=s.chars*' ').size/n=n.hex)**0.5).to_i+k=1&~n
(0..h=w+n/5*~-w).map{|i|s.slice!(0,[w+n/6*~-w,w**k+i,w+h+~i].min*2).center w*4}}

Try it online!

A function taking 2 arguments: a string s and a shape n as a single character in hexadecimal of value 1,2,6 or A. It returns an array of strings.


The input string is converted to an array, then joined back into a string with spaces between the characters. The length of this combined string is twice the length of the original string, minus 1. This is divided by a constant, square rooted and rounded to determine the side length w of the polygon to be drawn. The value of the constant is per the table below. For triangles, the square root is rounded down. For others, it needs to be rounded up, so we round down then add k=1&~n to correct.

 Shape   Triangle  Square  Hexagon  Octagon
 n =     1         2       6        10

We iterate through h rows of output. For the triangle and square, h=w. For the other shapes we need to add w-1 for the hexagon and twice this amount for the octagon. This is done by adding n/5*~-w.

Each line of output is centred and padded to w*4 characters which is enough for all cases. Chunks of s are removed using slice! and outputted. For the octagon the number of symbols to be outputted on each line is the minimum of three numbers: w+w-1 for the centre section, w+i for the top section and w+h-i-1 = w+h+~i for the bottom section. These formulas also work for the hexagon. Note that the numbers have to be multiplied by 2 to account for the spaces.

For the square and triangle, the width of the output is restricted to w. Therefore the formula for the centre section becomes w+n/6*~-w to cover all shapes . For the triangle, the width of the output is further restricted to 1+i. Therefore the formula for the top section becomes w**k+i to cover all shapes.


JavaScript (ES8),  191 176 174  171 bytes

Takes input as (type)(string), where the 1st parameter is 0123 instead of TSHO and the 2nd parameter is an array of characters.

i=>g=(s,n=1,p=0)=>s[o=(h=k=>k?(F=j=>j?s.slice(p,p+=w+=k&6?k&2&&1:-1).join` `.padStart(n*2+w)+`

Try it online!


The shape of each polygon is encoded as groups of 3 bits, packed into a single integer.

Each group of bits is decoded as follows:

  • bit 0: if set, repeat \$n-1\$ times; if cleared, repeat \$n\$ times
  • bits 1-2: if both bits are cleared, decrement the width at each iteration; if only bit 1 is set, increment the width after at iteration; otherwise, leave it unchanged

The initial width of the shape is:

$$w=\cases{ 0,&\text{if $i=0$}\\ n,&\text{if $i=1$}\\ n-1,&\text{if $i>1$} }$$

Triangle (\$i=0\$)


One group: repeat \$n\$ times, increment the width → encoded as \$2\$

Square (\$i=1\$)


One group: repeat \$n\$ times, leave the width unchanged → encoded as \$4\$

Hexagon (\$i=2\$)


Two groups:

  • group 1: repeat \$n\$ times, increment the width → encoded as \$2\$
  • group 2: repeat \$n-1\$ times, decrement the width → encoded as \$1\$

Final encoding: \$2+8\times1=10\$

Octagon (\$i=3\$)


Three groups:

  • group 1: repeat \$n\$ times, increment the width → encoded as \$2\$
  • group 2: repeat \$n-1\$ times, leave the width unchanged → encoded as \$5\$
  • group 3: repeat \$n-1\$ times, decrement the width → encoded as \$1\$

Final encoding: \$2+8\times5+8^2\times1=106\$


Charcoal, 70 bytes

≔⁰ζWφ«⎚≦⊕ζ≔∨⁼θ3ζδ≔E⮌ηκφP⪫Eδ⊟φ F§⪪”;↧↷$↑R{P≧⍘”,IθF⊖ζ«M✳Iκ≧⁺⁻⁶IκδP⪫Eδ⊟φ 

Try it online! Link is to verbose version of code. Takes input as the number of sides and the characters. Explanation:


Keep track of the size of the polygon.


Repeat until we find a size that consumes the input. (This variable is predefined to the truthy value 1000 but I'm going to redefine it.)

Clear the previous attempt from the canvas.


Increment the polygon size.


Calculate the number of characters in the first row, which is 1 for the triangle and the size for the other shapes.


Convert the characters from a string into a list and reverse them. This allows us to iterate over them by popping from the list, which will become empty if we pop all of them.


Print the first row of the polygon.


Loop over one of four strings, 5, 6, 57 or 567, depending on the number of sides. These correspond to the Charcoal directions of the left-most character in each row.


Loop for one fewer row than the polygon size.


Move in the desired direction.


Calculate the number of characters in this row.


Print the row.


05AB1E, 32 bytes


Try it online!

First input is 0 for triangle, 1 for hex, 2 for oct, 3 for square. Second input is a list of characters. The program will hang forever if the list isn't of a valid length.

∞                       # infinite list [1, 2, 3...]
 ε                   }  # map each integer y to a polygon with side length y:
  L                     #  range 1..y (=> triangle)
   D                    #  duplicate
    N                   #  0-based iteration count (effectively y-1)
     +                  #  vectorized add, yielding y..(2y-1)
      D                 #  duplicate
       û                #  palindromize (=> hexagon)
        s               #  swap to get the other copy of y..(2y-1)
         Â              #  push a reversed copy: (2y-1)..y
          ć             #  head extract: 2y-1
           Nи           #  repeat that y-1 times
             ì          #  prepend that to the reversed list
              «         #  append it to the original list (=> octogon)
               y        #  push y
                Dи      #  repeat it y times (=> square)
                  )     #  wrap all polygons in a list
                   ¹è   #  index into the list using the first input
 .Δ   }                 # get the first polygon such that:
   O                    #  its sum
    ∍Q                  #  is equal to the length of the input
 £                      # cut the input in this shape
  »                     # join by newlines, joining sublists with spaces
   .c                   # center

Python 3, 600 548 503 458 358 349 346 326 324 bytes

lambda n,s:p(s,q(s,(0,1,1,1.2)[n],(1,.5,3,5)[n]),n)
q=lambda s,p,q:int(p/2+(p*p/4+len(s)/q)**.5)
g=lambda s,j,i,c,n=1,l=[]:s and-~i and g(s[i:],j,i+(-1,1,0)[(2,1,n<j,(n<j*2-1)+(0<=n-j<j-1))[c]],c,n+1,l+[s[:i]])or l
p=lambda s,k,c:"\n".join([" ".join(i).center(k*(1,(2,4)[c>1])[c>0]-2)for i in g(s,(0,k)[c>1],(k,0)[c==1],c)])

Try it online!

I Know it is a bit long but i want to commit it and improve it over time...

The inputs are:
0 = Triangle
1 = square
2 = Hexagon
3 = Octagon

Thanks to:
- @caird coinheringaahing for saving 2 bytes

  • \$\begingroup\$ You can use an unnamed function as a submission here, so you can remove the f= for -2 bytes: Try it online! \$\endgroup\$ Oct 29, 2019 at 0:15
  • \$\begingroup\$ @cairdcoinheringaahing Thank you for allowing this :) \$\endgroup\$
    – Paul-B98
    Oct 29, 2019 at 10:32

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.