// Download this in a .zip file.


/* Mugwump 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=115
 
 There are four mugwumps hiding on a 10x10 grid. You enter your guess and then
 are told if you've found a mugwump and how far to the mugwumps.
 The first number is x, the second is y.
 You get ten turns and then you are told where the mugwumps are hiding.
 
 The earliest reference to Mugwump is: http://www.svipx.com/pcc/gameslist.html
 Feb 1973, Bob Albrecht of People's Computing Company and Bud Valenti (and students).
 This Arduino version is based on the listing from Basic Computer Games 1978, David Ahl
 
 by eturnerx@gmail.com 201210
 
 */
/*--------------------------------------------------------------------------------------
 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_TURNSTART           20  // The turn begins
#define STATE_OPTIONX             30  // Getting input for x axis
#define STATE_OPTIONY             40  // Getting input for y axis
#define STATE_GAMEOVER            99

// 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 turn, score, scanx, scany, mugwumpx[4], mugwumpy[4];
boolean mugwumpfound[4];
int distance;

/*--------------------------------------------------------------------------------------
 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( "--_ Mugwump. _--" );
  lcd.setCursor( 0, 1 );
  lcd.print( '.' );
  lcd.print(  '.' );   
  flashbacklight();
  lcd.print(   '.' );  
  flashbacklight();
  lcd.print(    '.' ); 
  flashbacklight();
  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( "==  Mugwump   ==");
    lcd.setCursor( 0, 1 );
    lcd.print( "Press [any] key" );
    turn = score = 0;
    for(byte i = 0; i < 4; i++) {
      mugwumpx[i] = random(10);
      mugwumpy[i] = random(10);
      mugwumpfound[i] = false;
    }
    nextstate = STATE_TURNSTART;
    state = STATE_KEYWAIT;
    break;

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

  case STATE_TURNSTART:
    turn++;
    lcd.clear();
    if(turn > 10) {
      //          1234567890123456
      lcd.print("Sorry, that's 10"); 
      lcd.setCursor(0,1);
      lcd.print("tries.They're at");
      flashbacklight();
      delay(MSGFLASHDELAY);
      score = 0;
      for(int i = 0; i < 4; i++) {
        lcd.clear();
        //        1234567890123456
        lcd.print("Mugwump");
        lcd.print((char)('A' + i));
        lcd.print(" at: ");
        lcd.print(mugwumpx[i]);
        lcd.print(',');
        lcd.print(mugwumpy[i]);
        lcd.setCursor(0,1);
        if(!mugwumpfound[i]) {
          lcd.print("Still hidden.");
        } 
        else {
          lcd.print("You found it!");
          score++;
        }
        delay(MSGFLASHDELAY);
      }
      lcd.clear();
      lcd.print("Final score = ");
      lcd.print(score);
      delay(MSGFLASHDELAY);
      state = STATE_GAMEOVER;
      break;
    }

    //        1234567890123456
    //        Ad   Bd  Cd   Dd
    for(int i = 0; i < 4; i++) {
      lcd.print((char)('A' + i));
      if(mugwumpfound[i]) lcd.print('X');
      else if(turn == 1) lcd.print('?');
      else {
        distance = (int)(sqrt(sq(scanx - mugwumpx[i])+sq(scany - mugwumpy[i])) + 0.5f);
        if(distance > 9) lcd.print('>'); else lcd.print(distance);
      }
      if(i < 3) {
        lcd.print("  ");
        if(i == 0 || i == 2) lcd.print(' ');
      }
    }
    lcd.setCursor(0,1);
    lcd.print("Turn:" );
    lcd.print(turn);
    lcd.setCursor(9,1);
    lcd.print("X:0");
    scanx = 0;
    state = STATE_OPTIONX;
    break;

  case STATE_OPTIONX:
    if(buttonJustPressed) {
      switch(button) {
      case BUTTON_LEFT: 
      case BUTTON_DOWN:
        if(scanx == 0) scanx = 9; 
        else scanx--;
        break;
      case BUTTON_RIGHT: 
      case BUTTON_UP:
        if(scanx == 9) scanx = 0; 
        else scanx++;
        break;
      case BUTTON_SELECT:
        scany = 0;
        lcd.setCursor(13,1);
        lcd.print("Y:0");
        state = STATE_OPTIONY;
        break;
      }
    }
    lcd.setCursor(11,1);
    lcd.print(scanx);
    break;

  case STATE_OPTIONY:
    if(buttonJustPressed) {
      switch(button) {
      case BUTTON_LEFT: 
      case BUTTON_DOWN:
        if(scany == 0) scany = 9; 
        else scany--;
        break;
      case BUTTON_RIGHT: 
      case BUTTON_UP:
        if(scany == 9) scany = 0; 
        else scany++;
        break;
      case BUTTON_SELECT:
        for(int i = 0; i < 4; i++) {
          if(!mugwumpfound[i] && mugwumpx[i] == scanx && mugwumpy[i] == scany) {
            lcd.clear();
            lcd.print("Found Mugwump");
            lcd.print((char)('A' + i));
            mugwumpfound[i] = true;
            score++;
            flashbacklight();
            delay(MSGFLASHDELAY);
          }
        }
        if(score == 4) {
          lcd.clear();
          //        1234567890123456
          lcd.print("You got them all"); lcd.setCursor(0,1);
          lcd.print("Final score = ");
          lcd.print(15 - turn);
          flashbacklight();
          state = STATE_KEYWAIT;
          nextstate = STATE_GAMEOVER;
        } 
        else
          state = STATE_TURNSTART;
        break;
      }
    }
    if(state == STATE_OPTIONY) {
      lcd.setCursor(15,1);
      scany = constrain(scany, 0, 9);
      lcd.print(scany);
    }
    break;

  case STATE_GAMEOVER:
    lcd.clear();
    //          1234567890123456
    lcd.print( "== GAME OVER  ==");
    lcd.setCursor( 0, 1 );
    lcd.print( "Press [any] key" );
    nextstate = STATE_TITLE;
    state = STATE_KEYWAIT;
    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 );
}