Given an input integer n > 1, output an ASCII-art octagon with side lengths composed of n characters. See examples below:

n=2
 ##
#  #
#  #
 ##

n=3
  ###
 #   #
#     #
#     #
#     #
 #   #
  ###

n=4
   ####
  #    #
 #      #
#        #
#        #
#        #
#        #
 #      #
  #    #
   ####

n=5
    #####
   #     #
  #       #
 #         #
#           #
#           #
#           #
#           #
#           #
 #         #
  #       #
   #     #
    #####

and so on.

You can print it to STDOUT or return it as a function result.

Any amount of extraneous whitespace is acceptable, so long as the characters line up appropriately.

Rules and I/O

  • Input and output can be given by any convenient method.
  • You can use any printable ASCII character instead of the # (except space), but the "background" character must be space (ASCII 32).
  • Either a full program or a function are acceptable.
  • Standard loopholes are forbidden.
  • This is so all usual golfing rules apply, and the shortest code (in bytes) wins.
  • 1
    Can we use different output characters, or does it need to be consistent? – Emigna Nov 6 at 18:13
  • @Emigna Different characters are fine. – AdmBorkBork Nov 6 at 18:26
  • 1
    Quite related. – Charlie Nov 7 at 11:06

18 Answers 18

05AB1E, 3 bytes

7ÝΛ

Try it online!

Explanation

      # implicit input as length
      # implicit input as string to print
7Ý    # range [0...7] as directions
  Λ   # canvas print

See this answer to understand the 05AB1E canvas.

  • Surely this should be 5 bytes? Or do code golf challenges see bytes and characters as interchangeable – Doug Nov 7 at 17:10
  • 3
    @Doug: It is 3 bytes in 05ab1e's code page – Emigna Nov 7 at 17:11
  • Oh, cool! Thanks for the docs link! – Doug Nov 7 at 17:58
  • >:( damnit, adnan – ASCII-only Nov 11 at 5:14

JavaScript (ES6), 114 106 105 104 103 bytes

n=>(g=x=>v=x*2>w?w-x:x,F=x=>~y?`# 
`[~x?(h=g(x--))*g(y)>0&h+v!=n|n>h+v:(y--,x=w,2)]+F(x):'')(y=w=--n*3)

Try it online!

How?

This builds the output character by character.

Given the input \$n\$, we compute:

$$n'=n-1\\w=3n'$$

For each character at \$(x,y)\$, we compute \$(h,v)\$:

$$h=w/2-\left|x-w/2\right|\\v=w/2-\left|y-w/2\right|$$

The cells belonging to the octagon satisfy one of the following conditions:

  • (\$h=0\$ OR \$v=0\$) AND \$h+v\ge n'\$ (in red below)
  • \$h+v=n'\$ (in orange below)

For example, with \$n=4\$ (and \$n'=3\$):

$$\begin{matrix}(0,0)&(1,0)&(2,0)&\color{red}{(3,0)}&\color{red}{(4,0)}&\color{red}{(4,0)}&\color{red}{(3,0)}&(2,0)&(1,0)&(0,0)\\ (0,1)&(1,1)&\color{orange}{(2,1)}&(3,1)&(4,1)&(4,1)&(3,1)&\color{orange}{(2,1)}&(1,1)&(0,1)\\ (0,2)&\color{orange}{(1,2)}&(2,2)&(3,2)&(4,2)&(4,2)&(3,2)&(2,2)&\color{orange}{(1,2)}&(0,2)\\ \color{red}{(0,3)}&(1,3)&(2,3)&(3,3)&(4,3)&(4,3)&(3,3)&(2,3)&(1,3)&\color{red}{(0,3)}\\ \color{red}{(0,4)}&(1,4)&(2,4)&(3,4)&(4,4)&(4,4)&(3,4)&(2,4)&(1,4)&\color{red}{(0,4)}\\ \color{red}{(0,4)}&(1,4)&(2,4)&(3,4)&(4,4)&(4,4)&(3,4)&(2,4)&(1,4)&\color{red}{(0,4)}\\ \color{red}{(0,3)}&(1,3)&(2,3)&(3,3)&(4,3)&(4,3)&(3,3)&(2,3)&(1,3)&\color{red}{(0,3)}\\ (0,2)&\color{orange}{(1,2)}&(2,2)&(3,2)&(4,2)&(4,2)&(3,2)&(2,2)&\color{orange}{(1,2)}&(0,2)\\ (0,1)&(1,1)&\color{orange}{(2,1)}&(3,1)&(4,1)&(4,1)&(3,1)&\color{orange}{(2,1)}&(1,1)&(0,1)\\ (0,0)&(1,0)&(2,0)&\color{red}{(3,0)}&\color{red}{(4,0)}&\color{red}{(4,0)}&\color{red}{(3,0)}&(2,0)&(1,0)&(0,0)\end{matrix} $$

  • Wow, this is awesome! I think \$h + v \geq n'\$ can be simplified to \$h+v>n'\$, although I'm not sure if that helps the golfing logic at all. – Giuseppe Nov 6 at 22:20
  • @Giuseppe It could indeed be simplified that way if both conditions were tested. But in the code, the cases \$hv=0\$ and \$hv\neq0\$ are separated. However, I'm actually testing the opposite condition (\$n'>h+v\$), which already is 1 byte shorter. – Arnauld Nov 6 at 22:29
  • @Giuseppe Your comment prompted me to have a closer look at the formula and I finally saved a byte by writing it a bit differently. :) – Arnauld Nov 6 at 22:41
  • 1
    heh, well your comment about \$hv=0\$ prompted me to go look at my port of your logic and save another couple of bytes! – Giuseppe Nov 6 at 22:44

