Blackjack Strategy

I am creating a Blackjack simulator in Java in order to better understand the language. I have written the basic strategy section for a player and it is terribly long.

Is there a better way to do this other than if/else statements? I know a switch might work but that wouldn't make it much shorter. The reality is that there are quite a few situations and reactions when playing Blackjack.

public void play(Card upCard){

//Use this block to check for situations that happen right after dealing
//like blackjack, doubledown and splits.
if (getHand().size() == 2){
if (getHandValue() == 21){
blackjack();
}

//scenario with ace as one of the cards.
else if (getHand().get(0).getName().equals("Ace") |
getHand().get(1).getName().equals("Ace")){

//Check if both cards are aces, always split aces
if (getHand().get(0).getName().equals("Ace") &
getHand().get(1).getName().equals("Ace")){

split();
}

//Double down on Ace, 2 against dealer 5 or 6
else if(getHand().get(0).getName().equals("Two") |
getHand().get(1).getName().equals("Two") &
upCard.getValue() >= 5 & upCard.getValue() <= 6
){
doubleDown();
return;

}

//Double down on Ace, 3 against dealer 5 or 6 (same as 2, broken
//up to make code easier to read
else if(getHand().get(0).getName().equals("Three") |
getHand().get(1).getName().equals("Three") &
upCard.getValue() >= 5 & upCard.getValue() <= 6
){
doubleDown();
return;
}

//Double down on Ace 4 against 4,5, or 6.
else if(getHand().get(0).getName().equals("Four") |
getHand().get(1).getName().equals("Four") &
upCard.getValue() >= 4 & upCard.getValue() <= 6
){
doubleDown();
return;
}

//Double down on Ace, 5 against 4,5, or 6, same as 4, broken up
//to make code easier to read
else if(getHand().get(0).getName().equals("Five") |
getHand().get(1).getName().equals("Five") &
upCard.getValue() >= 4 & upCard.getValue() <= 6
){
doubleDown();
return;
}

//Double down on Ace, 6 against dealer 3-6
else if(getHand().get(0).getName().equals("Six") |
getHand().get(1).getName().equals("Six") &
upCard.getValue() >= 3 & upCard.getValue() <= 6
){
doubleDown();
return;
}

//Double down on Ace, 7 against dealer 3-6, same as 6, broken up
//to make code easier to read
else if(getHand().get(0).getName().equals("Seven") |
getHand().get(1).getName().equals("Seven") &
upCard.getValue() >= 3 & upCard.getValue() <= 6
){
doubleDown();
return;
}
}

//If pair of 2's, split if dealer card is between 4 and 7
else if (getHand().get(0).getName().equals("Two") &
getHand().get(1).getName().equals("Two") &
upCard.getValue() >= 4 & upCard.getValue() <= 7){

split();
}

//If pair of 3's, split if dealer card is between 4 and 7
//Same as pair of 2's, broken up for ease of reading code
else if (getHand().get(0).getName().equals("Three") &
getHand().get(1).getName().equals("Three") &
upCard.getValue() >= 4 & upCard.getValue() <= 7){

split();
}

//If pair of 6's, split against dealer's 3-6
else if (getHand().get(0).getName().equals("Six") &
getHand().get(1).getName().equals("Six") &
upCard.getValue() >= 3 & upCard.getValue() <= 6){

split();
}

//If pair of 7's, split against dealer's 2-7
else if (getHand().get(0).getName().equals("Seven") &
getHand().get(1).getName().equals("Seven") &
upCard.getValue() >= 2 & upCard.getValue() <= 7){

split();
}

//Check if both cards are 8's, always split 8's
else if (getHand().get(0).getName().equals("Eight") &
getHand().get(1).getName().equals("Eight")){
split();
}

//If pair of 9's, split against dealer's 2-6 and 8-9
//handles 2-6
else if (getHand().get(0).getName().equals("Nine") &
getHand().get(1).getName().equals("Nine") &
upCard.getValue() >= 2 & upCard.getValue() <= 6){

split();
}

//If pair of 9's, split against dealer's 2-6 and 8-9
//handles 8-9
else if (getHand().get(0).getName().equals("Nine") &
getHand().get(1).getName().equals("Nine") &
upCard.getValue() >= 8 & upCard.getValue() <= 9){

split();
}

}

while(getHandValue() <= 17 & !busted()){
if (getHandValue() <= 8){
hit();
}

else if (getHandValue() == 9){

if(upCard.getValue() >= 3 & upCard.getValue() <= 6){
doubleDown();
return;
}

else{ //dealer up card is 2 or 7 or above
hit();
}
}

else if (getHandValue() == 10){

if(upCard.getValue() >= 2 & upCard.getValue() <= 9){
doubleDown();
return;
}

else{ //dealer up card is 10 or Ace
hit();
}
}

else if (getHandValue() == 11){

if(upCard.getValue() >= 2 & upCard.getValue() <= 10){
doubleDown();
return;
}

else{ //dealer up card is Ace
hit();
}
}

else if (getHandValue() == 12){

//upcard is 2,3 or greater than or eqaual to 7.
//stand otherwise, no else statement needed for standing
if(upCard.getValue() <= 3 & upCard.getValue() >= 7){
hit();
}
}

else if (getHandValue() >= 13 & getHandValue() <= 16){

if(upCard.getValue() >= 7){
hit();
}
}

//hand is at least 17
else{
return;
}
}
}

