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

24 Answers 24

23
\$\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\$
4
  • \$\begingroup\$ Surely this should be 5 bytes? Or do code golf challenges see bytes and characters as interchangeable \$\endgroup\$
    – Doug
    Commented Nov 7, 2018 at 17:10
  • 3
    \$\begingroup\$ @Doug: It is 3 bytes in 05ab1e's code page \$\endgroup\$
    – Emigna
    Commented Nov 7, 2018 at 17:11
  • \$\begingroup\$ Oh, cool! Thanks for the docs link! \$\endgroup\$
    – Doug
    Commented Nov 7, 2018 at 17:58
  • \$\begingroup\$ >:( damnit, adnan \$\endgroup\$
    – ASCII-only
    Commented Nov 11, 2018 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\$
4
  • \$\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
    Commented Nov 6, 2018 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
    Commented Nov 6, 2018 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
    Commented Nov 6, 2018 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
    Commented Nov 6, 2018 at 22:44
7
\$\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\$
1
  • 3
    \$\begingroup\$ For those who prefer verbose Charcoal, that's PolygonHollow(:*, InputNumber(), "#");. \$\endgroup\$
    – Neil
    Commented Nov 6, 2018 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\$
1
  • \$\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
    Commented Nov 7, 2018 at 15:05
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\$
0
2
\$\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\$
6
  • 1
    \$\begingroup\$ 46: (' '@~5 6∊⍨1⊥⊢∘,)⌺3 3⊢<(⍉⌽⌊⊢)⍣2∘(∘.+⍨∘⍳¯2+3×⊢) \$\endgroup\$
    – Adám
    Commented Nov 7, 2018 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
    Commented Nov 7, 2018 at 13:52
  • \$\begingroup\$ @Adám Thanks! I don't know how to edit the header, can you do it for me? \$\endgroup\$ Commented Nov 7, 2018 at 14:01
  • \$\begingroup\$ What do you mean by editing the header? \$\endgroup\$
    – Adám
    Commented Nov 7, 2018 at 14:22
  • 1
    \$\begingroup\$ Edit and copy from here. \$\endgroup\$
    – Adám
    Commented Nov 7, 2018 at 14:40
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
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\$
1
  • \$\begingroup\$ You can remove the space after (n+2*i). \$\endgroup\$
    – Adalynn
    Commented Nov 8, 2018 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\$

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\$
2
  • \$\begingroup\$ Welcome to PPCG! You can probably shave off a few bytes here and there by using tricks found in this post. \$\endgroup\$
    – user45941
    Commented Nov 8, 2018 at 5:14
  • \$\begingroup\$ @Mego Yep. I was able to save 4 bytes by using $" instead of " ". \$\endgroup\$ Commented Nov 8, 2018 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\$
0
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\$
1
  • \$\begingroup\$ @ceilingcat Thank you. \$\endgroup\$ Commented Jan 21, 2019 at 7:53
1
\$\begingroup\$

Vyxal RM, 4 bytes

S7ø∧

Try it Online!

new canvas builtin go brr

\$\endgroup\$
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\$
1
  • \$\begingroup\$ saved 20 bytes reordering some instructions TIO, why \s and not just a space in last regex \$\endgroup\$ Commented Jan 22, 2019 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 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.