34
\$\begingroup\$

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

*****
 ***
  *
 ***
*****
\$\endgroup\$
14
  • \$\begingroup\$ edited accordingly, thanks :-) \$\endgroup\$ – Sickboy Oct 25 '16 at 11:42
  • 3
    \$\begingroup\$ Possible duplicate of Draw an asterisk triangle \$\endgroup\$ – Oliver Ni Oct 25 '16 at 14:59
  • 3
    \$\begingroup\$ @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. \$\endgroup\$ – Sherlock9 Oct 25 '16 at 16:01
  • 20
    \$\begingroup\$ 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. \$\endgroup\$ – James Oct 25 '16 at 16:08
  • 2
    \$\begingroup\$ @JDL: No, why would you? Ah, now I understand what you meant by square... :-D \$\endgroup\$ – Sickboy Oct 26 '16 at 8:34

52 Answers 52

1
2
1
\$\begingroup\$

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.

\$\endgroup\$
4
  • \$\begingroup\$ 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; \$\endgroup\$ – Jörg Hülsermann Oct 25 '16 at 17:09
  • \$\begingroup\$ 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 \$\endgroup\$ – Jörg Hülsermann Oct 25 '16 at 17:18
  • \$\begingroup\$ Replace implode() with join() to save 6 bytes. \$\endgroup\$ – Alex Howansky Oct 25 '16 at 17:38
  • \$\begingroup\$ Replace \n with an actual new line to save a byte. \$\endgroup\$ – Alex Howansky Oct 25 '16 at 17:39
1
\$\begingroup\$

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

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);
  }
}
\$\endgroup\$
1
\$\begingroup\$

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

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

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:

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

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

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.

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

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

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}

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

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

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

Actually, 34 bytes

;╗R`1&`░`'**╜kd#"{:^%d}"%f`M;pXR+i

Try it online!

Explanation:

;╗R`1&`░`'**╜kd#"{:^%d}"%f`M;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}"%f`M        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
\$\endgroup\$
1
\$\begingroup\$

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

SOGL V0.12, 12 bytes

.↔∫ **}⁰№§╬3

Try it Here!

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

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

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.

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

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

Japt, 11 bytes

õ_î*ÃÔë ê û

Try it online

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

Python 3, 79 bytes

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

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  /

LPAD('',CEIL(N/2)-X-1,'')||RPAD('*',X*2+1,'*')
--------------------------------------------------------------------------------
*******
 *****
  ***
   *
  ***
 *****
*******

7 rows selected.
\$\endgroup\$
1
\$\begingroup\$

Perl 5 -a, 51 bytes

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

Try it online!

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

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!

Code with comments:

#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

\$\endgroup\$
2
  • \$\begingroup\$ You can cut that down to 42 bytes since the trailing spaces are not needed. \$\endgroup\$ – Xcali Sep 25 '20 at 21:42
  • \$\begingroup\$ @Xcali Ah yes, cutting those off makes this much easier. Thanks :) \$\endgroup\$ – Chris Sep 26 '20 at 1:03
0
\$\begingroup\$

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)

\$\endgroup\$
1
1
2

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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