0

I am attempting to make my own matrix writing algorith that writes a matrix to the C console using a Run Length Encoding. My idea is that instead of printing every single value in a matrix we could clump up the matrix values in a row that are the same into one line and print that line, then do the same for the next group of values that are the same and continue doing this till we reach the end of the row, this would allow me do use theSetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE)) for coloured ASCII graphics as I could just colour each group. As an example if we had 01110000 as a row in our matrix we would use three print commands only: one for the initial zero, another one for the 3 ones and a finall one for the 4 zeroes. We could do this to avoid wasting proccesing time printing all the values to the console individually, the problem is that there is a lot of undefined behaviour in my code and this causes the console output to be very different to the expected one.

code:

#include <stdio.h>
#include <time.h>
#include <windows.h>

const int sizeX = 20;
const int sizeY = 20;
char line[20];

int grid[20][20] = {{1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,0},
                    {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},               
                    {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},                   
                    {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},                       
                    {0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0},                               
                    {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},                                   
                    {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},                                   
                    {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},                                   
                    {0,0,0,1,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0},                                   
                    {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},                                   
                    {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},                                       
                    {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},                                   
                    {0,0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0},                                   
                    {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},                                   
                    {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},                                   
                    {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},                                   
                    {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},                                   
                    {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},                                   
                    {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}};
                                                                                
void clearScreen(){ 
 COORD cursorPosition;  cursorPosition.X = 0;   cursorPosition.Y = 0;   SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), cursorPosition);
}

void draw_screen(){
 clearScreen();
 int y, x;
 for(y=0;y<sizeY;y++){
  for(x=0;x<sizeX;x++){
    if(grid[y][x] == 1){
        line[x] = '1';
        if(grid[y][x+1] != 1){
            fputs(line, stdout);
            if(grid[y][x+1] > sizeX){
                putc('\n', stdout);
            }
        }
    }
    else if(grid[y][x] == 2){
        line[x] = '2';
        if(grid[y][x+1] != 2){
            fputs(line, stdout);
            if(grid[y][x+1] > sizeX){
                putc('\n', stdout);
            }
        }
    }
    else if(grid[y][x] == 0){
        line[x] = '0';
        if(grid[y][x+1] != 0){
            fputs(line, stdout);
            if(grid[y][x+1] > sizeX){
                putc('\n', stdout);
            }
        }
    }
  }
  putc('\n', stdout);
 }
} 

int main(void) {
 int x, y;
 float frameTime, FPS;

 while (1) {
  clock_t start = clock();
  draw_screen();
  clock_t stop = clock();
  frameTime = (float)(stop - start) / CLOCKS_PER_SEC;
  FPS = 1.0 / frameTime;
  printf("FPS: %f\n", FPS);
 }
}

output:

100000000000000000001000000000000000000010000000000000001110



0000000000000000000000000000010000000000



00000000000000000000000100000000000000000001000000000000000000010000010000000000



0000000000000000000000000000000200000000






00000000000000000000

expected output:

10000000000000001110
00000000000000000000
00000000000000000000
00000000000000000000
00000000010000000000
00000000000000000000
00000000000000000000
00000000000000000000
00010000010000000000
00000000000000000000
00000000000000000000
00000000000000000000
00000000000200000000
00000000000000000000
00000000000000000000
00000000000000000000
00000000000000000000
00000000000000000000
00000000000000000000
00000000000000000000

expected FPS: 100-500

Saw
  • 115
  • 1
  • 16
  • Your attempt to group identical characters is at best useless and at worse conterproductive. The `puts` function does not care about the string content and is not slower nor faster with identical characters. You'd better perform a single `puts` per row, or even a single one for the whole matrix. –  Sep 18 '22 at 15:10
  • @Yves Daoust While more ineficient than printing the whole line, it will allow me to colour each group. – Saw Sep 18 '22 at 16:42
  • Sorry, I did not guess that you would change the text 40 minutes later. –  Sep 18 '22 at 17:02
  • 2
    The concept you're thinking about is called run length encoding. Congrats on that idea. It's used to reduce certain kinds of overhead in computation and communication. However here it will not result in a win because writing to the console is such a cheap process. If you were transmitting a big, sparse matrix through some slow medium to another machine for drawing, then would be a win. Here, just go for the simplest method. Of course if you want to do it anyway for learning, have at it. But I recommend looking for problems that actually yield a win. Life is too short. :) – Gene Sep 18 '22 at 18:26
  • Thanks for the advice, I didnt know the algorithm had a name. If you see my previous question: https://stackoverflow.com/questions/73682672/why-are-console-graphics-so-slow-in-c you will see that console graphics are in fact incredibly slow, especially if you try to draw a big matix. The most popular option is just to print row by row but this means that I cannot give colour to the ascii values and that is the aim of my project, I thought that my algorithm aka. run length encoding would be of great use. Thanks for your help, I can now do some more reaserch since I know the the algorithm name. – Saw Sep 18 '22 at 18:35
  • You can absolutely color cells in your output. Printing row-by-row is not a barrier to that, and **Fe2O3** gave you an example of exactly where to inject color codes into your output exactly according to your stated requirements. – Dúthomhas Sep 19 '22 at 15:08
  • You cannot, Fe2O3 just printed every value individually not row by row. – Saw Sep 19 '22 at 15:13
  • @Dúthomhas can you give me a single example where you have printed a whole line and yet given individual colour to every character in the line. Where can you specify such a thing, if you say printf(row) there is no space to put a function to colour the values, if you put a function to colour the characters before it will result in the entire line having the same colour if you put it after nothing will happen – Saw Sep 19 '22 at 20:56
  • Part of your problem might be that your array is 20x20, but your data is 19x20... You really should take a _chill pill_ and stop demanding that others solve your problems for you... – Fe2O3 Sep 19 '22 at 22:19

