Task: Galton box animation

C: #include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define BALLS 1024
int n, w, h = 45, *x, *y, cnt = 0;
char *b;

#define B(y, x) b[(y)*w + x]
#define C(y, x) ' ' == b[(y)*w + x]
#define V(i) B(y[i], x[i])
inline int rnd(int a) { return (rand()/(RAND_MAX/a))%a; }

void show_board()
{
	int i, j;
	for (puts("\033[H"), i = 0; i < h; i++, putchar('\n'))
		for (j = 0; j < w; j++, putchar(' '))
			printf(B(i, j) == '*' ?
				C(i - 1, j) ? "\033[32m%c\033[m" :
				"\033[31m%c\033[m" : "%c", B(i, j));
}

void init()
{
	int i, j;
	puts("\033[H\033[J");
	b = malloc(w * h);
	memset(b, ' ', w * h);

	x = malloc(sizeof(int) * BALLS * 2);
	y = x + BALLS;

	for (i = 0; i < n; i++)
		for (j = -i; j <= i; j += 2)
			B(2 * i+2, j + w/2) = '*';
	srand(time(0));
}

void move(int idx)
{
	int xx = x[idx], yy = y[idx], c, kill = 0, sl = 3, o = 0;

	if (yy < 0) return;
	if (yy == h - 1) { y[idx] = -1; return; }

	switch(c = B(yy + 1, xx)) {
	case ' ':	yy++; break;
	case '*':	sl = 1;
	default:	if (xx < w - 1 && C(yy, xx + 1) && C(yy + 1, xx + 1))
				if (!rnd(sl++)) o = 1;
			if (xx && C(yy, xx - 1) && C(yy + 1, xx - 1))
				if (!rnd(sl++)) o = -1;
			if (!o) kill = 1;
			xx += o;
	}

	c = V(idx); V(idx) = ' ';
	idx[y] = yy, idx[x] = xx;
	B(yy, xx) = c;
	if (kill) idx[y] = -1;
}

int run(void)
{
	static int step = 0;
	int i;
	for (i = 0; i < cnt; i++) move(i);
	if (2 == ++step && cnt < BALLS) {
		step = 0;
		x[cnt] = w/2;
		y[cnt] = 0;
		if (V(cnt) != ' ') return 0;
		V(cnt) = rnd(80) + 43;
		cnt++;
	}
	return 1;
}

int main(int c, char **v)
{
	if (c < 2 || (n = atoi(v[1])) <= 3) n = 5;
	if (n >= 20) n = 20;
	w = n * 2 + 1;
	init();

	do { show_board(), usleep(60000); } while (run());

	return 0;
}




Go: package main

import (
    "fmt"
    "math/rand"
    "time"
)

const boxW = 41      
const boxH = 37      
const pinsBaseW = 19 
const nMaxBalls = 55 

const centerH = pinsBaseW + (boxW-pinsBaseW*2+1)/2 - 1

const (
    empty  = ' '
    ball   = 'o'
    wall   = '|'
    corner = '+'
    floor  = '-'
    pin    = '.'
)

type Ball struct{ x, y int }

func newBall(x, y int) *Ball {
    if box[y][x] != empty {
        panic("Tried to create a new ball in a non-empty cell. Program terminated.")
    }
    b := Ball{x, y}
    box[y][x] = ball
    return &b
}

func (b *Ball) doStep() {
    if b.y <= 0 {
        return 
    }
    cell := box[b.y-1][b.x]
    switch cell {
    case empty:
        box[b.y][b.x] = empty
        b.y--
        box[b.y][b.x] = ball
    case pin:
        box[b.y][b.x] = empty
        b.y--
        if box[b.y][b.x-1] == empty && box[b.y][b.x+1] == empty {
            b.x += rand.Intn(2)*2 - 1
            box[b.y][b.x] = ball
            return
        } else if box[b.y][b.x-1] == empty {
            b.x++
        } else {
            b.x--
        }
        box[b.y][b.x] = ball
    default:
        
    }
}

type Cell = byte


var box [boxH][boxW]Cell

func initializeBox() {
    
    box[0][0] = corner
    box[0][boxW-1] = corner
    for i := 1; i < boxW-1; i++ {
        box[0][i] = floor
    }
    for i := 0; i < boxW; i++ {
        box[boxH-1][i] = box[0][i]
    }

    
    for r := 1; r < boxH-1; r++ {
        box[r][0] = wall
        box[r][boxW-1] = wall
    }

    
    for i := 1; i < boxH-1; i++ {
        for j := 1; j < boxW-1; j++ {
            box[i][j] = empty
        }
    }

    
    for nPins := 1; nPins <= pinsBaseW; nPins++ {
        for p := 0; p < nPins; p++ {
            box[boxH-2-nPins][centerH+1-nPins+p*2] = pin
        }
    }
}

func drawBox() {
    for r := boxH - 1; r >= 0; r-- {
        for c := 0; c < boxW; c++ {
            fmt.Printf("%c", box[r][c])
        }
        fmt.Println()
    }
}

func main() {
    rand.Seed(time.Now().UnixNano())
    initializeBox()
    var balls []*Ball
    for i := 0; i < nMaxBalls+boxH; i++ {
        fmt.Println("\nStep", i, ":")
        if i < nMaxBalls {
            balls = append(balls, newBall(centerH, boxH-2)) 
        }
        drawBox()

        
        
        for _, b := range balls {
            b.doStep()
        }
    }
}