-

Two words.

Lookup tables.

Here's one I randomly found on the internet:

All you gotta do is make a 2D array, populate it with the right values (enum?), and alter your play(Card upCard) method to use the lookup table.

That solves your complexity issue, your implicit data storage (you're trying to algorithmically map the values of 3 variables (card 1, card 2, dealer up card) to a lot of different values), and it should be a speedup too. Plus, what are blackjack explanations doing in your code?

Note that you might have to split your lookup table in three sections: One for the doubles, one for the case where you have an ace, and one for the last case, where you need to use the total of your hand.

That said, looking at your code, there's some things that could also look a bit differently:

getHand().get(0).getName().equals("Ace") |
getHand().get(1).getName().equals("Ace")


Seems like this could get wrapped into a method called containsCard(String name). I'm worried about a single pipe though - Isn't it ||?

getHand().get(0).getName().equals("Ace") &
getHand().get(1).getName().equals("Ace")


Same thing here: this could be boolean hasPair(). Also, isn't it &&?

getHand().get(0).getName().equals("Two") |
getHand().get(1).getName().equals("Two") &
upCard.getValue() >= 5 & upCard.getValue() <= 6


This looks like it contains a bug.

Let's rephrase it:

firstCard is "Two" |
secondCard is "Two" &
(dealerCard is 5 or dealerCard is 6)


Notice the problem? (I'm not sure if using | compared to || prevents this from becoming a bug, but...) || short-circuit evaluates. Basically, if firstCard is "Two" is true, then the whole if statement is true. Whoops.

-
Thanks @Pimgd. I will have a look at this, certainly easier than typing each case out one at a time. As far as the explanations, it is so I know which line does what without having to read the code. After staring at it for so long the text helps. –  user2926358 Jul 23 '14 at 20:15
@user2926358 updated my answer with some details about the code, because you're also doing some things that I consider strange. –  Pimgd Jul 23 '14 at 20:25
Using || or && instead of only a single character is to short-circuit. So if you can determine the result from the first operand, the second operand is not evaluated. I would say it is better practice in cases like this to use the short-circuiting version. –  Simon André Forsberg Jul 23 '14 at 20:28
Even then, | or ||, it doesn't change the result here. Because you still end up with true OR something else, which is true. So that's a bug. –  Pimgd Jul 23 '14 at 20:34
That's actually not a bug, it is far from ideal code. The statement you used as an example is found within an if statement that is true only if one of the cards is an Ace. However, I didn't create a method to remember which of the two cards is the ace, so I do the check again, on both cards, for the card I am looking for. This is confusing and hardly a great way to do this. –  user2926358 Jul 23 '14 at 20:51