2 Answers2

2

Your question has been misunderstood. My complaint is that the code is hard to read (and repetitious).

puts() will unavoidably append a '\n' to each string it outputs.

You could try to "buffer up" a series of repeating characters, only outputting that 'block' when there is a change in the data (or end of grid row reached), but that makes the code messier. imho, it's more important that, for learning, the code be as clean as possible. Work on the algorithm and leave optimisations (like speed) until you have solved the bigger issues.

You have a 20x20 'grid' of ints and want to output that as a grid of characters, but injecting an operation to change the font colour for the next series of characters.

The following example 'injects' an extra character indicating the location where you might change the font colour for subsequent output.

Keep things simple, and avoid the temptation to copy/paste/adapt. Use less code.

#include <stdio.h>

int grid[][20] = {
    {1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,0},
    {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
    {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
    {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
    {0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0},
    {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
    {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
    {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
    {0,0,0,1,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0},
    {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
    {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
    {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
    {0,0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0},
    {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
    {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
    {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
    {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
    {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
    {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
};

void draw_screen() {
    int r, c; // I prefer 'r'ow and 'c'ol
    int prev = -1; // force code to select 1st colour

    for( r = 0; r < 20; r++ ) {
        for( c = 0; c < 20; c++) {
            if( grid[r][c] != prev ) {
                // pick a colour (or character) for subsequent output
                char cc = grid[r][c] == 0 ? '*' : grid[r][c] == 1 ? '+' : '=';
                putchar( cc ); // demonstrate "colour change" happening
            }
            putchar( grid[r][c] + '0' ); // int to char conversion
            prev = grid[r][c];
        }
        putchar( '\n' );
    }
}

int main() {
    draw_screen();
    return 0;
}

Output

+1*000000000000000+111*0
00000000000000000000
00000000000000000000
00000000000000000000
000000000+1*0000000000
00000000000000000000
00000000000000000000
00000000000000000000
000+1*00000+1*0000000000
00000000000000000000
00000000000000000000
00000000000000000000
00000000000=2*00000000
00000000000000000000
00000000000000000000
00000000000000000000
00000000000000000000
00000000000000000000
00000000000000000000
00000000000000000000

EDIT: Trying to point this OP toward a compact solution only seems to generate frustration for all involved.

Here is an (obvious) extension of the above code that works. I don't want to "change colors", but by inserting "special characters" in the output stream, this shows how the program detects and responds to transitions in the data.

(How much are we being paid to write free code for others? Please remind me.)

const char c_red = '*', c_green = '+', c_blue = '=';

void setFontColor( char color ) {
    // do whatever you need to do here.
    // This version outputs a "special" character
    // indicating the color to be used
    fwrite( &color, sizeof color, 1, stdout );
}

void out( char buf[], int n ) {
    char color;
    switch( buf[0] ) {
        case '0': color = c_red; break;
        case '1': color = c_green; break;
        case '2': color = c_blue; break;
        default:
            fprintf( stderr, "Unknown character '%c'\n", buf[0] );
            exit( 1 );
    }
    setFontColor( color );
    fwrite( buf, sizeof buf[0], n, stdout );
}

void draw_screen() {
    char prev = '#', obuf[ 20 ];

    for( int bInd = 0, r = 0; r < 20; r++ ) {
        for( int c = 0; c < 20; c++ ) {
            char ch = (char)(grid[r][c] + '0');
            if( ( c || r ) && ch != prev )
                out( obuf, bInd ), bInd = 0;
            prev = obuf[ bInd++ ] = ch;
        }
        out( obuf, bInd ),  bInd = 0; // End of row
        putchar( '\n' ); // or, tack LF onto end of obuf[]...
    }
}

int main() {
    draw_screen();
    return 0;
}

In the above, each buffered row is discrete (max 20 chars). Buffering LF's and not starting each row with its own colour is left as an exercise for the reader.

Fe2O3
  • 6,077
  • 2
  • 4
  • 20
  • Thanks but this is not at all speedier than just printing every character to the console individually. I checked the update time is the same. – Saw Sep 19 '22 at 14:01
  • This just puts an arithmetic sighn every time the number change, it does not froup them into a string. – Saw Sep 19 '22 at 14:03
  • @BOOTRE I think you are missing the point: the console is _slow_. You aren’t going to find a magic wand to make it work faster. Tying your code in knots to try is, until proven otherwise, a waste of your time. **Fe2O3** is exactly correct in his analysis. – Dúthomhas Sep 19 '22 at 14:53
  • I know. I’ve been using it to write console applications for more than 30 years. – Dúthomhas Sep 19 '22 at 15:01
  • I know for a fact that fputs is so slow that making my own take on a Run Length Encoding algorithm is worth it as it will allow me to give the chars colour and is fast. Other options could be printing row by row but this stops me from giving the values colour. Printing every matrix value individually is another otion but it is slow, the best method I believe to speed up and keep colour would be a RLE algorithm wich is what I am trying to do, this answer only provides me with a solution for adding colour to the chars, however he just prints the values individually making it very slow. – Saw Sep 19 '22 at 15:11
  • I had a question previously where I showcased how slow printing every matrix value was https://stackoverflow.com/questions/73682672/why-are-console-graphics-so-slow-in-c – Saw Sep 19 '22 at 15:12
  • @BOOTRE Oh to have so little imagination... Substitute your own "spooling" function for `putchar()` in the code above. One-by-one, the _spooler_ gathers characters into its own buffer. When the spooler sees a '\n', it flushes its buffer (fast fast) and resets. When the spooler sees a "control char" (like +, *, = above), it flushes the buffer and calls another function to change text colour for the next batch of characters. If you want to develop software, you have to learn to see beyond the confines of your expectations. – Fe2O3 Sep 19 '22 at 21:19
  • @Dúthomhas Thank you for trying to help. `:-)` _Rigid thinkiing_ is a real problem in this industry... Thanks :-) – Fe2O3 Sep 19 '22 at 21:22
  • @Fe2O3 My apologies, I did not fully understand what you were trying to convey in your intitiall code. Thank you for all the advice and your dedication to helping others. – Saw Sep 20 '22 at 16:50
0

I believe I have also solved it, I changed the line string for a character drawing algorith.

#include <stdio.h>
#include <time.h>
#ifdef __unix__
# include <unistd.h>
#elif defined _WIN32
# include <windows.h>
#define sleep(x) Sleep(1000 * (x))
#endif
#include <windows.h>

const int sizeX = 20;
const int sizeY = 20;
int counter0,counter1,counter2;

int grid[20][20] = {{1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,0},
                    {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},               
                    {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},                   
                    {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},                       
                    {0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0},                               
                    {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},                                   
                    {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},                                   
                    {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},                                   
                    {0,0,0,1,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0},                                   
                    {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},                                   
                    {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},                                       
                    {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},                                   
                    {0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0},                                   
                    {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},                                   
                    {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},                                   
                    {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},                                   
                    {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},                                   
                    {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},                                   
                    {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}};

void print_chars(unsigned int number_of_spaces, char character) {
  char* spaces = malloc(sizeof(char)*number_of_spaces + 1);
  memset (spaces,character,number_of_spaces);
  spaces[number_of_spaces] = '\0';
  fputs(spaces, stdout);
  free(spaces);
}

void draw_screen(){
 int y, x;
 for(y=0;y<sizeY;y++){
  for(x=0;x<sizeX;x++){
    sleep(0.1);
   if(grid[y][x] == 0){
    counter0++;
    if(grid[y][x+1] != 0 || x+1 >= sizeX){
     print_chars(counter0, '1');
     counter0 = 0;
    }
   }
   else if(grid[y][x] == 1){
    counter1++;
    if(grid[y][x+1] != 1 || x+1 >= sizeX){
     print_chars(counter1, '@');
     counter1 = 0;
    }
   }
  }
  printf("\n");
 }
}

int main(void) {
 int x, y;
 float frameTime, FPS;

 clock_t start = clock();
 draw_screen();
 clock_t stop = clock();
 frameTime = (float)(stop - start) / CLOCKS_PER_SEC;
 printf("Frame: %f\n", frameTime);
}
Saw
  • 115
  • 1
  • 16