// Galton Board
// ------------
//
// NOT FINISHED. I NEED TO MAKE A SIMPLIFIED VERSION.
//
//
// 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:
//
// 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, 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;
}