// Download this in a .zip file.


/* Nicomachus for the Freetronics 16x2 LCD shield
 http://www.freetronics.com/pages/16x2-lcd-shield-quickstart-guide#.UBKMm7T9OK9
 http://www.atariarchives.org/basicgames/showpage.php?page=117
 
 This guy from 90AD has got your number (1 - 100).
 
 by eturnerx@gmail.com 201211
 
 */
/*--------------------------------------------------------------------------------------
 Includes
 --------------------------------------------------------------------------------------*/
#include    // include LCD library
/*--------------------------------------------------------------------------------------
 Defines
 --------------------------------------------------------------------------------------*/
// Pins in use
#define BUTTON_ADC_PIN           A0  // A0 is the button ADC input
#define LCD_BACKLIGHT_PIN         3  // D3 controls LCD backlight
// ADC readings expected for the 5 buttons on the ADC input
#define RIGHT_10BIT_ADC           0  // right
#define UP_10BIT_ADC            145  // up
#define DOWN_10BIT_ADC          329  // down
#define LEFT_10BIT_ADC          505  // left
#define SELECT_10BIT_ADC        741  // right
#define BUTTONHYSTERESIS         10  // hysteresis for valid button sensing window
//return values for ReadButtons()
#define BUTTON_NONE               0  // 
#define BUTTON_RIGHT              1  // 
#define BUTTON_UP                 2  // 
#define BUTTON_DOWN               3  // 
#define BUTTON_LEFT               4  // 
#define BUTTON_SELECT             5  // 

// Define our states
#define STATE_TITLE               00  // A title screen
#define STATE_KEYWAIT             05  // Waiting for a keypress
#define STATE_GAMESTART           20  // The game begins
#define STATE_INPUTNUMBERA        30
#define STATE_INPUTNUMBERB        40
#define STATE_INPUTNUMBERC        50

// Misc. Defines
#define MSGFLASHDELAY           1500  // How long to flash messages for (ms)?

/*--------------------------------------------------------------------------------------
 Variables
 --------------------------------------------------------------------------------------*/
byte buttonJustPressed  = false;         //this will be true after a ReadButtons() call if triggered
byte buttonJustReleased = false;         //this will be true after a ReadButtons() call if triggered
byte buttonWas          = BUTTON_NONE;   //used by ReadButtons() for detection of button events

// Setup the variables that will be used
byte state, nextstate;
byte a, c, b;
int result;

/*--------------------------------------------------------------------------------------
 Init the LCD library with the LCD pins to be used
 --------------------------------------------------------------------------------------*/
LiquidCrystal lcd( 8, 9, 4, 5, 6, 7 );   //Pins for the freetronics 16x2 LCD shield. LCD: ( RS, E, LCD-D4, LCD-D5, LCD-D6, LCD-D7 )


/*--------------------------------------------------------------------------------------
 setup()
 Called by the Arduino framework once, before the main loop begins
 --------------------------------------------------------------------------------------*/
void setup() {
  // Setup pins. 
  pinMode(BUTTON_ADC_PIN, INPUT );         // Analogue input for the LCD's buttons
  digitalWrite(BUTTON_ADC_PIN, LOW );
  pinMode(LCD_BACKLIGHT_PIN, OUTPUT );     // Backlight control (off/on)
  digitalWrite(LCD_BACKLIGHT_PIN, HIGH );  // Turn the backlight on
  randomSeed(analogRead(1));               // Send the random number generator

  lcd.begin( 16, 2 );                      // Tell LCD library this is 16x2 display
  lcd.setCursor( 0, 0 );
  //          1234567890123456
  lcd.print( "--_NICOMACHUS_--" );
  lcd.setCursor( 0, 1 );
  lcd.print( '.' );
  lcd.print(  '.' );   
  flashbacklight();
  lcd.print(   '.' );  
  flashbacklight();
  lcd.print(    '.' ); 
  flashbacklight();
  a = b = c = 0;
  state = STATE_TITLE;  
}


/*--------------------------------------------------------------------------------------
 loop()
 Arduino main loop
 --------------------------------------------------------------------------------------*/