Charcoal, 5 bytes

GH*N#

My first answer with Charcoal!

Explanation:

GH*N#      //Full program
GH          //Draw a hollow polygon
   *         //with 8 sides
    N       //of side length from input
      #      //using '#' character

Try it online!

  • 3
    For those who prefer verbose Charcoal, that's PolygonHollow(:*, InputNumber(), "#");. – Neil Nov 6 at 18:51

Canvas, 15 14 12 bytes

/⁸⇵╷+×+:⤢n╬┼

Try it here!

Explanation:

/             a diagonal of length n
 ⁸            the input,
  ⇵           ceiling divided by 2, (storing the remainder)
   ╷          minus one
    #×        repeat "#" that many times
      +       append that to the diagonal
       :⤢n    overlap that with its transpose
          ╬┼  quad-palindromize with the overlap being the remainder stored earlier

Alternative 12-byter.

R, 122 117 115 bytes

function(n){n=n-1
m=matrix(0,y<-3*n+1,y)
v=t(h<-(w=3*n/2)-abs(row(m)-1-w))
m[h*v&h+v-n|h+v<n]=' '
write(m,1,y,,"")}

Try it online!

Ports the logic from Arnauld's answer, specifically this revision in case there are further improvements. Another 2 bytes saved thanks to Arnauld's suggestion of inverting the logic!

  • -2 bytes by doing it the other way around (I can't do h*v&h+v-n in JS because & is a bitwise operator; but it's a logical one in R, so that works). – Arnauld Nov 7 at 15:05
  • @Arnauld thanks! – Giuseppe Nov 7 at 15:39

Python 2, 96 bytes

a=b=n=input()
while a>2-n-n:a-=1;b-=a/~-n+1;s=(-~b*' '+'#').ljust(n);print s+s[-1]*(n-2)+s[::-1]

Try it online!

Python 2, 81 bytes

a=d=n=input()-1
while a<=n:print' '*a+'#'+' #'[a==n]*(3*n-a+~a)+'#';d-=1;a-=d/n+1

Try it online!


Python 2, 75 bytes

