14
\$\begingroup\$

The goal is to animate a star exploding in ASCII art, starting from a single star character * to a supernovae and finally to the space void.

Rules :

  • You have to display only 80 characters per line on 24 lines (this is the default terminal size on Linux)
  • Only these 95 printable characters from ASCII are allowed
  • Steps:
    1. The initial star must be * and centred horizontally and vertically
    2. The star has to grow to show that it explodes
    3. At the end the screen must be empty (the star has disappeared)
  • Length of the code or languages are irrelevant
  • Votes will decide of the most creative and beautiful results. Have you ever seen an exploding star? Let's see how you imagine this.

I've added an answer using Python as an example, but you're encouraged to create something different or better.

You have one week to participate, the winner will be chosen on 2014-04-01.

Samples of output (with some frames only):

# start





                                        *





# during explosion

                                    ****#****
                                 ***#@##*#@#****
                               *@**@@@*##**#*#**#*
                              **@@*#**#@*#@****#***
                              ****@*@***#****@**@@*
                             *******@***@@***#****#*
                              *#***#*##@****##@@@**
                              **#@###****@*********
                               *****@**@*@*****@**
                                 ************@**
                                    ****#****


# star is expanding

                                        *                                       
                               ********* **@******
                           ****   **#**@ #**#*#   ****
                         ***  **  **##** *@@*@*  **  ***
                       **  *  @@   *@*#* ***@*   *#  *  **
                     *** #  *  *#  *@#** ***@*  **  @  * *@*
                    ** *  * ** *@  ****@ @****  ** #* *  * **
                   ** * @* * ** *@  #### *#**  ** ** * @* * **
                  *# * # ** * #* *  **** ****  @ ** * ** * * #*
                 ** * *# * @ * #  @  @*@ *#*  *  @ # # * @* * **
                 *# * * * * # * @* * **# *#* * ** * * * * * # **
                 ** # * * @ * * # * # ** @* * * * * * # @ @ * **
                *# * * * * * * * * # * * * * * * @ @ * * * * * **
                 *# * @ * @ * @ * * * ** *@ * * # * * * * * @ @*
                 *# # @ * * # * *@ * *** @#@ @ ** * @ @ * * # **
                 *# * ** * * * @  @  **@ ***  *  @ * * * @* * #*
                  ** * * ** * #@ *  #*** **##  * #* * #@ * * @*
                   *# * *@ * @@ *#  **** @***  ** ** * #* * #*
                    *# *  * *@ **  @**@* *@#**  ** ** *  * @*
                     *#* @  *  @@  **##* ****@  **  #  * @**
                       **  @  #*   @*@#* @*@*#   @#  *  **
                         *#*  @*  @#*@*# **#*@#  **  ***
                           ****   *##**# #***@*   @***
                               ****@**@* *****@***

# star is disappearing

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

# the star is gone
















(there is nothing)

Animated example of output:

Exploding star

If you want some inspiration, you can look at an explosion from Captain Blood, a game from 1988.

\$\endgroup\$
6
  • 1
    \$\begingroup\$ There isn't really a spec here to determine whether an answer is "correct" or not. \$\endgroup\$ Commented Mar 18, 2014 at 15:56
  • \$\begingroup\$ I'm removing the word correct from the question. The most popular answer will be declared as the winner. \$\endgroup\$ Commented Mar 18, 2014 at 15:57
  • 1
    \$\begingroup\$ Whether the word is in the question or not, all questions should have a clear spec against which answers can be measured. \$\endgroup\$ Commented Mar 18, 2014 at 16:01
  • 1
    \$\begingroup\$ Is "display something which look like the output examples, but feel free to be creative and artistic" enough? I can add more rules (time, number of frames, sizes, etc.) but I'm pretty sure it will make it less interesting. \$\endgroup\$ Commented Mar 18, 2014 at 16:07
  • \$\begingroup\$ Please explain if the rules are not clear enough and how I can improve them. \$\endgroup\$ Commented Mar 19, 2014 at 1:53

3 Answers 3

29
\$\begingroup\$

C + curses

ASCII explosion

I haven't made much of an effort to tidy up the source code. Basically it calculates an expanding shock wave, with a solid background added to the first few frames to give a sort of flash effect. [[EDIT: The explosion looked a bit odd appearing from a single asterisk, so I added a few frames of expansion at the beginning of the animation.]]

Random particles are superimposed on top of this, with positions determined according to a basic perspective calculation to give it a slightly 3D feel. (Well that was the idea, anyway.)

#include <curses.h>
#include <stdlib.h>
#include <math.h>

