Simplify the following C ascii graphing function

3

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.

    E d i t
--------------

Thanks, @Arnauld, for that Tio edit. Very cute! I wasn't (and still am not) familiar with that, so I failed trying to re-edit it myself. Actually, seeing the gobbledy-gook encoding, I didn't even try editing it. So, why did I want to edit it in the first place??? The Tio stuff looks real cute, and if I'd known about it, I'd have put in a commensurately cuter (see comments following code for explanation of cuteness) test driver, with just four extra lines, as follows.

First, in the header section put the additional #include <stdlib.h> (to accommodate the system() call). And then replace the entire test driver with this (very similar but much cuter) one...

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

Explanation of cuteness...
  That'll generate a   50-frame ascii animation   of a travelling wave when you execute it, rather than the single ho-hum static image you're now seeing. (Of course, that's unrelated to asciigraph()'s original and useful debugging purpose, but I think we've maybe gotten a little carried away from that:)

    E d i t # 2
------------------

Darn, I tested the Tio by manually changing the code already there, but system("clear") doesn't seem to work for the Tio. Each frame just appears below the previous one, resulting in 50 static frames of output. But everything works fine in a linux terminal. So if anybody wants to play with it, just download the code and compile on your own box. ...Unless there's maybe an effective way to "clear screen" for that Tio output (n.b., printing the \033[2J ansi escape sequence for clear screen also doesn't work)???

John Forkosh

Posted 2019-11-24T04:28:14.987

Reputation: 133

1No, it has to be $2$ passes either way. – Alexey Burdin – 2019-11-24T04:57:58.813

1

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?

– Jo King – 2019-11-24T05:40:48.470

1I'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. – Arnauld – 2019-11-24T08:26:59.190

1Would this be a better [code-golf] question than a C tips question? – my pronoun is monicareinstate – 2019-11-24T09:03:48.767

1"This wasn't originally intended for code-golf..." Hm... does that mean it should be tagged as "code-golf"? You do say you are more interested in a more clever algorithm, but it seems to be stated more as a preference than a straight-up requirement. – gastropner – 2019-11-24T09:04:18.583

1

Just FYI, we have the atomic-code-golf tag to focus on the number of statements. But be aware that 1) it is much harder to specify correctly and 2) I don't think it would be interesting in that particular case.

– Arnauld – 2019-11-24T09:04:33.873

1BTW is the yval calculation correct? yval = bigf*((double)(nrows/2-row))/((double)(nrows/2)) is functionally equivalent to yval = bigf * (nrows / 2 - row) / (nrows / 2), since you do the integer calculations and then convert the result to double, after it has already been truncated. – gastropner – 2019-11-24T09:33:36.730

3I 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. – Nick Kennedy – 2019-11-24T11:27:08.840

@Arnauld Thanks for the TIO link edited into the post. Very cute! And please see both additional Edits (especially the second one) at the bottom, which I made in response to yours. Thanks again. – John Forkosh – 2019-11-24T11:29:48.130

@gastropner Re yval, nrows/2 is meant to be truncated (although in this case, 24/2=12 isn't). That simply tells us which row represents the x-axis. The "outer division" has to be float. Note: in C, I always explicitly and very carefully cast everything in a mixed mode expression. And I can pretty much guarantee you that if you don't, then you'll sooner-or-later (and much more likely sooner) shoot yourself in the foot (or maybe an even more painful place:) – John Forkosh – 2019-11-24T11:44:13.170

1

I've put a sandbox post at https://codegolf.meta.stackexchange.com/a/18310/42248 with a suggested modified version of this for a [tag:code-golf] challenge that's open to all languages. Modifications include display of x-axis and scaling to y-range (without assuming symmetric about x-axis). I've acknowledged the inspiration of this question.

– Nick Kennedy – 2019-11-25T18:50:35.427

1TIO does not work like a console so ansii escape codes will not work, TIO's output is the raw output. You will not be able to get animations or anything. I'm not sure if you figured this out, but if you want a new link for TIO you can hit the chain button at the top. – Post Rock Garf Hunter – 2019-11-29T13:22:14.943

Answers

3

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]);
}

ceilingcat

Posted 2019-11-24T04:28:14.987

Reputation: 5 503