a=d=n=input()-1
while a<=n:print' '*a+`' `'[a==n]*(3*n-a+~a)`;d-=1;a-=d/n+1

Try it online!

If mixing output characters is OK.

Powershell, 91 bytes

param($n)($s=' '*--$n+'#'*$n+'#')
--$n..0+,0*$n+0..$n|%{' '*$_+"#$(' '*(3*$n-2*$_+2))#"}
$s

PowerShell, 107 97 bytes

param($n)($z=$n-1)..1+,0*$n+1..$z|%{" "*$_+"#"+($x=" "*($z-$_))+(" ","#")[!($_-$z)]*($n-2)+"$x#"}

Try it online!

If there was a cheap way to reverse the first half, this answer would feel a lot better. It builds the left half, then the core (which is either x #'s or spaces), then mirrors the left's logic to make the right. Fun fact, you don't need to copy over trailing white-space.

Unrolled and explained:

param($n)
($z=$n-1)..1 + ,0*$n + 1..$z |%{  #Range that repeats 0 n times in the middle
" "*$_ + "#" +($x=" "*($z-$_)) +  #Left side
(" ","#")[!($_-$z)]*($n-2) +      #Core that swaps when it's the first or last row
"$x#"}                            #Right side which is left but backwards

C (clang), -DP=printf( -DF=for(i + 179 = 199 bytes

i;*m="%*d%*d\n";g(n){P"%*d",n,0);F=0;i<n-1;i++)P"%d",0);P "\n");}
f(n){g(n);F=1;i<n;i++)P m,n-i,0,n+i+i-1,0);F=0;i<n-2;i++)P m,1,0,3*n-3,0);F=n-1;i;i--)P m,n-i,0,n+i+i-1,0);g(n);}

Try it online!

Ungolfed:

f(n){
	int i;
	printf("%*d",n,0);
	for(i=0;i<n-1;i++){
		printf("0");
	}
	printf("\n");
	for(i=1;i<n;i++){
		printf("%*d%*d\n",n-i,0,n+i+i-1,0);
	}
	for(i=0;i<n-2;i++){
		printf("0%*d\n",n+n+n-3,0);
	}
	for(i=n-1;i>0;i--){
		printf("%*d%*d\n",n-i,0,n+i+i-1,0);
	}
	printf("%*d",n,0);
	for(i=0;i<n-1;i++){
		printf("0");
	}
}

Python 2, 130 bytes

def f(n):
 a=[' '*~-n+n*'#']
 b=[' '*(n-i-2)+'#'+' '*(n+2*i) +'#'for i in range(n-2)]
 return a+b+['#%*s'%(3*n-3,'#')]*n+b[::-1]+a

Try it online!

On mobile, so not incredibly golfed.

  • You can remove the space after (n+2*i). – Zacharý Nov 8 at 13:26

Batch, 260 bytes

@echo off
set s=
for /l %%i in (1,1,%1)do call set s= %%s%%
echo %s% %s: =#%
call:c %1,-1,3
for /l %%i in (1,1,%1)do echo   #%s:~2%%s%%s:~2%#
call:c 3,1,%1
echo %s% %s: =#%
exit/b
:c
for /l %%i in (%*)do call echo %%s:~,%%i%%#%%s:~%%i%%%s%%%s:~%%i%%#

