// Players
var p1 = 0;
var p2 = 0;
var p3 = 0;
var p4 = 0;
var human = 0;

// If set to true, and alert for each turn (flop, turn and river)
var isShowAlertOnFlipCards = true;

var LOW_LIMIT = 250;
var HIGH_LIMIT = 500;

// The five cards at top, flipped over as follows:
//   * Flop: First three
//   * Turn: Fourth
//   * River: Fifth
//
// Will be initialized to an Array(5) of Card objects
var dealer = 0;

var STRAIGHT_FLUSH  = 9;
var FOUR_OF_A_KIND  = 8;
var FULL_HOUSE      = 7;
var FLUSH           = 6;
var STRAIGHT        = 5;
var THREE_OF_A_KIND = 4;
var TWO_PAIR        = 3;
var PAIR            = 2;
var HIGH_CARD       = 1;

var HEART   = 'Heart';
var SPADE   = 'Spade';
var DIAMOND = 'Diamond';
var CLUB    = 'Club';

var PREFLOP = 0;
var FLOP = 1;
var TURN = 2;
var RIVER = 3;

/********************************************************
 * Possible names for players. Randomly chosen. Can add
 *   more.
 ********************************************************/
var namePool = [
   'Akira',
   'April',
   'Beatrice',
   'Bryan',
   'Dante',
   'Ebenezer',
   'Gretchen',
   'Henry', 
   'James',
   'Jessica',
   'Jezebel',
   'Luigi',
   'Mario',
   'Peach',
   'Samus',
   'Sara',
   'Stephen',
   'Tina',
   'Tori',
   'Virgil',
   'Vlad'
];

function alertRules() {
  var BETTING = ">>> BETTING INCREMENTS <<<\n\n"+
                "Bet in fixed increments based on round:\n\n"+
                "  * Pre-flop and flop: $"+LOW_LIMIT+"\n"+
                "  * Turn and river: $"+HIGH_LIMIT+"\n\n";
  alert(BETTING);

  var BETTING_CONT = ">>> HOW MANY TIMES CAN PLAYER BET DURING ROUND? <<<\n\n"+
                     "Anyone can bet as many times as they choose so long as\n"+
                     "any opponent bets since that player's last bet.\n\n"+
                     "So betting is indefinite if players continue to re-raise\n" +
                     "each other.\n\n";
  alert(BETTING_CONT );
}


/********************************************************
 * Class for card
 ********************************************************/
function Card(value, suit, imgSrc) {
  this._value = value;
  this._suit = suit;
  this._imgSrc = imgSrc;
}

Card.prototype._value;
Card.prototype._suit;
Card.prototype._imgSrc;

/**
 * 2-10 for numeric cards,
 * Jack is 11,
 * Queen is 12,
 * King is 13,
 * Ace is 14
 */
Card.prototype.getValue = function() {
  return this._value;
}

/**
 * "Spade", "Heart", "Club", "Diamond"
 */
Card.prototype.getSuit = function() {
  return this._suit;
}

/**
 * Relative location for image representing card
 */
Card.prototype.getImgSrc = function() {
  return this._imgSrc;
}

/**
 * Human-understandable name
 */
Card.prototype.getName = function() {
  if (this.getValue() <= 10) {
    return this.getValue()+' of '+this.getSuit();
  } else if (this.getValue() == 11) {
    return 'Jack of '+this.getSuit();
  } else if (this.getValue() == 12) {
    return 'Queen of '+this.getSuit();
  } else if (this.getValue() == 13) {
    return 'King of '+this.getSuit();
  } else if (this.getValue() == 14) {
    return 'Ace of '+this.getSuit();
  } else {
    return 'Unrecognized card: value='+this.getValue()+', suit= '+this.getSuit();
  }
}

/********************************************************
 * Pot (for each hand)
 ********************************************************/
var pot = 0;

function clearPot() {
  pot = 0;
  document.getElementById('pot_cash').innerHTML = pot;
}

function adjustPot(delta) {
  pot += delta;

  document.getElementById('pot_cash').innerHTML = pot;
}

function getPot() {
  return pot;
}

/********************************************************
 * Display console
 ********************************************************/

consoleLine = 0;

function clearConsole() {
  document.getElementById('console').innerHTML = '';
  consoleLine = 0;
}

function writeToConsole(msg) {
  var console = document.getElementById('console');

  consoleLine++;

  if (console.innerHTML == '') {
    console.innerHTML = consoleLine + ': ' + msg;
  } else {
    console.innerHTML = consoleLine + ': ' + msg + '<br />' + console.innerHTML;
  }
}

/********************************************************
 * Control console
 ********************************************************/

function clearControlConsole() {
  document.getElementById('console_control').innerHTML = '';
}

function writeToControlConsole(myHTML) {
  document.getElementById('console_control').innerHTML = myHTML;
}

/********************************************************
 * Class for player
 ********************************************************/
function Player(fName, money, domPrefix, playerId) {
  this._name = fName;
  this._money = money;
  this._domPrefix = domPrefix;
  this._playerId = playerId;

  // This is the amount required to call a hand. If 0, stay
  this._callAmt = 0;
  this._isOut = 0;
}

Player.prototype._name;
Player.prototype._money;
Player.prototype._domPrefix;
Player.prototype._playerId;
Player.prototype._card1;
Player.prototype._card2;
Player.prototype._isFolded;
Player.prototype._bestHand;
Player.prototype._callAmt;
Player.prototype._isOut;

Player.prototype.getBestHand = function() {
  return this._bestHand;
}

Player.prototype.setBestHand = function(bestHand) {
  this._bestHand = bestHand;
}

Player.prototype.getName = function() {
  return this._name;
}

Player.prototype.getCallAmount = function() {
  return this._callAmt;
}

Player.prototype.clearCallAmount = function() {
  this._callAmt = 0;
}

Player.prototype.adjustCallAmount = function(delta) {
  this._callAmt = this._callAmt + delta;
}

Player.prototype.isOut = function() {
  return this._isOut;
}

Player.prototype.setOut = function() {
  this._isOut = true;
  document.getElementById(this._domPrefix+'name').innerHTML = '<del>'+this._name+'</del>';
}

Player.prototype.getMoney = function() {
  return this._money;
}

Player.prototype.adjustMoney = function(delta) {
  this._money = this._money + delta;

  // Update amount shown on board
  var cashDOM = this.getMoneyHTMLElement();
  cashDOM.innerHTML = this._money;
}

Player.prototype.getMoneyHTMLElement = function() {
  return document.getElementById(this._domPrefix+'cash');
}

Player.prototype.getTokenHTMLElement = function() {
  return document.getElementById(this._domPrefix+'token');
}

Player.prototype.alertProperties = function() {
  alert(this._name + ' has '+this._money);
}

Player.prototype.setCard1 = function(card) {
  this._card1 = card;
}

Player.prototype.setCard2 = function(card) {
  this._card2 = card;
}

Player.prototype.getCard1 = function() {
  return this._card1;
}

Player.prototype.getCard2 = function() {
  return this._card2;
}

Player.prototype.isFolded = function() {
  return this._isFolded || this._isOut;
}

Player.prototype.setFolded = function(isFolded) {
  this._isFolded = isFolded;

  // If just folded, toggle visibility of cards so out of game
  if (isFolded) {
    if (this._playerId == 'p1') {
      hidePlayer1Cards();
    } else if (this._playerId == 'p2') {
      hidePlayer2Cards();
    }  else if (this._playerId == 'p3') {
      hidePlayer3Cards();
    }  else if (this._playerId == 'p4') {
      hidePlayer4Cards();
    }  else if (this._playerId == 'human') {
      hideHumanCards();
    }  else {
      alertBug("Unrecognized id "+this._playerId+" in setFolded");
    } 
  }
}

/********************************************************
 * Class for player's best hand for comparison
 *
 * Parameters:
 *    handType: STRAIGHT_FLUSH, FOUR_OF_A_KING, etc.
 *    cards: Array of (up to) five relevant cards making
 *    up hand
 ********************************************************/
function BestHand(handType, cards) {
  this._handType = handType;

  // Note that the cards are sorted
  this._cards = sortCards(cards);
}

BestHand.prototype._handType;
BestHand.prototype._cards;

/**
 *
 */
BestHand.prototype.getHandType = function() {
  return this._handType;
}

/**
 *
 */
BestHand.prototype.getCards = function() {
  return this._cards;
}

/**
 * Compares this hand with another.
 *
 * Parameter:
 *    otherHand - A different BestHand object
 *
 * Returns:
 *    1 if this is greater
 *    0 if equal
 *    -1 if other hand is greater
 */
