Draw an hourglass

Again inspired by a task for Programming 101 here's another challenge.

Input:

• A positive integer n >= 3. (has to be odd)

Output:

• n lines of asterisks, where the first line has n asterisks and every new line has two asterisks less than the line before. Until hitting 1 asterisk. From there every new line has two asterisks more than the line before until getting back to n asterisks. Spaces or something like spaces have to used to align the asterisks so that it really will look like an hourglass.

General rules:

• Trailing newlines are allowed but do not have to be used.
• indentation is a must.
• This is code-golf, so shortest answer in bytes wins.
• Since the course is taught in C++, I'm eager to see solutions in C++.

Test case (n=5):

*****
***
*
***
*****

• edited accordingly, thanks :-) Oct 25, 2016 at 11:42
• Possible duplicate of Draw an asterisk triangle Oct 25, 2016 at 14:59
• @Oliver Considering OP wrote "Draw an asterisk triangle", I'm not entirely sure that calling this challenge a duplicate is fair. It is definitely related, though. Oct 25, 2016 at 16:01
• Since not everyone here knows the full context, OP originally posted the "Draw an asterisk triangle" and edited this challenge in as an additional challenge. We told them to remove that part and make it a different challenge (which they did). This challenge is not a duplicate. OP is doing what many high rep users, and even a few mods have recommended. Oct 25, 2016 at 16:08
• @JDL: No, why would you? Ah, now I understand what you meant by square... :-D Oct 26, 2016 at 8:34

Java 7, 156 bytes

Fairly simple. Keeps track of lines with n, stars with j, spaces with s, and direction with d. I really just wanted a non-recursive Java answer on the board, but it doesn't hurt that it's also a bit shorter :)

String f(int n){String o="";int j=n,s=0,i,d=0;for(;n-->0;o+="\n"){for(i=0;i++<s;)o+=" ";for(i=0;i++<j;)o+="*";d+=j<2?1:0;j+=d<1?-2:2;s+=d<1?1:-1;}return o;}


With line breaks:

String f(int n){
String o="";
int j=n,s=0,i,d=0;
for(;n-->0;o+="\n"){
for(i=0;i++<s;)
o+=" ";
for(i=0;i++<j;)
o+="*";
d+=j<2?1:0;
j+=d<1?-2:2;
s+=d<1?1:-1;
}
return o;
}

• 146 bytes May 17, 2021 at 6:42

Groovy, 66 Bytes

{n->((n..1)+(2..n)).each{if(it%2>0){println(("*"*it).center(n))}}}


Explained:

((n..1)+(2..n)) - Reverse palindromize to n [n,..,1,..,n]

.each{if(it%2>0){...} - Iterate through odd elements.

println(("*"*it).center(n)) - Center n stars and print each on newline.

• .each's code block could be {it%2&&println(("*"*it).center(n))}. Oct 26, 2016 at 8:35

PHP, 191 bytes

$b=[];for($i=$a=$argv[1]+1;$i>0;$i--){$i--;if($i<=1){$c=str_pad("*",$a," ",2)."\n";break;}$b[]=str_pad(str_repeat("*",$i),$a," ",2)."\n";}echo implode("",$b).$c.implode("",array_reverse($b));


Run like php -f golf_hourglass.php 15

# php -f golf_hourglass.php 15
***************
*************
***********
*********
*******
*****
***
*
***
*****
*******
*********
***********
*************
***************


The idea behind it is to create the top half (the part before the single *), then just echo the top part twice, but the second time in reverse order.

• I think this is a better start for this task for(;$i<$a=$argv[1];$i+=2){$t=str_pad(str_pad("",$i+1,"*"),$a," ",2)."\n";$i?$s.=$t:$r=$t;}echo strrev($s)."\n".$r.$s; Oct 25, 2016 at 17:09 • for(;$i<$a=$argv[1];$i++){$t=str_pad(str_pad("",$i+1+$i%2,"*"),$a," ",2)."\n";$i%2?$s.=$t:$s=$t.$s;}echo$s; this is better Oct 25, 2016 at 17:18
• Replace implode() with join() to save 6 bytes. Oct 25, 2016 at 17:38
• Replace \n with an actual new line to save a byte. Oct 25, 2016 at 17:39

Pyke, 22 19 bytes

F-ed*ih\**+)2%'X_OX


Try it here!

F          )        -    for i in range(input)
-                  -        Q-i
e                 -       floor(^/2)
d*               -      ^*" "
+         -     ^+V
ih             -       i+1
\**          -      ^*"*"
2%      -   ^[::2]
'X_   - splat(^),
reversed(^)
OX - splat(^[:-1])


