/******************************************************************************* * * Digital ASCII rain - the single thread variant. * 2012 (C) by Author, 2020 GPL licensed for RossetaCode * *******************************************************************************/ #include /* assertions */ #include /* console operations */ #include /* l10n and i18n */ #include /* operations on strings and memory blocks */ #include /* Microsoft Windows API for threads etc. */ #include /* standard i/o library */ #include /* standard library with rand() function */ #include /* time(NULL) used as srand(time(NULL)) */ #include /* just the Microsoft Windows main header for C */ /* * Global variables - you could use local variables instead... * but then e.g. the memory for the buffer variable would be unnecessarily * allocated and released manifold, which would lead to a worse performance. */ HANDLE hStdOut; /* the handle to console "out" */ CONSOLE_SCREEN_BUFFER_INFO csbi; /* numbers of rows, columns etc. */ PCHAR_INFO buffer = NULL; /* an enough big buffer */ COORD bufferSize, bufferCoord; /* the size of buffer etc. */ /* * A structure that holds data that is distinct for each column. */ struct Data { DWORD armed; /* time in ms to synchronize */ int delay; /* armed = current_time + delay */ BOOL show; /* TRUE - draw, FALSE - erase */ COORD ncp; /* position for drawing new characters */ SMALL_RECT s, d; /* source/destination regions for copy */ }; /* * A function to generate random numbers from the range (a, b). * NOTE: POSIX rand () is not thread safe, * but we use secure rand() from MS runtime libraries. */ int rnd(int a, int b) { return a + rand() % (b + 1 - a); } BOOL randomShow(void) { return rnd(0, 99) < 65; } int randomDelay(void) { return rnd(0, 150) + rnd(0, 150) + rnd(0, 150); } BOOL randomRegenerate(void) { return rnd(0, 99) < 2; } void column_init(struct Data* data, int k) { data->armed = 0; data->show = randomShow(); data->delay = randomDelay(); data->s.Left = k; data->s.Top = 0; data->s.Bottom = bufferSize.Y - 2; data->s.Right = k; data->d.Left = data->s.Left; data->d.Top = data->s.Top + 1; data->d.Right = data->s.Right; data->d.Bottom = data->s.Bottom + 1; data->ncp.X = k; data->ncp.Y = 0; } void column_run(struct Data* data) { char c; WORD a; DWORD result; /* * Shift down a column. */ ReadConsoleOutput (hStdOut, buffer, bufferSize, bufferCoord, &data->s); WriteConsoleOutput(hStdOut, buffer, bufferSize, bufferCoord, &data->d); /* * If show == TRUE then generate a new character. * If show == FALSE write the space to erase. */ if(data->show) { c = (rnd(1,100) <= 15) ? ' ' : rnd( 'a', 'z' ); a = FOREGROUND_GREEN | ((rnd(1,100) > 10) ? 0 : FOREGROUND_INTENSITY); } else { c = ' '; a = FOREGROUND_GREEN; } WriteConsoleOutputCharacter(hStdOut, &c, 1, data->ncp, &result); WriteConsoleOutputAttribute(hStdOut, &a, 1, data->ncp, &result); /* * Randomly regenerate the delay and the visibility state of the column. */ if(randomRegenerate()) data->show = randomShow(); if(randomRegenerate()) data->delay = randomDelay(); data->armed = GetTickCount() + data->delay; } int main(int argc, char* argv[]) { int j; struct Data* table; srand((unsigned int)time(NULL)); hStdOut = GetStdHandle( STD_OUTPUT_HANDLE ); SetConsoleTitle("Digital ASCII rain"); /* * An attempt to run in the full-screen mode. */ if(argc == 1) { COORD coord; CONSOLE_CURSOR_INFO cci; cci.bVisible = FALSE; cci.dwSize = 0; SetConsoleDisplayMode(hStdOut, CONSOLE_FULLSCREEN_MODE, &coord); SetConsoleCursorInfo(hStdOut, &cci); } GetConsoleScreenBufferInfo(hStdOut, &csbi ); //SetConsoleTextAttribute(hStdOut, FOREGROUND_GREEN); bufferSize.X = 1; bufferSize.Y = csbi.dwSize.Y - 1; bufferCoord.X = 0; bufferCoord.Y = 0; buffer = (PCHAR_INFO)calloc(bufferSize.Y, sizeof(CHAR_INFO)); assert(buffer != NULL); table = (struct Data*)calloc(csbi.dwSize.X, sizeof(struct Data)); assert(table != NULL); for(j = 0; j < csbi.dwSize.X; j++) column_init(&table[j], j); /* * Main loop. Sleep(1) significally decreases the CPU load. */ while(!_kbhit()) { DWORD t = GetTickCount(); for(j = 0; j < csbi.dwSize.X; j++) if(table[j].armed < t) column_run(&table[j]); Sleep(1); } free(table); free(buffer); return 0; }