BestHand.prototype.compareTo = function(otherHand) {

  // Check to see whether not same type
  if (otherHand.getHandType() > this.getHandType()) {
    return -1;
  }
  else if (this.getHandType() > otherHand.getHandType()) {
    return 1;
  }

  // If get here, must be same type. 
  var isStraightFlush = this.getHandType() == STRAIGHT_FLUSH;
  var isFourOfAKind   = this.getHandType() == FOUR_OF_A_KIND;
  var isFullHouse     = this.getHandType() == FULL_HOUSE;
  var isFlush         = this.getHandType() == FLUSH;
  var isStraight      = this.getHandType() == STRAIGHT;
  var isThreeOfAKind  = this.getHandType() == THREE_OF_A_KIND;
  var isTwoPair       = this.getHandType() == TWO_PAIR;
  var isPair          = this.getHandType() == PAIR;
  var isHighCard      = this.getHandType() == HIGH_CARD;

  // Each player MUST have the same number of cards in hands
  var cardsInEachHand = this.getCards().length;

  // Assert same number of cards for both hands
  if (this.getCards().length != otherHand.getCards().length) {
    alertBug("Unequal number of cards in two hands: "+this.getCards().length+" vs "+otherHand.getCards().length);
  }

  // Assert has maximum of five cards
  if (cardsInEachHand > 5) {
    alertBug("Should not have more than five cards (has "+cardsInEachHand+").");
  }

  // Assert has minimum of two cards
  if (cardsInEachHand < 2) {
    alertBug("Should not have less than two cards (has "+cardsInEachHand+").");
  }

  // ----------------------------------------------------------------
  // The easy ones: if flush or straight (or both), rank cards
  // and start checking highest first...
  // ----------------------------------------------------------------
  if (isStraightFlush || isFlush || isStraight) {
    
    // Count cards and assert makes sense
    if (cardsInEachHand != 5) {
      alertBug("Has a straight and/or flush <type="+this.getHandType()+">, but not five cards (only "+cardsInEachHand+").");
    }

    // Cards are in order. Check for highest card
    for (var i=4; i>=0; i--) {
      if (this.getCards()[i].getValue() > otherHand.getCards()[i].getValue()) {
        return 1;
      } else if (otherHand.getCards()[i].getValue() > this.getCards()[i].getValue()) {
        return -1;
      }
    }

    // All the same! 
    return 0;
  }

  // Yuck, do a count of all possible cards. Need for all
  // remaining hands.
  // 2=2, 3-3, ..., Ace = 14
  // E.g., if array[14] == 3 and array[2] == 7,
  // then Full house 3xAce, 2x7's
  // Indices 0 and 1 not used.
  var thisAllPossibleCardsCount = new Array(15);
  var otherAllPosslbeCardsCount = new Array(15);

  // Initialize
  for (var i=2; i<=14; i++) {
    thisAllPossibleCardsCount[i] = 0;
    otherAllPosslbeCardsCount[i] = 0;
  }

  // Perform counts
  for (var i=0; i<cardsInEachHand; i++) {
    thisAllPossibleCardsCount[this.getCards()[i].getValue()]++;
    otherAllPosslbeCardsCount[otherHand.getCards()[i].getValue()]++;
  }

  // Perform reasonable assertions
  var thisCount = 0;
  for (var i=2; i<thisAllPossibleCardsCount.length; i++) {
    thisCount += thisAllPossibleCardsCount[i];
  }

  if (thisCount != cardsInEachHand) {
    alertBug("thisCount<"+thisCount+"> != cardsInEachHand<"+cardsInEachHand+">");
  }

  var otherCount = 0;
  for (var i=2; i<otherAllPosslbeCardsCount.length; i++) {
    otherCount += otherAllPosslbeCardsCount[i];
  } 

  if (otherCount != cardsInEachHand) {
    alertBug("otherCount<"+otherCount+"> != cardsInEachHand<"+cardsInEachHand+">");
  }

  // ----------------------------------------------------------------
  // If four-of-a-kind, check quadruple first, then kicker
  // ----------------------------------------------------------------
  if (isFourOfAKind) {

    // Count cards and assert makes sense
    if (cardsInEachHand < 4) {
      alertBug("Has four of a kind, but less than four cards (only "+cardsInEachHand+").");
    }

    // Identify the quadrules and kickers
    var thisQuadruple = 0;
    var otherQuadruple = 0;
    var thisKicker = 0;
    var otherKicker = 0;

    // Iterate all the cards counts (2..Ace) to see what user has
    for (var i=2; i<=14; i++) {

      // This hand
      if (thisAllPossibleCardsCount[i] == 1) {
        if (thisKicker != 0) {
          alertBug("thisKicker<"+thisKicker+"> already defined for four-of-a-kind.");
        }
        thisKicker = i;
      } else if (thisAllPossibleCardsCount[i] == 4) {
        if (thisQuadruple != 0) {
          alertBug("thisKicker<"+thisQuadruple+"> already defined for four-of-a-kind");
        }
        thisQuadruple = i;
      } else if (thisAllPossibleCardsCount[i] != 0) {
        alertBug("Unexpected count for this hand, four-of-a-kind: "+thisAllPossibleCardsCount[i]);
      }

      // The other hand
      if (otherAllPosslbeCardsCount[i] == 1) {
        if (otherKicker != 0) {
          alertBug("otherKicker<"+otherKicker+"> already defined for four-of-a-kind");
        }
        otherKicker = i;
      } else if (otherAllPosslbeCardsCount[i] == 4) {
        if (otherQuadruple != 0) {
          alertBug("otherQuadruple<"+otherQuadruple+"> already defined for four-of-a-kind");
        }
        otherQuadruple = i;
      } else if (otherAllPosslbeCardsCount[i] != 0) {
        alertBug("Unexpected count for other hand, four-of-a-kind: "+otherAllPosslbeCardsCount[i]);
      }
    }

    if (thisQuadruple > otherQuadruple) {
      return 1;
    } else if (otherQuadruple > thisQuadruple) {
      return -1;
    }

    // Is there a kicker?
    if (cardsInEachHand == 5) {
      if (thisKicker > otherKicker) {
        return 1;
      } else if (otherKicker > thisKicker) {
        return -1;
      }
    }
    
    // They are equal
    return 0;
  }

  // ----------------------------------------------------------------
  // If full house, three-of-a-kind first, pair last, or kicker,
  // depending on number of cards in hand
  // ----------------------------------------------------------------
  if (isFullHouse) {

    // Count cards and assert makes sense
    if (cardsInEachHand != 5) {
      alertBug("Has full house, but not exactly five cards (instead, has"+cardsInEachHand+").");
    }
    
    // Identify the tripple and pair
    var thisThreeOfAKind = 0;
    var otherThreeOfAKind = 0;
    var thisPair = 0;
    var otherPair = 0;

    // Iterate all the cards counts (2..Ace) to see what user has
    for (var i=2; i<=14; i++) {

      // This hand
      if (thisAllPossibleCardsCount[i] == 2) {
        if (thisPair != 0) {
          alertBug("thisPair<"+thisPair+"> already defined for full house.");
        }
        thisPair = i;
      } else if (thisAllPossibleCardsCount[i] == 3) {
        if (thisThreeOfAKind != 0) {
          alertBug("thisThreeOfAKind<"+thisThreeOfAKind+"> already defined for full house.");
        }
        thisThreeOfAKind = i;
      } else if (thisAllPossibleCardsCount[i] != 0) {
        alertBug("Unexpected count for this hand, full house: "+thisAllPossibleCardsCount[i]);
      }

      // The other hand
      if (otherAllPosslbeCardsCount[i] == 2) {
        if (otherPair != 0) {
          alertBug("otherPair<"+otherPair+"> already defined for full house.");
        }
        otherPair = i;
      } else if (otherAllPosslbeCardsCount[i] == 3) {
        if (otherThreeOfAKind != 0) {
          alertBug("otherThreeOfAKind<"+otherThreeOfAKind+"> already defined for full house.");
        }
        otherThreeOfAKind = i;
      } else if (otherAllPosslbeCardsCount[i] != 0) {
        alertBug("Unexpected count for other hand, full house: "+otherAllPosslbeCardsCount[i]);
      }
    }

    if (thisThreeOfAKind > otherThreeOfAKind) {
      return 1;
    } else if (otherThreeOfAKind > thisThreeOfAKind) {
      return -1;
    }

    // Is there a kicker?
    if (thisPair > otherPair) {
      return 1;
    } else if (otherPair > thisPair) {
      return -1;
    }

    // They are equal
    return 0;
  }

  // ----------------------------------------------------------------
  // If three-of-a-kind, tripple first, then pair/kick (if any)
  // ----------------------------------------------------------------
  if (isThreeOfAKind) {
    // Count cards and assert makes sense
    if (cardsInEachHand < 3) {
      alertBug("Has three of a kind, but has less than three cards (has "+cardsInEachHand+").");
    }

    // Identify the tripple and pair
    var thisThreeOfAKind = 0;
    var otherThreeOfAKind = 0;
    var thisKicker1 = 0;
    var thisKicker2 = 0;
    var otherKicker1 = 0;
    var otherKicker2 = 0;

    // Iterate all the cards counts (2..Ace) to see what user has
    for (var i=2; i<=14; i++) {

      // This hand
      if (thisAllPossibleCardsCount[i] == 1) {
        if (thisKicker1 != 0 && thisKicker2 != 0) {
          alertBug("Both kickers already defined for three-of-a-kind, this hand.");
        }

        // If first kicker not used yet, define it. Else define second
        if (thisKicker1 == 0) {
          thisKicker1 = i;
        } else {
          thisKicker2 = i;
        }
      } else if (thisAllPossibleCardsCount[i] == 3) {
        if (thisThreeOfAKind != 0) {
          alertBug("thisThreeOfAKind<"+thisThreeOfAKind+"> already defined for three-of-a-kind.");
        }
        thisThreeOfAKind = i;
      } else if (thisAllPossibleCardsCount[i] != 0) {
        alertBug("Unexpected count for this hand, full house: "+thisAllPossibleCardsCount[i]);
      }

      // The other hand
      if (otherAllPosslbeCardsCount[i] == 1) {
        if (otherKicker1 != 0 && otherKicker2 != 0) {
          alertBug("Both kickers already defined for three-of-a-kind, other hand.");
        }

        // If first kicker not used yet, define it. Else define second
        if (otherKicker1 == 0) {
          otherKicker1 = i;
        } else {
          otherKicker2 = i;
        }
      } else if (otherAllPosslbeCardsCount[i] == 3) {
        if (otherThreeOfAKind != 0) {
          alertBug("otherThreeOfAKind<"+otherThreeOfAKind+"> already defined for three-of-a-kind.");
        }
        otherThreeOfAKind = i;
      } else if (otherAllPosslbeCardsCount[i] != 0) {
        alertBug("Unexpected count for other hand, full house: "+otherAllPosslbeCardsCount[i]);
      }
    }

    if (thisThreeOfAKind > otherThreeOfAKind) {
      return 1;
    } else if (otherThreeOfAKind > thisThreeOfAKind) {
      return -1;
    }

    // Order both players kickers so that the first is larger. This simplifies
    // logic for number of cards in each hand
    if (thisKicker2 > thisKicker1) {
      var tmp = thisKicker1;
      thisKicker1 = thisKicker2;
      thisKicker2 = tmp;
    }

    if (otherKicker2 > otherKicker1) {
      var tmp = otherKicker1;
      otherKicker1 = otherKicker2;
      otherKicker2 = tmp;
    }

    // Is there a kicker?
    if (cardsInEachHand >= 4) {
      if (thisKicker1 > otherKicker1) {
        return 1;
      } else if (otherKicker1 > thisKicker1) {
        return -1;
      }
    }

    // Is there another kicker?
    if (cardsInEachHand == 5) {
      if (thisKicker2 > otherKicker2) {
        return 1;
      } else if (otherKicker2 > thisKicker2) {
        return -1;
      }
    }

    // The hands are equal
    return 0;
  }

  // ----------------------------------------------------------------
  // If two pair, high pair first, low pair, and kicker (if one)
  // ----------------------------------------------------------------
  if (isTwoPair) {
    // Count cards and assert makes sense
    if (cardsInEachHand < 4) {
      alertBug("Has two pair, but has less than four cards (has "+cardsInEachHand+").");
    }

    // Identify the high pair, low pair and kicker (if one)
    var thisHighPair = 0;
    var otherHighPair = 0;
    var thisLowPair = 0;
    var otherLowPair = 0;
    var thisKicker = 0;
    var otherKicker = 0;

    // Iterate all the cards counts (2..Ace) to see what user has
    for (var i=2; i<=14; i++) {

      // This hand
      if (thisAllPossibleCardsCount[i] == 2) {

        // If found third pair...
        if (thisHighPair != 0 && thisLowPair != 0) {
          //alertBug("Both pairs already defined for two pair, this hand.");
          // Order two existing pair
          if (thisHighPair < thisLowPair) {
            var tmp = thisHighPair;
            thisHighPair = thisLowPair;
            thisLowPair = tmp;
          }
          // If this is higher than high pair, replace and
          // knock high pair down to low pair
          if (thisHighPair < i) {
            thisLowPair = thisHighPair;
            thisHighPair = i;
          }
          // If this is higher than low pair, simply replace
          else if (thisLowPair < i) {
            thisLowPair = i;
          }
        } 

        // At least one of the pair is undefined
        else {
          // If first hand not used yet, define it. Else define second.
          // Will swap hands to reflect higher/lower later
          if (thisHighPair == 0) {
            thisHighPair = i;
          } else {
            thisLowPair = i;
          }
        }
      } else if (thisAllPossibleCardsCount[i] == 1) {
        if (thisKicker != 0) {
          alertBug("thisKicker<"+thisKicker+"> already defined for two pair.");
        }
        thisKicker = i;
      } else if (thisAllPossibleCardsCount[i] != 0) {
        alertBug("Unexpected count for this hand, two pair: "+thisAllPossibleCardsCount[i]);
      }

      // The other hand
      if (otherAllPosslbeCardsCount[i] == 2) {

        // If found third pair...
        if (otherHighPair != 0 && otherLowPair != 0) {
          //alertBug("Both pairs already defined for two pair, other hand.");
          // Order two existing pair
          if (otherHighPair < otherLowPair) {
            var tmp = otherHighPair;
            otherHighPair = otherLowPair;
            otherLowPair = tmp;
          }

          // If this is higher than high pair, replace and
          // knock high pair down to low pair
          if (otherHighPair < i) {
            otherLowPair = otherHighPair;
            otherHighPair = i;
          }
          // If this is higher than low pair, simply replace
          else if (otherLowPair < i) {
            otherLowPair = i;
          }
        } 

        // At least one of the pair is undefined
        else {
          // If first hand not used yet, define it. Else define second.
          // Will swap hands to reflect higher/lower later
          if (otherHighPair == 0) {
            otherHighPair = i;
          } else {
            otherLowPair = i;
          }
        }
      } else if (otherAllPosslbeCardsCount[i] == 1) {
        if (otherKicker != 0) {
          alertBug("otherKicker<"+otherKicker+"> already defined for two pair.");
        }
        otherKicker = i;
      } else if (otherAllPosslbeCardsCount[i] != 0) {
        alertBug("Unexpected count for other hand, two pair: "+otherAllPosslbeCardsCount[i]);
      }
    }

    // If pairs do not reflect correct ordering, swap
    if (thisHighPair < thisLowPair) {
      var tmp = thisHighPair;
      thisHighPair = thisLowPair;
      thisLowPair = tmp;
    }
 
    if (otherHighPair < otherLowPair) {
      var tmp = otherHighPair;
      otherHighPair = otherLowPair;
      otherLowPair = tmp;
    }

    if (thisHighPair > otherHighPair) {
      return 1;
    } else if (otherHighPair > thisHighPair) {
      return -1;
    }

    if (thisLowPair > otherLowPair) {
      return 1;
    } else if (otherLowPair > thisLowPair) {
      return -1;
    }

    if (cardsInEachHand == 5) {
      if (thisKicker > otherKicker) {
        return 1;
      } else if (otherKicker > thisKicker) {
        return -1;
      }
    }

    // They are equal
    return 0;
  }

  // ----------------------------------------------------------------
  // If pair, pair first, then order of remaining cards
  // ----------------------------------------------------------------
  if (isPair) {
     var thisPair = 0;
     var otherPair = 0;

     // Used for assertions
     var thisKickerCount = 0;
     var otherKickerCount = 0;
     
     // Iterate all the cards counts (2..Ace) to see what user has
     for (var i=2; i<=14; i++) {
       // This hand
       if (thisAllPossibleCardsCount[i] == 2) {
         if (thisPair != 0) {
           alertBug("thisPair<"+thisPair+"> already defined for pair.");
         }

         thisPair = i;
       } else if (thisAllPossibleCardsCount[i] == 1) {
         thisKickerCount++;
       } else if (thisAllPossibleCardsCount[i] != 0) {
         alertBug("Unexpected count for this hand, pair: "+thisAllPossibleCardsCount[i]);
       }

      // The other hand
       if (otherAllPosslbeCardsCount[i] == 2) {
         if (otherPair != 0) {
           alertBug("otherPair<"+otherPair+"> already defined for pair, using hand: "+getHandString(otherHand.getCards()));
         }

         otherPair = i;
       } else if (otherAllPosslbeCardsCount[i] == 1) {
         otherKickerCount++;
       } else if (otherAllPosslbeCardsCount[i] != 0) {
         alertBug("Unexpected count for other hand, pair: "+otherAllPosslbeCardsCount[i]);
       }
     }
     
     // Assert correct number of kickers
     if (thisKickerCount != cardsInEachHand - 2) {
       alertBug("thisKickerCount<"+thisKickerCount+"> != cardsInEachHand<"+cardsInEachHand+"> - 2");
     }
     if (otherKickerCount != cardsInEachHand - 2) {
       alertBug("otherKickerCount<"+otherKickerCount+"> != cardsInEachHand<"+cardsInEachHand+"> - 2");
     }

     if (thisPair > otherPair) {
       return 1;
     } else if (otherPair > thisPair) {
       return -1;
     }

     // Fall back on kickers. First hand with a higher kicker wins.
     for (var i=14; i>=2; i--) {
       if (thisAllPossibleCardsCount[i] == 1 && otherAllPosslbeCardsCount[i] == 0) {
         return 1;
       } else if (otherAllPosslbeCardsCount[i] == 1 && thisAllPossibleCardsCount[i] == 0) {
         return -1;
       }
     }

     // They are equal
     return 0;
  }

  // ----------------------------------------------------------------
  // Order of remaining cards
  // ----------------------------------------------------------------
  if (isHighCard) {

     // First hand with higher cards wins
     for (var i=14; i>=2; i--) {
       if (thisAllPossibleCardsCount[i] == 1 && otherAllPosslbeCardsCount[i] == 0) {
         return 1;
       } else if (otherAllPosslbeCardsCount[i] == 1 && thisAllPossibleCardsCount[i] == 0) {
         return -1;
       }
     }

     // They are equal
     return 0;
  }

  // If get here, there's a bug
  alertBug("Unknown hand type: "+this.getHandType());

} // compareTo(otherHand)