void loop() {
  byte button;

  button = ReadButtons();
  switch(state) {                  //    Do different things depending on "state"

  case STATE_TITLE:
    lcd.setCursor( 0, 0 );
    //          1234567890123456
    lcd.print( "== NICOMACHUS ==");
    lcd.setCursor( 0, 1 );
    lcd.print( "Press [any] key" );
    a = b = c = 0;
    nextstate = STATE_GAMESTART;
    state = STATE_KEYWAIT;
    break;

  case STATE_KEYWAIT:
    if(buttonJustPressed) state = nextstate;
    break;

  case STATE_GAMESTART:
    lcd.clear();
    //         1234567890123456
    lcd.print("Choose a number"); 
    lcd.setCursor(0,1);
    lcd.print("between 1 & 100");
    delay(MSGFLASHDELAY);
    lcd.clear();
    //         1234567890123456
    lcd.print("Remainder of num"); 
    lcd.setCursor(0,1);
    lcd.print("divided by 3 = 0");
    state = STATE_INPUTNUMBERA;
    break;

  case STATE_INPUTNUMBERA:
    if(buttonJustPressed) {
      switch(button) {
      case BUTTON_LEFT: 
      case BUTTON_DOWN:
        if(a == 0) a = 2; 
        else a--;
        break;
      case BUTTON_RIGHT: 
      case BUTTON_UP:
        if(a == 2) a = 0; 
        else a++;
        break;
      case BUTTON_SELECT:
        b = 0;
        lcd.clear();
        lcd.print("Remainder of num"); 
        lcd.setCursor(0,1);
        lcd.print("divided by 5 = 0");
        delay(150);
        state = STATE_INPUTNUMBERB;
        break;
      }
      if(state == STATE_INPUTNUMBERA) {
        lcd.setCursor(15,1);
        lcd.print(a);
      }
    }
    break;

  case STATE_INPUTNUMBERB:
    if(buttonJustPressed) {
      switch(button) {
      case BUTTON_LEFT: 
      case BUTTON_DOWN:
        if(b == 0) b = 4; 
        else b--;
        break;
      case BUTTON_RIGHT: 
      case BUTTON_UP:
        if(b == 4) b = 0; 
        else b++;
        break;
      case BUTTON_SELECT:
        c = 0;
        lcd.clear();
        lcd.print("Remainder of num"); 
        lcd.setCursor(0,1);
        lcd.print("divided by 7 = 0");
        delay(150);
        state = STATE_INPUTNUMBERC;
        break;
      }
      if(state == STATE_INPUTNUMBERB) {
        lcd.setCursor(15,1);
        lcd.print(b);
      }
    }
    break;

  case STATE_INPUTNUMBERC:
    if(buttonJustPressed) {
      switch(button) {
      case BUTTON_LEFT: 
      case BUTTON_DOWN:
        if(c == 0) c = 6; 
        else c--;
        break;
      case BUTTON_RIGHT: 
      case BUTTON_UP:
        if(c == 6) c = 0; 
        else c++;
        break;
      case BUTTON_SELECT:
        lcd.clear();
        //         1234567890123456
        lcd.print("Let me think");
        delay(MSGFLASHDELAY/2);
        lcd.print('.');
        delay(MSGFLASHDELAY/3);
        lcd.print('.');
        delay(MSGFLASHDELAY/4);
        lcd.print('.');
        delay(MSGFLASHDELAY/5);
        lcd.print('.');
        delay(MSGFLASHDELAY/6);
        result = (70 * a) + (21 * b) + (15 * c);
        while(result > 104) result -= 105;
        lcd.clear();
        //         1234567890123456
        lcd.print("The number = ");
        lcd.print(result);
        lcd.setCursor(0,1); 
        lcd.print( "Press [any] key" );
        nextstate = STATE_TITLE;
        state = STATE_KEYWAIT;
        break;
      }
      if(state == STATE_INPUTNUMBERC) {
        lcd.setCursor(15,1);
        lcd.print(c);
      }
    }
    break;

  default: 
    break;
  }
  //clear the buttonJustPressed & buttonJustReleased flags; they've already done their job
  if(buttonJustPressed)  buttonJustPressed  = false;
  if(buttonJustReleased) buttonJustReleased = false;
  delay(25); // Delay to stop buttons "jittering" false presses. Not as good as a real debounce.
}

void flashbacklight() {
  digitalWrite(LCD_BACKLIGHT_PIN, LOW);  
  delay(150);
  digitalWrite(LCD_BACKLIGHT_PIN, HIGH); 
  delay(150);
}

/*--------------------------------------------------------------------------------------
 ReadButtons()
 Detect the button pressed and return the value
 Uses global values buttonWas, buttonJustPressed, buttonJustReleased.
 --------------------------------------------------------------------------------------*/
byte ReadButtons() {
  unsigned int buttonVoltage;
  byte button = BUTTON_NONE;   // return no button pressed if the below checks don't write to btn

  //read the button ADC pin voltage
  buttonVoltage = analogRead( BUTTON_ADC_PIN );
  //sense if the voltage falls within valid voltage windows
  if( buttonVoltage < ( RIGHT_10BIT_ADC + BUTTONHYSTERESIS ) )
  {
    button = BUTTON_RIGHT;
  }
  else if(   buttonVoltage >= ( UP_10BIT_ADC - BUTTONHYSTERESIS )
    && buttonVoltage <= ( UP_10BIT_ADC + BUTTONHYSTERESIS ) )
  {
    button = BUTTON_UP;
  }
  else if(   buttonVoltage >= ( DOWN_10BIT_ADC - BUTTONHYSTERESIS )
    && buttonVoltage <= ( DOWN_10BIT_ADC + BUTTONHYSTERESIS ) )
  {
    button = BUTTON_DOWN;
  }
  else if(   buttonVoltage >= ( LEFT_10BIT_ADC - BUTTONHYSTERESIS )
    && buttonVoltage <= ( LEFT_10BIT_ADC + BUTTONHYSTERESIS ) )
  {
    button = BUTTON_LEFT;
  }
  else if(   buttonVoltage >= ( SELECT_10BIT_ADC - BUTTONHYSTERESIS )
    && buttonVoltage <= ( SELECT_10BIT_ADC + BUTTONHYSTERESIS ) )
  {
    button = BUTTON_SELECT;
  }
  //handle button flags for just pressed and just released events
  if( ( buttonWas == BUTTON_NONE ) && ( button != BUTTON_NONE ) )
  {
    //the button was just pressed, set buttonJustPressed, this can optionally be used to trigger a once-off action for a button press eve
    //it's the duty of the receiver to clear these flags if it wants to detect a new button change event
    buttonJustPressed  = true;
    buttonJustReleased = false;
  }
  if( ( buttonWas != BUTTON_NONE ) && ( button == BUTTON_NONE ) )
  {
    buttonJustPressed  = false;
    buttonJustReleased = true;
  }

  //save the latest button value, for change event detection next time round
  buttonWas = button;

  return( button );
}