// Galton Board
// ------------
//
// NOT FINISHED. I NEED TO MAKE A SIMPLIFIED VERSION.
// PLEASE DO NOT MAKE THE LINK TO THIS PROJECT PUBLIC.
//
//
// The Galton Board has balls falling through a pattern of pegs or nails.
// The balls randomly choose between falling to the left or to right.
// At the bottom, there will be a "Normal Distribution".
//
//
// Excellent video from Michael Stevens about the math of the Galton Board:
// https://www.youtube.com/watch?v=UCmPmkHqHXk
//
// Galton Board simulation in browser:
// https://www.mathsisfun.com/data/quincunx.html
//
//
//
// Ideas:
// 1. The shift register is a memory of its own.
// If every output shift register would have a input shift register,
// then the Arduino does not need memory to hold the data.
// 2. The Arduino Mega 2560 is fast in the Wokwi simulation,
// but it has only 8kbytes of SRAM.
// The ESP32 is slow in the simumlation,
// but the Raspberry Pi Pico is reasonable.
// To make it larger, the Raspberry Pi Pico seems the best choice.
//
// Numbering of the Pegs.
// The index of the array is also the number of the peg.
// For example a Galton board size of 6 and 3 rows for the resorvoir.
// 0
// 1 2
// 3 4 5
// 6 7 8 9
// 10 11 12 13 14
// 15 16 17 18 19 20
// ----------------------
// 21 22 23 24 25 26
// 27 28 29 30 31 32
// 33 34 35 36 37 38
//
//
//
// Numbering of the row, colomn.
// For example a Galton board size of 4 and 2 rows for the resorvoir.
// (0,0)
// (1,0) (1,1)
// (2,0) (2,1) (2,2)
// (3,0) (3,1) (3,2) (3,3)
// -----------------------
// (4,0) (4,1) (4,2) (4,3)
// (5,0) (5,1) (5,2) (5,3)
//
//
// The number of rows is the same as the number of columns.
// There is a reservoir at the bottom.
#define GALTON_SIZE 2
#define RESORVOIR_ROWS 2 // the number of vertical led bars of the resorvoir
#define NUMBER_PEGS (((GALTON_SIZE + 1) * GALTON_SIZE) / 2)
#define NUMBER_RESORVOIR (GALTON_SIZE * RESORVOIR_ROWS)
// byte ball[GALTON_SIZE][GALTON_SIZE][LEDS_PER_BAR];
// byte ballInReservoir[GALTON_SIZE][LEDS_PER_BAR];
// All the shift registers are in a array.
// Every ball is a bit in the shift registers and a led in the bar graph.
// Because of the orientation of the shift register in the circuit,
// the highest bit (bit 7) is at the top.
byte Registers[NUMBER_PEGS];
byte Reservoir[NUMBER_RESORVOIR];
const int dataPin = 8; // Arduino data output to DS input of first shift register
const int latchPin = 9; // Latch pin, connected to all STCP of all shift registers
const int clockPin = 10; // Clock pin, connected to all SHCP of all shift registers
void setup()
{
// Run the function GenerateDiagram() once to generate the diagram.json code in the Serial Monitor output.
GenerateDiagram();
pinMode( dataPin, OUTPUT);
pinMode( latchPin, OUTPUT);
digitalWrite( latchPin, HIGH); // default HIGH
pinMode( clockPin, OUTPUT);
digitalWrite( clockPin, HIGH); // default HIGH
}
void loop()
{
// Let all the balls drop one position lower
// When they reach the bottom (bit 0), then they will fall randomly to left or right.
// Since the balls fall down, the lowest ball is handled first to
// prevent overlap.
for( int i=(NUMBER_PEGS-1); i>=0; i--)
{
// Let the ball fall inside the led bar
// Start also here with the lowest ball (bit 0) to prevent overlap.
for( int j=0; j<8; j++)
{
if( bitRead( Registers[i], j) == 1)
{
bitClear( Registers[i], j); // remove ball from old position
if( j == 0) // fall out of led bar ?
{
// Let the lowest ball fall into the next row with random
int row, column;
GetRowColumn( i, row, column);
if( row >= (GALTON_SIZE - 1)) // lowest row ?
{
// drop in reservoir
}
else
{
int rnd = random( 0, 2); // 0 = fall left, 1 = fall right
int x = GetNumber( row + 1, column + rnd);
bitSet( Registers[x], 7); // set the ball in the lower row
}
}
else
{
bitSet( Registers[i], j - 1); // new position: one lower
}
}
}
}
// Now that all the balls have dropped one position, add a new one at the top.
// Drop a ball now and then.
if( random(0,100) < 5)
{
bitSet( Registers[0], 7);
}
UpdateRegisters();
// Warning: Simulation makes it look as if they are two or three led bars.
// Make the delay 500 ms or more to see the single led bar.
delay( 50);
}
void UpdateRegisters()
{
digitalWrite( latchPin, LOW);
for( int i=(NUMBER_PEGS-1); i>=0; i--)
{
for( int j=7; j>=0; j--)
{
digitalWrite( clockPin, LOW);
digitalWrite( dataPin, bitRead( Registers[i], j) == 1 ? HIGH : LOW);
digitalWrite( clockPin, HIGH);
}
}
digitalWrite( latchPin, HIGH);
}
// Helper function to translate the number of the peg into row and column.
//
// If someone knows better code, let me know.
// The total goes one step beyond the wanted row, therefor a correction
// is required for the row and the column has to be calculated everytime,
// so it will keep the value of the previous row.
void GetRowColumn( int _number, int &_row, int &_column)
{
// Find the row and column of the item of the Galton Board.
// The parameter is number of the peg.
// This is calculating the fibonacci sequence in reverse.
// I decided to use a loop and count up until I get there.
int total = 0;
_row = 0;
_column = 0;
while( total <= _number)
{
_row++;
_column = _number - total;
total += _row;
}
_row--;
return;
}
// Helper function to translate the row and column into the number of the peg
int GetNumber( int _row, int _column)
{
int _number = 0;
int pegsPerRow = 0;
for( int i=0; i<_row; i++)
{
pegsPerRow++;
_number += pegsPerRow;
}
_number += _column;
return _number;
}