C, 117 bytes

void p(c,n){while(n--)putchar(c);}void h(n){for(int i=n;i>=-n;i-=i==1?4:2){p(32,(n-abs(i))/2);p(42,abs(i));p(10,1);}}


Ungolfed

void printNum(c, n) {
while (n--)
putchar(c);
}

void hourGlass(n) {
for (int i = n; i >= -n; i-=i==1?4:2) {
printNum(32, (n - abs(i)) / 2);
printNum(42, abs(i));
printNum(10, 1);
}
}


PHP, 158 bytes

(-2 if I can omit the start tag)

<?$v=$argv[1]/2;while($c++<$v){$a="";$b=$v+.5;while(--$b)$a.=$b<$c?" ":"*";$z[]=strrev($a)."*$a";}$c=array_pop($z);$a=join($z,"
");echo"$a$c
".strrev($a);  A similar approach to the one @hd took but uses manual concatenation instead of built-in string functions with overly long names. I wish I could call$z->pop()

Pyth, 22 bytes

VQ+*d-J/Q2aJN*\*hatQyN


Try online.

Lame attempt at explaining:

VQ+*d-J/Q2aJN*\*hatQyN   The whole thing.
VQ                       For N in [0,1,2, ... ,Q-1]; Q takes the input.
J/Q2               Integer division Q/2, store in J
aJN            Absolute difference between N and J
-                   Subtract the above numbers...
*d                    ...and repeat whitespace that many times
+                      Concatenate that with output from the next block
atQyN   Absolute difference between Q-1 and 2*N
*\*h        Repeat an asterisk the above number of times + 1


Interestingly I matched TheBikingViking's score on the same language using a different approach... O:

Racket 177 bytes

(let p((n N)(d #t))(for((i(/(- N n)2)))(display" "))(for((i n))(display #\*))(displayln"")
(cond[(and(not d)(= n N))][(= n 1)(set! d #f)(p(+ 2 n)d)][d(p(- n 2)d)][(p(+ 2 n)d)]))


Ungolfed:

(define (f N)
(let loop ((n N)
(d #t))                      ; descending
(for ((i (/(- N n)2)))
(display " "))
(for ((i n))
(display #\*))
(displayln "")
(cond
[(and (not d) (= n N))]             ; end
[(= n 1) (set! d #f)
(loop (+ 2 n) d)]
[d (loop (- n 2) d)]
[(loop (+ 2 n) d)]))
)


Testing:

(f 9)


Output:

*********
*******
*****
***
*
***
*****
*******
*********
#t


Batch, 191 154 bytes

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


The u subroutine generates the top half of the hourglass by prepending a space and removing two trailing stars each time. The l subroutine generates the bottom half of the hourglass by removing a space and appending two trailing stars each time. The code falls through into the l subroutine at the end to save bytes. The second half of the l subroutine is also used to build up the initial string, although it has to be called twice as many times as the string doesn't have its spaces yet.

Edit: Saved 37 bytes by using an idea by @ConorO'Brien.

CJam, 26 bytes

ri:A,_W%.e<{_S*A@2*-'**N}/


Try it online!

ri:A     e# read integer, save as A
,_W%     e# make range and mirror (e.g.[0,1,2,3,4],[4,3,2,1,0])
.e<      e# take minimums (e.g.[0,1,2,1,0])
{        e# for each minimum "m"
_S*    e#   push m spaces
A@2*-  e#   calculate A-2m (rotates to spare m)
'**    e#   push A-2m asterisks
N      e#   push a newline
}/       e# end, prints stack


Swift 3, 139 130

let c={String(repeating:$0,count:$1)};func d(_ n:Int,_ i:Int=0)->String{let b=c(" ",i)+c("*",n)+"\n";return n>1 ?b+d(n-2,i+1)+b:b}


On-line test

let c={(s,i)in(0..<i).reduce(""){(b,_)in b+s}};func d(_ n:Int,_ i:Int=0)->String{let b=c(" ",i)+c("*",n)+"\n";return n>1 ?b+d(n-2,i+1)+b:b}

T-SQL, 93 bytes

This will also provide an answer when an even number is used - the bottom row will be missing.

Golfed:

DECLARE @ INT=5

,@i INT=0z:PRINT SPACE(@/2-abs(@/2-@i))+REPLICATE('*',abs(@/2-@i)*2+1)SET
@i+=1IF @i<@ GOTO z


Ungolfed:

DECLARE @ INT=5

,@i INT=0
z:
PRINT SPACE(@/2-abs(@/2-@i))+REPLICATE('*',abs(@/2-@i)*2+1)
SET @i+=1
IF @i<@ GOTO z


A bit longer alternative solution without looping

USE master
DECLARE @ INT=5
SELECT top(@)SPACE(@/2-abs(@/2-number))+REPLICATE('*',abs(@/2-number)*2+1)
FROM spt_values
WHERE'P'=type


Fiddle

Actually, 34 bytes

;╗R1&░'**╜kd#"{:^%d}"%fM;pXR+i


Try it online!

Explanation:

;╗R1&░'**╜kd#"{:^%d}"%fM;pXR+i
;╗                                  store a copy of n in register 0
R                                 range(1, n+1) ([1, n])
1&░                            filter: keep only odd values
'**╜kd#"{:^%d}"%fM        for each value:
'**                          push a string containing that many asterisks
╜                         push n
kd#                      list from stack, dequeue, listify (make both stack elements singleton lists)
"{:^%d}"%             format string to center string in n-wide field
f            do the formatting
;pXR+   duplicate list, discard the extra "*" element, reverse, and append original list (vertical mirroring)
i  flatten and implicitly print


Common Lisp, 101 bytes

(dotimes(i(1+(set'x(read))))(format t"~v:@<~v{*~}~>~&"x(set'b(abs(- x(* i 2))))1)(if(= 1 b)(incf i)))


SOGL V0.12, 12 bytes

.↔∫ **}⁰№§╬3


Try it Here!

Excel VBA, 68 67 Bytes

Anonymous VBE immediate window function that takes input from [A1] and outputs to the VBE immediate window

n=[Int(A1/2)]:For i=-n To n:j=abs(i):?Spc(n-j)String(2*j+1,42):Next


Ruby, 66 bytes

->n{(-n..n).each{|x|x.abs>1?(puts"*"*x.abs):(puts"*"*x if x>0)}}


Probably a shorter way of doing this, but this is the most straight forward one i came up with.

Japt -R, 11 bytes

1õU2_ç*Ãê û


Try it

                :Implicit input of integer U
1õU2            :Range [U,1] in increments of 2
_           :Map each Z
ç*         :  Repeat "*" Z times
Ã        :End map
ê       :Palindromise
û     :Centre pad each element with spaces to the length of the longest
:Implicitly join with newlines and output


Japt, 11 bytes

õ_î*ÃÔë ê û


Try it online

Python 3, 79 bytes

def f(i):[print(' '*(i//2-abs(j-i//2))+'*'*2*abs(j-i//2)+'*')for j in range(i)]


Oracle SQL, 119 bytes

SQL> select lpad(' ',ceil(n/2)-x-1,' ')||rpad('*',x*2+1,'*')from(select n,abs(ceil(n/2)-rownum)x from t connect by level<=n)
2  /

--------------------------------------------------------------------------------
*******
*****
***
*
***
*****
*******

7 rows selected.


Perl 5-a, 51 bytes

$_%2&&say$"x(("@F"-abs)/2),'*'x abs for-$_..0,2..$_


Try it online!

sed 4.2.2, 54 42 bytes

:
H
s/00/ /
t
:a
s/ 0/000/
H
//ba
g
y/0/*/


Takes input in unary (e.g. 3 is 000), allowed as per meta consensus. Try it online!

#Create an unnamed branch label (sed 4.2 only)
:
#Append pattern buffer to hold buffer
H
#Replace first two zeroes with a space
s/00/ /
#If we did the previous replacement, branch back to the unnamed label
t
#Create a label a
:a
#Replace last space with two zeroes
s/ 0/000/
#Append pattern buffer to the hold buffer
H
#Check if there is still something to replace (// refers to the previous pattern)
#If so, branch to label a
//ba
#Replace the pattern buffer with the hold buffer
g
#Replace zeroes with asterisks
y/0/*/


-12 bytes thanks to Xcali

• You can cut that down to 42 bytes since the trailing spaces are not needed. Sep 25, 2020 at 21:42
• @Xcali Ah yes, cutting those off makes this much easier. Thanks :) Sep 26, 2020 at 1:03

Rockstar, 141 bytes

listen to N
let X be N--2
F takes X
let Y be N-X
let Y be/2
say " "*Y+"*"*X

while X-1
let X be-2
F taking X

while N-X
let X be+2
F taking X


Try it here (Code will need to be pasted in)