22
\$\begingroup\$

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.
\$\endgroup\$
  • 1
    \$\begingroup\$ Can we use different output characters, or does it need to be consistent? \$\endgroup\$ – Emigna Nov 6 '18 at 18:13
  • \$\begingroup\$ @Emigna Different characters are fine. \$\endgroup\$ – AdmBorkBork Nov 6 '18 at 18:26
  • 1
    \$\begingroup\$ Quite related. \$\endgroup\$ – Charlie Nov 7 '18 at 11:06

23 Answers 23

22
\$\begingroup\$

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.

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

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} $$

\$\endgroup\$
  • \$\begingroup\$ 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. \$\endgroup\$ – Giuseppe Nov 6 '18 at 22:20
  • \$\begingroup\$ @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. \$\endgroup\$ – Arnauld Nov 6 '18 at 22:29
  • \$\begingroup\$ @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. :) \$\endgroup\$ – Arnauld Nov 6 '18 at 22:41
  • 1
    \$\begingroup\$ heh, well your comment about \$hv=0\$ prompted me to go look at my port of your logic and save another couple of bytes! \$\endgroup\$ – Giuseppe Nov 6 '18 at 22:44
8
\$\begingroup\$

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!

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

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.

\$\endgroup\$
4
\$\begingroup\$

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!

\$\endgroup\$
  • \$\begingroup\$ -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). \$\endgroup\$ – Arnauld Nov 7 '18 at 15:05
  • \$\begingroup\$ @Arnauld thanks! \$\endgroup\$ – Giuseppe Nov 7 '18 at 15:39
3
\$\begingroup\$

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!

\$\endgroup\$
3
\$\begingroup\$

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.

\$\endgroup\$
3
\$\begingroup\$

Powershell, 91 bytes

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

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
\$\endgroup\$
2
\$\begingroup\$

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

i;*m="%*s%*s\n";g(n){P"%*s",n,H;F;--i;)P H;P"\n");}f(n){g(n);F;--i;)P m,i,(H,3*n-i+~i,H;F-2;i--;)P"#%*s\n",3*n-3,H;F;--i;)P m,n-i,(H,n+i+i-1,H;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");
	}
}

-19 bytes thanks to @ceilingcat

\$\endgroup\$
1
\$\begingroup\$

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.

\$\endgroup\$
  • \$\begingroup\$ You can remove the space after (n+2*i). \$\endgroup\$ – Zacharý Nov 8 '18 at 13:26
1
\$\begingroup\$

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).

\$\endgroup\$
1
\$\begingroup\$

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.

\$\endgroup\$
1
\$\begingroup\$

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 
]
\$\endgroup\$
1
\$\begingroup\$

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.

\$\endgroup\$
  • 1
    \$\begingroup\$ 46: (' '@~5 6∊⍨1⊥⊢∘,)⌺3 3⊢<(⍉⌽⌊⊢)⍣2∘(∘.+⍨∘⍳¯2+3×⊢) \$\endgroup\$ – Adám Nov 7 '18 at 13:51
  • 1
    \$\begingroup\$ You can't actually use Classic here because of . Rather count 1 byte/char by referring to SBCS as per Meta. \$\endgroup\$ – Adám Nov 7 '18 at 13:52
  • \$\begingroup\$ @Adám Thanks! I don't know how to edit the header, can you do it for me? \$\endgroup\$ – Galen Ivanov Nov 7 '18 at 14:01
  • \$\begingroup\$ What do you mean by editing the header? \$\endgroup\$ – Adám Nov 7 '18 at 14:22
  • 1
    \$\begingroup\$ Edit and copy from here. \$\endgroup\$ – Adám Nov 7 '18 at 14:40
1
\$\begingroup\$

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.

\$\endgroup\$
  • \$\begingroup\$ Welcome to PPCG! You can probably shave off a few bytes here and there by using tricks found in this post. \$\endgroup\$ – Mego Nov 8 '18 at 5:14
  • \$\begingroup\$ @Mego Yep. I was able to save 4 bytes by using $" instead of " ". \$\endgroup\$ – Nathan Mills Nov 8 '18 at 17:14
1
\$\begingroup\$

