40
\$\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\$
13
  • \$\begingroup\$ I don't see how this is the duplicate of "Draw an asterisk triangle". This has indentation and a down-up sequence where the triangle is only upwards. \$\endgroup\$
    – Karl Napf
    Commented Oct 25, 2016 at 11:51
  • 3
    \$\begingroup\$ Possible duplicate of Draw an asterisk triangle \$\endgroup\$
    – Oliver Ni
    Commented Oct 25, 2016 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
    Commented Oct 25, 2016 at 16:01
  • 21
    \$\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\$
    – DJMcMayhem
    Commented Oct 25, 2016 at 16:08
  • 2
    \$\begingroup\$ @JDL: No, why would you? Ah, now I understand what you meant by square... :-D \$\endgroup\$
    – Sickboy
    Commented Oct 26, 2016 at 8:34

58 Answers 58

1
2
2
\$\begingroup\$

Haskell, 84 bytes

f n|l<-div n 2,k<-[-l..l]=putStr$unlines[[" *"!!(fromEnum$abs x<=abs y)|x<-k]|y<-k]
\$\endgroup\$
1
  • \$\begingroup\$ Nice solution! But I'm pretty sure you don't need the putStr and you can get rid of the fromEnum like this. \$\endgroup\$ Commented Dec 31, 2017 at 17:27
2
\$\begingroup\$

C (gcc), 80 74 bytes

Thank ceilingcat for 6 bytes

-Du(x,k)=for(x=~n;x+=2,x<n;putchar(k))

i;j;f(n){u(i,10)u(j,i*i<j*j?32:42);}

Try it online!

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

Jelly, 15 bytes

HŻżḂƇṚ$⁾ *xⱮŒḄY

Try it online!

I'm surprised there wasn't a Jelly answer yet. Jelly is not good at strings.

Explanation

HŻżḂƇṚ$⁾ *xⱮŒḄY   Main monadic link
H                 Halve: n/2
 Ż                Range from 0 to floor(n/2)
  ż               Zip with
      $           (
                    Range from 1 to n
    Ƈ               Filter by
   Ḃ                  parity [to get the odd numbers]
     Ṛ              Reverse
      $           )
           Ɱ      For each pair of numbers
          x         repeat
       ⁾ *            the characters ' ' and '*' that many times
            ŒḄ    Bounce (palindromize)
              Y   Join with newlines
\$\endgroup\$
2
\$\begingroup\$

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

Vyxal CRM, 5 bytes

y×ẋṘ∞

Try it Online!

Newer Vyxal 2 version with a ton of flags but hey 5 bytes

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

Groovy, 66 Bytes

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

Try it: https://groovyconsole.appspot.com/script/5145735624392704

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.

\$\endgroup\$
1
  • \$\begingroup\$ .each's code block could be {it%2&&println(("*"*it).center(n))}. \$\endgroup\$
    – manatwork
    Commented Oct 26, 2016 at 8:35
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\$ Commented Oct 25, 2016 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\$ Commented Oct 25, 2016 at 17:18
  • \$\begingroup\$ Replace implode() with join() to save 6 bytes. \$\endgroup\$ Commented Oct 25, 2016 at 17:38
  • \$\begingroup\$ Replace \n with an actual new line to save a byte. \$\endgroup\$ Commented Oct 25, 2016 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
    Commented Sep 25, 2020 at 21:42
  • \$\begingroup\$ @Xcali Ah yes, cutting those off makes this much easier. Thanks :) \$\endgroup\$
    – Chris
    Commented Sep 26, 2020 at 1:03
1
\$\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\$
2
1
2

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.