4
\$\begingroup\$

This wasn't originally intended for code-golf, just as a little debugging routine to roughly visualize something "goofy" going on in a model of some (irrelevant here) physical process. But when I saw how surprisingly short it was, compared to my expectations, I just wondered if it can be further shortened. And that's primarily with respect to #statements (rather than #chars just by shortening variable names).

So, the function's void asciigraph ( double *f, int n ) where f[] contains n doubles representing values of some more-or-less continuous function to be illustrated/graphed on your terminal. My implementation is below, along with a test driver, whose output is below that. Can you do better/shorter?...

#include <stdio.h>
#include <math.h>
/* --- entry point --- */
void asciigraph ( double *f, int n ) {
  int    row=0,nrows=24, col=0,ncols=78;
  double bigf=0.0;
  for ( col=0; col<n; col++ )
    if ( fabs(f[col]) > bigf ) bigf = fabs(f[col]);
  for ( row=0; row<nrows; row++ ) {
    double yval = bigf*((double)(nrows/2-row))/((double)(nrows/2));
    for ( col=0; col<ncols; col++ )
      printf("%c",(yval*f[(col*(n-1))/(ncols-1)]>=yval*yval? '*':' '));
    printf("\n"); }
  } /* --- end-of-function asciigraph() --- */

#ifdef TESTDRIVE
int main ( int argc, char *argv[] ) {
  double f[999], pi=3.14159;        /* stored function to be graphed */
  int    i=0, N=511;            /* f[] index */
  void   asciigraph();
  for ( i=0; i<N; i++ ) {
    double x = 2.0*pi*((double)i)/((double)(N-1));
    f[i] = .5*sin(2.*x+pi/3.) + 1.*sin(1.*x+pi/2.); }
  asciigraph(f,N);
  } /* --- end-of-function main() --- */
#endif

Try it online!

Compile it (for linux) as cc -DTESTDRIVE asciigraph.c -lm -o asciigraph and then the sample output is

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

So, the eleven lines comprising asciigraph() above can be reduced two ways: (a) just "syntactically compressing" the code (e.g., put the final } on the same line as the final statement), or (b) by finding a niftier algorithm than mine, which compares each f-value to the yval "level line" (in which case maybe your algorithm wouldn't need to find that bigf at all). Obviously (I'd think it's obvious), the "niftier algorithm" approach is what I'm more interested in.

Replacing the test driver with the following will yield a moving wave:

int main ( int argc, char *argv[] ) {
  double f[999], pi=3.14159;            /* stored function to be graphed */
  double t=0.0, dt=0.05, w1=16.,w2=3.;  int Nt=50;
  int    i=0, N=511;                    /* f[] index */
  void   asciigraph();
  while ( --Nt > 0 ) {
    for ( i=0; i<N; i++ ) {
      double x = 2.0*pi*((double)i)/((double)(N-1));
      f[i] = .75*sin(2.*x+pi/3.+w1*t) + 1.*sin(1.*x+pi/2.+w2*t); }
    system("sleep 0.25; clear");
    asciigraph(f,N);
    t += dt; }
  } /* --- end-of-function main() --- */
\$\endgroup\$
12
  • 1
    \$\begingroup\$ No, it has to be \$2\$ passes either way. \$\endgroup\$ Nov 24, 2019 at 4:57
  • 1
    \$\begingroup\$ A quick start would be removing whitespace, comments and shortening variable names. Have you had a look at the Tips for golfing in C page? \$\endgroup\$
    – Jo King
    Nov 24, 2019 at 5:40
  • 1
    \$\begingroup\$ I've added a TIO link to make it easier to test and modify your code. My understanding is that only the asciigraph() function needs to be golfed, so I've put the other parts in the Header and Footer sections. But feel free to edit if that's not what you meant. \$\endgroup\$
    – Arnauld
    Nov 24, 2019 at 8:26
  • 1
    \$\begingroup\$ Would this be a better [code-golf] question than a C tips question? \$\endgroup\$ Nov 24, 2019 at 9:03
  • 3
    \$\begingroup\$ I think this should be changed to a codegolf challenge and opened to all languages. Is there interest in doing so? Otherwise it would be good to have a separate challenge. \$\endgroup\$ Nov 24, 2019 at 11:27

1 Answer 1

5
\$\begingroup\$

C (gcc), 164 167 162 bytes

r,c;void asciigraph(f,n,b,y)double*f,b,y;{for(b=0,r=n;r--;)b=fmax(b,fabs(f[r]));for(;++r<24;puts(""))for(y=b-b/12*r,c=0;c<78;)putchar("* "[y*f[c++*~-n/77]<y*y]);}

Try it online!

Thanks to @Arnauld for -4 and @JohnForkosh for the bugfix.

Slightly golfed less

r,c;
void asciigraph(f,n,b,y)double*f,b,y;{
  for(b=0,r=n;r--;)
    b=fmax(b,fabs(f[r]));
  for(;++r<24;puts(""))
    for(y=b-b/12*r,c=0;c<78;)
      putchar("* "[y*f[c++*~-n/77]<y*y]);
}
\$\endgroup\$
0

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.