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.




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
        }
    }
}