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

74HC595
74HC595
74HC595