/********************************************************
 * Load all initial properties of game
 ********************************************************/
window.onload = function () {
  createUsers();

  // Start the first hand
  playHandAtCurrentTable();
}

/********************************************************
 * Determines who has the small blind and, subsequently,
 * big blind and better.
 *
 * 0: Player 1
 * 1: Player 2
 * 2: Human
 * 3: Player 3
 * 4: Player 4
 ********************************************************/
var smallBlindPos = 4;

function getPlayerByPosition(pos) {
  if (pos == 0) {
    return p1;
  } else if (pos == 1) {
    return p2;
  } else if (pos == 2) {
    return human;
  } else if (pos == 3) {
    return p3;
  } else if (pos == 4) {
    return p4;
  } else {
    alertBug("Unrecognized user position: "+pos);
  } 
}

function getPositionByPlayer(player) {
  if (player == p1) {
    return 0;
  } else if (player == p2) {
    return 1;
  } else if (player == p3) {
    return 2;
  } else if (player == p4) {
    return 3;
  } else if (player == human) {
    return 4;
  } else {
    alertBug("Unrecognized player: "+player+" in getPositionByPlayer");
  }
}

function getOnlyPlayerRemainingAtTable() {
  
  var remainingPlayers = getNumberPlayersRemainingAtTable();

  if (remainingPlayers > 1) {
    return false;
  } else if (remainingPlayers == 0) {
    alertBug("remainingPlayers is 0, in isOnlyOnePlayerRemainingAtTable");
    return false;
  }

  var remainingPlayer = false;

  if (!p1.isOut()) {
    remainingPlayer = p1;
  }
  else if (!p2.isOut()) {
    remainingPlayer = p2;
  }
  else if (!p3.isOut()) {
    remainingPlayer = p3;
  }
  else if (!p4.isOut()) {
    remainingPlayer = p4;
  }
  else if (!human.isOut()) {
    remainingPlayer = human;
  } else {
    alertBug('Should be 1 remaining player, but cannot find one in isOnlyOnePlayerRemainingAtTable');
    return false;
  }

  return remainingPlayer;
}

function getNumberPlayersRemainingAtTable() {
  var remainingPlayers = 0;
  if (!p1.isOut()) {
    remainingPlayers++;
  }
  if (!p2.isOut()) {
    remainingPlayers++;
  }
  if (!p3.isOut()) {
    remainingPlayers++;
  }
  if (!p4.isOut()) {
    remainingPlayers++;
  }
  if (!human.isOut()) {
    remainingPlayers++;
  }
  return remainingPlayers;
}

/********************************************************
 * Handles the flow of the hand
 ********************************************************/
function playHandAtCurrentTable() {
  
  // Variable used to help AI players bet consistently 
  // (E.g., not fold after re-raising)
  currentTurn = -1;

  clearPot();
  clearConsole();
  clearControlConsole();

  // Start hand
  writeToConsole('Dealing cards...');
  dealCards();
  setSmallAndBigBlind();
  
  // Start the betting... From here out, there is a
  // chain of commands that are triggered by user decisions.
  startPreFlop();
}

function incrementSmallBlindPlayer() {
  // Update the small blind position. The modulus restarts if greater than pos 4
  smallBlindPos = (smallBlindPos + 1) % 5;

  var smallBlindPlayer = getPlayerByPosition(smallBlindPos);

  var count = 0;

  // Keep trying until get small blind player
  while (smallBlindPlayer.isOut()) {
    smallBlindPos = (smallBlindPos + 1) % 5;
    smallBlindPlayer = getPlayerByPosition(smallBlindPos);

    // Prevent a bug from causing an infinite loop
    count++;
    if (count >= 100) {
      alertBug("Loop searching for small blind player infinite, in incrementSmallBlindPlayer");
      return;
    }
  }
}

function getSmallBlindPlayer() {
  // Have to use temp variables. This method invoked multiple
  // places, and don't want to change the value of the pos
  var tmpPos = smallBlindPos;

  var smallBlindPlayer = getPlayerByPosition(tmpPos);

  var count = 0;

  // Keep trying until get small blind player
  while (smallBlindPlayer.isOut()) {
    tmpPos = (tmpPos + 1) % 5;
    smallBlindPlayer = getPlayerByPosition(tmpPos);

    // Prevent a bug from causing an infinite loop
    count++;
    if (count >= 100) {
      alertBug("Loop searching for small blind player infinite, in getSmallBlindPlayer");
      return;
    }
  }

  return smallBlindPlayer;
}

function getBigBlindPosition() {
  var count = 0;

  var bigBlindPos = (smallBlindPos + 1) % 5;

  var bigBlindPlayer = getPlayerByPosition(bigBlindPos);

  // Keep trying until get big blind player
  while (bigBlindPlayer.isOut()) {
    bigBlindPos = (bigBlindPos + 1) % 5;
    bigBlindPlayer = getPlayerByPosition(bigBlindPos);

    // Prevent a bug from causing an infinite loop
    count++;
    if (count >= 100) {
      alertBug("Loop searching for big blind player infinite, in getBigBlindPlayer");
      return;
    }
  }
  return bigBlindPos;
}

function getBigBlindPlayer() {
  return getPlayerByPosition(getBigBlindPosition());
}

/********************************************************
 * Keeps track of the small and big blind and the person
 * who opens the betting.
 *
 * Positions are inferred from small blind, clockwise:
 *  - Small blind
 *  - Big blind
 *  - Better
 ********************************************************/
function setSmallAndBigBlind() {

  // Note that we incremented the small blind player at the end of the last hand. Now we're just setting details
  // with regard to this hand
  var smallBlindPlayer = getSmallBlindPlayer();

  // Assert has enough money
  if (smallBlindPlayer.getMoney() < LOW_LIMIT) {
    alertBug(smallBlindPlayer.getName()+' is the small blind player and has '+smallBlindPlayer.getMoney()+', which is less than '+LOW_LIMIT);
    return;
  }

  if (smallBlindPlayer != human) {
    writeToConsole(smallBlindPlayer.getName() + ' pays small blind ($'+LOW_LIMIT+')  <img src="images/small_blind.gif" />');
  } else {
    writeToConsole('You pay small blind ($'+LOW_LIMIT+')  <img src="images/small_blind.gif" />');
  }
  smallBlindPlayer.adjustMoney(-LOW_LIMIT);
  adjustPot(LOW_LIMIT);

  // Get big blind player. Again, skips players who are out
  var bigBlindPos = getBigBlindPosition();
  var bigBlindPlayer = getBigBlindPlayer();

  // Assert has enough money
  if (bigBlindPlayer.getMoney() < HIGH_LIMIT) {
    alertBug(bigBlindPlayer.getName()+' is the big blind player and has '+bigBlindPlayer.getMoney()+', which is less than '+HIGH_LIMIT);
    return;
  }

  if (bigBlindPlayer != human) {
    writeToConsole(bigBlindPlayer.getName() + ' pays big blind ($'+HIGH_LIMIT+') <img src="images/big_blind.gif" />');
  } else {
    writeToConsole('You pay big blind ($'+HIGH_LIMIT+')  <img src="images/big_blind.gif" />');
  }

  bigBlindPlayer.adjustMoney(-HIGH_LIMIT);
  adjustPot(HIGH_LIMIT);

  // Only set the leading better token if more than two remaining players
  if (getNumberPlayersRemainingAtTable() > 2) {
    var firstPos = (bigBlindPos + 1) % 5;
    var firstPlayer = getPlayerByPosition(firstPos);
    var count = 0;

    // Keep trying until get leading better
    while (firstPlayer.isOut()) {
      firstPos = (firstPos + 1) % 5;
      firstPlayer = getPlayerByPosition(firstPos);

      // Prevent a bug from causing an infinite loop
      count++;
      if (count >= 100) {
        alertBug("Loop searching for leading better infinite, in setSmallAndBigBlind");
        return;
      }
    }

    if (firstPos != 2) {
      writeToConsole(firstPlayer.getName() + ' leads the betting <img src="images/first.gif" />');
    } else {
      writeToConsole('You lead the betting  <img src="images/first.gif" />');
    }

    // Now set the markers (or "tokens") for the users. If not
    // big blind nor small blind, 13x13 spacer
    for (var i=0; i<5; i++) {
      if (i == smallBlindPos) {
        getPlayerByPosition(i).getTokenHTMLElement().src = 'images/small_blind.gif';
      } else if (i == bigBlindPos) {
        getPlayerByPosition(i).getTokenHTMLElement().src = 'images/big_blind.gif';
      } else if (i == firstPos) {
        getPlayerByPosition(i).getTokenHTMLElement().src = 'images/first.gif';
      } else {
        getPlayerByPosition(i).getTokenHTMLElement().src = 'images/spacer_17_17.gif';
      }
    }
  }
}

/********************************************************
 * Create all the users for the table
 ********************************************************/
function createUsers() {

  // Grab random names
  var p1NameIndex = Math.floor(Math.random()*namePool.length);
  var p2NameIndex = Math.floor(Math.random()*namePool.length);
  var p3NameIndex = Math.floor(Math.random()*namePool.length);
  var p4NameIndex = Math.floor(Math.random()*namePool.length);

  // In order, get rid of any redundancies
  while (p2NameIndex == p1NameIndex) {
    p2NameIndex = Math.floor(Math.random()*namePool.length);
  }
  while (p3NameIndex == p1NameIndex || p3NameIndex == p2NameIndex) {
    p3NameIndex = Math.floor(Math.random()*namePool.length);
  }
  while (p4NameIndex == p1NameIndex || p4NameIndex == p2NameIndex || p4NameIndex == p3NameIndex) {
    p4NameIndex = Math.floor(Math.random()*namePool.length);
  }

  // Create the players and set them at the table
  p1 = new Player(namePool[p1NameIndex], 10000, "player1_", 'p1');
  document.getElementById('player1_name').innerHTML = p1.getName();
  document.getElementById('player1_cash').innerHTML = p1.getMoney();
  
  p2 = new Player(namePool[p2NameIndex], 10000, "player2_", 'p2');
  document.getElementById('player2_name').innerHTML = p2.getName();
  document.getElementById('player2_cash').innerHTML = p2.getMoney();
  
  p3 = new Player(namePool[p3NameIndex], 10000, "player3_", 'p3');
  document.getElementById('player3_name').innerHTML = p3.getName();
  document.getElementById('player3_cash').innerHTML = p3.getMoney();
  
  p4 = new Player(namePool[p4NameIndex], 10000, "player4_", 'p4');
  document.getElementById('player4_name').innerHTML = p4.getName();
  document.getElementById('player4_cash').innerHTML = p4.getMoney();
  
  human = new Player("You", 10000, "human_", 'human');
  document.getElementById('human_name').innerHTML = human.getName();
  document.getElementById('human_cash').innerHTML = human.getMoney();
}

/********************************************************
 * Deal cards.
 ********************************************************/
function dealCards() {

  // Set all users to not be folded (unless they are out)
  p1.setFolded(p1.isOut());
  p1.clearCallAmount();
  p2.setFolded(p2.isOut());
  p2.clearCallAmount();
  p3.setFolded(p3.isOut());
  p3.clearCallAmount();
  p4.setFolded(p4.isOut());
  p4.clearCallAmount();
  human.setFolded(human.isOut());
  human.clearCallAmount();

  // New hand, put card faces down. 
  // (Even though not dealt yet, just show backs of cards.)
  setAllCardsFaceDown();

  // Create the deck. Note a card is not available if it is set to 0
  var deck = createDeck();

  // Need five cards for the board 
  dealer = new Array(5);
  dealer[0] = 0;
  dealer[1] = 0;
  dealer[2] = 0;
  dealer[3] = 0;
  dealer[4] = 0;

  while (dealer[0] == 0) {
    var index = Math.floor(Math.random()*deck.length);
    dealer[0] = deck[index];
    deck[index] = 0;
  }

  while (dealer[1] == 0) {
    var index = Math.floor(Math.random()*deck.length);
    dealer[1] = deck[index];
    deck[index] = 0;
  }

  while (dealer[2] == 0) {
    var index = Math.floor(Math.random()*deck.length);
    dealer[2] = deck[index];
    deck[index] = 0;
  }

  while (dealer[3] == 0) {
    var index = Math.floor(Math.random()*deck.length);
    dealer[3] = deck[index];
    deck[index] = 0;
  }

  while (dealer[4] == 0) {
    var index = Math.floor(Math.random()*deck.length);
    dealer[4] = deck[index];
    deck[index] = 0;
  }

  // Order doesn't matter. Deal everyone two of remaining cards
  p1.setCard1(0);
  p1.setCard2(0);
  
  while (p1.getCard1() == 0) {
    var index = Math.floor(Math.random()*deck.length);
    p1.setCard1(deck[index]);
    deck[index] = 0;
  }

  while (p1.getCard2() == 0) {
    var index = Math.floor(Math.random()*deck.length);
    p1.setCard2(deck[index]);
    deck[index] = 0;
  }

  p2.setCard1(0);
  p2.setCard2(0);
  
  while (p2.getCard1() == 0) {
    var index = Math.floor(Math.random()*deck.length);
    p2.setCard1(deck[index]);
    deck[index] = 0;
  }

  while (p2.getCard2() == 0) {
    var index = Math.floor(Math.random()*deck.length);
    p2.setCard2(deck[index]);
    deck[index] = 0;
  }

  p3.setCard1(0);
  p3.setCard2(0);
  
  while (p3.getCard1() == 0) {
    var index = Math.floor(Math.random()*deck.length);
    p3.setCard1(deck[index]);
    deck[index] = 0;
  }

  while (p3.getCard2() == 0) {
    var index = Math.floor(Math.random()*deck.length);
    p3.setCard2(deck[index]);
    deck[index] = 0;
  }

  p4.setCard1(0);
  p4.setCard2(0);
  
  while (p4.getCard1() == 0) {
    var index = Math.floor(Math.random()*deck.length);
    p4.setCard1(deck[index]);
    deck[index] = 0;
  }

  while (p4.getCard2() == 0) {
    var index = Math.floor(Math.random()*deck.length);
    p4.setCard2(deck[index]);
    deck[index] = 0;
  }
 
  human.setCard1(0);
  human.setCard2(0);
  
  while (human.getCard1() == 0) {
    var index = Math.floor(Math.random()*deck.length);
    human.setCard1(deck[index]);
    deck[index] = 0;
  }

  while (human.getCard2() == 0) {
    var index = Math.floor(Math.random()*deck.length);
    human.setCard2(deck[index]);
    deck[index] = 0;
  }

  // Show human's cards so knows hand
  if (!human.isOut()) {
    flipHumanCards();
  }
} // dealCards

