ASCII Art Octagons

22

4

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.

AdmBorkBork

Posted 2018-11-06T17:50:28.807

Reputation: 41 581

1Can we use different output characters, or does it need to be consistent? – Emigna – 2018-11-06T18:13:56.833

@Emigna Different characters are fine. – AdmBorkBork – 2018-11-06T18:26:21.383

1Quite related. – Charlie – 2018-11-07T11:06:55.820

Answers

23

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.

Emigna

Posted 2018-11-06T17:50:28.807

Reputation: 50 798

Surely this should be 5 bytes? Or do code golf challenges see bytes and characters as interchangeable – Doug – 2018-11-07T17:10:05.387

3

@Doug: It is 3 bytes in 05ab1e's code page

– Emigna – 2018-11-07T17:11:53.470

Oh, cool! Thanks for the docs link! – Doug – 2018-11-07T17:58:40.497

ockquote>

:( damnit, adnan

– ASCII-only – 2018-11-11T05:14:10.950

11

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

Arnauld

Posted 2018-11-06T17:50:28.807

Reputation: 111 334

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 – 2018-11-06T22:20:37.870

@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 – 2018-11-06T22:29:00.907

@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 – 2018-11-06T22:41:22.577

1heh, well your comment about $hv=0$ prompted me to go look at my port of your logic and save another couple of bytes! – Giuseppe – 2018-11-06T22:44:24.483

8

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!

Cowabunghole

Posted 2018-11-06T17:50:28.807

Reputation: 1 590

3For those who prefer verbose Charcoal, that's PolygonHollow(:*, InputNumber(), "#");. – Neil – 2018-11-06T18:51:53.953

5

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.

dzaima

Posted 2018-11-06T17:50:28.807

Reputation: 19 048

4

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!

Giuseppe

Posted 2018-11-06T17:50:28.807

Reputation: 21 077

-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 – 2018-11-07T15:05:58.953

@Arnauld thanks! – Giuseppe – 2018-11-07T15:39:39.540

3

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!

Lynn

Posted 2018-11-06T17:50:28.807

Reputation: 55 648

3

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.

xnor

Posted 2018-11-06T17:50:28.807

Reputation: 115 687

3

Powershell, 91 bytes

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

mazzy

Posted 2018-11-06T17:50:28.807

Reputation: 4 832

2

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

Veskah

Posted 2018-11-06T17:50:28.807

Reputation: 3 580

2

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

Logern

Posted 2018-11-06T17:50:28.807

Reputation: 845

176 bytes – ceilingcat – 2018-11-20T16:34:27.990

1

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.

TFeld

Posted 2018-11-06T17:50:28.807

Reputation: 19 246

You can remove the space after (n+2*i). – Zacharý – 2018-11-08T13:26:53.797

1

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

Neil

Posted 2018-11-06T17:50:28.807

Reputation: 95 035

1

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.

G B

Posted 2018-11-06T17:50:28.807

Reputation: 11 099

1

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 
]

Galen Ivanov

Posted 2018-11-06T17:50:28.807

Reputation: 13 815

1

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.

Galen Ivanov

Posted 2018-11-06T17:50:28.807

Reputation: 13 815

1

You can't actually use Classic here because of . Rather count 1 byte/char by referring to SBCS as per Meta.

– Adám – 2018-11-07T13:52:40.523

@Adám Thanks! I don't know how to edit the header, can you do it for me? – Galen Ivanov – 2018-11-07T14:01:38.153

What do you mean by editing the header? – Adám – 2018-11-07T14:22:39.067

@Adám I don't know if "APL (Dyalog Classic)SBCS" is ok – Galen Ivanov – 2018-11-07T14:32:00.637

1

Edit and copy from here.

– Adám – 2018-11-07T14:40:39.703

1

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.

Nathan Mills

Posted 2018-11-06T17:50:28.807

Reputation: 31

Welcome to PPCG! You can probably shave off a few bytes here and there by using tricks found in this post.

– Mego – 2018-11-08T05:14:11.793

@Mego Yep. I was able to save 4 bytes by using $" instead of " ". – Nathan Mills – 2018-11-08T17:14:09.710

1

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!

Jonathan Frech

Posted 2018-11-06T17:50:28.807

Reputation: 6 681

@ceilingcat Thank you. – Jonathan Frech – 2018-11-08T21:00:29.000

@ceilingcat Thank you. – Jonathan Frech – 2019-01-21T07:53:38.523

1

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

Rich Farmbrough

Posted 2018-11-06T17:50:28.807

Reputation: 11

1

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.

nwellnhof

Posted 2018-11-06T17:50:28.807

Reputation: 10 037

0

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!

glietz

Posted 2018-11-06T17:50:28.807

Reputation: 101

0

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

Rich Farmbrough

Posted 2018-11-06T17:50:28.807

Reputation: 11

saved 20 bytes reordering some instructions TIO, why \s and not just a space in last regex

– Nahuel Fouilleul – 2019-01-22T10:56:04.997

0

J, 59 45 41 bytes

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

Try it online!

Will add explanation tonight.

Jonah

Posted 2018-11-06T17:50:28.807

Reputation: 8 729

0

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

Nahuel Fouilleul

Posted 2018-11-06T17:50:28.807

Reputation: 5 582