Matrix digital rain: Difference between revisions

From Rosetta Code
Content added Content deleted
(Added Wren)
Line 2,043: Line 2,043:
{{trans|Yabasic}}
{{trans|Yabasic}}
{{libheader|DOME}}
{{libheader|DOME}}
The ''memory.ttf'' file is included with the DOM 'fonts' example and can be downloaded from [https://github.com/domeengine/dome/blob/main/examples/fonts/memory.ttf here].
The ''memory.ttf'' file is included with the DOME 'fonts' example and can be downloaded from [https://github.com/domeengine/dome/blob/main/examples/fonts/memory.ttf here].
<lang ecmascript>import "dome" for Window
<lang ecmascript>import "dome" for Window
import "graphics" for Canvas, Color, Font
import "graphics" for Canvas, Color, Font

Revision as of 12:57, 17 July 2021

Task
Matrix digital rain
You are encouraged to solve this task according to the task description, using any language you may know.

Implement the Matrix Digital Rain visual effect from the movie "The Matrix" as described in Wikipedia.

Provided is a reference implementation in Common Lisp to be run in a terminal.

Ada

Should work with ANSI terminals. <lang Ada>with Ada.Text_IO; with Ada.Numerics.Discrete_Random; with Ada.Strings.Fixed;

procedure Digital_Rain is

  type Column_Type     is range 0 .. 150;
  type Row_Type        is range 0 .. 25;
  type Character_Range is new Character range 'A' .. 'Z';
  subtype Delay_Range  is Integer range 2 .. 6;
  package Column_Random    is new Ada.Numerics.Discrete_Random (Column_Type);
  package Row_Random       is new Ada.Numerics.Discrete_Random (Row_Type);
  package Character_Random is new Ada.Numerics.Discrete_Random (Character_Range);
  package Delay_Random     is new Ada.Numerics.Discrete_Random (Delay_Range);
  Column_Generator    : Column_Random.Generator;
  Row_Generator       : Row_Random.Generator;
  Character_Generator : Character_Random.Generator;
  Delay_Generator     : Delay_Random.Generator;
  Esc     : constant Character := Character'Val (8#33#);
  Running : Boolean   := True;
  Char    : Character;
  use Ada.Text_IO;
  protected Text_Out is
     procedure Put_At (Row : Row_Type; Col : Column_Type;
                       Color : Integer; Item : Character);
  end Text_Out;
  protected body Text_Out is
     procedure Put_At (Row : Row_Type; Col : Column_Type;
                       Color : Integer; Item : Character)
     is
        use Ada.Strings;
     begin
        Put (Esc & "[" &
               Fixed.Trim (Row'Image, Left) & ";" &
               Fixed.Trim (Col'Image, Left) & "H");             -- Place Cursor
        Put (Esc & "[" & Fixed.Trim (Color'Image, Left) & "m"); -- Foreground color
        Put (Item);
     end Put_At;
  end Text_Out;
  task type Rainer;
  task body Rainer is
     Row : Row_Type;
     Col : Column_Type;
     Del : Delay_Range;
  begin
     Outer_Loop:
     loop
        Col := Column_Random.Random (Column_Generator);
        Row := Row_Random.Random (Row_Generator);
        for Rain in reverse Boolean loop
           Del := Delay_Random.Random (Delay_Generator);
           for R in 0 .. Row loop
              if Rain then
                 if R in 1 .. Row then
                    Text_Out.Put_At (R - 1, Col, Color => 32, Item => Char);
                 end if;
                 Char := Character (Character_Random.Random (Character_Generator));
                 if R in 0 .. Row - 1 then
                    Text_Out.Put_At (R, Col, Color => 97, Item => Char);
                 end if;
              else
                 Text_Out.Put_At (R, Col, Color => 30, Item => ' ');
              end if;
              delay 0.020 * Del;
              exit Outer_Loop when not Running;
           end loop;
        end loop;
     end loop Outer_Loop;
  end Rainer;
  Dummy   : Character;
  Rainers : array (1 .. 50) of Rainer;

begin

  Put (ESC & "[?25l");        -- Hide the cursor
  Put (ESC & "[40m");         -- Black background
  Put (ESC & "[2J");          -- Clear Terminal
  Get_Immediate (Dummy);
  Running := False;
  delay 0.200;
  Put (ESC & "[?25h");        -- Restore the cursor
  Put (ESC & "[0;0H");        -- Place cursor
  Put (ESC & "[39m");         -- Default foreground
  Put (ESC & "[49m");         -- Default backgound
  Put (ESC & "[2J");          -- Clear Terminal

end Digital_Rain;</lang>

Batch File

Works with: Windows 10

This code uses Windows 10 VT100 escape sequences. <lang dos>:: Matrix Digital Rain Task from RosettaCode

Batch File Implementation

@echo off setlocal enabledelayedexpansion

rem escape character (for Windows 10 VT100 escape sequences) rem info: https://docs.microsoft.com/en-us/windows/console/console-virtual-terminal-sequences for /f %%e in ('echo prompt $e^| cmd') do @set "esc=%%e"

rem set window size set "col=120"  %== please don't make this too large ==% set "row=30"  %== please don't make this too large ==% mode con cols=%col% lines=%row%

rem set up the variables for display set "rain_length=12" for /l %%y in (1,1,%col%) do set "disp_col[%%y]= "  %== what to display ==% for /l %%y in (1,1,%col%) do set "ctr_col[%%y]=0"  %== counter for rain length ==%

rem hide the cursor, and clear the screen <nul set /p "=%esc%[?25l" cls

matrix_loop

for /l %%y in (1,1,%col%) do (

   if !ctr_col[%%y]! equ 0 (
       set "disp_col[%%y]= "
   ) else (
       set /a "rnd_digit=!random! %% 10"
       if !ctr_col[%%y]! equ 1 (
           set "disp_col[%%y]=%esc%[97m!rnd_digit!%esc%[32m"
       ) else if !ctr_col[%%y]! equ 2 (
           set "disp_col[%%y]=%esc%[92m!rnd_digit!%esc%[32m"
       ) else (
           set "disp_col[%%y]=!rnd_digit!"
       )
       set /a "ctr_col[%%y]=(!ctr_col[%%y]! + 1) %% (%rain_length% + 1)"
   )
   rem drop rain randomly
   set /a "rnd_drop=!random! %% 20"
   if !rnd_drop! equ 0 set "ctr_col[%%y]=1"

) set "disp_line=%esc%[32m" for /l %%y in (1,1,%col%) do set "disp_line=!disp_line!!disp_col[%%y]!" <nul set /p "=%esc%[1T%esc%[1;1H"  %== scroll down and set cursor position to home ==% echo(%disp_line% goto matrix_loop</lang>

C

NCURSES version

Adaptation of Dan Ruscoe's C code. Header files added and code rearranged to suppress implicit declaration warnings.

Requires ncurses, install on Ubuntu as follows :

$ sudo apt install libncurses5-dev

Compile with ncurses flag :

$ cc digiRain.c -lncurses

And here's the code : <lang C> /**

* Loosely emulates the "digital rain" effect from The Matrix.
*
* @author Dan Ruscoe <dan@ruscoe.org>
*/
  1. include <unistd.h>
  2. include <time.h>
  3. include <stdio.h>
  4. include <stdlib.h>
  5. include <ncurses.h>

/* Time between row updates in microseconds.

  Controls the speed of the digital rain effect.
  • /
  1. define ROW_DELAY 40000

/**

* Gets a random integer within a given range.
*
* @param int min The low-end of the range.
* @param int max The high-end of the range.
*
* @return int The random integer.
*/

int get_rand_in_range(int min, int max) {

 return (rand() % ((max + 1) - min) + min);

}

int main(void) {

 /* Basic seed for random numbers. */
 srand(time(NULL));
 /* Characters to randomly appear in the rain sequence. */
 char chars[] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9'};
 int total_chars = sizeof(chars);
 /* Set up ncurses screen and colors. */
 initscr();
 noecho();
 curs_set(FALSE);
 start_color();
 init_pair(1, COLOR_GREEN, COLOR_BLACK);
 attron(COLOR_PAIR(1));
 int max_x = 0, max_y = 0;
 getmaxyx(stdscr, max_y, max_x);
 /* Create arrays of columns based on screen width. */
 /* Array containing the current row of each column. */
 int columns_row[max_x];
 /* Array containing the active status of each column.
    A column draws characters on a row when active.
 */
 int columns_active[max_x];
 int i;
 /* Set top row as current row for all columns. */
 for (i = 0; i < max_x; i++)
 {
   columns_row[i] = -1;
   columns_active[i] = 0;
 }
 while (1)
 {
   for (i = 0; i < max_x; i++)
   {
     if (columns_row[i] == -1)
     {
       /* If a column is at the top row, pick a
          random starting row and active status.
       */
       columns_row[i] = get_rand_in_range(0, max_y);
       columns_active[i] = get_rand_in_range(0, 1);
     }
   }
   /* Loop through columns and draw characters on rows. */
   for (i = 0; i < max_x; i++)
   {
     if (columns_active[i] == 1)
     {
       /* Draw a random character at this column's current row. */
       int char_index = get_rand_in_range(0, total_chars);
       mvprintw(columns_row[i], i, "%c", chars[char_index]);
     }
     else
     {
       /* Draw an empty character if the column is inactive. */
       mvprintw(columns_row[i], i, " ");
     }
     columns_row[i]++;
     /* When a column reaches the bottom row, reset to top. */
     if (columns_row[i] >= max_y)
     {
       columns_row[i] = -1;
     }
     /* Randomly alternate the column's active status. */
     if (get_rand_in_range(0, 1000) == 0)
     {
       columns_active[i] = (columns_active[i] == 0) ? 1 : 0;
     }
   }
   usleep(ROW_DELAY);
   refresh();
 }
 endwin();
 return 0;

} </lang>

Microsoft Windows console version

Single threaded

<lang C>/*******************************************************************************

  • Digital ASCII rain - the single thread variant.
  • 2012 (C) by Author, 2020 GPL licensed for RossetaCode
                                                                                                                                                              • /
  1. include <assert.h> /* assertions */
  2. include <conio.h> /* console operations */
  3. include <locale.h> /* l10n and i18n */
  4. include <string.h> /* operations on strings and memory blocks */
  5. include <process.h> /* Microsoft Windows API for threads etc. */
  6. include <stdio.h> /* standard i/o library */
  7. include <stdlib.h> /* standard library with rand() function */
  8. include <time.h> /* time(NULL) used as srand(time(NULL)) */
  9. include <windows.h> /* 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;

}</lang>

Multiple threads

<lang C>/*******************************************************************************

  • Digital ASCII rain - multithreaded.
  • 2012 (C) by Author, 2020 GPL licensed for RossetaCode
                                                                                                                                                              • /
  1. include <assert.h> /* assertions */
  2. include <conio.h> /* console operations */
  3. include <locale.h> /* l10n and i18n */
  4. include <string.h> /* operations on strings and memory blocks */
  5. include <process.h> /* Microsoft Windows API for threads etc. */
  6. include <stdio.h> /* standard i/o library */
  7. include <stdlib.h> /* standard library with rand() function */
  8. include <time.h> /* time(NULL) used as srand(time(NULL)) */
  9. include <windows.h> /* just the Microsoft Windows main header for C */


//#define SYNCHRONIZED_INIT


/*

* Global variables, common for all threads. 
* The handle eventThreadInitialized is to synchronize threads initializations.
*/
  1. ifdef SYNCHRONIZED_INIT

HANDLE eventThreadInitialized;

  1. endif

BOOL loop = TRUE; /* FALSE - przerwać działanie */ HANDLE hStdOut; /* uchwyt do konsoli "out" */ CONSOLE_SCREEN_BUFFER_INFO csbi; /* liczby wierszy i kolumn itd.*/


struct InitThreadData {

 int      column;
 unsigned seed;

};


/*

* 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;

}


/*

* Update a single column.
*/

void one_column(void *arg) {

 BOOL show; 
 int  k;
 char c;
 WORD a;
 int  delay;
 PCHAR_INFO buffer;
 COORD bufferSize, bufferCoord, newCharPosition;
 SMALL_RECT s, d;
 DWORD result;
 /* 
  * Retrieve the column number, initialize the (pseudo)random numbers
  * generator with a "private" seed, check assertions.
  */
 k = ((struct InitThreadData*)arg)->column;
 srand(((struct InitThreadData*)arg)->seed);
 assert(csbi.dwSize.X != 0 && csbi.dwSize.Y != 0);
 assert(0 <= k && k <= csbi.dwSize.X);
 show   = randomShow();
 delay  = randomDelay();
 bufferSize.X = 1;
 bufferSize.Y = csbi.dwSize.Y - 1;
 bufferCoord.X = 0;
 bufferCoord.Y = 0;
 buffer = (PCHAR_INFO)malloc(bufferSize.Y * sizeof(CHAR_INFO));
 assert( buffer != NULL );
 s.Left   = k;
 s.Top    = 0;
 s.Bottom = bufferSize.Y - 2;
 s.Right  = k;
 d.Left   = s.Left;
 d.Top    = s.Top + 1;
 d.Right  = s.Right;
 d.Bottom = s.Bottom + 1;
 newCharPosition.X = k;
 newCharPosition.Y = 0;
 
  1. ifdef SYNCHRONIZED_INIT
 SetEvent(eventThreadInitialized);
  1. endif
 /*
  * Main loop inside this one thread.
  */
 while(loop)
 {
   ReadConsoleOutput ( hStdOut, buffer, bufferSize, bufferCoord, &s);
   WriteConsoleOutput( hStdOut, buffer, bufferSize, bufferCoord, &d); 
   if(show)
   {
     c = (rnd(1,100) <= 15) ? ' ' : rnd( 'a', 'z' );
     a = FOREGROUND_GREEN | ((rnd(1,100) >  1) ? 0 : FOREGROUND_INTENSITY);
   }
   else
   {
     c = ' ';
     a = FOREGROUND_GREEN;
   }
   WriteConsoleOutputCharacter( hStdOut, &c, 1, newCharPosition, &result );
   WriteConsoleOutputAttribute( hStdOut, &a, 1, newCharPosition, &result );
   if(randomRegenerate())  show  = randomShow();
   if(randomRegenerate())  delay = randomDelay();
   Sleep( delay );
 }
 free(buffer);

}


int main(int argc, char* argv[]) {

 int j;
 struct InitThreadData *init = NULL;
 srand((time_t)time(NULL));
 hStdOut = GetStdHandle( STD_OUTPUT_HANDLE );
 SetConsoleTitle("MATRIX");
 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);
  1. ifdef SYNCHRONIZED_INIT
 eventThreadInitialized = CreateEvent(NULL,FALSE,FALSE,NULL);
 assert( eventThreadInitialized != NULL );
  1. endif
 init = (struct InitThreadData*)
         malloc(csbi.dwSize.X * sizeof(struct InitThreadData));
 assert( init != NULL );
 for(j = 0; j < csbi.dwSize.X; j++)
 {
   init[j].column = j;
   init[j].seed = rand();
   _beginthread(one_column, 0, (void*)&init[j]);
  1. ifdef SYNCHRONIZED_INIT
   WaitForSingleObject(eventThreadInitialized,INFINITE);
  1. endif
 }
 getchar();
 free(init);
  1. ifdef SYNCHRONIZED_INIT
 CloseHandle(eventThreadInitialized);
  1. endif SYNCHRONIZED_INIT
 return 0;

}</lang>

Fibers

<lang C>/*******************************************************************************

  • Digital ASCII rain - multithreaded.
  • 2012 (C) by Author, 2020 GPL licensed for RossetaCode
                                                                                                                                                              • /
  1. include <assert.h> /* assertions */
  2. include <conio.h> /* console operations */
  3. include <locale.h> /* l10n and i18n */
  4. include <string.h> /* operations on strings and memory blocks */
  5. include <process.h> /* Microsoft Windows API for threads etc. */
  6. include <stdio.h> /* standard i/o library */
  7. include <stdlib.h> /* standard library with rand() function */
  8. include <time.h> /* time(NULL) used as srand(time(NULL)) */
  9. include <windows.h> /* just the Microsoft Windows main header for C */


/*

* Global variables, shared by fibers.
*/

BOOL loop = TRUE; HANDLE hStdOut; CONSOLE_SCREEN_BUFFER_INFO csbi;

LPVOID mainFiber = NULL;

struct WorkingFiber {

 int      column;
 DWORD    callAfter;
 unsigned seed;
 LPVOID   fiber;

} *workingFibersTable = NULL;

/*

* 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 CALLBACK one_column(void *arg) {

 struct WorkingFiber *ptr;
 BOOL show; 
 int  k;
 char c;
 WORD a;
 int  delay;
 PCHAR_INFO buffer;
 COORD bufferSize, bufferCoord, newCharPosition;
 SMALL_RECT s, d;
 DWORD result;
 ptr = (struct WorkingFiber*)arg;
 k = ptr->column;
 ptr->callAfter = 0;
 srand(ptr->seed);
 assert(csbi.dwSize.X != 0 && csbi.dwSize.Y != 0);
 assert(0 <= k && k <= csbi.dwSize.X);
 show  = randomShow();
 delay = randomDelay();
 bufferSize.X = 1;
 bufferSize.Y = csbi.dwSize.Y - 1;
 bufferCoord.X = 0;
 bufferCoord.Y = 0;
 buffer = (PCHAR_INFO)malloc(bufferSize.Y * sizeof(CHAR_INFO));
 assert( buffer != NULL );
 s.Left        = k;
 s.Top         = 0;
 s.Bottom      = bufferSize.Y - 2;
 s.Right       = k;
 d.Left   = s.Left;
 d.Top    = s.Top + 1;
 d.Right  = s.Right;
 d.Bottom = s.Bottom + 1;
 newCharPosition.X = k;
 newCharPosition.Y = 0;
 
 while(loop)
 {
   ReadConsoleOutput ( hStdOut, buffer, bufferSize, bufferCoord, &s);
   WriteConsoleOutput( hStdOut, buffer, bufferSize, bufferCoord, &d); 
   if(show)
   {
     c = (rnd(1,100) <= 15) ? ' ' : rnd( 'a', 'z' );
     a = FOREGROUND_GREEN | ((rnd(1,100) >  1) ? 0 : FOREGROUND_INTENSITY);
   }
   else
   {
     c = ' ';
     a = FOREGROUND_GREEN;
   }
   WriteConsoleOutputCharacter( hStdOut, &c, 1, newCharPosition, &result );
   WriteConsoleOutputAttribute( hStdOut, &a, 1, newCharPosition, &result );
   if(randomRegenerate())  show  = randomShow();
   if(randomRegenerate())  delay = randomDelay();
   ptr->callAfter = GetTickCount() + delay;
   SwitchToFiber(mainFiber);
 }
 free(buffer);

}


int main(int argc, char* argv[]) {

 int j;
 srand((unsigned int)time(NULL));
 hStdOut = GetStdHandle( STD_OUTPUT_HANDLE );
 SetConsoleTitle("MATRIX - FIBERS");
 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);
 mainFiber = ConvertThreadToFiber(NULL);
 assert( mainFiber != NULL );
 workingFibersTable = (struct WorkingFiber*)
                      calloc(csbi.dwSize.X, sizeof(struct WorkingFiber));
 assert( workingFibersTable != NULL );
 for(j = 0; j < csbi.dwSize.X; j++)
 {
   workingFibersTable[j].column = j;
   workingFibersTable[j].callAfter = 0;
   workingFibersTable[j].seed = rand();
   workingFibersTable[j].fiber = CreateFiber( 0, one_column, &workingFibersTable[j] );
 }
 loop = TRUE;
 while(!_kbhit())
 {
   DWORD t = GetTickCount();
   for(j = 0; j < csbi.dwSize.X; j++)
     if(workingFibersTable[j].callAfter < t)
       SwitchToFiber( workingFibersTable[j].fiber );
   Sleep(1);
 }
 loop = FALSE;
 for(j = 0; j < csbi.dwSize.X; j++)
 {
   SwitchToFiber( workingFibersTable[j].fiber );
   DeleteFiber(workingFibersTable[j].fiber);
 }
 free(workingFibersTable);
 return 0;

}</lang>

Common Lisp

Works with: SBCL

Runs in the terminal (using the Ncurses C library and the croatoan Lisp wrapper).

<lang lisp> (defun matrix-digital-rain ()

 (with-screen (scr :input-echoing nil :input-blocking nil :cursor-visible nil)
   (let* ((width (width scr))
          (height (height scr))
          ;; start at a random height in each column.
          (positions (loop repeat width collect (random height)))
          ;; run each column at a random speed.
          (speeds (loop repeat width collect (random 4))))
     ;; generate a random ascii char
     (flet ((randch () (+ 64 (random 58))))
       ;; hit the q key to exit the main loop.
       (bind scr #\q 'exit-event-loop)
       (bind scr nil
         (lambda (win event)
           (loop for col from 0 to (1- width) do
             (loop repeat (nth col speeds) do
               ;; position of the first point in the current column
               (let ((pos (nth col positions)))
                 (setf (attributes win) '(:bold))
                 (setf (fgcolor win) :green)
                 (add win (randch) :y (mod pos height) :x col :fgcolor :white)
                 (add win (randch) :y (mod (- pos 1) height) :x col)
                 (add win (randch) :y (mod (- pos 2) height) :x col)
                 (setf (attributes win) '())
                 (add win (randch) :y (mod (- pos 3) height) :x col)
                 ;; overwrite the last char half the height from the first char.
                 (add win #\space  :y (mod (- pos (floor height 2)) height) :x col)
                 (refresh win)
                 ;; advance the current column
                 (setf (nth col positions) (mod (+ pos 1) height))))))))
     (setf (frame-rate scr) 20)
     (run-event-loop scr))))

</lang>

Sample output:

https://i.imgur.com/17b36O3.png

Go

Library: goncurses

This is a translation of the C code here which uses the ncurses library.

Rather than pressing Ctrl+C to stop the program, I've added code so that it stops automatically after 1 minute and restores the terminal to its original state. <lang go>package main

import (

   gc "github.com/rthornton128/goncurses"
   "log"
   "math/rand"
   "time"

)

// Time between row updates in microseconds. // Controls the speed of the digital rain effect. const rowDelay = 40000

func main() {

   start := time.Now()
   rand.Seed(time.Now().UnixNano())
   // Characters to randomly appear in the rain sequence.
   chars := []byte("0123456789")
   totalChars := len(chars)
   // Set up ncurses screen and colors.
   stdscr, err := gc.Init()
   if err != nil {
       log.Fatal("init", err)
   }
   defer gc.End()
   gc.Echo(false)
   gc.Cursor(0)
   if !gc.HasColors() {
       log.Fatal("Program requires a colour capable terminal")
   }
   if err := gc.StartColor(); err != nil {
       log.Fatal(err)
   }
   if err := gc.InitPair(1, gc.C_GREEN, gc.C_BLACK); err != nil {
       log.Fatal("InitPair failed: ", err)
   }
   stdscr.ColorOn(1)
   maxY, maxX := stdscr.MaxYX()
   /* Create slices of columns based on screen width. */
   // Slice containing the current row of each column.
   columnsRow := make([]int, maxX)
   // Slice containing the active status of each column.
   // A column draws characters on a row when active.
   columnsActive := make([]int, maxX)
   // Set top row as current row for all columns.
   for i := 0; i < maxX; i++ {
       columnsRow[i] = -1
       columnsActive[i] = 0
   }
   for {
       for i := 0; i < maxX; i++ {
           if columnsRow[i] == -1 {
               // If a column is at the top row, pick a
               // random starting row and active status.
               columnsRow[i] = rand.Intn(maxY + 1)
               columnsActive[i] = rand.Intn(2)
           }
       }
       // Loop through columns and draw characters on rows.
       for i := 0; i < maxX; i++ {
           if columnsActive[i] == 1 {
               // Draw a random character at this column's current row.
               charIndex := rand.Intn(totalChars)
               stdscr.MovePrintf(columnsRow[i], i, "%c", chars[charIndex])
           } else {
               // Draw an empty character if the column is inactive.
               stdscr.MovePrintf(columnsRow[i], i, "%c", ' ')
           }
           columnsRow[i]++
           // When a column reaches the bottom row, reset to top.
           if columnsRow[i] >= maxY {
               columnsRow[i] = -1
           }
           // Randomly alternate the column's active status.
           if rand.Intn(1001) == 0 {
               if columnsActive[i] == 0 {
                   columnsActive[i] = 1
               } else {
                   columnsActive[i] = 0
               }
           }
       }
       time.Sleep(rowDelay * time.Microsecond)
       stdscr.Refresh()
       elapsed := time.Since(start)
       // Stop after 1 minute.
       if elapsed.Minutes() >= 1 {
           break
       }
   }

}</lang>

Julia

The font used is based on Leonardo da Vinci's notebooks. The font is freely obtainable at https://www.wfonts.com/font/leonardos-mirrorwriting. <lang Julia>using Gtk, Colors, Cairo import Base.iterate, Base.IteratorSize, Base.IteratorEltype

const caps = [c for c in "ABCDEFGHIJKLMNOPQRSTUVWXYZ"] startfall() = rand() < 0.2 endfall() = rand() < 0.2 startblank() = rand() < 0.1 endblank() = rand() < 0.05 bechangingchar() = rand() < 0.03

struct RainChars chars::Vector{Char} end Base.IteratorSize(s::RainChars) = Base.IsInfinite() Base.IteratorEltype(s::RainChars) = Char function Base.iterate(rain::RainChars, state = (true, false, 0))

   c = '\0'
   isfalling, isblank, blankcount = state
   if isfalling # falling, so feed the column
       if isblank
           c = ' '
           ((blankcount += 1) > 12) && (isblank = endblank())
       else
           c = bechangingchar() ? '~' : rand(rain.chars)
           isblank, blankcount = startblank(), 0
       end
       endfall() && (isfalling = false)
   else
       isfalling = startfall()
   end   
   return c, (isfalling, isblank, blankcount)

end

function digitalrain()

   mapwidth, mapheight, fontpointsize = 800, 450, 14
   windowmaxx = div(mapwidth, Int(round(fontpointsize * 0.9)))
   windowmaxy = div(mapheight, fontpointsize)
   basebuffer = fill(' ', windowmaxy, windowmaxx)
   bkcolor, rcolor, xcolor = colorant"black", colorant"green", colorant"limegreen"
   columngenerators = [Iterators.Stateful(RainChars(caps)) for _ in 1:windowmaxx]
   win = GtkWindow("Digital Rain Effect", mapwidth, mapheight) |>
       (GtkFrame() |> (can = GtkCanvas()))
   set_gtk_property!(can, :expand, true)
   draw(can) do widget
       ctx = Gtk.getgc(can)
       select_font_face(ctx, "Leonardo\'s mirrorwriting", Cairo.FONT_SLANT_NORMAL,
           Cairo.FONT_WEIGHT_BOLD)
       set_font_size(ctx, fontpointsize)
       set_source(ctx, bkcolor)
       rectangle(ctx, 0, 0, mapwidth, mapheight)
       fill(ctx)
       set_source(ctx, rcolor)
       for i in 1:size(basebuffer)[1], j in 1:size(basebuffer)[2]
           move_to(ctx, j * fontpointsize * 0.9, i * fontpointsize)
           c = basebuffer[i, j]
           if c == '~'

set_source(ctx, xcolor)

               show_text(ctx, String([rand(caps)]))

set_source(ctx, rcolor)

           else
               show_text(ctx, String([c]))
           end
       end
   end
   while true
       for col in 1:windowmaxx
           c = popfirst!(columngenerators[col])
           if c != '\0'
               basebuffer[2:end, col] .= basebuffer[1:end-1, col]
               basebuffer[1, col] = c
           end
       end
       draw(can)
       Gtk.showall(win)
       sleep(0.05)
   end

end

digitalrain() </lang>

Locomotive Basic

<lang locobasic>10 mode 0:defint a-z:randomize time:ink 0,0:ink 1,26:ink 2,19:border 0 20 dim p(20):mm=5:dim act(mm):for i=1 to mm:act(i)=rnd*19+1:next 30 md=mm-2:dim del(md):for i=1 to md:del(i)=rnd*19+1:next 40 for i=1 to mm:x=act(i):locate x,p(x)+1:pen 1:print chr$(rnd*55+145); 50 if p(x)>0 then locate x,p(x):pen 2:print chr$(rnd*55+145); 60 p(x)=p(x)+1:if p(x)=25 then locate x,25:pen 2:print chr$(rnd*55+145);:p(x)=0:act(i)=rnd*19+1 70 next 80 for i=1 to md:x=del(i):locate x,p(x)+1:print " "; 90 p(x)=p(x)+1:if p(x)=25 then p(x)=0:del(i)=rnd*19+1 100 next 110 goto 40</lang> The program above runs at an acceptable speed on a 4 MHz Z80, but can be made much faster by using the CPCBasic JavaScript emulator. CPCBasic also adds an 80x50 display mode ("mode 3") which real CPC hardware lacks. Changing screen size from 20x25 to 80x50 and adding a delay with "frame" in line 100 results in a far more impressive display in CPCBasic: <lang locobasic>10 mode 3:defint a-z:randomize time:ink 0,0:ink 1,26:ink 2,19:border 0 20 dim p(80):mm=12:dim act(mm):for i=1 to mm:act(i)=rnd*79+1:next 30 md=mm-2:dim del(md):for i=1 to md:del(i)=rnd*79+1:next 40 for i=1 to mm:x=act(i):locate x,p(x)+1:pen 1:print chr$(rnd*55+145); 50 if p(x)>0 then locate x,p(x):pen 2:print chr$(rnd*55+145); 60 p(x)=p(x)+1:if p(x)=50 then locate x,50:pen 2:print chr$(rnd*55+145);:p(x)=0:act(i)=rnd*79+1 70 next 80 for i=1 to md:x=del(i):locate x,p(x)+1:print " "; 90 p(x)=p(x)+1:if p(x)=50 then p(x)=0:del(i)=rnd*79+1 100 next:frame 110 goto 40</lang>

Nim

Translation of: C
Library: nim-ncurses

<lang Nim>import os, random, sequtils import ncurses

const RowDelay = 40 # In milliseconds.

proc exit() {.noconv.} =

 endwin()
 quit QuitSuccess

proc run() =

 const
   Chars = "0123456789"    # Characters to randomly appear in the rain sequence.
 let stdscr = initscr()
 noEcho()
 cursSet(0)
 startColor()
 initPair(1, COLOR_GREEN, COLOR_BLACK)
 attron(COLOR_PAIR(1).cint)
 var width, height: cint
 stdscr.getmaxyx(height, width)
 let maxX = width - 1
 let maxY = height - 1
 # Create arrays of columns based on screen width.
 # Array containing the current row of each column.
 # Set top row as current row for all columns.
 var columnsRow = repeat(cint -1, width)
 # Array containing the active status of each column.
 # A column draws characters on a row when active.
 var columnsActive = newSeq[bool](width)


 setControlCHook(exit)
 while true:
   for i in 0..maxX:
     if columnsRow[i] == -1:
       # If a column is at the top row, pick a random starting row and active status.
       columnsRow[i] = cint(rand(maxY))
       columnsActive[i] = bool(rand(1))
   # Loop through columns and draw characters on rows.
   for i in 0..maxX:
     if columnsActive[i]:
       # Draw a random character at this column's current row.
       let charIndex = rand(Chars.high)
       mvprintw(columnsRow[i], i, "%c", Chars[charIndex])
     else:
       # Draw an empty character if the column is inactive.
       mvprintw(columnsRow[i], i, " ")
     inc columnsRow[i]
     # When a column reaches the bottom row, reset to top.
     if columnsRow[i] > maxY: columnsRow[i] = -1
     # Randomly alternate the column's active status.
     if rand(999) == 0: columnsActive[i] = not columnsActive[i]
   sleep(RowDelay)
   refresh()

run()</lang>

Perl

Probably shouldn't, but until someone posts something better... here's something somewhat relevant I wrote back in 2006.

Follow the bouncing Neo!

<lang perl>#!/user/bin/perl

use strict; use warnings; use Tk;

my $delay = 50; # milliseconds my $fade = 8; # number of characters to "fade" my $base_color = '#004000'; # dark green my $fontname = 'Times'; # Whatever my $fontsize = 12; # point size my $font = "{$fontname} $fontsize bold"; my @objects;

my ( $xv, $yv ) = ( 0, 0 );

my $top = MainWindow->new();

$top->geometry('800x600');

my $run = 1;

$top->protocol( 'WM_DELETE_WINDOW' => sub { $run = 0; } );

my @letters = ( 'A' .. 'Z', 'a' .. 'z', '0' .. '9' );

my $canvas = $top->Canvas(

   -background => 'black'

)->pack(

   -fill   => 'both',
   -expand => 'y'

);

my $testch = $canvas->createText(

   100, 100,
   -text => 'o',
   -fill => 'black',
   -font => $font

);

$top->update;

my @coords = $canvas->bbox($testch);

$canvas->delete($testch);

my $lwidth = $coords[2] - $coords[0]; my $lheight = ( $coords[3] - $coords[1] ) * .8; my $cols = int $canvas->width / $lwidth; my $rows = int $canvas->height / $lheight;

for my $y ( 0 .. $rows ) {

   for my $x ( 0 .. $cols ) {
       $objects[$x][$y] = $canvas->createText(
           $x * $lwidth, $y * $lheight,
           -text => $letters[ int rand @letters ],
           -fill => $base_color,
           -font => $font
       );
   }

}

my $neo_image = $top->Photo( -data => neo() );

my $neo = $canvas->createImage(

   $canvas->width / 2,
   $canvas->height / 2,
   -image => $neo_image

);

while ($run) {

   drop('Nothing Like The Matrix');

} exit;

MainLoop;

sub drop {

   my @phrase = split //, reverse shift;
   my $x = int rand $cols;
   my @orig;
   for my $y ( 0 .. $rows ) {
       $orig[$y] = $canvas->itemcget( $objects[$x][$y], '-text' );
   }
   for my $y ( 0 .. $rows + @phrase + $fade ) {
       for my $letter ( 0 .. @phrase ) {
           last if ( $y - $letter < 0 );
           $canvas->itemconfigure(
               $objects[$x][ $y - $letter ],
               -text => $phrase[$letter],
               -fill => "#00FF00"
           );
       }
       if ( $y > @phrase ) {
           $canvas->itemconfigure(
               $objects[$x][ $y - @phrase ],
               -text => $orig[ $y - @phrase ],
               -fill => "#009000"
           );
       }
       if ( $y > @phrase + 2 ) {
           $canvas->itemconfigure( $objects[$x][ $y - @phrase - int ($fade / 2) ],
               -fill => "#006000" );
           $canvas->itemconfigure( $objects[$x][ $y - @phrase - $fade + 1 ],
               -fill => $base_color );
       }
       last unless $run;
       $top->after($delay);
       neo_move();
       $top->update;
   }

}

sub neo_move {

   $xv += ( ( rand 2 ) - 1 > 0 ) ? 1 : -1;
   $yv += ( ( rand 2 ) - 1 > 0 ) ? 1 : -1;
   my ( $x, $y ) = $canvas->coords($neo);
   $xv = -$xv if ( ( $x < 0 ) or ( $x > $canvas->width ) );
   $yv = -$yv if ( ( $y < 0 ) or ( $y > $canvas->height ) );
   $canvas->move( $neo, $xv, $yv );

}

sub neo { return ' R0lGODlhjAC1APcAAAQDBISCZEJDM8nDq6eihGJjSyQjFOzj0oSEhGRlZCktLKKkpEhNTMHFxBES BFBSNDMyJJSSbOru7HFyVGt0aqm1q5OVhMnTy2RELLmzmy0UEUQiDMa1pZSEbCo4MrSVhIh0Z9jO tLSljNPV1IhkSPjy3D9EPCYDBB4VHJGVlCIjHGxKTOrl3GFkVLnCuUU5LQsLCXmDesq9tk9XTBEa FOXVxSEqImRUQvv37HF1dJOahZmkmOzq3Km6tMe8qEJLQJhyVIZ9Z9bFtJeEeLamlG5sVFEzIUAp LKSUfGBeXN/FxBIUFHN7XF9FPiwcDD0kITc9PF5ZTk1LNFlbQ3p9ZYqMdXhqZOna1C4lHREOFOXb xLesl/z6/JeNdj47JJqafLy7pZ+Vhjs+MKurlYl8dBUEBa2chNrQvBUNC9e8qkAsHIqLbGtrTH10 W8O6m1Q+PPXs301EMpyMhMGtpFhURNzbykU+MVBMPS8dHPTq1B4UDD0zI4qTfDo4LU5FPJKclvTm 3G1dTYFjVHhcXIOMhlprZKqrpFxMNJWLbEw+JF1NPqSbfGBQTKyri8XMxEQsFNbb1CYMBB8dHGRa PHR9dD8xLNvNxH1tWbCelOXQvG5lVUgcEIhcTM/KrDAqFPny7bTKvLq+t3RuZC8yLk1STZRqTPz6 5FFeUfz+86B6XNbKt+ve1dTDrG1lTC4jFPTl1Hh1Zru2pNzUxMS0nEAWENW1qaiCaJh2aMzFtFw6 NImFdte9tCEcEzEsIaCchqmmlLa4tZZ7aayGdFQiHMWtnKaNfJ+blo6OiWlsZm9OQbWqjGFeTtvW vFU+MszKtohuaPfu7B4VFH97XIhoZJmUfHRKNGQ6JLyafIeEbKijjBITDFZTPJWSdHJ0XIqUjGJE NEQkFKySjPvy5EJFRB8lJOPm5FtkXFBYVPv59Obs5KR2XNfDvLqmnG5sXC8dFOfczK6djD4tJKSO jHxiXKutrMvMzNnd3CYODCQeJGVcRHx9fEkxNNTOzPz+/OTWvL+elCH5BAEAAP0ALAAAAACMALUA Bwj/APsJHEiwoMGDCBMqXMiwocOHECNKnEixosWLGDNq3Mixo8ePIEOKHEmypMmTKFOqXMmSJReJ LxPGbEkT5EyBM2PerMmT4E6D6NJdAAasgQtHF8qNKFpOwqeeUB+iG4iqH6pyhpCJ8fCDEr0dpGio UKBCGylHUweiwzHwZ1So9kJVoHTKgzYYo855+0PpBwwAKlTYsGHCnDkKlCgQqoD0goR+bt+yRGfP WwJChGgAAGDDw4xxNLRtBqDNg4dxM86pAADDhjYVMbzF+AMpreSeKUbFuKCDHAAa4zzA+Du6+Oi/ MNCQI4eM0igxFW5HNQblD4sdNgAogMJgifHv4AGQ/7PRulAPCY+lW4yM8Ga6GD/+9FC9ZMko7+Hz h18C5RylChdAkp56Gb2UU0ExwKDCDKSIpt+DEALmwSiU2GMbZAqxR2A/TwF1jDY26PbDZsSxFuGJ Jv6lAgM/mPNHPR1qeFtk6NSIHiT9pFNOP+gk+MMpXZVonJAofofGEsmpYNooEyrAgCEXboghTv1I 0MMOLuCAwwihpNOPN4Rc4OUO2lDijVjZgUdkkSSWONwSnf1ggmnjnZNCA5B4yYUEMvYkwR/nnOOB CYWcQg4FFxQCgwl/hGKaNx4sIclwarJZJAza0DAKFB4sp4ACpDRAmYVTShdDWfiZthkNCoimQHY/ /P9Agw1orLkZkpZeCoByo4xCTqYepPBYlJLZYwMNJpggyRKBCkoiAJLcNUMhNHhHnJDKoZHrtkuQ gysAOUzVZ0vHAEAJKBSsRtqE4EkyirbJDYetJNpua1ytQ+JbHHKbESLlMTTEN8qrCghnYnjJoXFk vbfSa29+Cdt6bw4DSobOMcfeRYoJ9UF4JAz16QuApA4+jLDE3yWQ3oE0odPAOVD41ZqQtv6lDZIf HywpfibbS9wfBY0rEjos/EFDcuL5tq/E8carc7Q9Rz3KCG+hQw8DNZMo8pB/bX1kyVGjuCYpFdOE wx8mHKdmrQq7idxwtZZ45LdhP5xAhzVVIAnEKYL//C3cbTfN2ptLbF33g7bmIDRHOyUg9sJt+u1A 5KxlkcUSWRh+LZvyRgj5aKQQe9JLhlgbIXKB+62NwqkTHrjCrK0+uOEnU8o3a5IoQA5xJthTakr2 QFEcwwhvtrDCKCyxusJZyBu3Aw4s7PwS0WhjPS90R40cDfSWsdndLeWwL/H6Yaowksmt7jxrCt+8 evJfv9ZLH2L00UtgvKBRBgzed27vx39ZQgNY0oC9talI7QuZ+5yXHOU1EA3L44UEe7GNblChG+3o RjeW0QsJ5m843juY2FhTuF0BQB+iGwk6HCfCx1lPGxI0gAqw0Ic95E8Pq6vekap3PQjEYQpVGEMG /zIAhgGAgRrtoMMdBNAL2ZkMBjSoFxR2lBJHqCtXw9GGJMhhAAjQQRPt0AU28hGHF/TCFb3oBRb0 EA1epBEC2whCNsAgi3fY8R018EEGwgCLJPYiGiB8mBYDaAiVEOJZQ0IcBHkhhh+AMQwZcIMRs0EN bAShCHRYBiZ7AYE7LKMbYQADM1ggDjiI45TiAAQzMoANbMDiDmrkhfp8RilzpAQdI7LdidKnB1cI ABbUyEYswCCEGsjimLgAQyy20IUgdCEMRWhFK7CxBVzIggefwAEceHBKHvDgFbLYQjayEYRJFEAA nuigHvjFNM39ZRReOkk9fIMy8ERPD70QAB1g8f+LWMRCFSE4gzHfUYd31FELqvABLnCRgV/4QgQD qCML4EDRim6TBweQhSpUkQFfUEMTSryDH7BAOxQtoZAnMQab9qcHFfQhCrCoAjyGiYsznEEWA73j K/LwigP49B1n8EFE7ThRHlDUqBc1ak8RGosudAEbXaACLHnBvzJY1X8QQgZKkHEpNLhRCt2oQjC3 oNCNCnSgB1jFO3wKiFf0FKNaeEdPX2HKbVK0BCWwKw5KCYc8aDQW2RABNoqQDylMyqr7858uv6OA AZYEHeNAURm82oc7FIEavsjGGLYAhpratAbGjKtPferNtr6CB3nwaR62aQq+nrIEfG3tKeHAggP/ hMAHPlCGCKjRhju4wgH8498BW3gcFTiWJOlQACLzM1kViGEbRcAGNQgwxAwo1KbYjes7VjFab3rX m241JSotKl5xmEJLfH3FO5gRghD4wwciCEIc8re/ItHAESaRQGSJa5wyaAMLdqBDASZQBV/4Ygtb sO5CsXsGOx6gp6Q1Kja/6d3y8pWis8UwX7/5Ci3ENahmyIcNX/cdN5HoGIu7CAv5qzWX3mEKBWBC FcaZYCIOQBUMrsFaD7BjpEq4wqfEQTa1hN7WVjTDSuXxOyzhhjbQoQ8qkOXciCSyv/yATyXRhyIl 0Qs7xEEKBQhAgUWA4FgIoaay+Oxa3arTI19U/7w4qBE6qmKVtQjZtXk9ak/foYUzbKGch3hB/tpH ZZoBoy0h4cKK1bQELMzPy3coADb44At4/GILHBiAEG6aZh3v2KentbA3xaElOc+ZznZGr5u9yVMe 1+AMYPBFEDRhBz18LW4lBddjGVA+SfTBDnbogwC2EWax+mIMseAAGMx6TO3e8cGAQGpFuWnqaqMC FTVSNYYvytM9A3UAYaDCMrCwLPbtimG6HAXeQiKBUYTHe9rI5x3uIOwotIAK0vXFL4bIASHg+Jie Xit3WSBtCcMhzpCpdrXRO95SotatqeWzFo7YBilggaoH06XtBEiSevACYoy8wzbuIAYpLKMA3f/A RoGzcellc/qmce0pd426YW8evEZcuHaNsL1zO6OylDX3rloPoIUMBCAfHQRkIpd7jJFwYQEQgwEv BDCFkUtBwEXA9xowe+Ai2hSnnlbvWr/r44MjXOd0FojO94rKvNo8qd9c6xl8MQEB2BpealtuAkhS rrVRdt53kMKLi8CEVlLj8NkgRgZwIYQ6PpvHB+h2hWlealMTRM53fi3cedDWPKSWx2DARhRc0bR6 QqFsHkEAwhRmAGHPWwDDLgAbqCBmpx44AwPAhSoC/o7PQ9ibBKd85RWO+U+Md5t5AES0H75TPg+g C8uwwweRlh9y1GMkhyQu3NwogCX24QUCtiD/7Y2dDRurQhZacPyDIQ9xb/qU1NmU89p57vPykh21 D84Dn/1cgDusEW71lAIjoVIltn1YAHsCEGxSMAVZRwXOVGnZQFY+gGM39XgOBmrqdQBw8AkUFWf0 V21DBnRJlQes1mpExwxgEAR0MGJwox8UkEIasQNqAjctZQdiYAcCIGwMaEFQdXhdN4FpBnbapV2j BXl11YFEloRrEYJHmFR2JXZaUAMDsAitYAdYIDuLZRwMsDIf8QcnA0EqEGwvAGxxQAdFUAQ86FTB RAwcgGZpJlB45GlF6FME93ND9gl3SGp7JV6b93BKVgMIJQK91UHoox/kQDUhQQHLpTVguAd9//CI AiAF26AJFhQErWSJ1LBvYDCBXwd2oPVsFzhRR3hhxmdKDLeH30SCfviHWsAKGdAFK6gC0ZBrt3Jc HoEOpLCI7LNI8/OIcSByBZB1KrcGAYAN3AAPCdZvneaJoPWJbWZXDmdzNodKROZwO1WCEXcAr8YK bpANrbAHBuBEWTgaC8BuaaMmL+RGv+ZlkohyrVQF3IAI2IAIvkAEHNBvnch7DmZHq8BNQDdbNDeN QcZ2DqeK36RkfFYDIdBRRWAH4Ygv47gZ+hAS5SA84aEN1YNPeyAAcfBlMBZdUNUFawBVSJANyjaB zIZTj6dk3DRtPzZtFvZzqeh5YheHWrCQ1P/QDVLQQZSCMkmQYhDxCfvVX8kRDUbZRb/4i7I3Ae9Y SYdnBglWRLrHac22Y3f0DqLoj5wXbR04kEV2VJwHYXbkYXwWAspAANjQAmJwFyGUHx6AehphD+72 HfsDQUfpQ/MmYK1ABVXQl9TQBYdXDCIwCz5wZueHU1GImH12RwT3YD7GAxOlh0kognUllkOoBcyA C7s1WAJQFm0ZHocIEqFgOsVRl9rARl0UidvwkdIQACMJmE5VkpwlBJvmeMYEiOkXcATXU6L4ZpI5 ma4Vd2M5lmmmCluADd3wZB30meCBBqEAEgRIJKbJRm5kB4JHbGcIkoApXWYQgW14U552mwn/iVaR +XZ1JQ5DloSyVUpuxWZ8RpzMcAZu0AVFIAX2ow3MCR7l+BExMDhEaT04xH3bQAf5wAZFMAEp10q2 F5VlpZJ3JJ549A6QKW1AZ3zpSWQXxZ4YyGOilX5nkFDUUAR9AAEqEJHG0XQegQrmwF+ThZHVU53b kA9oWARtIGaWtKC4t1EqyV3e5mxYSVGAsIFEZnxxlmqfUGEQ9mBieUdpxlAhCgEd5J/6gQAfIQHn WGJ2WT2O9gJgNgFMoEHG5oOJh48NhkerwAK15VauRlRaYopDOmRLCJDbNFft+VMe5mFnMAAiQJ9e 8Ef8oh8T6RHJtXrWY5Rc1gdxgHJUgG/U//AFHjVWs+ByDaZWWIlR6wdUY0dqXnmh2TZeHAZhasqh HiYLuABYg2VDN2Oio5EDH1EOu5Nxm2GaRulGXpCo0oANa8ANh6ervkAAItBv/vZZooVRp5VWcbUK GPYJmIeea2FneMiBehaqP8WkeboFBMANBQAB2DMpxSORH/EJI8IzxwFBbBQNKjA/kcaXmHV4gElj HLBRFEiEPRVtxrpddqWsp1ZKRYpepYhK7ammYzmqsoCT2EAHKlAf2RMerPoRx8ALrWI76eOiesBl VLeXYgUP2QAPlaax8NCGC/Vv6ueYaxqkD2Z2DFcjHIieRNZagHAAbfVTOpZ+QXgGGcBb2/8waLQ4 GuewbhsBDLmDK3BTqNaDBk7QB1MwAdKwBl/QCAm2BSKQDb5QDEhABNXFARP4oDrGYwNbAw+mYy1J eShLanNWjW3lTWtlRzDXZzY1ANlQcQbANpPzIArgOx5RASDjMBBkPfVxF2iABZalrgTQCGAABhmA YJREDWYgAiKQuGS1e68GhzWgCqywe0CVtbXVm9kGp3ZmChTVbUvVbNhlnLpwCG+bWN0KGHTbEYaA KUcDAJfDC9GgPGyDDwqQRvMjAD/QAu0QBDJFY4m3BUjQBWRWmELACpsWuT5ATHXkDzb1DoCApkjl gXMmEDjAAsckCwfwdiWgXsf0oaogBBz/QASt4Ar4eSKSkLocQQ83gx9SF2U5exyvIQZREATIGAu+ AJgHhltW+72sAAaN+w7+oAoDcAYHYF44gG04kA6OYAjeoA/IcA4mYD+jYAc/EAUUIAeG4Ax1pF4h wAqs4AMcgA1egHHlsxmSgIgdAQx30ZPmKgn5yWL7wkiaEARhALVbMAvEgGkffI8J5gNn4A8DkGCq 4FPrMAYxEAV98HEQ4l+94AewkAEeFgKdwArEQA2+RT75sQTX5xEF5ACiMRzLUk+7NArLoAtjQAC+ esM+gGAcwIaq0AndSAEUgAw/oAJxu0vPoiBR8AsNpgpusAhIBzb64ZwfMQIeIAklyj5i/4w4xmEA y8Cu9YhgW0AMl9a0LSDI2wIyw+EEUTAHB8BRRbAHWIwwh+YR5dAdq6GqDwMDAtYO8FC4OUwMlASY BXDHD7M/kxUN3oMFvtBnurAHd3Ei+8k4/UAJXnU44MELfUAHXUAE8EAERKBHT6UJShw2dTmLmKIJ nSUAwLXIACCAH6E3c4LMRsILe3DOXkajcbAHvYDMIIQGsastfaAJWOAAL5x3AOAvHsEF5eABp6Bl 5Fwc2mAHNAwPyLgFIfoC/XM4cLMEybMZfdAOdqA0pwvOHyEolIDJPcMw2iAAuqDG/jYAv0AHSufO eUspYuALENAHDDDK+0IPiQgDDGBAh/+jAPjRt9DVAWZABL9An71AKVrE0NowOc3TB2AgBQ6gLHyj DbaoETFBCVJaN5o8GliQCPkQBNXkDBkwAa4wGt3izbt0PDAgAM4QBN7jQHlXL1r8OwXSD1wV0P3l CofQAVtwBjyAA3mQDWoA1+xDPVTVCz6gC8JlQkYyHPQAlAwREyPC18WxB9gwCwR8wKYwC1gAwyZz zYCEBT4QBiR8QMSROQBQyh3BBTtgLVnA2ACwB74wADXAAlMhDqywB4ydMEgC2GFgADDcNePgO4i9 ECPgblgV0DCwB11ARwdQIzygBYpg2Zc9HNEAAIoABvDQC225OaNBDjC9z5CwGi5dN0v/4ACODdmr gIffBAKoDQO6XAa6kAHZIAAogwV0EASWAIMWgQPLgEhgzSZZUAZ90AUTeNyf0FbsgAez/Rd3kAGz sAXL4CCIdSt0EAZ9zAIfgQqcrT+kIa6HEw13gAQb9Q7ZBGqMMNsA0AfvGsTtoMRykwSqAAd4dVr0 XRFDEA9YAG+9gOGrzD992wrZwAqysAqk1lZwsAVOMFn5zcjE4QmxoAUDjAvtMORSGgU+cG2mwAEg MAcvThFD4A6VoC3xBjU+Q0LBJQl7cAki4AOWAAhAJ1eyEAjyYt1FvjQAgAUZAFRnkH660NX8UgZh gG0H0AXf8ASMAAgfEQ56UAl/wWUq/+AwmeyfZRAN+PACbUBWNcCVPHBHv6AH5obPuyQkekAGdc4M oxoGM641vBALqPAK2LAHRmAEzXAFvb0QxhAJzy0eHSRB3Q0hmGJVesALvkbmQrBdQcpdzbjcbYNF QlIGBnAHYWBNd/oOWxAHIQQDlaAI72AKSDAJ+fANG5ALSnCLYRAJ/ZNGQmvhbOJVDuAKfmCFeLAH RSACrKBjaF7poAUHHCDb8IxullIGbnQHWK0K+xdXsUAHJtILsBAEld4GHB4Mm/AN6/ARclAr++Mr y9Pmg/0gjd4Le2AHbSBfvPAE8XAJZGUJVwAIe7UKoKWB7FAJVsU69wxyaBQHRQAPPv9QRxnlYT6Q D8QB5T7wCj4wCxKaBtVgBDLwEUNgVazRB+RgaxQvLw1emivft3aQD9yQAW2ABXjg8W0wC2lQA/3I gfJeA3TFDgodCWyUOU3fX3W5667w8m3gC2BQpn2GfgNAB8SxDKrgVj6wCm1qC0bADh9BBibECx5w hayz9CB0VW2uMNHQC5ow9b6gCFaPD2NOBO/u43vFAs4YecRwA3qw8ihglNFwd6yTBaDvDh7/AorQ Bl1Q1+g3lnVOqprQP9hAls6rrHBQC4IAD1cuEY5DVdogBiTVNvoz/AqDWGRP/GVQ6K3gCxkQX/Ml QX0QBJAN9nCADnMahWtFUWdQDIr/4AQncAL3EAmRcA96QPZO4A7goAZG8A35oPrRjGO4uVa5Wedn 0AZ/oQeYEKHVz3ZacA3/8OoA0U/gwIEs+pSppIeXABVoHKLR5kBPNF5Yeu15cefGskBRbihSVKSL iFkZzLR54Y4Xrz1BMpx5BwgODnFwXr17d+DAO3GocByYA0JRHKI3LgUJw24OBx9CZNU4wOMVHDh5 Xu3M+U7WmTOycBXRBgDLnHeqhBzAYYoHoDxp0hCEG1fu3AVooiXkZUKSHr7RKFrcE+dOvjZdfJkh QCCbsi2zBoDJsAWJphe98LAMskXVOx7iPonjcUBL1gN5xOFAhUrcga6yYn5KLTB1/2ocNOFI1Zn7 3RlVqs6AybcEgJ8zNdIEAyHkzCqZqnahmxtdej90CQBEwwNDhQksfVdiCSwlH5sJ2BaJ2JKBw3of 7X1kEEFt2R7LvF4EESGkBqDan3i8Gw0nzk5DZ7bUoOuHC3QKRAWd2h6siapXJrxpK9+YGSCfsDR5 RRVijKCFEyRUkSkTIXCYLkW4oDnILxjy6k4hLMB74ZBtiggCG8MI2IKp9ZhqjwMRgrijFywkMSCO LrbQD5BPHIQDq5y04OHJBQvkIst+fHqQS3G+BPPLmwTkiitZfNgGDQDIEEeVW+6hxZpgarGEhQNq QFFFPRuI5qEy0JihEm3+6uUFO//oWKYbbHTpgpowRMjAPfc4mGUyO2asaAokOFinhpnQAW2nAN95 paYnt7wyNVPEKeE0miKM8DYpteDKrDMyiAMGGIxBZRc1TtjkGyA+4IDUd/LUM0U5/oykjDJgiMIP hSqK54U4tsmnCFgWpcYX9GYBdxZKZyGmmHz2cAWLaHrRxIw0MvEUSkBEq6FeUlvFYUEuUK3tyxLg AJMHqm7DTTQttPAnBCEGEMKZX+yAoZJ1UBGiGXCGwYAEdf6x5JVVEEx2OmQAcPZPNEiJYiVq7VAk nwJa6SYIXahBjwgi2AHSB2LKvcEVJ440oB0zWKnhik9ve0WLemuACeB80VHwygX/P4FmLUCuBuQq nXA6mBlmeHsMlwF82UMPWOAwJY8tbuEkGRJKEcaHmEAOea5P7AAAhjJOiCSScdoxAI8jC1Xkhnxa kQaRL7LBhJgtiCCGKSF8ELKLfFzpQxMv9oBlCx9qWOUT6uBgARAAdQoBKkBOY722K3G4jfRVtsZJ 1HeY5moAysGIJYxt7vAB4NW0EiEVINQhYpWZ6pbOnnjydrbvUdrppSLBq1WEDsQRoQYem7eInD0f Zhkyjj6C+KWIO7AhxodMQu/ns3lzuo3WmFinikAHP/mEKkBWEZCAajAaWfSGFWAAAwcyEJluyaIz oOlJHohhi1Qk7zTMiw4/3AEA/wCgIRIaiAQ+WjCKisyoF/G4VraCsAZqEEEERAAfezhADCRc4hCt +EUGihAHELAjDZuBw2fWMiYeSKU4UTnNJ3DAvyVCA2BIA2AAcXcGISwMMgvcwhZikQFckOpLB4BD gV5Ri2vMgid0wyBBLsCLMpDsgxrghTnu4I4Z1TEw22hFG4IwBGqYIRswFNcMJdOGfMAiG76Ywh5u sCn9xOR2ATzAK/LAFS2A8UGf4R+s/hfAp9RKd+2JzBZ+kR4fDMAS78jDqlggOuq8Iw1CeAWy0giX dPSBZM66hzv00IJl1NEiT8BCH+KgvRw1ygyQAxcx2GEGarSBmN2QwkUuIQIf+P+GaWZRBVSkkofd FOcVAvsS/zLZGdopjYpVnBwCMzDKLGrRB7hwDQ/UwgMEoeMdrMjE8mYpF2Po4Zb30IAeftDLdM3o CSe01o2KuQh4NCaQIuhCFAKTj2Xc4QV7UEQx2tcecrUPJhRiDStgEhWnfYYqAsvaTiw0AN0lcD1b EEE2tBgLMLTHNWgL4kDgcLupQGOfckHFECTBwTJEQg9iaMceLNKLJwAToYpoRRGkYUxkUmpIdnBF HFrRjmjZ4Q6wMIzNwtCFYmSDA6zIJlS0oIrUcUZgS7wNIIp4ldudU3cKXGAos/ELmuICF6qQxQHC OZBPAKIGsczpT+GCChb4gaj/Re2DKPrQC8piAZgzisdgEIcNJKCHGDAkhhk0EQ87FCEfXqhIL/yg iaMMoQOwaMU2FBGIIJghUjV4RyZ6A7oiPnEtRYxiDVTBCh+osySOE+Uoa+qM3jCjVNBg5SeukNtX AOIKrFQsQdARBieUTA8QaEGRjOTLGb2ADppoQwC4QYAMEGMWW4DHIhSBhWX0rI15U8F585GPQNwB CzDgoAPicIkhbCETWhDCK3Hb29iNqa68Uad7HUeEX1R4C2DwKzyvIEmAOahonygLO3yA3ewKBAe/ gIUd/OkOMSzjB+Ml7xMykq0AdMEM4COXCDrwAj34IQ7dvS8M+kAH2c6Xg0fW/4Md8hEEVeQBwUIg 2jetNi+sbOUMuCiuAtnxOCLIND2xeKcqtCDXSHrmNg6yBDxA4IMSL1YXOCqSYE4xAwgYqbJ1JO02 NFHjzmaRCGEIhBPEUglX6AENAC4DFjTyESw4S2/OegIGLnGGVdUgwWf4JiDsND/cWrm4CZxwFkeZ gVjQ1Bk3Fc07hKeaGnAAHhwQR5vhwoFe0KEdmtDEHZaxjDpbZEZ4ADYWDtUKbFCDZlk0AzZe0MYy 8AIP0cgCDBziikMpwg8JUdOfNICHR3Qhljh4R4I3U92r0HWAXMEF7xS4s3aKUovLdQ0LWFAWTHeI HfCQGxxkTRAufEIRSc7eHv9+0AII+NIVKuvFsIvtLZh2oRV4yBvJHKI3h0RDxpWIB3102ayiPsIH r+KBcRoZyZxUGWzFzavjKjwG5Zby1DWoA1Vq0B4hEMEMHLCETLS07wTJowx6cAce9NCHdkzWl98p 1B1aAYJiwEME8WlDHKIBvfs+609/4gsWtFrRF7jCHWooBg+8dE+R4vYmUlrpY/Iq6r26cwB+LY6q aWJp4RINdDyHiwwCqod7RKISFBAAUynbCwNIAjBxCEQbOuBHM3SADYmYOlGP7Cw0PEsPtQ7CXn1R jDDAYxaCdVVNwn2GTJT8KlqQxVbE9h4sVjgbbUdgewYAWC3MxD9SsQS8WAD/nX3hnQs4uMQb9VCJ IJhA8E9QgQokUZHAtCJHYRgrIffQJ2k/mmR5e1FpfRGLAXQFF01zGk5N8QohZOKIE1Kp6lWh9i28 XgS+8IVMP/121xAADIBAR2gOAEDl9R7vA6kDUXgCNYgHP4CFF6sslQEMpWsDbOAGbgiCIqCDF1gJ Qzs0RysDBzAAAYAFfKsDAZkQcAqTLwm5bBIQktuNEFAFZ2ApDhAlzYs/UkMgDEu9ZAiEWRiNVQAg ftC3/4sLOOCAIZAZSgi8wVOBlXCHi5CCZZiAAECEAGgDCaSPI+GFaNCGh7i8O+iGLvgFeHKNSPqm 2BnBKKmXUeGaMlm/AYiF/yxiOfjLIVJrjy4igieIB5cQAkvAwzDywbmABhbgAUO4g8mirCNMrRcQ jyLQozZohQLYhj0QRHIgRIqAADqAhW7xhS3oCreSNxYQnlgpots5sEfaihAgxZZ6j1+AP1/IoVLL AATqoiAQnDvoAlyogd3bw+jYF1QAAwHogzrrheRLLa/SniKYADbYrzuwgxfog0rwRcp6AU3QhTDI BnjoFng6AE4sIk4kmCLixqSBF6VBPa4IARa8ojHIBh2IvzHIIshAIOa6ATzoBS+wgB68xRRxgcDz xeRbvoS7A2zZL5fJBykQADvoAzuwA0e0g2HqglITJfjzATASnvwhHTuJpP+ouIopSr2VQrkKQ8V0 zKJWBIPui4U9wIM+EANDqEfp6D1UcAQxMAAjJERhk4I7mMk4kAIik4I4MEibHIxLwIZiAIN6UYX3 8Jxvq42qCBUA+kBjEQ0VLBOuYMH3YLlDsoD4+8gLqylZgAfLqAQ6cIQESUmwnIsLMIGXHETlk4RC sck4UMYXEAApEEgf68cCACvIGYCYUCnAEqx8SYsSkAoAST0tqIM6kAWlUcEQ6ApPkspDQsdsUEct ksEBkIUg+BnwgoSwTBFIoAMTgsTkw4JqIQqCLEg7EIA7EAM/0LWoKgJEDIJiiAVZuILZ2Q1ckAFZ WIXOaJWdUr0zYAYrYwb/FfSNbgoBLMsAc6zKvVJHDqCpvjqDQAsmPpAlzJSLdGgBG6gsFRivXhjN UYAAk7SDnCSKfIgCa7MDjHuCSkiGIYicPwsGQbgEEOgAJNgCVgCjeUtDZzgDxOQNKDM/TwKDF0zH MWjFLWJFNSwCV3CFO7hM6ZQOCaAA67SzOuqFSkhGyupOMRjIFwAJO4gHYGqqJ9i2SmiCZGiGeIiH SmiGEZ0tEKAGH/AHWlm/4mIphsGm/ExDyDBHx8wrdcqrLeqCOOgFK/CpgfA/BhUIdKgCG8DOpRo8 R1SqXhiFPpDSFzCU8vzQI6iEfTiCI2gqLo2HLX2CI9gHOjwCxOuA+fSB//fzlgyYveFCqzT0gTV8 PcccA3VkxciIjP+8BDuQByOdjh0gPBUgh8GjLCm10Ck1yPKshCN4Nr+wiyxw1GjQgHgQunuIhntA A0zVAA1wh2YIBoiaACrghkZwgxkVgt5Qu+I8JOQU0C1iQ3V8uyGIAhnwU+kIBZdUAYvgzkIVRCm1 A9S8tid4tsq7ryObPGe5JWQtGWfBAx8TgG3oBmrIBjYVx6HEInNURXUktcj4BZYbJa4oBlhYULGs 1YGAhBnwTBuI0kKtDO1M1D54gj5Z1j9xh6DTAIB6ghWYBxAgg2KQgyGwgiZogn2IBmUtA4p4gVbo AjbtDRVkvalUxY8MJf9z7NYtkAVmoIZjILFyJQgHHUTt7MXuvKg9IMhfrLwjsws8iIc3GIRpmAYQ 4NdwqAXQgQNoMCxVqIU0KBdNaAZgyzY9uA9S9QG0wrItGgN4iL9urdMtqNNfgIc6fc0QgAcw4Njo qIJBlNJevIhktAMxIDxteJaKe4J9aIJBeAZ5+IcP+Ic5eKVX6pR62YVdEIJdyFmd/QB4CIZmQIFo 2IMiWIRIYQUhSLfIYMxUfD3Xm8Y6BYMz8IFsqIOqnYtQMIE6i1Kt7YMX8AM/6AMV0AMY0APDS0IN vYRnEIRgKIa7/QcicIvIydlaqIVdUAIl2AWz0JlZYAVlaIVDeACRaIT/WSil/0yPwoU/x3S9pM2G AWCG95AAyJULFmgBMYCAKBWDXuwDAdDJyeIFGbEzjIgDkNCEYBiC8G06diCCGdoZDsjZOfgs8t2C NCBFH0ACPaIGUiUuyIip+Bve+JvTQ0rFagIz5p0Lb/iB6TVJXhSmg5xCE1LGE9oDjPhVkAAJPyic QGCtIOiADugCJECCYkACIvBdIQiBTMgEMOCGLuARcDlFX+gW/tWBFrYAYwsDC5BhXxhaAI6OC2gB Ay5NgjRIQ6WsjLOWBubVPvjM8UJQi5ixVpiANpAGbqAmtjoDWgkBVsgAX2CvWXCD91LhFYY/auCD KgDjMOYDXfAFsbHh/7nAAWOggwIWAAJORkHECENhyz6gDwiw46WaEa/7zEqwlmyZgCBYBM95U94o xS24RCwWATP4Am4wtm7RgS/GBirAhhyZ5HbwBQw547mABCoQAAEwgU4eSDFQxj2ohEq43NAkyFKm rAZ+0sErZT6+oTagGWUYWrSaUaJ8vcjo4iqgBh0wNjCO5G4QZliAhRZogQzQgkyeC1QABgm8gx/4 gTtoY9FcRj72Kh+7KD6m48sVYhM10T0Qj8ThkQsLl8aw39cjgPv9ZXSkhirAhkimgm5YzQJYBjoI Aq5Q5rkQBwtoATqggztAxumN3lIuSKKQYEWgUqIw6GQ8UYxTAylAnP8MPmFKaScs2gIz8KNF8IUu wAYw5gYw1oVgLoICmIIp2IY4EIAwyOfm0YVT2IYHOE2tPdSE9AN//p25zIeTTgSi2Gk7wKPEQYTO +haY2gLGKGoRIIBF+IIIWAN4rgJf4IN3ZoKRnoKZnAJevIMzWGnpcARKMGkp6INREOs6gwBR7gPU BGg6eBkciUL0aocikORGWYRFQIIbc6GnS4/2a6dsWARuWIMAcGpq4AYqmABGpAMMRcY6C4NX2Oro QIcGaAdpJuvBE9mCnEls0RZsyIaQPLUCAiyckAVWWI8ECqX0wNMser9F/mtJ/ugA6AaS/h0pXVRJ UAStbuzowAELEEj/QSTU7rReKcAW50MCUoMJUpmrLzGFEniFa/KNAXCDLWiEtYOpQ+KGSHZAasAG eTbpO7hjLLhtPXGGGeDFZqRsn96GAggCw9iCt/MN13iHVfDDiAwNraiBENCdVpTu96sCKqACYJbn AoBmIjYAXnCC71ZJE7MAEzBJQj3UbZiCIuCGbGgEIoBD/JSFD9QJbSRBraCV/GTBlsKi+FiDKqju bqioTg5UXtAGA9eTUKCAwx4FBu9OB0fvbmkoz2EpC1+FOtBGqmCBMdmKD79l+PCFL6CGL6gCWCgA AehOEoqGJdCGJWBxPfkDWJgBrx08i+gD4H7wNQiD+IOUUlIFLLtw/x5IBzuJIsA8g04orlLqhAHI AAJ4wHbGhgIQg6WyQm3QlSlPljGgglPwgjozAC1/1imYABb6Anh42lKa0TOog2t8hw8UzA5PQ5aK 0+Ls678ugh+AAF5IvmiAgSzIGz5PFguIhRj4ARvwNSyAgO/cBjZYOF9oqFJ7u5AELMI0kyAXmxl9 DDBoBGpYgzWgginYXJUJi7yRclKvmxhgCM+cUNKkg26oAiP3hccsNTCrKVxwBpdjLixzD3Uagy8Q dhMXA87VBj3PGxogB2XHID4QAF4Y9ELF0CkQ1Qes9sho1YomJb/qdfZrhC/ABiYoACkouIhQEw5S d3bHIGfQBeOzM//pvYMC6G8wllb4E4ExaIRGeD1thQymXaAc1YE1KIJt6AMDMHhdSXflU3gMqgAK gADsrLPqLU2J5+93XhQ+KHJfaGHHdEzF4PlHroIgWHIVoAE9PzQOWnnF2gFzGAUVsOMCNoE7mIIC KIDVLIJ2iJlugIV4nngv/oIid2cqUJ9eoIFD0xsAS/oSGwNzoF4x8AATgOaZBG5p3gYiIzLYZoMi EGYmIGySJngVuEJdATCkT/sS44MBFgBo9mQC7kWx5s4CJuDSvAMToPw+sAEV15WjPzIYKPwSAwMd aIcfSHwC7rU6OsLkA0Z4V4EBX4mIADDBN9bOl7UdAH0CJtTOVBmcSVi+JYDyc3d9BxD86jNWzpd9 WeODHJ7eXiAHLNDHldD95fMLv4DyhzD7wT+y4sc79eFtQmx+lYmGibDCJXgI6yd87PdB22f+XJ2R I2R9vtCDKDf78jf/lCRvGFv9CrRClL/++ffTLM9V/geIfgIHEixo8CDChAoXMmzo8CHEiBInUqxo 8SLGjBo3cuzo8SPIkCJHkixp8iTKiQEBADs= '; } </lang>

Another Perl Solution

<lang perl>#!/usr/bin/perl

use strict; # http://www.rosettacode.org/wiki/Matrix_Digital_Rain use warnings; use Tk; use List::Util qw( shuffle );

my ($rows, $cols) = (20, 40); my @text = shuffle 'a'..'z', 'A'..'Z'; my $n = 0;

my $mw = MainWindow->new; $mw->geometry( '+850+300' ); my $c = $mw->Canvas( -width => $cols * 10, -height => $rows * 20,

 -bg => 'black',
 )->pack;

$c->Tk::bind('<ButtonRelease-1>' => sub { $mw->destroy } );

my @queue = [ 100, 0, 255, 'A' ]; # [ x, y, color, letter ] $mw->after( 5, \&step ); MainLoop;

sub step

 {
 $c->delete('all');
 my @new = [ 10 * int rand $cols, 0, 255, $text[++$n % @text] ];
 for ( @queue )
   {
   my $color = $_->[2] == 255 ? '#ffffff' : sprintf '#00%02x00', $_->[2];
   $c->createText($_->[0], $_->[1],
     -font => '10x20', -text => $_->[3], -fill => $color );
   $_->[2] == 255 and push @new, [ $_->[0], $_->[1] + 20, 255, $_->[3] ];
   $_->[2] -= 13;
   }
 @queue = grep $_->[2] > 0 && $_->[1] < $rows * 20, @queue, @new;
 $mw->after( 63, \&step );
 }</lang>

Phix

Library: Phix/pGUI

<lang Phix>-- demo\rosetta\Matrix_Digital_Rain.exw sequence sushii = {}, -- w x h of unicode char strings

        colours,       --  """  of their fading colours
        droplets       -- w column droplets, or zeroes

include pGUI.e

Ihandle dlg, canvas, timer cdCanvas cddbuffer, cdcanvas

procedure rain(integer w,h)

   for x=1 to w do
       integer y = droplets[x]
       if y or rand(40)=1 then
           if y<h then
               droplets[x] = y+1
               sushii[y+1][x] = utf32_to_utf8({0x30A0 + rand(96)})
               colours[y+1][x] = CD_WHITE
           end if
           if y then
               bool clear_droplet = true
               if colours[y][x]=CD_WHITE then
                   colours[y][x] = #00F800 -- (CD_GREEN to nearest #800)
                   clear_droplet = false
                   y -= 1
               end if
               for y=y to 1 by -1 do
                   integer cy = colours[y][x]
                   if cy=0 then exit end if
                   clear_droplet = false
                   cy -= #000800
                   colours[y][x] = cy
               end for
               if clear_droplet then
                   droplets[x] = 0
               end if
           end if
       end if
   end for

end procedure

function redraw_cb(Ihandle /*ih*/, integer /*posx*/, integer /*posy*/)

   integer {w,h} = IupGetIntInt(canvas, "DRAWSIZE"),
           {dx,dy} = cdCanvasGetTextSize(cddbuffer, "W")
   w = max(1,floor(w/dx))
   h = max(1,floor(h/dy))
   if length(sushii)!=h
   or length(sushii[1])!=w then
       sushii = repeat(repeat(" ",w),h)
       colours = repeat(repeat(CD_BLACK,w),h)
       droplets = repeat(0,w)
   end if
   cdCanvasActivate(cddbuffer)
   cdCanvasClear(cddbuffer)
   rain(w,h)
   for x=1 to w do
       for y=1 to h do
           cdCanvasSetForeground(cddbuffer, colours[y][x])
           cdCanvasText(cddbuffer,x*dx, (h-y)*dy, sushii[y][x]) 
       end for
   end for
   cdCanvasFlush(cddbuffer)
   return IUP_DEFAULT

end function

function timer_cb(Ihandle /*ih*/)

   IupUpdate(canvas)
   return IUP_IGNORE

end function

function map_cb(Ihandle ih)

   cdcanvas = cdCreateCanvas(CD_IUP, ih)
   cddbuffer = cdCreateCanvas(CD_DBUFFER, cdcanvas)
   cdCanvasSetBackground(cddbuffer, CD_BLACK)
   return IUP_DEFAULT

end function

procedure main()

   IupOpen()
   IupSetGlobal("UTF8MODE","YES") 
   canvas = IupCanvas(NULL)
   IupSetAttribute(canvas, "RASTERSIZE", "640x480")
   IupSetCallback(canvas, "MAP_CB", Icallback("map_cb"))
   IupSetCallback(canvas, "ACTION", Icallback("redraw_cb"))
   timer = IupTimer(Icallback("timer_cb"), 50)
   dlg = IupDialog(canvas)
   IupSetAttribute(dlg, "TITLE", "Matrix Digital Rain")
   IupShow(dlg)
   IupSetAttribute(canvas, "RASTERSIZE", NULL)
   IupMainLoop()
   IupClose()

end procedure main()</lang>

Python

<lang python> import curses import random import time

"""

Based on C ncurses version

http://rosettacode.org/wiki/Matrix_Digital_Rain#NCURSES_version

"""

""" Time between row updates in seconds Controls the speed of the digital rain effect. """

ROW_DELAY=.0001

def get_rand_in_range(min, max):

   return random.randrange(min,max+1)

try:

   # Characters to randomly appear in the rain sequence.
   chars = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9']
   
   total_chars = len(chars)
       
   stdscr = curses.initscr()
   curses.noecho()
   curses.curs_set(False)
       
   curses.start_color()
   curses.init_pair(1, curses.COLOR_GREEN, curses.COLOR_BLACK)
   stdscr.attron(curses.color_pair(1))
   
   max_x = curses.COLS - 1
   max_y = curses.LINES - 1
   
        
   # Create arrays of columns based on screen width.
   
   # Array containing the current row of each column.
   
   columns_row = []
   
   # Array containing the active status of each column.
   # A column draws characters on a row when active.
   
   columns_active = []
   
   for i in range(max_x+1):
       columns_row.append(-1)
       columns_active.append(0)
       
   while(True):
       for i in range(max_x):
           if columns_row[i] == -1:
               # If a column is at the top row, pick a
               # random starting row and active status.
               columns_row[i] = get_rand_in_range(0, max_y)
               columns_active[i] = get_rand_in_range(0, 1)
    
       # Loop through columns and draw characters on rows
       
       for i in range(max_x):
           if columns_active[i] == 1:
               # Draw a random character at this column's current row.
               char_index = get_rand_in_range(0, total_chars-1)
               #mvprintw(columns_row[i], i, "%c", chars[char_index])                
               stdscr.addstr(columns_row[i], i, chars[char_index])
           else:
               # Draw an empty character if the column is inactive.
               #mvprintw(columns_row[i], i, " ");
               stdscr.addstr(columns_row[i], i, " ");
               
    
           columns_row[i]+=1
    
           # When a column reaches the bottom row, reset to top.
           if columns_row[i] >= max_y:
               columns_row[i] = -1
   
           # Randomly alternate the column's active status.
           if get_rand_in_range(0, 1000) == 0:
               if columns_active[i] == 0:      
                   columns_active[i] = 1
               else:
                   columns_active[i] = 0
    
           time.sleep(ROW_DELAY)
           stdscr.refresh()
   

except KeyboardInterrupt as err:

   curses.endwin()    

</lang>

Racket

Translation of: Raku

<lang racket>#lang racket

(define codes '((Α Π) (Ѐ ѵ) (Ҋ ԯ) (Ϣ ϯ) (ヲ ン) (Ⲁ ⳩) (∀ ∗) (℀ ℺) (⨀ ⫿)))

(define (symbol->integer s) (char->integer (string-ref (symbol->string s) 0))) (define (pick xs) (list-ref xs (random (length xs))))

(define glyphs

 (map
  integer->char
  (append*
   (for/list ([c (in-list codes)])
     (range (symbol->integer (first c)) (add1 (symbol->integer (second c))))))))

(define palette (vector-append (vector "\e[38;2;255;255;255m")

                              (for/vector ([n (in-range 245 29 -10)])
                                (format "\e[38;2;0;~a;0m" n))
                              (make-vector 75 "\e[38;2;0;25;0m")))

(match-define (list (app (compose1 sub1 string->number) rows)

                   (app string->number cols))
 (string-split (with-output-to-string (thunk (system "stty size")))))

(define screen (for/vector ([_ (in-range (* rows cols))]) (pick glyphs))) (define offsets (for/vector ([col cols]) (random (vector-length palette))))

(display "\e[?25l\e[48;5;232m") ; hide the cursor, set the background color

(define (main)

 (for ([iter (in-naturals)])
   (sleep 0.1)
   (display "\e[1;1H") ; reset cursor to top left
   (for ([i (in-range 30)]) (vector-set! screen (random (* rows cols)) (pick glyphs)))
   (for ([i (in-range rows)])
     (for ([j (in-range cols)])
       (display (vector-ref palette (modulo (+ (- i) iter (vector-ref offsets j))
                                            (vector-length palette))))
       (display (vector-ref screen (+ (* cols i) j))))
     (display "\n"))))

(with-handlers ([exn:break? (thunk*

                            ; reset ANSI codes, reshow cursor, clear screen
                            (display "\e[0m")
                            (display "\e[H\e[J\e[?25h"))])
 (main))</lang>

Raku

(formerly Perl 6)

Works with: Rakudo version 2018.11

Kind-of cheap and cheesy, but what the heck... Probably will only work in a POSIX compatible terminal. Runs until you hit ^C to exit.

The "lightning" effect is actually a bug, but I liked it so I kept it.

<lang perl6># clean up on exit, reset ANSI codes, scroll, re-show the cursor & clear screen signal(SIGINT).tap: { print "\e[0m", "\n" xx 50, "\e[H\e[J\e[?25h"; exit(0) }

  1. a list of glyphs to use

my @codes = flat 'Α' .. 'Π', 'Ѐ' .. 'ѵ', 'Ҋ' .. 'ԯ', 'Ϣ' .. 'ϯ', 'ヲ'.. 'ン',

                'Ⲁ' .. '⳩', '∀' .. '∗', '℀' .. '℺', '⨀' .. '⫿';
  1. palette of gradient ANSI foreground colors

my @palette = flat "\e[38;2;255;255;255m", (255,245 … 30).map({"\e[38;2;0;$_;0m"}),

             "\e[38;2;0;25;0m" xx 75;

my @screen; # buffer to hold glyphs my @rotate; # palette rotation position buffer

my ($rows, $cols) = qx/stty size/.words; # get the terminal size init($rows, $cols); # set up the screen buffer and palette offsets

my $size-check;

print "\e[?25l\e[48;5;232m"; # hide the cursor, set the background color

loop {

    if ++$size-check %% 20 {                         # periodically check for
        my ($r, $c) = qx/stty size/.words;           # resized terminal and
        init($r, $c) if $r != $rows or $c != $cols;  # re-initialize screen buffer
        $size-check = 0
    }
    print "\e[1;1H";                                 # set cursor to top left
    print join , (^@screen).map: {
        @rotate[$_] = (@rotate[$_] + 1) % +@palette; # rotate the palettes
        flat @palette[@rotate[$_]], @screen[$_]      # and print foreground, glyph
    }
    @screen[(^@screen).pick] = @codes.roll for ^30;  # replace some random glyphs

}

sub init ($r, $c) {

   @screen = @codes.roll($r * $c);
   ($rows, $cols) = $r, $c;
   my @offset = (^@palette).pick xx $cols;
   for ^$rows -> $row {
       @rotate[$row * $cols ..^ $row * $cols + $cols] = @offset;
       # for no "lightning" effect, add   '1 + '  ↓ here: (1 + $_ % 3)
       @offset = (^@offset).map: {(@offset[$_] - ($_ % 3)) % +@palette};
   }

}</lang>

Sample output:

See matrix-digital-rain-perl6.png (offsite png image)

REXX

Digital rain note:   This REXX program favors the use of Latin letters   (both lower and uppercase)   letters over all others characters (glyphs) by a 25% factor. <lang rexx>/*REXX program creates/displays Matrix (the movie) digital rain; favors non-Latin chars.*/ signal on halt /*allow the user to halt/stop this pgm.*/ parse arg pc seed . /*obtain optional arguments from the CL*/ if pc== | pc=="," then pc= 20 /*Not specified? Then use the default.*/ if datatype(seed, 'W') then call random ,,seed /*Numeric? Use seed for repeatability.*/ parse value scrsize() with sd sw . /*obtain the dimensions of the screen. */ if sd==0 then sd= 54; sd= sd - 2 + 1 /*Not defined? Then use default; adjust*/ if sw==0 then sw= 80; sw= sw - 1 /* " " " " " " */ lowC= c2d(' ') + 1 /*don't use any characters ≤ a blank.*/ @.= ' ' /*PC is the % new Matric rain streams.*/ cloud= copies(@., sw) /*the cloud, where matrix rain is born.*/ cls= 'CLS' /*DOS command used to clear the screen.*/

                 do  forever;   call nimbus     /*define bottom of cloud  (the drops). */
                                call rain       /*generate rain, display the raindrops.*/
                 end   /*j*/

halt: exit /*stick a fork in it, we're all done. */ /*──────────────────────────────────────────────────────────────────────────────────────*/ rain: do a=sd by -1 for sd-1; _= a-1; @.a= @._; end; call fogger; return show: cls; @.1= cloud; do r=1 for sd; say strip(@.r, 'T'); end /*r*/; return /*──────────────────────────────────────────────────────────────────────────────────────*/ nimbus: if random(, 100)<pc then call mist /*should this be a new rain stream ? */

                            else call unmist    /*should any of the rain streams cease?*/
       return                                   /*note: this subroutine may pass──►MIST*/
       if random(, 100)<pc         then return  /*should this be a new rain stream ?   */
       ?= random(1, sw)                         /*pick a random rain cloud position.   */
       if substr(cloud,?,1)\==' '  then return  /*Is cloud position not a blank? Return*/

/*───── ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ────────────────────────────────────────────────────*/ mist:  ?= random(1, sw) /*obtain a random column in cloud. */

       if substr(cloud,?,1)\==' '  then return  /*if this stream is active, return.    */
       if random(, 100)<pc         then return  /*should this be a new rain stream ?   */
       cloud= overlay(drop(), cloud, ?)         /*seed cloud with new matrix rain drop.*/
       return

/*──────────────────────────────────────────────────────────────────────────────────────*/ unmist: ?= random(1, sw) /*obtain a random column in cloud. */

       if substr(cloud,?,1) ==' '  then return  /*if this stream is dry,  return.      */
       if random(, 100)>pc         then return  /*should this be a new dry stream ?    */
       cloud= overlay(' ', cloud, ?);   return  /*seed cloud with new matrix rain drop.*/

/*──────────────────────────────────────────────────────────────────────────────────────*/ drop: Lat= random(1, 4) /*Now, chose a matrix rain stream char.*/

       tChr= 254;    if Lat==1  then tChr= 127  /*choose the  type of rain stream char.*/
       return d2c( random(lowC, tChr) )         /*Lat = 1?   This favors Latin letters.*/

/*──────────────────────────────────────────────────────────────────────────────────────*/ fogger: do f=1 for sw /*display a screen full of rain streams*/

       if substr(cloud, f, 1) \== ' '   then cloud= overlay( drop(), cloud, f)
       end   /*f*/;        call show;   return  /* [↑]  if raindrop, then change drop. */</lang>

Programming note:

The use of      random(, 100)     is
the same as   random(0, 100)     (inclusive).


This REXX program makes use of   SCRSIZE   REXX program (or BIF) which is used to determine the screen
width and depth of the terminal (console).   Some REXXes don't have this BIF.

The   SCRSIZE.REX   REXX program is included here   ───►   SCRSIZE.REX.


output   when using the default input:

(A screen snapshot shown at half size.)

              ░      O       3              k       ò         cº     Θ     &           F
              ~      Ö       ¡              )       Å         tπ     ÷     ç           ╛
              a      ±       █              q       L         P}     8     ╕           y
              Ü      d       |              Γ       1         P»     ≥     ╙           ù
              ╖      ¥       9              ╠       ▒         SÉ     k     ╟           }
              o      U       '              Æ       í         (N     E     C           0
              e      2       ╜              ─       ₧         bH     ï     Z           ╢
              o      å       -              S       2         ò┘     ║     P           ╖
              s      @       ê              ≈       ╝         ·v     F     4           û
              Æ      k       ┤              .ÿ      s         ╕@     b     U           ^
              9      ╩       «              ┐·      w         ╛-     e     U           =
              ≈      a       ┬              δu      ┬         ît     ╤     ╖           σ
              N      Ä       F              'N      @         ╒R     φ     O
              ô      @       Y              ÷x      X         ∩P     ¡     8
              9      ║       ╔              #■      ▌         d%     v     «
              ô      m       I              xm      :         Um     E     &
              ╚      »       r              >       ~         H@     /     ┤
              ⌂      k       E              1       ╜         Wc     J     s
              8      g       E              m       ƒ         Ωε     q     U
              V      ë       &              |       ï         ■~     ≥     X
              à      Ö       .              î       3         ₧=     ⌂     ±
              π      ì       è              n       ┬         ·▌     Z     W
              ¢      6       n              S       £         [x     £     {
              '      &       ?              ╖       )         jÑ     ╞     α
              î      ╔       x              F       ╨         ☺▌     7     ┘
              á      ë       j              *       -         rⁿ     ú     )
              '      S       ╥              ∙       E         v≡     o     ú
              .      <       V              ²       °         ]G     7     H
              p      8       ╣              o       ╤         FÇ     S      
              ┴      t       ö              ^       ╣         n╟     ╕     >
              *      æ       ò              ╡       4         d_     ├     «
              ╔      â       -              Q       P         un     ≥     M
              6      ²       ╢              ╔       E         ≤A     °     *
              /      :       q              L       R         cë     %     ≡
              3      x       $              ⌡       ¥         .Ω     E     :
              E      ┤       y              i       U         Z      ú     O
              ⌂      Σ                      .       ¥         }      Ä     °
              V      φ                              ≤         ╙      ╫     j
              }      à                      ?       3         '      ≤     .
              í      ▐                      ╞       =         ~      Ä     ç
              B      î                      ö                 (      ¬     ╘
              ┬      [                      6                 ═      ╡     ▌
              r      %                      f                 ╔      [     C
         _    {      è                      %                 ╫      ▒     Z
         1    ≈      ]                      !                 -      ╨     o        ¢
              ?      ÿ                      9                 Å      e     ß        σ
         µ    ■      α                      Z                 h      4     ⌠        4
         ñ    ╙      2                      8                 ;      2     ╣        !
         J    Ñ      :                      ~                 █      ª     ░        I
         ╥    í      ┴                      c                 !      U     ;        [
         Ω    Ç      ╫                      N                 W      ⌂     $        ╨
         ì    +      ₧                      _                 v      k     ò        X
         c    s      ¿                      3                 %      ½     F        ╠
         x    Γ      p                      ?                 £      S     ç        -
         g           /                      ╡                 :      é     æ        ^
         √           ⌐                      0                 Ü      .     9        Γ
         ⌡           ╕                      w                 0      î     ╖        l
         ║           ñ                      ½                 █      D     ≥        Θ
         6           F                      ù                 ;      O     %        à
         m           -                      4                 n      '     ╬        {
         ├           ╚                      K                 &      G     R        ╗

Wren

Translation of: Yabasic
Library: DOME

The memory.ttf file is included with the DOME 'fonts' example and can be downloaded from here. <lang ecmascript>import "dome" for Window import "graphics" for Canvas, Color, Font import "random" for Random

var Rand = Random.new()

class MatrixDigitalRain {

   construct new(width, height) {
       Window.resize(width, height)
       Canvas.resize(width, height)
       Window.title = "Matrix digital rain"
       Font.load("Mem12", "memory.ttf", 24)
       Canvas.font = "Mem12"
   }
   letter(x, y, r, g, b) {
       if (y < 0 || y >= _my) return
       var col = Color.rgb(r, g, b)
       var c = String.fromByte(_scr[x][y])
       Canvas.print(c, x * 12.8 , y * 12.8 , col)
   }
   init() {
       _mx = 50
       _my = 42
       _scr = List.filled(_mx, null)
       for (x in 0..._mx) {
           _scr[x] = List.filled(_my, 0)
           for (y in 0..._my) _scr[x][y] = Rand.int(33, 128)
       }
       _ms = 50
       _sx = List.filled(_ms, 0)
       _sy = List.filled(_ms, 0)
   }
   update() {
       for (a in 0..._ms) {
           _sx[a] = Rand.int(_mx)
           _sy[a] = Rand.int(_my)
       }
   }
   draw(alpha) {
       for (s in 0..._ms) {
           var x = _sx[s]
           var y = _sy[s]
           letter(x, y, 0, 255, 0)
           y = y - 1
           letter(x, y, 0, 200, 0)
           y = y - 1
           letter(x, y, 0, 150, 0)
           y = y - 1
           if (y*12.8 + 3 < 0) y = 0
           var c = Color.rgb(0, 0, 0)
           Canvas.rectfill(x*12.8, y*12.8 + 3, 13, 14, c)
           letter(x, y, 0, 70, 0)
           y = y - 24
           if (y*12.8 + 3 < 0) y = 0
           Canvas.rectfill(x*12.8, y*12.8  + 3, 13, 14, c)
       }
       for (s in 0..._ms) {
           if (Rand.int(1, 6) == 1) _sy[s] = _sy[s] + 1
           if (_sy[s] > _my + 25) {
               _sy[s] = 0
               _sx[s] = Rand.int(_mx)
           }
       }               
   }

}

var Game = MatrixDigitalRain.new(640, 550)</lang>

Yabasic

<lang Yabasic>open window 640,512,"swiss12" backcolor 0,0,0 clear window mx=50 my=42 dim scr(mx,my) for y=0 to my

   for x=0 to mx
       scr(x,y)=int(ran(96)+33)
   next x

next y ms=50 dim sx(ms) dim sy(ms) for a=1 to ms

   sx(a)=int(ran(mx))
   sy(a)=int(ran(my))

next a do

   for s=1 to ms
       x=sx(s)
       y=sy(s)
       
       letter(0,255,0)
       y=y-1
       
       letter(0,200,0)
       y=y-1
       
       letter(0,150,0)
       y=y-1
       
       color 0,0,0
       fill rect x*12.8-1,y*12.8+4 to x*12.8+12,y*12.8-10
       letter(0,70,0)
       y=y-24
       
       color 0,0,0
       fill rect x*12.8-1,y*12.8+4 to x*12.8+12,y*12.8-10
   next s
   for s=1 to ms
       if int(ran(5)+1)=1 sy(s)=sy(s)+1
       if sy(s)>my+25 then
           sy(s)=0
           sx(s)=int(ran(mx))
       end if
   next s

loop

sub letter(r,g,b)

   if y<0 or y>my return
   c=scr(x,y)
   color r,g,b
   text x*12.8,y*12.8,chr$(c)

end sub</lang>

zkl

Translation of: Raku

<lang zkl>var [const] codes=Walker.chain( // a bunch of UTF non ascii chars

        [0x0391..0x03a0], [0x03a3..0x0475], [0x0400..0x0475],
        [0x048a..0x052f], [0x03e2..0x03ef], [0x2c80..0x2ce9],
        [0x2200..0x2217], [0x2100..0x213a], [0x2a00..0x2aff])

.apply(fcn(utf){ utf.toString(-8) }), // jeez this is lame

   codeSz=codes.len(),	// 970
   c=L("\e[38;2;255;255;255m",[255..30,-15].apply("\e[38;2;0;%d;0m".fmt),
       (250).pump(List,T(Void,"\e[38;2;0;25;0m"))).flatten(),
   csz=c.len(); // 267, c is ANSI escape code fg colors: 38;2;<r;g;b>m

// query the ANSI terminal rows,cols := System.popen("stty size","r").readln().split().apply("toInt");

o,s,fg := buildScreen(rows,cols); ssz:=s.len();

print("\e[?25l\e[48;5;232m"); // hide the cursor, set background color to dark while(1){ // ignore screen resizes

  print("\e[1;1H");	       // move cursor to 1,1
  foreach n in (ssz){	       // print a screen full
     print( c[fg[n]], s[n] ); // forground color, character
     fg[n]=(fg[n] + 1)%csz;   // fade to black
  }
  do(100){ s[(0).random(ssz)]=codes[(0).random(codeSz)] }  // some new chars
  Atomic.sleep(0.1);	       // frame rate for my system, up to 200x41 terminal

}

fcn buildScreen(rows,cols){ // build a row major array as list

  // s --> screen full of characters
  s:=(rows*cols).pump(List(), fcn{ codes[(0).random(codeSz)]});
  // array fb-->( fg color, fg ..) where fg is an ANSI term 48;5;<n>m color
  fg:=List.createLong(s.len(),0);
  o:=csz.pump(List()).shuffle()[0,cols];  // cols random #s
  foreach row in (rows){		   // set fg indices
     foreach col in (cols){ fg[row*cols + col] = o[col] }
     o=o.apply(fcn(n){ n-=1; if(n<0) n=csz-1; n%csz });  // fade out
  }
  return(o,s,fg);

}</lang> Offsite Image: Matrix rain dance

ZX Spectrum Basic

Appallingly slow (full speed on an emulator strongly recommended), but it works.

Authentic Matrix code includes reversed Latin characters and half-width Japanese. The Spectrum doesn't have anything like the space in the glyphs to support legible Japanese, but this program does use a machine code routine to generate a reversed Spectrum character set - and an upside down one and an upside down and reversed one, for good measure. (This could be duplicated in Basic but the program is slow enough as it is - ZX Spectrum Basic has no bitwise operations.) If you don't want to use the machine code (because this is a Basic demonstration, after all), or the alternate character sets, delete lines 30 and 4000; this will leave you with standard upright characters.

The left-right flip portion of the machine code routine is by John Metcalf, borrowed from his blog. (He has also posted a routine for the Matrix rain in Spectrum Z80 Assembly; his method, filling the screen with characters first and then applying the attributes, was one I had independently considered for this routine, but I didn't feel it resulted in enough character change.)

The PEEKs and POKEs throughout the routine are used for attribute handling. PEEK (22528+32*x+y) is the same thing as ATTR (x,y).

The next step for this routine would be a way to randomise the length of the character trails (currently fixed at 12).

<lang zxbasic>10 CLEAR 61999 20 BORDER 0: POKE 23624,4: POKE 23693,0: CLS: REM easier than "bright 0: flash 0: ink 0: paper 0" 30 PRINT INK 4; FLASH 1;"Initialising": GO SUB 9000: LET m=USR 62000: CLS: REM set up and run machine code; USR is the call function 40 DIM s(32,2): REM current top and bottom of character sequence for each of the 32 spaces across the screen

50 FOR x=1 TO 32: REM main loop 60 IF s(x,1)=0 AND RND>.95 THEN LET s(x,1)=1: LET s(x,2)=2: GO SUB 4000: GO SUB 3000: GO TO 80: REM start a new column; decrease the .95 modifier for a busier - but slower - screen 70 IF s(x,2)>0 THEN GO SUB 1000 80 NEXT x 90 FOR l=1 TO 10: REM matrix code switches existing glyphs occasionally 100 LET x=INT (RND*22): LET y=INT (RND*32) 110 IF PEEK (22528+32*x+y)=0 THEN GO TO 140: REM no point updating a blank space 120 GO SUB 4000 130 PRINT AT x,y; INK 8; BRIGHT 8;CHR$ (33+RND*95): REM ink 8 and bright 8 tells it to keep the cell's existing ink and bright values 140 NEXT l 150 GO TO 50

999 REM continue an existing column 1000 LET s(x,2)=s(x,2)+1 1010 IF s(x,2)<21 THEN GO SUB 3000 1020 IF s(x,2)>12 THEN LET s(x,1)=s(x,1)+1 1030 LET k=2 1040 GO SUB 2000 1050 LET k=6 1060 GO SUB 2000 1070 LET k=12 1080 GO SUB 2000 1090 IF s(x,1)=22 THEN LET s(x,1)=0: LET s(x,2)=0 1100 RETURN

1999 REM update colour 2000 LET a=22527+x+32*(s(x,2)-k) 2010 LET c=PEEK a 2020 IF c=4 THEN POKE a,0 2030 IF c=68 THEN POKE a,4 2040 IF c=71 THEN POKE a,68: REM this poke could be done with 'print at s(x,2)-k-1,x-1; ink 4; bright 1; over 1; " " ' but poking is FAR easier, especially considering the above pokes would be similar 2050 RETURN

2999 REM new character at bottom of column 3000 PRINT AT s(x,2)-1,x-1; INK 7; BRIGHT 1;CHR$ (33+RND*95) 3010 RETURN

3999 REM select character set 4000 POKE 23607,242+3*INT (RND*4): REM the spectrum character set is pointed to by the two-byte system value CHARS at 23606 and 23607, so repoking this selects a new character set - the machine code below has created four copies of the character set at suitable locations 4010 RETURN

8999 REM machine code routine to create multiple character sets 9000 RESTORE 9800 9010 LET h$="0123456789ABCDEF" 9020 LET o=62000 9030 IF PEEK o=33 AND PEEK 62121=201 THEN RETURN: REM saves storing it all again if the machine code is already there 9040 READ a$ 9050 IF a$="eof" THEN RETURN 9060 FOR x=1 TO 8 9070 LET n=0 9080 LET s=(x*2)-1 9090 LET t=s+1 9100 FOR m=1 TO 16 9110 IF h$(m)=a$(s) THEN LET n=n+16*(m-1) 9120 IF h$(m)=a$(t) THEN LET n=n+m-1 9130 NEXT m 9140 POKE o,n 9150 LET o=o+1 9160 NEXT x 9170 GO TO 9040

9800 DATA "21003D1100F30100" 9810 DATA "03EDB02100F31100" 9820 DATA "F6010009EDB01100" 9830 DATA "F901FF051A6F0707" 9840 DATA "ADE6AAAD6F070707" 9850 DATA "CB0DADE666AD1213" 9860 DATA "0B78B120E721FFF5" 9870 DATA "010000E5CD8BF2E1" 9880 DATA "E5CD8BF2E1E5CD8B" 9890 DATA "F2E1CD8BF2232323" 9900 DATA "23AF470C79FEC0C2" 9910 DATA "6BF2C9E5D1043E09" 9920 DATA "90835F8A93577885" 9930 DATA "6F8C95671AE521AA" 9940 DATA "F277E17E123AAAF2" 9950 DATA "77C9000000000000" 9960 DATA "eof"

9999 POKE 23606,0: POKE 23607,60: INK 4: REM reset to default character set and colour if you get lost</lang>

Offsite Image: Spectrum rain at imgur