#define NUM_FRAMES 150
#define NUM_BLOBS 800
#define PERSPECTIVE 50.0

typedef struct {
  double x,y,z;
} spaceblob;

double prng() {
  static long long s=1;
  s = s * 1488248101 + 981577151;
  return ((s % 65536) - 32768) / 32768.0;
}

int main() {
  char *frames[NUM_FRAMES], *p;
  int i,j,x,y,z,v,rows,cols,ith,i0;
  int maxx,minx,maxy,miny,delay=1E6;
  double bx,by,bz,br,r,th,t;
  spaceblob *blobs;
  
  /* Initialize ncurses and get window dimensions */
  initscr();
  getmaxyx(stdscr,rows,cols);
  minx = -cols / 2;
  maxx = cols+minx-1;
  miny = -rows / 2;
  maxy = rows+miny-1;
  
  /* Generate random blob coordinates */
  blobs = malloc(NUM_BLOBS * sizeof(spaceblob));
  for (i=0; i<NUM_BLOBS; i++) {
    bx = prng();
    by = prng();
    bz = prng();
    br = sqrt(bx*bx + by*by + bz*bz);
    blobs[i].x = (bx / br) * (1.3 + 0.2 * prng());
    blobs[i].y = (0.5 * by / br) * (1.3 + 0.2 * prng());;
    blobs[i].z = (bz / br) * (1.3 + 0.2 * prng());;
  }
  
  /* Generate animation frames */
  for (i=0; i<NUM_FRAMES; i++) {
    t = (1. * i) / NUM_FRAMES;
    p = frames[i] = malloc(cols * rows + 1);
    for (y=miny; y<=maxy; y++) {
      for (x=minx; x<=maxx; x++) {
        
        /* Show a single '*' in first frame */
        if (i==0) {
          *p++ = (x==0 && y==0) ? '*' : ' ';
          continue;
        }
        
        /* Show expanding star in next 7 frames */
        if (i<8) {
          r = sqrt(x*x + 4*y*y);
          *p++ = (r < i*2) ? '@' : ' ';
          continue;
        }
        
        /* Otherwise show blast wave */
        r = sqrt(x*x + 4*y*y) * (0.5 + (prng()/3.0)*cos(16.*atan2(y*2.+0.01,x+0.01))*.3);
        ith = 32 + th * 32. * M_1_PI;
        v = i - r - 7;
        if (v<0) *p++ = (i<19)?"%@W#H=+~-:."[i-8]:' '; /* initial flash */
        else if (v<20) *p++ = " .:!HIOMW#%$&@08O=+-"[v];
        else *p++=' ';
      }
    }
    
    /* Add blobs with perspective effect */
    if (i>6) {
      i0 = i-6;
      for (j=0; j<NUM_BLOBS; j++) {
        bx = blobs[j].x * i0;
        by = blobs[j].y * i0;
        bz = blobs[j].z * i0;
        if (bz<5-PERSPECTIVE || bz>PERSPECTIVE) continue;
        x = cols / 2 + bx * PERSPECTIVE / (bz+PERSPECTIVE);
        y = rows / 2 + by * PERSPECTIVE / (bz+PERSPECTIVE);
        if (x>=0 && x<cols && y>=0 && y<rows)
          frames[i][x+y*cols] = (bz>40) ? '.' : (bz>-20) ? 'o' : '@';
      }
    }
    
    /* Terminate the text string for this frame */
    *p = '\0';
  }
  
  /* Now play back the frames in sequence */
  curs_set(0); /* hide text cursor (supposedly) */
  for (i=0; i<NUM_FRAMES; i++) {
    erase();
    mvaddstr(0,0,frames[i]);
    refresh();
    usleep(delay);
    delay=33333; /* Change to 30fps after first frame */
  }
  curs_set(1); /* unhide cursor */
  endwin(); /* Exit ncurses */
  return 0;
}
\$\endgroup\$
2
  • 1
    \$\begingroup\$ That's nice! Can you please explain how you captured the terminal images? I think you should reduce the part when you're typing, if you haven't added it for teasing the viewer. \$\endgroup\$ Commented Mar 19, 2014 at 23:16
  • 1
    \$\begingroup\$ @n.1 You're right -- it was dragging on a bit. I use IShowU to capture the screen, and export the frames from QuickTime to ImageMagick to create an animated GIF, and then use Gifsicle to optimise the GIF animation. It's a bit of a palaver, really. Let me know if you've found a better method. \$\endgroup\$
    – r3mainer
    Commented Mar 20, 2014 at 1:45
7
\$\begingroup\$

Javascript