/********************************************************
 * Simple utility method to see whether only one player
 *   remaining.
 ********************************************************/
function isOnlyOnePlayerRemaining() {
  var count = 0;

  if (!p1.isFolded()) {
    count++;
  }
  if (!p2.isFolded()) {
    count++;
  }
  if (!p3.isFolded()) {
    count++;
  }
  if (!p4.isFolded()) {
    count++;
  }
  if (!human.isFolded()) {
    count++;
  }

  if (count == 0) {
    alertBug("Should never be no one in game, yet there is! In isOnlyOnePlayerRemaining");
  }

  if (count == 1) {
    return true;
  }

  return false;
}

/********************************************************
 * Simple way to fold from an event 
 ********************************************************/
function humanFolded() {
  playerFolds(human);
}
function humanCalls() {
  playerCalls(human);
}
function humanBets(bet) {
  playerBets(human,bet);
}

function isStillBetting() {
  if (!p1.isFolded() && p1.getCallAmount() > 0) {
    return true;
  } 
  if (!p2.isFolded() && p2.getCallAmount() > 0) {
    return true;
  } 
  if (!p3.isFolded() && p3.getCallAmount() > 0) {
    return true;
  } 
  if (!p4.isFolded() && p4.getCallAmount() > 0) {
    return true;
  } 
  if (!human.isFolded() && human.getCallAmount() > 0) {
    return true;
  } 
  return false;
}

/********************************************************
 * PART OF HAND FLOW:
 ********************************************************/
function startPreFlop() {
  startPreFlop(false);
}

function startPreFlop(isContinuedBetting) {

  if (!isContinuedBetting) {
    writeToConsole('');
    writeToConsole('Finished dealing cards. Pre-flop betting begins.');
  }

  // The person who opens up is always after the big blind, which
  // is after the small blind
  var startPlayerIndex = (smallBlindPos + 2) % 5;

  // The human player is always index #2
  var humanIndex = 2;

  var nextPlayerIndex = startPlayerIndex;

  // Players bet until you
  while (nextPlayerIndex != humanIndex) {
    // AI opponent goes
    var nextPlayer = getPlayerByPosition(nextPlayerIndex);

    // If betting going around, verify that betting hasn't finished going
    // around (nothing to call for this user)
    if (isContinuedBetting) {
      // Check to see whether only one player remaining
      if (isOnlyOnePlayerRemaining()) {
        handOver();
        return;
      }
      if (!nextPlayer.isFolded() && nextPlayer.getCallAmount() == 0) {
        // Round of betting is over!
        startFlop();
        return;
      }
    }

    // Verify not folded, and let player go otherwise
    if (!nextPlayer.isFolded()) {
      // AI: computer opponent choose what to do
      chooseNextMove(nextPlayer, PREFLOP);
    }

    // Check to see whether only one player remaining
    if (isOnlyOnePlayerRemaining()) {
      handOver();
      return;
    }

    // Update index to next player
    nextPlayerIndex = (nextPlayerIndex+1) % 5;
  }

  // If human not fold, let go!
  if (!human.isFolded()) {

    if (isContinuedBetting && human.getCallAmount() == 0) {
      // Round of betting is over!
      startFlop();
      return;
    }

    // Ask human what want to do
    writeToConsole('<span style="color: #ccc;">You are up</span>');

    var CALL_HTML = false;
    
    // Does human have enough to call?
    if (human.getCallAmount() <= human.getMoney()) {
      var callLabel = "Stay";
      if (human.getCallAmount() > 0) {
        callLabel = "Call $"+human.getCallAmount();
      }
      CALL_HTML = '<a href="" onclick="humanCalls(); resumePreFlop('+isContinuedBetting+'); return false;">'+callLabel+'</a>';
    }

    var requiredBet = getRequiredBet(PREFLOP);
    var totalToRaise = human.getCallAmount() + requiredBet;
    var RAISE_HTML = false;

    // Does human have enough to stay?
    if (totalToRaise <= human.getMoney()) {
      var raiseLabel = "Bet $"+requiredBet;
      
      if (human.getCallAmount() > 0) {
        raiseLabel = "Raise $"+requiredBet+" (Total with call $"+totalToRaise+")";
      }
      RAISE_HTML = '<a href="" onclick="humanBets('+requiredBet+'); resumePreFlop('+isContinuedBetting+'); return false;">'+raiseLabel+'</a>'; 
    }

    // Human can always fold
    var FOLD_HTML = '<a href="" onclick="humanFolded(); resumePreFlop('+isContinuedBetting+'); return false;">Fold</a>';

    var myHTML =  FOLD_HTML;
    if (RAISE_HTML) {
      myHTML = RAISE_HTML + ' ' + myHTML;
    } 
    if (CALL_HTML) {
      myHTML = CALL_HTML + ' ' + myHTML;
    }
    writeToControlConsole(myHTML);
   }
  
  // Human folded. Keep running game automatically.
  else {
    resumePreFlop(isContinuedBetting);
  }
}

/********************************************************
 * PART OF HAND FLOW:
 ********************************************************/
function resumePreFlop(isContinuedBetting) {
  clearControlConsole();

  // Check to see whether human folded and only one 
  // player remaining
  if (isOnlyOnePlayerRemaining()) {
    handOver();
    return;
  }

  // Since human player is index 2, next opponent
  var nextPlayerIndex = 3;

  // Stop when reach the first user who went
  var alreadyWentIndex = (smallBlindPos + 2) % 5;

  // Remaining players bet
  while (nextPlayerIndex != alreadyWentIndex) {
    // AI opponent goes
    var nextPlayer = getPlayerByPosition(nextPlayerIndex);

    // If betting going around, verify that betting hasn't finished going
    // around (nothing to call for this user)
    if (isContinuedBetting) {
      // Check to see whether only one player remaining
      if (isOnlyOnePlayerRemaining()) {
        handOver();
        return;
      }
      if (!nextPlayer.isFolded() && nextPlayer.getCallAmount() == 0) {
        // Round of betting is over!
        startFlop();
        return;
      }
    }

    // Verify not folded, and let player go otherwise
    if (!nextPlayer.isFolded()) {
      // AI: computer opponent choose what to do
      chooseNextMove(nextPlayer, PREFLOP);
    }

    // Check to see whether only one player remaining
    if (isOnlyOnePlayerRemaining()) {
      handOver();
      return;
    }

    // Update index to next player
    nextPlayerIndex = (nextPlayerIndex+1) % 5;
  }

  // See if there is more betting going on
  if (isStillBetting()) {
    startPreFlop(true);
  } else {
    // It's a chain -- start the next turn!
    startFlop();
  }
}

/********************************************************
 * PART OF HAND FLOW:
 ********************************************************/
function startFlop() {
  startFlop(false);
}

function startFlop(isContinuedBetting) {
  flipFlop();

  if (!isContinuedBetting) {
    writeToConsole('');
    writeToConsole('The flop!');
    if (isShowAlertOnFlipCards && !human.isFolded()) {
      alert('The flop!');
    }
  }

  // The person who opens up is always after the big blind, which
  // is after the small blind
  var startPlayerIndex = (smallBlindPos + 2) % 5;

  // The human player is always index #2
  var humanIndex = 2;

  var nextPlayerIndex = startPlayerIndex;

  // Players bet until you
  while (nextPlayerIndex != humanIndex) {
    // AI opponent goes
    var nextPlayer = getPlayerByPosition(nextPlayerIndex);

    // If betting going around, verify that betting hasn't finished going
    // around (nothing to call for this user)
    if (isContinuedBetting) {
      // Check to see whether only one player remaining
      if (isOnlyOnePlayerRemaining()) {
        handOver();
        return;
      }
      if (!nextPlayer.isFolded() && nextPlayer.getCallAmount() == 0) {
        // Round of betting is over!
        startTurn();
        return;
      }
    }
    
    // Verify not folded, and let player go otherwise
    if (!nextPlayer.isFolded()) {
      // AI: computer opponent choose what to do
      chooseNextMove(nextPlayer, FLOP);
    }

    // Check to see whether only one player remaining
    if (isOnlyOnePlayerRemaining()) {
      handOver();
      return;
    }

    // Update index to next player
    nextPlayerIndex = (nextPlayerIndex+1) % 5;
  }

  // If human not fold, let go!
  if (!human.isFolded()) {

    if (isContinuedBetting && human.getCallAmount() == 0) {
      // Round of betting is over!
      startTurn();
      return;
    }

    // Ask human what want to do
    writeToConsole('<span style="color: #ccc;">You are up</span>');

    var CALL_HTML = false;
    
    // Does human have enough to call?
    if (human.getCallAmount() <= human.getMoney()) {
      var callLabel = "Stay";
      if (human.getCallAmount() > 0) {
        callLabel = "Call $"+human.getCallAmount();
      }
      CALL_HTML = '<a href="" onclick="humanCalls(); resumeFlop('+isContinuedBetting+'); return false;">'+callLabel+'</a>';
    }

    var requiredBet = getRequiredBet(FLOP);
    var totalToRaise = human.getCallAmount() + requiredBet;
    var RAISE_HTML = false;

    // Does human have enough to stay?
    if (totalToRaise <= human.getMoney()) {
      var raiseLabel = "Bet $"+requiredBet;
      
      if (human.getCallAmount() > 0) {
        raiseLabel = "Raise $"+requiredBet+" (Total with call $"+totalToRaise+")";
      }
      RAISE_HTML = '<a href="" onclick="humanBets('+requiredBet+'); resumeFlop('+isContinuedBetting+'); return false;">'+raiseLabel+'</a>'; 
    }

    // Human can always fold
    var FOLD_HTML = '<a href="" onclick="humanFolded(); resumeFlop('+isContinuedBetting+'); return false;">Fold</a>';

    var myHTML =  FOLD_HTML;
    if (RAISE_HTML) {
      myHTML = RAISE_HTML + ' ' + myHTML;
    } 
    if (CALL_HTML) {
      myHTML = CALL_HTML + ' ' + myHTML;
    }
    writeToControlConsole(myHTML);
   }
  
  // Human folded. Keep running game automatically.
  else {
    resumeFlop(isContinuedBetting);
  }
}

/********************************************************
 * PART OF HAND FLOW:
 ********************************************************/
function resumeFlop(isContinuedBetting) {
  clearControlConsole();

  // Check to see whether human folded and only one 
  // player remaining
  if (isOnlyOnePlayerRemaining()) {
    handOver();
    return;
  }

  // Since human player is index 2, next opponent
  var nextPlayerIndex = 3;

  // Stop when reach the first user who went
  var alreadyWentIndex = (smallBlindPos + 2) % 5;

  // Remaining players bet
  while (nextPlayerIndex != alreadyWentIndex) {
    // AI opponent goes
    var nextPlayer = getPlayerByPosition(nextPlayerIndex);

    // If betting going around, verify that betting hasn't finished going
    // around (nothing to call for this user)
    if (isContinuedBetting) {
      // Check to see whether only one player remaining
      if (isOnlyOnePlayerRemaining()) {
        handOver();
        return;
      }
      if (!nextPlayer.isFolded() && nextPlayer.getCallAmount() == 0) {
        // Round of betting is over!
        startTurn();
        return;
      }
    }
    
    // Verify not folded, and let player go otherwise
    if (!nextPlayer.isFolded()) {
      // AI: computer opponent choose what to do
      chooseNextMove(nextPlayer, FLOP);
    }

    // Check to see whether only one player remaining
    if (isOnlyOnePlayerRemaining()) {
      handOver();
      return;
    }

    // Update index to next player
    nextPlayerIndex = (nextPlayerIndex+1) % 5;
  }

  // See if there is more betting going on
  if (isStillBetting()) {
    startFlop(true);
  } else {
    // It's a chain -- start the next turn!
    startTurn();
  }
}

