Task: Galton box animation




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


Java: import java.util.Random;
import java.util.List;
import java.util.ArrayList;

public class GaltonBox {
    public static void main( final String[] args ) {
        new GaltonBox( 8, 200 ).run();
    }

    private final int        m_pinRows;
    private final int        m_startRow;
    private final Position[] m_balls;
    private final Random     m_random = new Random();

    public GaltonBox( final int pinRows, final int ballCount ) {
        m_pinRows  = pinRows;
        m_startRow = pinRows + 1;
        m_balls    = new Position[ ballCount ];

        for ( int ball = 0; ball < ballCount; ball++ )
            m_balls[ ball ] = new Position( m_startRow, 0, 'o' );
    }

    private static class Position {
        int  m_row;
        int  m_col;
        char m_char;

        Position( final int row, final int col, final char ch ) {
            m_row  = row;
            m_col  = col;
            m_char = ch;
        }
    }

    public void run() {
        for ( int ballsInPlay = m_balls.length; ballsInPlay > 0;  ) {
            ballsInPlay = dropBalls();
            print();
        }
    }

    private int dropBalls() {
        int ballsInPlay = 0;
        int ballToStart = -1;

        
        for ( int ball = 0; ball < m_balls.length; ball++ )
            if ( m_balls[ ball ].m_row == m_startRow )
                ballToStart = ball;

        
        for ( int ball = 0; ball < m_balls.length; ball++ )
            if ( ball == ballToStart ) {
                m_balls[ ball ].m_row = m_pinRows;
                ballsInPlay++;
            }
            else if ( m_balls[ ball ].m_row > 0 && m_balls[ ball ].m_row != m_startRow ) {
                m_balls[ ball ].m_row -= 1;
                m_balls[ ball ].m_col += m_random.nextInt( 2 );
                if ( 0 != m_balls[ ball ].m_row )
                    ballsInPlay++;
            }

        return ballsInPlay;
    }

    private void print() {
        for ( int row = m_startRow; row --> 1;  ) {
            for ( int ball = 0; ball < m_balls.length; ball++ )
                if ( m_balls[ ball ].m_row == row )
                    printBall( m_balls[ ball ] );
            System.out.println();
            printPins( row );
        }
        printCollectors();
        System.out.println();
    }

    private static void printBall( final Position pos ) {
        for ( int col = pos.m_row + 1; col --> 0;  )
            System.out.print( ' ' );
        for ( int col = 0; col < pos.m_col; col++ )
            System.out.print( "  " );
        System.out.print( pos.m_char );
    }

    private void printPins( final int row ) {
        for ( int col = row + 1; col --> 0;  )
            System.out.print( ' ' );
        for ( int col = m_startRow - row; col --> 0;  )
            System.out.print( ". " );
        System.out.println();
    }

    private void printCollectors() {
        final List<List<Position>> collectors = new ArrayList<List<Position>>();

        for ( int col = 0; col < m_startRow; col++ ) {
            final List<Position> collector = new ArrayList<Position>();

            collectors.add( collector );
            for ( int ball = 0; ball < m_balls.length; ball++ )
                if ( m_balls[ ball ].m_row == 0 && m_balls[ ball ].m_col == col )
                    collector.add( m_balls[ ball ] );
        }

        for ( int row = 0, rows = longest( collectors ); row < rows; row++ ) {
            for ( int col = 0; col < m_startRow; col++ ) {
                final List<Position> collector = collectors.get( col );
                final int            pos       = row + collector.size() - rows;

                System.out.print( '|' );
                if ( pos >= 0 )
                    System.out.print( collector.get( pos ).m_char );
                else
                    System.out.print( ' ' );
            }
            System.out.println( '|' );
        }
    }

    private static final int longest( final List<List<Position>> collectors ) {
        int result = 0;

        for ( final List<Position> collector : collectors )
            result = Math.max( collector.size(), result );

        return result;
    }
}