Thought it was worth a shot in JS. Suggest saving and running; very very slow if you paste in console.

var boomboom = (function() {
    //Clear any existing page
    document.getElementsByTagName("body")[0].innerHTML="";

    var space=document.createElement("div");
    var iterator=0;
    var stars = 50;
    var timer=100;

    //Set container
    space.setAttribute("id","space");
    space.setAttribute("style","width:1000px;height:600px;margin:auto;border:solid 1px #000;position:relative;overflow:hidden;background:#000;color:#fff");
    document.getElementsByTagName("body")[0].appendChild(space);

    //Set interval and draw...
    var interval = setInterval(function(){ drawStars(iterator,stars); }, timer);
    drawStars(iterator, stars);

    function drawStars(r,c) {
        clearInterval(interval);

        //a container for this set of stars
        var starContainer=document.createElement("div");

        //don't draw more if there are too many, it's got to go
        if(iterator < 1000) {
            for(var i = 0; i < c; i++) {
                var x,y;

                if(iterator < 100) {
                    x=500 + r * Math.cos(2 * Math.PI * i / c) * 0.7;
                    y=300 + r * Math.sin(2 * Math.PI * i / c) * 0.7;
                }

                //add some randomness for the boom boom
                if(iterator > 100) { 
                    x=500 + r * Math.cos(2 * Math.PI * i / c) * 0.7*Math.random();
                    y=300 + r * Math.sin(2 * Math.PI * i / c) * 0.7*Math.random();
                }

                //Make a star
                var star=document.createElement("div");
                star.setAttribute("class","star");

                //Exploding stars are red, I hope
                var color = iterator < 100 ? "color:#fff" : "color:rgb("+parseInt(255*Math.random())+",0,0)";
                star.setAttribute("style","position:absolute; left:"+ x +"px;top:"+ y +"px;"+color);

                //Change the star character as boom boom gets bigger
                if(iterator <= 200) {
                    star.innerText="*";
                }
                else if(iterator >=200 & iterator <= 400) {
                    star.innerText="O";
                }
                else  {
                    star.innerText="-";
                }
                //Add the star to its container
                starContainer.appendChild(star);
            }
        }
        //draw the container
        space.appendChild(starContainer);

        //increment the iterator.  It's an iterator because we're using intervals and it's late.
        iterator+=5;

        //remove stars when we get too many
        if(iterator > 200) {
            space.removeChild(space.firstChild);
        }
        if(iterator < 1500) { //do it all again
            timer = timer > 10 ? timer-10 : timer;
            interval=setInterval(function(){ drawStars(iterator,stars); }, timer);
        }

        //make sure it's actually empty
        else {
            space.innerHTML="";
        }
    }
}());

Edits In JSBin: http://jsbin.com/worofeqi/5/watch?js,output

Oddly runs much more smoothly when done from local file system than in JSBin. Honestly not sure why; may look at that this evening.

\$\endgroup\$
2
  • \$\begingroup\$ Can you please place it on jsfiddle, jsbin or another website where we can see easily? When adding the question I only thought about displaying the result in a terminal but I won't update the rules now that you've added your answer, I should have added this rule before. \$\endgroup\$ Commented Mar 20, 2014 at 14:48
  • \$\begingroup\$ Answer updated (note the (very odd) caveat) \$\endgroup\$ Commented Mar 20, 2014 at 15:45
1
\$\begingroup\$

Python

#!/usr/bin/python

import sys, math, time, random

def distance(x, y):
    # ((y - y_center) * 2) -> correct ratio for a true circle
    return math.ceil(math.sqrt(((x - 40) ** 2) + (((y - 12) * 2) ** 2)))

def star(radiuses):
    for r in radiuses:
        #~ clear screen: http://stackoverflow.com/a/2084521/2257664
        print chr(27) + "[2J"

        # width
        for y in range(24):
            # height
            for x in range(80):
                d = distance(x, y)

                #~ border
                if (d == r):
                    sys.stdout.write('*')
                #~ inside the star
                elif (d < r):
                    if (r > 35):
                        sys.stdout.write(' ')
                    elif (r > 25) and ((d % 2) != 0):
                        sys.stdout.write('-')
                    elif (r > 15) and ((d % 2) == 0):
                        sys.stdout.write(' ')
                    else :
                        sys.stdout.write(random.choice('****#@'))
                #~ space outside the star
                else:
                    sys.stdout.write(' ')
            print
        time.sleep(0.1)

star(range(0, 12) + range(10, 0, -1) + range(0, 50))

Example output (using less steps and without clearing the screen).

Excerpts of the output are also displayed in the question.

\$\endgroup\$
0

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