/********************************************************
 * PART OF HAND FLOW:
 ********************************************************/
function startTurn() {
  startTurn(false);
}

function startTurn(isContinuedBetting) {
  flipTurn();

  if (!isContinuedBetting) {
    writeToConsole('');
    writeToConsole('The turn!');
    if (isShowAlertOnFlipCards && !human.isFolded()) {
      alert('The turn!');
    }
  }

  // The person who opens up is always after the big blind, which
  // is after the small blind
  var startPlayerIndex = (smallBlindPos + 2) % 5;

  // The human player is always index #2
  var humanIndex = 2;

  var nextPlayerIndex = startPlayerIndex;

  // Players bet until you
  while (nextPlayerIndex != humanIndex) {
    // AI opponent goes
    var nextPlayer = getPlayerByPosition(nextPlayerIndex);

    // If betting going around, verify that betting hasn't finished going
    // around (nothing to call for this user)
    if (isContinuedBetting) {
      // Check to see whether only one player remaining
      if (isOnlyOnePlayerRemaining()) {
        handOver();
        return;
      }
      if (!nextPlayer.isFolded() && nextPlayer.getCallAmount() == 0) {
        // Round of betting is over!
        startRiver();
        return;
      }
    }
    
    // Verify not folded, and let player go otherwise
    if (!nextPlayer.isFolded()) {
      // AI: computer opponent choose what to do
      chooseNextMove(nextPlayer, TURN);
    }

    // Check to see whether only one player remaining
    if (isOnlyOnePlayerRemaining()) {
      handOver();
      return;
    }

    // Update index to next player
    nextPlayerIndex = (nextPlayerIndex+1) % 5;
  }

  // If human not fold, let go!
  if (!human.isFolded()) {

    if (isContinuedBetting && human.getCallAmount() == 0) {
      // Round of betting is over!
      startRiver();
      return;
    }

    // Ask human what want to do
    writeToConsole('<span style="color: #ccc;">You are up</span>');

    var CALL_HTML = false;
    
    // Does human have enough to call?
    if (human.getCallAmount() <= human.getMoney()) {
      var callLabel = "Stay";
      if (human.getCallAmount() > 0) {
        callLabel = "Call $"+human.getCallAmount();
      }
      CALL_HTML = '<a href="" onclick="humanCalls(); resumeTurn('+isContinuedBetting+'); return false;">'+callLabel+'</a>';
    }

    var requiredBet = getRequiredBet(TURN);
    var totalToRaise = human.getCallAmount() + requiredBet;
    var RAISE_HTML = false;

    // Does human have enough to stay?
    if (totalToRaise <= human.getMoney()) {
      var raiseLabel = "Bet $"+requiredBet;
      
      if (human.getCallAmount() > 0) {
        raiseLabel = "Raise $"+requiredBet+" (Total with call $"+totalToRaise+")";
      }
      RAISE_HTML = '<a href="" onclick="humanBets('+requiredBet+'); resumeTurn('+isContinuedBetting+'); return false;">'+raiseLabel+'</a>'; 
    }

    // Human can always fold
    var FOLD_HTML = '<a href="" onclick="humanFolded(); resumeTurn('+isContinuedBetting+'); return false;">Fold</a>';

    var myHTML =  FOLD_HTML;
    if (RAISE_HTML) {
      myHTML = RAISE_HTML + ' ' + myHTML;
    } 
    if (CALL_HTML) {
      myHTML = CALL_HTML + ' ' + myHTML;
    }
    writeToControlConsole(myHTML);
  }
  
  // Human folded. Keep running game automatically.
  else {
    resumeTurn(isContinuedBetting);
  }
}

/********************************************************
 * PART OF HAND FLOW:
 ********************************************************/
function resumeTurn(isContinuedBetting) {
  clearControlConsole();

  // Check to see whether human folded and only one 
  // player remaining
  if (isOnlyOnePlayerRemaining()) {
    handOver();
    return;
  }

  // Since human player is index 2, next opponent
  var nextPlayerIndex = 3;

  // Stop when reach the first user who went
  var alreadyWentIndex = (smallBlindPos + 2) % 5;

  // Remaining players bet
  while (nextPlayerIndex != alreadyWentIndex) {
    // AI opponent goes
    var nextPlayer = getPlayerByPosition(nextPlayerIndex);

    // If betting going around, verify that betting hasn't finished going
    // around (nothing to call for this user)
    if (isContinuedBetting) {
      // Check to see whether only one player remaining
      if (isOnlyOnePlayerRemaining()) {
        handOver();
        return;
      }
      if (!nextPlayer.isFolded() && nextPlayer.getCallAmount() == 0) {
        // Round of betting is over!
        startRiver();
        return;
      }
    }
    
    // Verify not folded, and let player go otherwise
    if (!nextPlayer.isFolded()) {
      // AI: computer opponent choose what to do
      chooseNextMove(nextPlayer, TURN);
    }

    // Check to see whether only one player remaining
    if (isOnlyOnePlayerRemaining()) {
      handOver();
      return;
    }

    // Update index to next player
    nextPlayerIndex = (nextPlayerIndex+1) % 5;
  }

  // See if there is more betting going on
  if (isStillBetting()) {
    startTurn(true);
  } else {
    // It's a chain -- start the next turn!
    startRiver();
  }
}

/********************************************************
 * PART OF HAND FLOW:
 ********************************************************/
function startRiver() {
  startRiver(false);
}

function startRiver(isContinuedBetting) {
  flipRiver();

  if (!isContinuedBetting) {
    writeToConsole('');
    writeToConsole('The river!');
    if (isShowAlertOnFlipCards && !human.isFolded()) {
      alert('The river!');
    }
  }

  // The person who opens up is always after the big blind, which
  // is after the small blind
  var startPlayerIndex = (smallBlindPos + 2) % 5;

  // The human player is always index #2
  var humanIndex = 2;

  var nextPlayerIndex = startPlayerIndex;

  // Players bet until you
  while (nextPlayerIndex != humanIndex) {
    // AI opponent goes
    var nextPlayer = getPlayerByPosition(nextPlayerIndex);

    // If betting going around, verify that betting hasn't finished going
    // around (nothing to call for this user)
    if (isContinuedBetting) {
      // Check to see whether only one player remaining
      if (isOnlyOnePlayerRemaining()) {
        handOver();
        return;
      }
      if (!nextPlayer.isFolded() && nextPlayer.getCallAmount() == 0) {
        // Round of betting is over!
        handOver();
        return;
      }
    }
    
    // Verify not folded, and let player go otherwise
    if (!nextPlayer.isFolded()) {
      // AI: computer opponent choose what to do
      chooseNextMove(nextPlayer, RIVER);
    }

    // Check to see whether only one player remaining
    if (isOnlyOnePlayerRemaining()) {
      handOver();
      return;
    }

    // Update index to next player
    nextPlayerIndex = (nextPlayerIndex+1) % 5;
  }

  // If human not fold, let go!
  if (!human.isFolded()) {

    if (isContinuedBetting && human.getCallAmount() == 0) {
      // Round of betting is over!
      handOver();
      return;
    }

    // Ask human what want to do
    writeToConsole('<span style="color: #ccc;">You are up</span>');

    var CALL_HTML = false;
    
    // Does human have enough to call?
    if (human.getCallAmount() <= human.getMoney()) {
      var callLabel = "Stay";
      if (human.getCallAmount() > 0) {
        callLabel = "Call $"+human.getCallAmount();
      }
      CALL_HTML = '<a href="" onclick="humanCalls(); resumeRiver('+isContinuedBetting+'); return false;">'+callLabel+'</a>';
    }

    var requiredBet = getRequiredBet(RIVER);
    var totalToRaise = human.getCallAmount() + requiredBet;
    var RAISE_HTML = false;

    // Does human have enough to stay?
    if (totalToRaise <= human.getMoney()) {
      var raiseLabel = "Bet $"+requiredBet;
      
      if (human.getCallAmount() > 0) {
        raiseLabel = "Raise $"+requiredBet+" (Total with call $"+totalToRaise+")";
      }
      RAISE_HTML = '<a href="" onclick="humanBets('+requiredBet+'); resumeRiver('+isContinuedBetting+'); return false;">'+raiseLabel+'</a>'; 
    }

    // Human can always fold
    var FOLD_HTML = '<a href="" onclick="humanFolded(); resumeRiver('+isContinuedBetting+'); return false;">Fold</a>';

    var myHTML =  FOLD_HTML;
    if (RAISE_HTML) {
      myHTML = RAISE_HTML + ' ' + myHTML;
    } 
    if (CALL_HTML) {
      myHTML = CALL_HTML + ' ' + myHTML;
    }
    writeToControlConsole(myHTML);
  }
  
  // Human folded. Keep running game automatically.
  else {
    resumeRiver(isContinuedBetting);
  }
}

/********************************************************
 * PART OF HAND FLOW:
 ********************************************************/
function resumeRiver(isContinuedBetting) {
  clearControlConsole();

  // Check to see whether human folded and only one 
  // player remaining
  if (isOnlyOnePlayerRemaining()) {
    handOver();
    return;
  }

  // Since human player is index 2, next opponent
  var nextPlayerIndex = 3;

  // Stop when reach the first user who went
  var alreadyWentIndex = (smallBlindPos + 2) % 5;

  // Remaining players bet
  while (nextPlayerIndex != alreadyWentIndex) {
    // AI opponent goes
    var nextPlayer = getPlayerByPosition(nextPlayerIndex);

    // If betting going around, verify that betting hasn't finished going
    // around (nothing to call for this user)
    if (isContinuedBetting) {
      // Check to see whether only one player remaining
      if (isOnlyOnePlayerRemaining()) {
        handOver();
        return;
      }
      if (!nextPlayer.isFolded() && nextPlayer.getCallAmount() == 0) {
        // Round of betting is over!
        handOver();
        return;
      }
    }
    
    // Verify not folded, and let player go otherwise
    if (!nextPlayer.isFolded()) {
      // AI: computer opponent choose what to do
      chooseNextMove(nextPlayer, RIVER);
    }

    // Check to see whether only one player remaining
    if (isOnlyOnePlayerRemaining()) {
      handOver();
      return;
    }

    // Update index to next player
    nextPlayerIndex = (nextPlayerIndex+1) % 5;
  }

  // See if there is more betting going on
  if (isStillBetting()) {
    startRiver(true);
  } else {
    // See who won!
    handOver();
  }
}

/********************************************************
 * PART OF HAND FLOW:
 ********************************************************/
function handOver() {
  
  // Only flip cards for non-folders if more than one
  // player left. (If only one player left, then does
  // not have to show cards by rules.)
  if (!isOnlyOnePlayerRemaining()) {
    if (!p1.isFolded()) {
      flipPlayer1Cards();
    }
    if (!p2.isFolded()) {
      flipPlayer2Cards();
    }
    if (!p3.isFolded()) {
      flipPlayer3Cards();
    }
    if (!p4.isFolded()) {
      flipPlayer4Cards();
    }
  }

  checkForWinner(true);

  // Now that pot is awarded, see if anyone is out!
  if (!p1.isOut() && p1.getMoney() == 0) {
    p1.setOut();
    alert(p1.getName()+' is out of money and left the table.');
  }
  if (!p2.isOut() && p2.getMoney() == 0) {
    p2.setOut();
    alert(p2.getName()+' is out of money and left the table.');
  }
  if (!p3.isOut() && p3.getMoney() == 0) {
    p3.setOut();
    alert(p3.getName()+' is out of money and left the table.');
  }
  if (!p4.isOut() && p4.getMoney() == 0) {
    p4.setOut();
    alert(p4.getName()+' is out of money and left the table.');
  }
  if (!human.isOut() && human.getMoney() == 0) {
    human.setOut();
    alert('Woah! You are out of money. Refresh page to go to different table.');
  }

  // Determine who is the next small blind player
  incrementSmallBlindPlayer();

  // Anyone not having enough for the small blind has to leave
  var smallBlindPlayer = getSmallBlindPlayer();
  
  while (smallBlindPlayer.getMoney() < LOW_LIMIT && getNumberPlayersRemainingAtTable() > 1) {
    alert(smallBlindPlayer.getName()+' does not have enough money for small blind. Leaves table.');
    smallBlindPlayer.setOut();
    incrementSmallBlindPlayer();
    smallBlindPlayer = getSmallBlindPlayer();
  }
  
  // Anyone not having enough for the big blind has to leave
  var bigBlindPlayer = getBigBlindPlayer();

  while (bigBlindPlayer.getMoney() < HIGH_LIMIT && getNumberPlayersRemainingAtTable() > 1) {
    alert(bigBlindPlayer.getName()+' does not have enough money for big blind. Leaves table.');
    bigBlindPlayer.setOut();
    bigBlindPlayer = getBigBlindPlayer();
  }

  // See whether there is only one player remaining
  var remainingPlayer = getOnlyPlayerRemainingAtTable();

  if (remainingPlayer) {
    // Nope, game over! Clear controls.
    clearControlConsole();
    writeToControlConsole('<a href="" onclick="location.reload(true); return false;">Go to different table</a>');

    if (!human.isOut()) {
      alert('You cleared the table! Congratulations!\n\nRefresh page to go to different table (but without the money you just won).');
    } else {
      alert(remainingPlayer.getName()+' won all the money at the table.  Refresh page to go to different table.');
    }
  }
}


/********************************************************
 * Create a new deck
 ********************************************************/
