It’s time to free yourself from the math-nerd aspect of the exp() function and put it to legitimate use by animating an asterisk bouncing across the screen. Such a feat may be considered unusual for a stream-oriented programming language like C, but I find animation more interesting than math.
The update to the code from last week’s Lesson includes a lot of defined constants plus a few more macros. The point of all the defined constants is to allow the animation to be adjusted in a central spot (at the top of the source code file) as opposed to fishing around the code for specific literals to adjust.
Here is the code:
2025_08_23-Lesson.c
#include <stdio.h> #include <math.h> #include <time.h> /* screen edges */ #define RIGHT_EDGE 78 #define LEFT_EDGE 2 /* movement delay in milliseconds */ #define PAUSE 50 /* values for e exponent */ #define TOP 25 #define BOTTOM 64 /* starting row */ #define ROW 20 /* larger momentum values = narrower arcs */ #define MOMENTUM 7 /* repeat count */ #define REPEAT 1013; #define clear() printf("\e[H\e[2J") #define cursor_off() printf("\e[?25l"); #define cursor_on() printf("\e[?25h"); /* move the cursor to position x (columns) and y (rows) and set character c */ void putat(x,y,c) { printf("\e[%d;%dH%c",y,x,c); } /* pause for m milliseconds */ void delay(int m) { long pause; clock_t now,then; pause = m*(CLOCKS_PER_SEC/1000); now = then = clock(); while( (now-then) < pause ) now = clock(); } int main() { int r,x,height,column,direction; char buffer[BUFSIZ]; /* initialization */ setvbuf(stdout,buffer,_IONBF,BUFSIZ); clear(); cursor_off(); column = 3; /* starting column */ direction = 1; /* go right */ r = REPEAT; /* loop repeat count */ while(r>0) { for( x=TOP; x<BOTTOM; x++ ) { height = (int)exp( (double)x/ROW ); if( x%MOMENTUM==0 ) { column+=direction; if( column>RIGHT_EDGE || column<LEFT_EDGE ) direction=-direction; } printf("\e[0;0H%04d",r--); putat( column, height, '*' ); delay(PAUSE); putat( column, height, ' ' ); } for( ; x>TOP; x-- ) { height = (int)exp( (double)x/ROW ); if( x%MOMENTUM==0 ) { column+=direction; if( column>RIGHT_EDGE || column<LEFT_EDGE ) direction=-direction; } printf("\e[0;0H%04d",r--); putat( column, height, '*' ); delay(PAUSE); putat( column, height, ' ' ); } } putat(LEFT_EDGE,ROW,'\n'); cursor_on(); return 0; }
The slew of defined constants set parameters for the bouncing asterisk. Among them are values for the screen width: LEFT_EDGE
and RIGHT_EDGE
assume that the terminal window is 80 columns wide. (See my Lesson here for info on obtaining the screen’s true size.)
I’ve added two more macros: cursor_off() and cursor_on(). These macros use ANSI codes to set the cursor’s visibility. Having the cursor on proves to be distracting when animating the asterisk. Also, turning the cursor back on after the program finishes is important. Otherwise, the terminal window lacks a cursor, which annoys me.
In the main() function, I’ve added the direction
variable. It’s used to move the asterisk to the right as it bounces. Its value is reversed once the asterisk “hits” the right or left edge of the window.
Before animation begins, the cursor_off() function is called, which cleans up output.
The while loop repeats as long as the value of variable r
is greater then the REPEAT
defined constant’s value. Within the loop, two for loops bounce the asterisk. The first loop sends it down. The second loop returns it back up.
Looping variable x
helps set the momentum, or the point at which the asterisk moves over a column. The effect isn’t smooth, but it’s the best you can get from a character-based terminal window.
After the loop times out, the cursor_on() function restores the cursor, and the program exits.
Output is shown in the video below:
I wanted to have the asterisk bounce based on the effects of gravity. I even wrote the gravity equation, which caps the height at which the asterisk bounces back up. But the process was too complex for me to figure out before this post is published. Still, adding gravity effects may be something I try again in the future.