// Download this in a .zip file.


/* Acecy Deucey 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=2
 
 Loosely based on the original game, though changed for a heads up format.
 Similar to Red Dog Poker, but played heads rather than vs the house
 
 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_OPTION              30  // Getting input
#define STATE_AITURN              40  // The AI gives it a go
#define STATE_GAMEOVER            99

// Misc. Defines
#define DECKRESHUFFLE             38  // How many cards dealt (more than or equal) triggers a reshuffle
#define MSGFLASHDELAY           1300  // 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 deck[52], card1, card2, card3;
const char ranklabels[13] = { 
  '2','3','4','5','6','7','8','9','T','J','Q','K','A' };
int pot, ante, turn, cashplayer, cashai, deckpos, bet;
byte betdregs;                           //for tracking "sub ante" bets caused by pot or player stack being too small

/*--------------------------------------------------------------------------------------
 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( "_ Acey  Deucey _" );
  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( "= Acey  Deucey =" );
    shuffle();
    pot = turn = 0;
    ante = 1;
    cashplayer = cashai = 50;
    lcd.setCursor( 0, 1 );
    lcd.print( "Press [any] key" );
    nextstate = STATE_TURNSTART;
    state = STATE_KEYWAIT;
    break;

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

  case STATE_TURNSTART:
    turn++; 
    bet = betdregs = 0;
    printStatusLine();

    if(turn % 6 == 5 && ante < 20) {
      ante++;
      lcd.clear();
      lcd.print("ANTE INCREASE");
      lcd.setCursor(0,1);
      lcd.print("Ante is now: ");
      lcd.print(ante);
      flashbacklight();
      delay(MSGFLASHDELAY);
    }

    if(deckpos >= DECKRESHUFFLE) {
      shuffle();
      lcd.clear();
      lcd.print("*DECK  SHUFFLED*");
      flashbacklight();
      delay(MSGFLASHDELAY);
    }      

    if(cashplayer == 0) {
      lcd.clear();
      lcd.print("You have no ANTE");
      lcd.setCursor(0,1);
      lcd.print(">>  YOU LOSE  <<");
      state = STATE_GAMEOVER;
      return;
    } 
    else if(cashplayer <= ante) {
      printStatusLine();
      lcd.print("*You are ALL-IN*");
      pot += cashplayer;
      cashplayer = 0;
      flashbacklight();
      delay(MSGFLASHDELAY);
    } 
    else {
      printStatusLine();
      lcd.print("ANTE ");
      lcd.print(ante);
      lcd.print("  ");
      lcd.print("ANTE ");
      lcd.print(ante);
      pot += ante;
      cashplayer -= ante;
    }
    printStatusLine();
    delay(MSGFLASHDELAY);

    card1 = deck[deckpos]; 
    deckpos++;
    card2 = deck[deckpos]; 
    deckpos++;
    if(card1 > card2) {
      byte tmp = card1;
      card1 = card2;
      card2 = tmp;
    }

    //         1234567890123456
    //         Cards:TQ  Bet:00
    lcd.print("Cards:");
    lcd.print(ranklabels[card1]);
    lcd.print(ranklabels[card2]);
    lcd.print("  Bet:");
    if(bet < 10) lcd.print('0');
    lcd.print(bet);
    state = STATE_OPTION;
    break;

  case STATE_OPTION:
    if(cashplayer == 0) {
      buttonJustPressed = true;
      button = BUTTON_SELECT;
    }
    if(buttonJustPressed) {
      switch(button) {
      case BUTTON_LEFT: 
      case BUTTON_DOWN:
        if(bet > 0) {
          if(betdregs > 0) {
            bet -= betdregs;
            betdregs = 0;
          } 
          else bet -= ante;
        }
        break;
      case BUTTON_RIGHT: 
      case BUTTON_UP:
        if(bet < cashplayer && bet < pot) {
          bet += ante;
          if(bet > cashplayer) {
            betdregs = (cashplayer + ante) - bet;
            bet = cashplayer;
          }
          if(bet > pot) {
            betdregs = (pot + ante) - bet;
            bet = pot;
          }
        }
        break;
      case BUTTON_SELECT:
        lcd.clear();
        if(bet == 0 && cashplayer > 0) {
          lcd.print("You pass....");
          delay(MSGFLASHDELAY*1.5);
          state = STATE_AITURN;
        }  
        else {
          card3 = deck[deckpos]; 
          deckpos++;
          lcd.print("Drawing a card");
          lcd.setCursor(0,1);
          lcd.print(ranklabels[card1]);
          lcd.print("  -  ");
          lcd.print(ranklabels[card2]);
          delay(MSGFLASHDELAY/2);
          lcd.setCursor(3,1);
          lcd.print('/');
          delay(MSGFLASHDELAY/2);
          lcd.setCursor(3,1);
          lcd.print('-');
          delay(MSGFLASHDELAY/2);
          lcd.setCursor(3,1);
          lcd.print(ranklabels[card3]);
          lcd.setCursor(9,1);
          if(card1 < card3 && card2 > card3) {
            pot -= bet;
            cashplayer += bet;
            lcd.print("Win: ");
            lcd.print(bet);
            lcd.setCursor(2,1);
            lcd.print('>');
            lcd.setCursor(4,1);
            lcd.print('<');
          } 
          else {
            pot += bet;
            cashplayer -= bet;
            lcd.print("Lose:");
            if(bet<10) lcd.print(' ');
            lcd.print(bet);
            lcd.setCursor(2,1);
            if(card3 > card1) lcd.print('<'); 
            else if(card3 < card1) lcd.print('>'); 
            else lcd.print('=');
            lcd.setCursor(4,1);
            if(card3 > card2) lcd.print('>'); 
            else if(card3 < card2) lcd.print('<'); 
            else lcd.print('=');
          }
          delay(MSGFLASHDELAY);
          state = STATE_AITURN;
        }
        break;
      }
    }
    if(state == STATE_OPTION) {
      lcd.setCursor(14,1);
      if(bet < 10) lcd.print(' ');
      lcd.print(bet);
    }
    break;

  case STATE_AITURN:
    if(deckpos >= DECKRESHUFFLE) {
      shuffle();
      lcd.clear();
      lcd.print("*DECK  SHUFFLED*");
      flashbacklight();
      delay(MSGFLASHDELAY);
    }      

    if(cashai == 0) {
      lcd.clear();
      lcd.print("Arduino is BROKE");
      delay(MSGFLASHDELAY);
      lcd.setCursor(0,1);
      lcd.print(">>  YOU WIN!  <<");
      state = STATE_GAMEOVER;
      return;
    } 
    else if(cashai <= ante) {
      lcd.clear();
      lcd.print("Arduino's ALL-IN");
      pot += cashai;
      cashai = 0;
      flashbacklight();
      delay(MSGFLASHDELAY);
    } 
    else {
      pot += ante;
      cashai -= ante;
    }
    lcd.setCursor(0,1);
    lcd.print("                ");
    lcd.setCursor(0,1);

    card1 = deck[deckpos]; 
    deckpos++;
    card2 = deck[deckpos]; 
    deckpos++;
    if(card1 > card2) {
      byte tmp = card1;
      card1 = card2;
      card2 = tmp;
    }

    // Okay now what'll the AI do?
    // In a clean deck, bet when the spread is seven or more
    // Bet more when the spread is larger
    lcd.setCursor(0,0);
    lcd.print("Arduino's Turn  ");
    lcd.setCursor(0,1);
    lcd.print(ranklabels[card1]);
    lcd.print("  -  ");
    lcd.print(ranklabels[card2]);
    lcd.setCursor(9,1);
    if(card2 - card1 < 7 && cashai > 0) {
      lcd.print("PASS!");
    } 
    else {
      // How much to bet?
      bet = (card2 - card1) * ante;
      if(bet > int(cashai/2) && cashai > (ante * 2)) {
        bet = int(cashai/2);
        bet -= (bet % ante);
      }
      if(bet > cashai) bet = cashai;
      if(bet > pot) bet = pot;
      lcd.print("BET:");
      lcd.print(bet);
      lcd.setCursor(0,0);
      lcd.print("Drawing a card  ");
      lcd.setCursor(0,1);
      card3 = deck[deckpos]; 
      deckpos++;
      delay(MSGFLASHDELAY/2);
      lcd.setCursor(3,1);
      lcd.print('/');
      delay(MSGFLASHDELAY/2);
      lcd.setCursor(3,1);
      lcd.print('-');
      delay(MSGFLASHDELAY/2);
      lcd.setCursor(3,1);
      lcd.print(ranklabels[card3]);
      lcd.setCursor(9,1);
      if(card1 < card3 && card2 > card3) {
        pot -= bet;
        cashai += bet;
        lcd.print("Win: ");
        lcd.print(bet);
        lcd.setCursor(2,1);
        lcd.print('>');
        lcd.setCursor(4,1);
        lcd.print('<');
      } 
      else {
        pot += bet;
        cashai -= bet;
        lcd.print("Lose:");
        if(bet<10) lcd.print(' ');
        lcd.print(bet);
        lcd.setCursor(2,1);
        if(card3 > card1) lcd.print('<'); 
        else if(card3 < card1) lcd.print('>'); 
        else lcd.print('=');
        lcd.setCursor(4,1);
        if(card3 > card2) lcd.print('>'); 
        else if(card3 < card2) lcd.print('<'); 
        else lcd.print('=');
      }
    }
    delay(MSGFLASHDELAY);
    state = STATE_TURNSTART;
    break;

  case STATE_GAMEOVER:
    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);
}

void printStatusLine() {
  lcd.clear();
  //         1234567890123456
  if(cashai < 10) lcd.print('0');
  lcd.print(cashai);
  lcd.print(":A");
  if(pot < 10) lcd.print("   0");
  else lcd.print("   ");
  lcd.print(pot);
  lcd.print("   U:");
  if(cashplayer < 10) lcd.print('0');
  lcd.print(cashplayer);
  lcd.setCursor(0,1);
}

void shuffle() {
  // Setup a clean deck
  for(int suit = 0; suit < 4; suit++) {
    for(byte rank = 0; rank < 13; rank++) {
      deck[(suit * 13) + rank] = rank;
    }
  }

  // Shuffle  
  //  First pass ... each position is shuffled
  for(int pos1 = 0; pos1 < 52; pos1++) {
    int pos2 = random(52);
    byte tmp = deck[pos1];
    deck[pos1] = deck[pos2];
    deck[pos2] = tmp;
  }
  //  Second pass is a series of random swaps
  for(int cnt = 0; cnt < 1000; cnt++) {
    int pos1 = random(52);
    int pos2 = random(52);
    byte tmp = deck[pos1];
    deck[pos1] = deck[pos2];
    deck[pos2] = tmp;
  }
  deckpos = 0;
}

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