function createDeck() {
  var deck = new Array(52);

  var heartOffset = 0;
  var diamondOffset = 13;
  var clubOffset = 26;
  var spadeOffset = 39;
  
  // Create cards
  for (var val=2; val<=14; val++) {
    if (val <= 10) {
      deck[val-2+heartOffset] = new Card(val,HEART,"images/h-"+val+".gif");
      deck[val-2+diamondOffset] = new Card(val,DIAMOND,"images/d-"+val+".gif");
      deck[val-2+clubOffset] = new Card(val,CLUB,"images/c-"+val+".gif");
      deck[val-2+spadeOffset] = new Card(val,SPADE,"images/s-"+val+".gif");
    } else if (val == 11) {
      deck[val-2+heartOffset] = new Card(val,HEART,"images/h-j.gif");
      deck[val-2+diamondOffset] = new Card(val,DIAMOND,"images/d-j.gif");
      deck[val-2+clubOffset] = new Card(val,CLUB,"images/c-j.gif");
      deck[val-2+spadeOffset] = new Card(val,SPADE,"images/s-j.gif");
    } else if (val == 12) {
      deck[val-2+heartOffset] = new Card(val,HEART,"images/h-q.gif");
      deck[val-2+diamondOffset] = new Card(val,DIAMOND,"images/d-q.gif");
      deck[val-2+clubOffset] = new Card(val,CLUB,"images/c-q.gif");
      deck[val-2+spadeOffset] = new Card(val,SPADE,"images/s-q.gif");
    } else if (val == 13) {
      deck[val-2+heartOffset] = new Card(val,HEART,"images/h-k.gif");
      deck[val-2+diamondOffset] = new Card(val,DIAMOND,"images/d-k.gif");
      deck[val-2+clubOffset] = new Card(val,CLUB,"images/c-k.gif");
      deck[val-2+spadeOffset] = new Card(val,SPADE,"images/s-k.gif");
    } else if (val == 14) {
      deck[val-2+heartOffset] = new Card(val,HEART,"images/h-a.gif");
      deck[val-2+diamondOffset] = new Card(val,DIAMOND,"images/d-a.gif");
      deck[val-2+clubOffset] = new Card(val,CLUB,"images/c-a.gif");
      deck[val-2+spadeOffset] = new Card(val,SPADE,"images/s-a.gif");
    }
  }

  return deck;
}

/********************************************************
 * 
 ********************************************************/
function setAllCardsFaceDown() {

  // Player 1 -- set to visible perchance folded
  if (!p1.isOut()) {
    document.getElementById('player1_card_1').style.visibility = 'visible';
    document.getElementById('player1_card_2').style.visibility = 'visible';

    document.getElementById('player1_card_1').src = 'images/back.gif';
    document.getElementById('player1_card_2').src = 'images/back.gif';
  } else {
    hidePlayer1Cards();
  }

  // Player 2 -- set to visible perchance folded
  if (!p2.isOut()) {
    document.getElementById('player2_card_1').style.visibility = 'visible';
    document.getElementById('player2_card_2').style.visibility = 'visible';

    document.getElementById('player2_card_1').src = 'images/back.gif';
    document.getElementById('player2_card_2').src = 'images/back.gif';
  } else {
    hidePlayer2Cards();
  }

  // Player 3 -- set to visible perchance folded
  if (!p3.isOut()) {
    document.getElementById('player3_card_1').style.visibility = 'visible';
    document.getElementById('player3_card_2').style.visibility = 'visible';

    document.getElementById('player3_card_1').src = 'images/back.gif';
    document.getElementById('player3_card_2').src = 'images/back.gif';
  } else {
    hidePlayer3Cards();
  }

  // Player 4 -- set to visible perchance folded
  if (!p4.isOut()) {
    document.getElementById('player4_card_1').style.visibility = 'visible';
    document.getElementById('player4_card_2').style.visibility = 'visible';

    document.getElementById('player4_card_1').src = 'images/back.gif';
    document.getElementById('player4_card_2').src = 'images/back.gif';
  } else {
    hidePlayer4Cards();
  }

  // Human -- set to visible perchance folded
  if (!human.isOut()) {
    document.getElementById('human_card_1').style.visibility = 'visible';
    document.getElementById('human_card_2').style.visibility = 'visible';

    document.getElementById('human_card_1').src = 'images/back.gif';
    document.getElementById('human_card_2').src = 'images/back.gif';
  } else {
    hideHumanCards();
  }

  // Dealer
  document.getElementById('dealer_card_1').src = 'images/back.gif';
  document.getElementById('dealer_card_2').src = 'images/back.gif';
  document.getElementById('dealer_card_3').src = 'images/back.gif';
  document.getElementById('dealer_card_4').src = 'images/back.gif';
  document.getElementById('dealer_card_5').src = 'images/back.gif';
}

/********************************************************
 * 
 ********************************************************/
function flipPlayer1Cards() {
  document.getElementById('player1_card_1').style.visibility = 'visible';
  document.getElementById('player1_card_1').src = p1.getCard1().getImgSrc();
  document.getElementById('player1_card_2').style.visibility = 'visible';
  document.getElementById('player1_card_2').src = p1.getCard2().getImgSrc();
}

/********************************************************
 * 
 ********************************************************/
function flipPlayer2Cards() {
  document.getElementById('player2_card_1').style.visibility = 'visible';
  document.getElementById('player2_card_1').src = p2.getCard1().getImgSrc();
  document.getElementById('player2_card_2').style.visibility = 'visible';
  document.getElementById('player2_card_2').src = p2.getCard2().getImgSrc();
}

/********************************************************
 * 
 ********************************************************/
function flipPlayer3Cards() {
  document.getElementById('player3_card_1').style.visibility = 'visible';
  document.getElementById('player3_card_1').src = p3.getCard1().getImgSrc();
  document.getElementById('player3_card_2').style.visibility = 'visible';
  document.getElementById('player3_card_2').src = p3.getCard2().getImgSrc();
}

/********************************************************
 * 
 ********************************************************/
function flipPlayer4Cards() {
  document.getElementById('player4_card_1').style.visibility = 'visible';
  document.getElementById('player4_card_1').src = p4.getCard1().getImgSrc();
  document.getElementById('player4_card_2').style.visibility = 'visible';
  document.getElementById('player4_card_2').src = p4.getCard2().getImgSrc();
}

/********************************************************
 * 
 ********************************************************/
function flipHumanCards() {
  document.getElementById('human_card_1').style.visibility = 'visible';
  document.getElementById('human_card_1').src = human.getCard1().getImgSrc();
  document.getElementById('human_card_2').style.visibility = 'visible';
  document.getElementById('human_card_2').src = human.getCard2().getImgSrc();
}

/********************************************************
 * 
 ********************************************************/
function hidePlayer1Cards() {
  document.getElementById('player1_card_1').style.visibility = 'hidden';
  document.getElementById('player1_card_2').style.visibility = 'hidden';
}

/********************************************************
 * 
 ********************************************************/
function hidePlayer2Cards() {
  document.getElementById('player2_card_1').style.visibility = 'hidden';
  document.getElementById('player2_card_2').style.visibility = 'hidden';
}

/********************************************************
 * 
 ********************************************************/
function hidePlayer3Cards() {
  document.getElementById('player3_card_1').style.visibility = 'hidden';
  document.getElementById('player3_card_2').style.visibility = 'hidden';
}

/********************************************************
 * 
 ********************************************************/
function hidePlayer4Cards() {
  document.getElementById('player4_card_1').style.visibility = 'hidden';
  document.getElementById('player4_card_2').style.visibility = 'hidden';
}

/********************************************************
 * 
 ********************************************************/
function hideHumanCards() {
  document.getElementById('human_card_1').style.visibility = 'hidden';
  document.getElementById('human_card_2').style.visibility = 'hidden';
}

/********************************************************
 * 
 ********************************************************/
function flipFlop() {
  document.getElementById('dealer_card_1').src = dealer[0].getImgSrc();
  document.getElementById('dealer_card_2').src = dealer[1].getImgSrc();
  document.getElementById('dealer_card_3').src = dealer[2].getImgSrc(); 
}

/********************************************************
 * 
 ********************************************************/
function flipTurn() {
  document.getElementById('dealer_card_4').src = dealer[3].getImgSrc();
}

/********************************************************
 * 
 ********************************************************/
function flipRiver() {
  document.getElementById('dealer_card_5').src = dealer[4].getImgSrc();
}

/********************************************************
 * Check for a winner between every step:
 *   1. Deal
 *   2. Flop
 *   3. Turn
 *   4. River
 *
 * If river and more than one user remaining, calculate
 *   winner.
 *
 * Returns: 
 *   1. Single Player array, if one winner
 *   2. Multiple Player array, if split (tie)
 *   3. The integer 0 if no winner yet.
 ********************************************************/
function checkForWinner(isRiver) {

  var players = new Array();
 
  /*
  players[0] = p1;
  players[1] = p2;
  players[2] = p3;
  players[3] = p4;
  players[4] = human;

  for (var i=0; i++; i<players.length) {
 
    // Don't consider anyone who folded
    if (players[i].isFolded()) {
      players[i] = 0;
    }
  }
  */
  if (!p1.isFolded()) {
    players.push(p1);
  }
  if (!p2.isFolded()) {
    players.push(p2);
  } 
  if (!p3.isFolded()) {
    players.push(p3);
  } 
  if (!p4.isFolded()) {
    players.push(p4);
  } 
  if (!human.isFolded()) {
    players.push(human);
  }

  // If it's the river, need to determine the winner(s) no matter what
  if (isRiver) {
    for (var i=0; i<players.length; i++) {

      if (players[i] == 0) {
        continue;
      }

      var cards = new Array(7);
      cards[0] = players[i].getCard1();
      cards[1] = players[i].getCard2();
      cards[2] = dealer[0];
      cards[3] = dealer[1];
      cards[4] = dealer[2];
      cards[5] = dealer[3];
      cards[6] = dealer[4];

      var bestHand = getBestHandFromCards(cards);
      players[i].setBestHand(bestHand);
      // alert(players[i].getName()+' has a '+getNameForHand(bestHand.getHandType()));
    }

    // Find the winner(s). This is an array perchance splitting the pot.
    var winners = new Array();
    winners.push(players[0]);
    for (var i=1; i<players.length; i++) {
      // Just use first player in winners. If more than one winner, equivalent
      // anyhow.
      var compareVal = players[i].getBestHand().compareTo(winners[0].getBestHand());

      // This player is wining
      if (compareVal > 0) {
        // Clear winner array. 1+ previous winners are out of consideration
        winners = new Array();
        winners.push(players[i]);
      } 

      // Don't change anything -- current winner(s) still winning
      else if (compareVal < 0) {

      } 

      // Equal, add to array
      else {
        winners.push(players[i]);
      }
    }

    var msg = false;

    if (winners.length == 1) {

      msg = winners[0].getName()+' won $'+getPot()+' with a '+getNameForHand(winners[0].getBestHand().getHandType());

      // If only one player left, does not show hand
      if (isOnlyOnePlayerRemaining()) {
        msg = winners[0].getName()+' won $'+getPot()+'. Everyone folded, so not showing hand.';
      }

      writeToConsole('');
      writeToConsole(msg);

      winners[0].adjustMoney(getPot());

      
    } else if (winners.length > 1) {
      var winnersStr = '';
      
      // Build up string of list of winners:
      //     winner1, winner2, ..., winnerN-1 and winnerN
      for (var i=0; i<winners.length; i++) {
        winnersStr+=winners[i].getName();
        if (i < winners.length - 2) {
          winnersStr+=', ';
        } else if (i == winners.length - 2) {
          winnersStr+= ' and ';
        }
      }
      
      var amountPerWinner = getPot() / winners.length;

      msg = winnersStr+' won $'+amountPerWinner+' each with a '+getNameForHand(winners[0].getBestHand().getHandType());
      
      writeToConsole('');
      writeToConsole(msg);
      

      for (var i=0; i<winners.length; i++) {
        winners[i].adjustMoney(amountPerWinner);
      }
    } else {
      alertBug("Expecting winners.length to be 1 or more, but is: "+winners.length);
    }
    clearPot();

    if (isShowAlertOnFlipCards && msg) {
      alert(msg);
    }

    var playAgainHTML = '<a href="" onclick="javascript: playHandAtCurrentTable(); return false;">Deal next hand!</a>';
    writeToControlConsole(playAgainHTML);
    
  }
}

/********************************************************
 *
 *******************************************************/
function getNameForHand(hand) {
  if (hand == STRAIGHT_FLUSH) {
    return 'straight flush';
  } else if (hand == FOUR_OF_A_KIND) {
    return 'four of a kind';
  } else if (hand == FULL_HOUSE) {
    return 'full house';
  } else if (hand == FLUSH) {
    return 'flush';
  } else if (hand == STRAIGHT) {
    return 'straight';
  } else if (hand == THREE_OF_A_KIND) {
    return 'three of a kind';
  } else if (hand == TWO_PAIR) {
    return 'two pair';
  } else if (hand == PAIR) {
    return 'pair';
  } else if (hand == HIGH_CARD) {
    return 'high card';
  } else {
    var handStr = '';
    for (var i=0; i<hand.length; i++) {
      handStr+=hand[i].getValue() + ' ';
    }

    return 'Unrecognized hand: '+handStr;
  }
} 

/********************************************************
 * Makes the best hand from available cards 
 *
 * Param:
 *   cards - Variable number of Card objects (between 2 and 7)
 *
 * Return:
 *   Constant value representing the best possible hand:
 *     - STRAIGHT_FLUSH
 *     - ...
 *     - HIGH_CARD
 ********************************************************/