Perl 5, 176 bytes

$f=$a=<>;$b=3*$a-4;$c='$"x($e-$_)."#".$"x$f."#\n"';$e=$a-1;$d=$"x$e."#"x$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

Based on Nathan Mills' answer above (which I have insufficient rep to comment on!).

$e can be simplified to $a-1 saving 6 bytes; $f can be chain assigned; saving two bytes; Not sure where the other two come from!

While $e can be replaced with $a-1 in the two places it occurs, the extra brackets needed means this only breaks even.

Ungolfed:

$f = $a = <>;
$b = 3 * $a - 4;
$c = '$"x($e-$_)."#".$"x$f."#\n"';
$e = $a - 1;
$d = $" x $e . "#" x $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
\$\endgroup\$
1
\$\begingroup\$

Perl 6, 76 73 bytes

-3 bytes thanks to Jo King

{(' 'x$_-1~\*x$_,{S/.\S(.)+/* {' 'x$0}*/}...*)[^$_,$_ xx$_-2,[R,] ^$_;*]}

Try it online!

Returns a list of lines.

\$\endgroup\$
1
\$\begingroup\$

C (gcc), 158 153 150 bytes

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

Try it online!

\$\endgroup\$
  • \$\begingroup\$ @ceilingcat Thank you. \$\endgroup\$ – Jonathan Frech Nov 8 '18 at 21:00
  • \$\begingroup\$ @ceilingcat Thank you. \$\endgroup\$ – Jonathan Frech Jan 21 at 7:53
0
\$\begingroup\$

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!

\$\endgroup\$
0
\$\begingroup\$

Perl 5, 170 168 166 bytes

$a=<>-1;$\="#\n";print$x=$_=$"x$a."#"x$a;if(s/^( *)  #*/$1 #  $1 /){print}while (s/ #/#  /){print}$z=$_;for(1..$a){print$_=$z}while(s/#  (\s{$a})/ #$1/){print}print$x

This works by the magic of regex. The "if" is only needed to deal with the pathological case of n=2, which otherwise outputs something like:

 ##
 ##
#  #
 ##

probably this can be coded away.

I think there may be a lot more to gain by creating a string up to the mid-point then reversing it. Of course we then need to insert/delete an extra space if n is odd (or use thin-space :p).

Ungolfed

$a = <> -1;                          # Subtracting one is very useful! 
$\ = "#\n";                          # Every line ends with a '#' let perl provide.  
$x=$_ = " " x $a. "#" x $a;          # The horiz line (one short)  
print;                               # print it plus the extra #
if(s/^( *)  #*/$1 #  $1 /){print}    # create a hole and remove a leading space(if n=2 this fails)
while (s/ #/#  /){                   # make the hole bigger      
    print;                           # and print (with a trailing #)
}
$z=$_;                               # store $_ for later use
for (1 .. $a) {                      # nice that we don't have to do 2..$a but not golf-nice  
  $_ =$z;                            # restore $_ (we could use $z but since we have
  print;                             # to restore somewhere, doing  it here saves us bytes)
}
while (s/#  (\s{$a})/ #$1/){         # now move the # to the right and reduce the trailing spaces  
  print;
}
print $x;                            # and finish...

I think this can probably be golfed a bit more, quite apart from significant changes like pushing onto $@ and printing that at the end.

[Golfed spaces around .. and moved print before assigns in two cases, saving on semicolons.]

\$\endgroup\$
  • \$\begingroup\$ saved 20 bytes reordering some instructions TIO, why \s and not just a space in last regex \$\endgroup\$ – Nahuel Fouilleul Jan 22 at 10:56
0
\$\begingroup\$

J, 59 45 41 bytes

h=.|.,],~,:@{.#~_2+#
' #'{~h@|.@|:@h@=@i.

Try it online!

Will add explanation tonight.

\$\endgroup\$
0
\$\begingroup\$

Perl 5, 98 bytes

$_=2x$_.1x$_.$/;s/2//;s/.$/ #/,y/1/ /while$a.=$_,$b=$_.$b,s/2[#1]/# /;$_=$a.$_ x("@F"-2).$b;y/2/ /

TIO

\$\endgroup\$

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.