Outputs two leading spaces on each line. Explanation: Batch has no string repetition operator, limited string slicing capability and requires separate statements to perform arithmetic. It was therefore golfiest to make up a string of the input length in spaces (Batch can at least translate these to #s for the top and bottom lines) and then slice from or to a specific position ranging from 3 to the length to generate the diagonals (this is what the last line of the script achieves).

Ruby, 96 bytes

->n{[*(n-=2).step(z=n*3+2,2),*[z]*n,*z.step(n,-2)].map{|x|([?#]*2*('# '[x<=>n]*x)).center(z+2)}}

Try it online!

Not very golfed yet. Might golf if I find the time.

Red, 171 bytes

func[n][c:(a: n - 1)* 2 + n
b: collect[loop c[keep pad/left copy"^/"c + 1]]s: 1x1 s/1: n
foreach i[1x0 1 0x1 -1x1 -1x0 -1 0x-1 1x-1][loop a[b/(s/2)/(s/1): #"#"s: s + i]]b]

Try it online!

Explanation:

Red[]
f: func [ n ] [
    a: n - 1                                         ; size - 1
    c: a * 2 + n                                     ; total size of widht / height 
    b: collect [                                     ; create a block
        loop c [                                     ; composed of size - 1 rows
            keep pad/left copy "^/" c + 1            ; of empty lines of size c (and a newline)
        ]
    ]
    s: a * 1x0 + 1                                   ; starting coordinate
    foreach i [ 1x0 1 0x1 -1x1 -1x0 -1 0x-1 1x-1 ] [ ; for each offset for the 8 directions
        loop a [                                     ; repeat n - 1 times  
            b/(s/2)/(s/1): #"#"                      ; set the array at current coordinate to "#"
            s: s + i                                 ; next coordinate
        ]        
    ]
    b                                                ; return the block 
]

APL (Dyalog Unicode), 46 bytesSBCS

(' '@~5 6∊⍨1⊥⊢∘,)⌺3 3⊢<(⍉⌽⌊⊢)⍣2∘(∘.+⍨∘⍳¯2+3×⊢)

This solution was provided by Adám - thanks!

Try it online!

My (almost) original solution:

APL (Dyalog Unicode), 61 bytesSBCS

(((⊃∘' #'¨1+5∘=+6∘=)⊢)1⊥⊢∘,)⌺3 3⊢<(((⊖⌊⊢)⌽⌊⊢)(∘.+⍨(⍳¯2+3×⊢)))

Try it online!

Thanks to Adám for his help!

The idea is to find the "diamond" that lies partly in the square and apply an edge-detect filter to "outline" the octagone.

Perl 5, 201 197 188 187 186 bytes:

$a=<>;$b=3*$a-4;$c='$"x($e-$_)."#".$"x$f."#\n"';$e=($b-$a)/2+1;$d=$"x$e."#"x$a.$/;$f=$a;print$d,(map{(eval$c,$f+=2)[0]}1..$a-2),("#".$"x$b."#\n")x$a,(map{$f-=2;eval$c}reverse 1..$a-2),$d

Try it online!

Reads the size of the octagon from first line of STDIN.

New contributor
Nathan Mills is a new contributor to this site. Take care in asking for clarification, commenting, and answering. Check out our Code of Conduct.
  • Welcome to PPCG! You can probably shave off a few bytes here and there by using tricks found in this post. – Mego Nov 8 at 5:14
  • @Mego Yep. I was able to save 4 bytes by using $" instead of " ". – Nathan Mills Nov 8 at 17:14

C (gcc), 158 153 bytes

O,c,t,g;o(n){for(O=2*~-n,t=c=O+n;t--;puts(""))for(g=c;g--;)putchar(32+(!t|t>c-2?g>n-2&g<=O:t<n-1|t>O?t+O==g|t-O==g|c-g-t-n==n-1|c-g-t+n==3-n:!g|g>c-2));}

Try it online!

Python 3, 224 bytes

n=int(input())
z=" "*(n-1)+"#"*n+" "*(n-1)
print(z)
for i in range(n-2):print(" "*(n-i-2)+"#"+" "*(i*2+n)+"#")
print((("#"+" "*(n*3-4)+"#\n")*n)[:-1])
for i in range(n-3,-1,-1):print(" "*(n-i-2)+"#"+" "*(i*2+n)+"#")
print(z)

Try it online!

New contributor
glietz is a new contributor to this site. Take care in asking for clarification, commenting, and answering. Check out our Code of Conduct.

Your Answer

 

By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.

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