function getBestHandFromCards(cards) {

  // Check for straight flush
  var bestHand = checkForStraightFlush(cards);

  // Check for four of a kind
  if (!bestHand) {
    bestHand = checkForFourOfAKind(cards);
  }

  // Check for full house
  if (!bestHand) {
    bestHand = checkForFullHouse(cards);
  }

  // Check for flush
  if (!bestHand) {
    bestHand = checkForFlush(cards);
  }
  
  // Check for straight
  if (!bestHand) {
    bestHand = checkForStraight(cards);
  }

  // Check for three of a kind
  if (!bestHand) {
    bestHand = checkForThreeOfAKind(cards);
  }

  // Check for two pair
  if (!bestHand) {
    bestHand = checkForTwoPair(cards);
  }

  // Check for pair
  if (!bestHand) {
    bestHand = checkForPair(cards);
  }

  // If not found, high card
  if (!bestHand) {
    // Need top five cards (max). Could be less.
    var hand = new Array();
    for (var i=0; i<cards.length; i++) {
      hand.push(cards[i]);
    }

    while (hand.length > 5) {
      hand = removeLowestCard(hand);
    }

    bestHand = new BestHand(HIGH_CARD, hand);
  }

  return bestHand;
} // getBestHandFromCards

/********************************************************
 * Check for any straight flush (including royal flush)
 *
 * Param:
 *   cards - Variable number of Card objects (between 2 and 7)
 *
 * Return:
 *   True if found, false otherwise
 ********************************************************/
function checkForStraightFlush(cards) {

  if (cards.length >= 5) {

    // Going to handle one suit at time
    allPossibleSuites:
    for (var suiteIndex=0; suiteIndex<4; suiteIndex++) {

      var suite = 0;
      if (suiteIndex == 0) {
        suite = HEART;
      } else if (suiteIndex == 1) {
        suite = CLUB;
      } else if (suiteIndex == 2) {
        suite = DIAMOND;
      } else if (suiteIndex == 3) {
        suite = SPADE;
      }

      // For each value, check for start of straight
      // Note that 10 is last starting value
      allPossibleStartingValues:
      for (var start=10; start>=2; start--) {

      // Wrong: need highest straight!
      //for (var start=2; start<= 10; start++) {

        var bestHand = new Array();

        // Go through each necessary value for straight
        // at particular starting value
        necessaryValues:
        for (var val=start; val<=start+4; val++) {
        
          // Check each card for matching value
          findMatchingValue:
          for (var index=0; index<cards.length; index++) {

            // Found next card, continue to find next value
            if (cards[index].getValue() == val && cards[index].getSuit() == suite) {
               bestHand.push(cards[index]);
               continue necessaryValues;
            }
          }

          // If get here, didn't find matching value
          //   necessary for straight starting at...
          // Shortcut: set starting value to value
          //           not found! Will start next loop
          //           at the next value (after all,
          //           not found, so why look again?)
          //start = val; // <-- No, changed logic. Infinite loop
          continue allPossibleStartingValues;
        }

        // If get here, found a straight flush!
        if (bestHand.length != 5) {
          alertBug("bestHand.length<"+bestHand.length+"> != 5 in checkForStraightFlush");
        }
        return new BestHand(STRAIGHT_FLUSH, bestHand);
      }

    } // Each possible starting value
    
  } // Each possible suit

  // Not found
  return false;
}

/********************************************************
 * Check for any four-of-a-kind
 *
 * Param:
 *   cards - Variable number of Card objects (between 2 and 7)
 *
 * Return:
 *   True if found, false otherwise
 ********************************************************/
function checkForFourOfAKind(cards) {

  if (cards.length >= 4) {

    // For each value, determine if a four-of-a-kind
    for (var val=2; val<=14; val++) {
      var count = 0;

      // Used for building best hand
      var bestHand;
      if (cards.length == 4) {
        bestHand = new Array(4);
      } else {
        bestHand = new Array(5);
      }

      var bestHandIndex = 0;

      // Check with each card
      for (var index=0; index<cards.length; index++) {

        // Does card equal value?
        if (cards[index].getValue() == val) {
          bestHand[bestHandIndex] = cards[index];
          bestHandIndex++;
          count++;
        }

        // Got a four-of-a-kind?
        if (count >= 4) {

          // See if should be a kicker
          if (cards.length >= 5) {
            
            var highestValue = -1;
            var highestIndex = -1;

            // Find highest value that is NOT part of four-of-a-kind
            for (var i=0; i<cards.length; i++) {
              var thisValue = cards[i].getValue();
              if (thisValue != val && thisValue > highestValue) {
                highestValue = thisValue;
                highestIndex = i;
              }
            }

            // If get here, assert there is a kicker
            if (highestValue == -1 || highestIndex == -1) {
              alertBug("highestValue<"+highestValue+"> == 0 || highestIndex<"+highestIndex+"> == 0 in checkForFourOfAKind");
            }

            bestHand[4] = cards[highestIndex];
          }

          return new BestHand(FOUR_OF_A_KIND, bestHand);
        }
      }
    }
  }

  // Not found
  return false;
}

/********************************************************
 * Check for any full house
 *
 * Param:
 *   cards - Variable number of Card objects (between 2 and 7)
 *
 * Return:
 *   True if found, false otherwise
 ********************************************************/
function checkForFullHouse(cards) {
  var isThreeOfKind = false;
  var isTwoOfKind = false;
  if (cards.length >= 5) {
    
    var bestHand = new Array(5);

    // Go through each possible value looking for two or three+
    for (var val=14; val>=2; val--) {

    // Wrong, need highest first!
    //for (var val=2; val<=14; val++) {
      var count = 0;

      // Buffer holds potential pair or three-of-a-kind
      var buffer = new Array(3);
      var bufferIndex = 0;
      
      // How many copies of this value?
      valueCount:
      for (var i=0; i<cards.length; i++) {
        if (cards[i].getValue() == val) {
          count++;
          buffer[bufferIndex] = cards[i];
          bufferIndex++;

          // If more at three, stop... no need to go on
          if (count >= 3) {
            break valueCount;
          }
        }
      }
 
      if (count >= 3) {
        isThreeOfKind = true;
        bestHand[0] = buffer[0];
        bestHand[1] = buffer[1];
        bestHand[2] = buffer[2];
      } else if (count == 2) {
        isTwoOfKind = true;
        bestHand[3] = buffer[0];
        bestHand[4] = buffer[1];
      }

      if (isThreeOfKind && isTwoOfKind) {
        return new BestHand(FULL_HOUSE, bestHand);
      }
    }
  }
 
  // Not found
  return false;
}

/********************************************************
 * Check for any flush
 *
 * Param:
 *   cards - Variable number of Card objects (between 2 and 7)
 *
 * Return:
 *   True if found, false otherwise
 ********************************************************/
function checkForFlush(cards) {
  if (cards.length >= 5) {
    var heartCount   = 0;
    var clubCount    = 0;
    var spadeCount   = 0;
    var diamondCount = 0;

    var hearts   = new Array();
    var clubs    = new Array();
    var spades   = new Array();
    var diamonds = new Array();

    for (var i=0; i<cards.length; i++) {
      if (cards[i].getSuit() == HEART) {
        hearts.push(cards[i]);
      } else if (cards[i].getSuit() == CLUB) {
        clubs.push(cards[i]);
      } else if (cards[i].getSuit() == SPADE) {
        spades.push(cards[i]);
      } else if (cards[i].getSuit() == DIAMOND) {
        diamonds.push(cards[i]);
      }
    } // Categorizing each card
  }

  // If any flushes, shift off smallest elements
  if (hearts.length >= 5) {
    while (hearts.length > 5) {
      hearts = removeLowestCard(hearts);
    }

    return new BestHand(FLUSH, hearts);
  } else if (clubs.length >= 5) {
    while (clubs.length > 5) {
      clubs = removeLowestCard(clubs);
    }

    return new BestHand(FLUSH, clubs);
  } else if (spades.length >= 5) {
    while (spades.length > 5) {
      spades = removeLowestCard(spades);
    }

    return new BestHand(FLUSH, spades);
  } else if (diamonds.length >= 5) {
    while (diamonds.length > 5) {
      diamonds = removeLowestCard(diamonds);
    }

    return new BestHand(FLUSH, diamonds);
  }

  // Not found
  return false;
}

/********************************************************
 * Check for any straight
 *
 * Param:
 *   cards - Variable number of Card objects (between 2 and 7)
 *
 * Return:
 *   True if found, false otherwise
 ********************************************************/
function checkForStraight(cards) {
  if (cards.length >= 5) {
    
    // For each value, check for start of straight
    // Note that 10 is last starting value
    allPossibleStartingValues:
    for (var start=10; start>=2; start--) {

    // Wrong! Need highes straight
    //for (var start=2; start<= 10; start++) {

      var bestHand = new Array();

      // Go through each necessary value for straight
      // at particular starting value
      necessaryValues:
      for (var val=start; val<=start+4; val++) {
        
        // Check each card for matching value
        findMatchingValue:
        for (var index=0; index<cards.length; index++) {

          // Found next card, continue to find next value
          if (cards[index].getValue() == val) {
             bestHand.push(cards[index]);
             continue necessaryValues;
          }
        }

        // If get here, didn't find matching value
        //   necessary for straight starting at...
        // Shortcut: set starting value to value
        //           not found! Will start next loop
        //           at the next value (after all,
        //           not found, so why look again?)
        //start = val;  // <-- No, changed logic. Infinite loop
        continue allPossibleStartingValues;
      }

      // If get here, found a straight!
      if (bestHand.length != 5) {
        alertBug("bestHand.length<"+bestHand.length+"> != 5 in checkForStraight");
      }

      return new BestHand(STRAIGHT, bestHand);
    }
  }

  // Not found
  return false;
}

/********************************************************
 * Check for any three-of-a-kind
 *
 * Param:
 *   cards - Variable number of Card objects (between 2 and 7)
 *
 * Return:
 *   True if found, false otherwise
 ********************************************************/
function checkForThreeOfAKind(cards) {
  if (cards.length >= 3) {
    // For each value, determine if a three-of-a-kind
    for (var val=14; val>=2; val--) {

    // Wrong! Need highest three-of-a-kind
    //for (var val=2; val<=14; val++) {
      var count = 0;

      var bestHand = new Array();

      // Check with each card
      for (var index=0; index<cards.length; index++) {

        // Does card equal value?
        if (cards[index].getValue() == val) {
          bestHand.push(cards[index]);
        }

        // Got a three-of-a-kind?
        if (bestHand.length >= 3) {
          // Going to find best kickers
          var arrayOfKickers = new Array();
          for (var i=0; i<cards.length; i++) {
            var nextCard = cards[i];
            // Don't copy over any cards with value of triple
            if (nextCard.getValue() != val) {
              arrayOfKickers.push(nextCard);
            }
          }

          // Note there might be 0, 1 or 2 kickers. Get rid of
          // excess.
          while (arrayOfKickers.length > 2) {
            arrayOfKickers = removeLowestCard(arrayOfKickers);
          }

          // Add kickers to best hand
          for (var i=0; i<arrayOfKickers.length; i++) {
            bestHand.push(arrayOfKickers[i]);
          }

          // Assert size of best hand
          if (cards.length == 3 && bestHand.length != 3) {
            alertBug("cards.length == 3 && bestHand.length<"+bestHand.length+"> != 3 in checkForThreeOfAKind");
          } else if (cards.length == 4 && bestHand.length != 4) {
            alertBug("cards.length == 4 && bestHand.length<"+bestHand.length+"> != 4 in checkForThreeOfAKind");
          } else if (cards.lenth >= 5 && bestHand.length != 5) {
            alertBug("cards.length >= 5 && bestHand.length<"+bestHand.length+"> != 5 in checkForThreeOfAKind");
          }

          return new BestHand(THREE_OF_A_KIND, bestHand);
        }
      }
    }
  }

  // Not found
  return false;
}

/********************************************************
 * Check for any two pair
 *
 * Param:
 *   cards - Variable number of Card objects (between 2 and 7)
 *
 * Return:
 *   True if found, false otherwise
 ********************************************************/
function checkForTwoPair(cards) {
  if (cards.length >= 4) {
    var pair = 0;
    var pairValue1 = -1;
    var pairValue2 = -1;

    var bestHand = new Array();

    // For each value, determine if a pair.
    for (var val=14; val>=2; val--) {

    // Wrong! Need highest two pair
    //for (var val=2; val<=14; val++) {

      // Used to temp hold potential pair
      var buffer = new Array();

      // Check with each card
      checkForPair:
      for (var index=0; index<cards.length; index++) {

        // Does card equal value?
        if (cards[index].getValue() == val) {
          buffer.push(cards[index]);
        }

        // Got a pair?
        if (buffer.length >= 2) {
          pair++;
          if (pair == 1) {
            pairValue1 = val;
          } else if (pair == 2) {
            pairValue2 = val;
          } else {
            alertBug("Expecting 1 or 2 pairs in checkForTwoPair, instead found "+pair);
          }

          // Push buffer over to best hand
          for (var i=0; i<buffer.length; i++) {
            bestHand.push(buffer[i]);
          }
          break checkForPair;
        }
      }

      // Check for two pairs
      if (pair >= 2) {

        if (pairValue1 == -1) {
          alertBug("pairValue1 == -1 in checkForTwoPair");
        }
        if (pairValue2 == -1) {
          alertBug("pairValue2 == -1 in checkForTwoPair");
        }

        // Is there a kicker?
        if (cards.length >= 5) {
          // Find highest card for kicker
          var highestCardValue = 0;
          var highestCardIndex = -1;
          
          for (var i=0; i<cards.length; i++) {
            var nextCard = cards[i];
            if (nextCard.getValue() == pairValue1 || nextCard.getValue() == pairValue2) {
              continue;
            }
            if (nextCard.getValue() > highestCardValue) {
              highestCardValue = nextCard.getValue();
              highestCardIndex = i;
            }
          }

          if (highestCardValue == 0 || highestCardIndex == -1) {
            alertBug("highestCardValue<"+highestCardValue+"> == 0 || highestCardIndex<"+highestCardIndex+"> == -1 in checkForTwoPair");
          }

          bestHand.push(cards[highestCardIndex]);
        }

        if (cards.length == 4 && bestHand.length != 4) {
          alertBug("cards.length<"+cards.length+"> == 4 && bestHand.length<"+bestHand.length+"> != 4 in checkForTwoPair");
        } else if (cards.length >= 5 && bestHand.length != 5) {
          alertBug("cards.length<"+cards.length+"> >= 5 && bestHand.length<"+bestHand.length+"> != 5 in checkForTwoPair");
        }

        return new BestHand(TWO_PAIR, bestHand);
      }
    }
  }

  // Not found
  return false;
}

/********************************************************
 * Check for any pair
 *
 * Param:
 *   cards - Variable number of Card objects (between 2 and 7)
 *
 * Return:
 *   True if found, false otherwise
 ********************************************************/
function checkForPair(cards) {
  if (cards.length >= 2) {

    var bestHand = new Array();

    // For each value, determine if a pair
    for (var val=2; val<=14; val++) {
      var count = 0;
      var buffer = new Array();

      // Check with each card
      for (var index=0; index<cards.length; index++) {

        // Does card equal value?
        if (cards[index].getValue() == val) {
          buffer.push(cards[index]);
        }

        // Got a pair?
        if (buffer.length >= 2) {
          for (var i=0; i<buffer.length; i++) {
            bestHand.push(buffer[i]);
          }

          // Need to get the best kickers
          var kickers = new Array();
          for (var i=0; i<cards.length; i++) {
            var nextCard = cards[i];
            if (nextCard.getValue() == val) {
              continue;
            }
            kickers.push(nextCard);
          }

          // Need maximum of three kickers. May be less.
          while(kickers.length > 3) {
            kickers = removeLowestCard(kickers);
          }

          for (var i=0; i<kickers.length; i++) {
            bestHand.push(kickers[i]);
          }

          // Assert best hand size
          if (cards.length > 5 && bestHand.length != 5) {
            alertBug("cards.length<"+cards.length+"> > 5 && bestHand.length<"+bestHand.length+"> != 5 in checkForPair");
          } else if (cards.length <= 5 && (bestHand.length != cards.length)) {
            alertBug("cards.length <= 5 && (bestHand.length<"+bestHand.length+"> != cards.length<"+cards.length+">) in checkForPair");
          }

          return new BestHand(PAIR, bestHand);
        }
      }
    }
  }

  // Not found
  return false;
}

/********************************************************
 * Helper method to sort array of cards.
 *
 * Parameters:
 *   cards - Array of cards
 *
 * Returns:
 *   Array with same cards sorted
 ********************************************************/
function sortCards(cards) {
  // Don't do anything with empty array
  if (cards.length == 0) {
    return cards;
  }

  var unsortedCards = new Array();

  // Copy over to new array
  for (var i=0; i<cards.length; i++) {
    unsortedCards.push(cards[i]);
  }

  var sortedCards = new Array();

  var timeoutCount = 0;
  while (sortedCards.length < cards.length) {
    // Find lowest of unsorted cards and add to end of sorted
    var lowestRemainingValue = 15;
    var lowestRemainingIndex = -1;

    for (var i=0; i<unsortedCards.length; i++) {
      var nextCard = unsortedCards[i];

      if (nextCard != 0) {
        if (nextCard.getValue() < lowestRemainingValue) {
          lowestRemainingValue = nextCard.getValue();
          lowestRemainingIndex = i;
        }
      }
    }

    if (lowestRemainingValue == 15 || lowestRemainingIndex == -1) {
      alertBug("lowestRemainingValue<"+lowestRemainingValue+"> == 15 || lowestRemainingIndex<"+lowestRemainingIndex+"> == -1 in sortCards");
    }

    sortedCards.push(unsortedCards[lowestRemainingIndex]);
    
    // Flag card as already sorted
    unsortedCards[lowestRemainingIndex] = 0;

    timeoutCount++;
    if (timeoutCount > 1000) {
      alertBug("timeoutCount<"+timeoutCount+"> > 1000 in sortCards");
    }
  }

  // Assert cards are in order
  for (var i=1; i<sortedCards.length; i++) {
    var thisValue = sortedCards[i].getValue();
    var lastValue = sortedCards[i-1].getValue();
    if (thisValue < lastValue) {
      alertBug("thisValue<"+thisValue+"> < lastValue<"+lastValue+"> (i.e., failed sort) in sortCards");
    }
  }

  return sortedCards;
}

/********************************************************
 * Helper method to remove lowest card from an array of cards.
 *
 * Parameters:
 *   cards - Array of cards
 *
 * Returns:
 *   Array of cards (of size cards.length - 1) with lowest
 *   card removed
 ********************************************************/
function removeLowestCard(cards) {
  // Don't do anything with empty array
  if (cards.length == 0) {
    return cards;
  }

  var cardsWithLowestRemoved = new Array();

  var lowestValue = 15; // Set higher than ace
  var lowestIndex = -1;

  // Find lowest value
  for (var i=0; i<cards.length; i++) {
    var nextCard = cards[i];
    if (nextCard.getValue() < lowestValue) {
      lowestValue = nextCard.getValue();
      lowestIndex = i;
    }
  }

  // Assert that a lowest value was found
  if (lowestValue == 15) {
    alertBug("lowestValue == 15 in removeLowestCard");
  }

  // Copy over array
  for (var i=0; i<cards.length; i++) {
    if (i != lowestIndex) {
      cardsWithLowestRemoved.push(cards[i]);
    }
  }

  if (cardsWithLowestRemoved.length != cards.length - 1) {
    alertBug("cardsWithLowestRemoved.length<"+cardsWithLowestRemoved.length+"> should be one less than cards.length<"+cards.length+"> in removeLowestCard");
  }

  return cardsWithLowestRemoved;
}

/********************************************************
 * Get string representation for card
 ********************************************************/
function getCardString(card) {
  var val = card.getValue();

  if (card.getValue() == 11) {
    val = 'Jack';
  } else if (card.getValue() == 12) {
    val = 'Queen';
  } else if (card.getValue() == 13) {
    val = 'King';
  } else if (card.getValue() == 14) {
    val = 'Ace';
  }

  return val+' of '+card.getSuit();
}

/********************************************************
 * Get string representation for hand
 ********************************************************/
function getHandString(cards) {
  var handStr = '';

  for (var i=0; i<cards.length; i++) {
    handStr += getCardString(cards[i]);

    if (i<cards.length - 2) {
      handStr += ', ';
    } else if (i == cards.length - 2) {
      handStr += ' and ';
    }
  }

  return handStr;
}

/********************************************************
 * If there's a problem, alert user to please email me
 ********************************************************/
function alertBug(message, cards) {
  var handStr = '';
  for (var i=0; i<cards.length; i++) {
    var nextCard = cards[i];
    handStr += getCardString(nextCard) + ' ';
  }
  alertBug(message+'; for hand: '+handStr);
}

/********************************************************
 * If there's a problem, alert user to please email me
 ********************************************************/
function alertBug(message) {
  alert("An error has happened, which is almost certainly my (the developer's) fault:\n\n"+message+"\n\nIf you are willing, please notify me at bryanesmith@gmail.com with this information to fix. Thank you!");

  alert('Script killed due to error. You can refresh the page to try again.\n\nIf you are willing, please submit the follow to bryanesmith@gmail.com:\n\n'+message);

  // Throw the error
  throw message;
}

/********************************************************
 * http://www.sean.co.uk/a/webdesign/javascriptdelay.shtm
 ********************************************************/
function pause(millis) {
  var date = new Date();
  var curDate = null;

  do { curDate = new Date(); }
    while(curDate-date < millis);
}

/********************************************************
 * Handles a players bet, including
 *   - Modifying user's pile of chips
 *   - Modifying pot
 *   - Adjusting opponents amount to call
 ********************************************************/
function playerBets(player, bet) {

  var total = bet + player.getCallAmount();

  // Assert player has that much
  if (total > player.getMoney()) {
    alertBug(player.getName()+' bet '+bet+' and needs to call '+player.getCallAmount()+' for a total of '+total+', but on has '+player.getMoney());
  }

  player.adjustMoney(-total);
  adjustPot(total);

  if (player != p1 && !p1.isFolded()) {
    p1.adjustCallAmount(bet);
  } 
  if (player != p2 && !p2.isFolded()) {
    p2.adjustCallAmount(bet);
  }
  if (player != p3 && !p3.isFolded()) {
    p3.adjustCallAmount(bet);
  }
  if (player != p4 && !p4.isFolded()) {
    p4.adjustCallAmount(bet);
  }
  if (player != human && !human.isFolded()) {
    human.adjustCallAmount(bet);
  }

  player.clearCallAmount();

  if (total == bet) {
    writeToConsole(player.getName()+" bet "+bet);
  } else {
    writeToConsole(player.getName()+" raises "+bet);
  }
}

function playerCalls(player) {
  var amount = player.getCallAmount();
  
  // Assert player has that much
  if (amount > player.getMoney()) {
    alertBug(player.getName()+' called '+amount+', but on has '+player.getMoney());
  }

  player.adjustMoney(-amount);
  adjustPot(amount);

  player.clearCallAmount();

  if (amount > 0) {
    writeToConsole(player.getName()+" called");
  } else {
    writeToConsole(player.getName()+" stayed");
  }
}

function playerFolds(player) {
  player.setFolded(true);
  player.clearCallAmount();

  writeToConsole(player.getName()+" folded");
}

/********************************************************
 * Limit rules. How much is the required bet?
 *
 * Paramters:
 *   - turn: One of the following:
 *      PREFLOP
 *      FLOP
 *      TURN
 *      RIVER
 * 
 * Return:
 *   - The high limit if turn or river, else the low limit.
 ********************************************************/
function getRequiredBet(turn) {
  var betAmount = LOW_LIMIT;
  if (turn == TURN || turn == RIVER) {
    betAmount = HIGH_LIMIT;
  }
  return betAmount;
}

// >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> AI FUNCTIONALITY BELOW <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<

/********************************************************
 * AI opponent choose next move. Calls other methods
 *   for interesting results.
 *
 * Paramters:
 *   - player: The player who is making decision
 *   - turn: One of the following:
 *      PREFLOP
 *      FLOP
 *      TURN
 *      RIVER
 ********************************************************/
function chooseNextMove(player, turn) {
  chooseNextMoveDumbOpponent(player, turn);
}

/********************************************************
 * For this method, the player simply guesses randomly,
 *  as if didn't know how to player. Intended for testing.
 ********************************************************/
// Variable used to help AI players bet consistently 
// (E.g., not fold after re-raising)
currentTurn = -1;

// An array of who raised during the current turn.
// These should not fold
var raised = new Array(5);

function chooseNextMoveDumbOpponent(player, turn) {

  // If player doesn't have enough money to call, simply fold
  if (player.getCallAmount() > player.getMoney()) {
    playerFolds(player);
    return;
  }

  // Set the odds to do the following
  var oddsToStayCall = 80;
  var oddsToRaise = 20;
  var oddsToFold = 10;

  var requiredBet = getRequiredBet(turn);

  // Tweak odds so more likely to bet and fold at end
  if (turn == PREFLOP) {
    // Keep default
  } else if (turn == FLOP) {
    oddsToStayCall -= 5;
    oddsToRaise += 5;
  } else if (turn == TURN) {
    oddsToStayCall -= 10;
    oddsToRaise += 10;
  } else if (turn == RIVER) {
    oddsToStayCall -= 15;
    oddsToRaise += 15;
  } else {
    alertBug("Unrecognized turn constant "+turn+" in chooseNextMoveDumbOpponent");
  }

  // If no call amount, then player will not fold
  if (player.getCallAmount() == 0) {
    oddsToFold = 0;
  }

  // If player doesn't have enough money to raise, set odds to zero
  if (player.getCallAmount() + requiredBet > player.getMoney()) {
    oddsToRaise = 0;
  }

  // The players position at the table is used to store in array
  // of whether or not bet
  var index = getPositionByPlayer(player);

  if (currentTurn == turn) {
    if (raised[index]) {
      oddsToFold = 0;
    }
  } else {
    currentTurn = turn;
    raised = new Array(5);
  }

  // Roll die with arbitrary number sides
  var numberSides = oddsToStayCall + oddsToRaise + oddsToFold;
  var die = Math.floor(Math.random()*numberSides);

  // Player calls/stays 
  if (die < oddsToStayCall) {
    playerCalls(player);
    raised[index] = false;
  }

  // Player raises
  else if (die < oddsToStayCall + oddsToRaise) {
    playerBets(player, requiredBet);
    raised[index] = true;
  }

  // Player folds
  else if (die < oddsToStayCall + oddsToRaise + oddsToFold) {
    playerFolds(player);
    raised[index] = false;
  }

  else {
    alertBug(numberSides+"-sided die with an odd number "+die+" in chooseMethodDumpOpponent");
  }
}

function chooseNextMoveBasedOnOdds(player, turn) {
  
}
