Take the 2-minute tour ×
Code Review Stack Exchange is a question and answer site for peer programmer code reviews. It's 100% free, no registration required.

This is a command line BlackJack game created in Java as my final project for an advanced programming class.

  • What do you think about it?
  • Have I used OOP correctly?
  • What grade should I get for this? :D
  • Any concept in game that could be improved?

I used JRE 1.7. And to make Unicode characters work you must use Unicode in Eclipse.

This is my Class Diagram:

enter image description here

BlackJack.java

import java.io.*;

public class BlackJack
{
    private static int BLACKJACK = 21;
    private static int DECKSIZE = 52;
    private static boolean isPlayerDone;

    public static void main(String[] args) throws IOException 
    {
        Deck deck = null;
        Hand playersHand = null;
        Hand splitHand = null;
        Hand dealersHand = null;

        System.out.println("--------------------------------------------------------"); 
        System.out.println("-               BLACK               JACK               -");
        System.out.println("--------------------------------------------------------\n"); 
        boolean runGame = true;
        while(runGame)
            switch(options())
            {
                case "deal":
                    dealersHand = new Hand("Dealer");
                    playersHand = new Hand("Player");
                    splitHand = null;

                    isPlayerDone = false;

                    deck = initialDraw(deck, playersHand, splitHand, dealersHand);

                    if (playersHand.getHandTotal() == BLACKJACK)
                    {
                        System.out.print("Player has BLACKJACK!\n\n");
                        isPlayerDone = true;
                        System.out.print("Dealer uncovers card...\n\n");
                        showHands(playersHand, splitHand, dealersHand);
                        System.out.print("Dealer's move...\n\n");
                        deck = dealerDraw(deck, playersHand, splitHand, dealersHand);
                        showHands(playersHand, splitHand, dealersHand);
                        compareHands(playersHand, splitHand, dealersHand);
                    } // end if()

                    break; // end case "deal"

                case "hit":
                    if(!isPlayerDone)
                        deck = hit(deck, playersHand, splitHand, dealersHand);
                    else
                        System.out.print("You must deal cards first!\n\n");
                    break; // end case "hit"

                case "stand":
                    if(!isPlayerDone)
                    {
                        isPlayerDone = true;
                        deck = stand(deck, playersHand, splitHand, dealersHand);
                    } // end if()
                    else
                        System.out.print("You must deal cards first!\n\n");
                    break; // end case "stand"

                case "split":
                    if(!isPlayerDone)
                        splitHand = split(playersHand, splitHand, dealersHand);
                    else
                        System.out.print("You must deal cards first!\n\n");
                    break; // end case "split"

                case "exit":
                    runGame = false;
                    System.out.print("Game ended.\n\n");
                    break; // end case "exit"

                default:
                    System.out.print("Invalid entry\n\n");
            } // end switch()
    } // end main()

    private static Hand split(Hand player, Hand split, Hand dealer)
    {
        if(player == null)
            System.out.print("You must deal cards first!\n\n");
        else if(player.getHandSize() == 2 && player.bothEqual())
        {
            split = new Hand("Player");
            split.insert(player.deleteFirst());

            showHands(player, split, dealer);
            compareHands(player, split, dealer);
        } // end else if()
        else if(!player.bothEqual())
            System.out.print("Both card values must be the same!\n\n");
        else
            System.out.print("You must have no more than 2 cards to split!\n\n");

        return split;
    } // end split()

    private static Deck stand(Deck deck, Hand player, Hand split, Hand dealer)
    {
        if(player == null)
            System.out.print("You must deal cards first!\n\n");
        else
        {   
            isPlayerDone = true;
            System.out.print("Dealer uncovers card...\n\n");
            showHands(player, split, dealer);
            System.out.print("Dealer's move...\n\n");
            deck = dealerDraw(deck, player, split, dealer);
            showHands(player, split, dealer);
            compareHands(player, split, dealer);
        } // end else

        return deck;
    } // end stay()

    private static Deck hit(Deck deck, Hand player, Hand split, Hand dealer)
    {
        if(player == null)
            System.out.print("You must deal cards first!\n\n");
        else
        {       
            deck = drawFromDeck(deck, player);
            System.out.print("\n");

            if(split != null)
            {
                deck = drawFromDeck(deck, split);
                System.out.print("\n");
            } // end if()

            showHands(player, split, dealer);
            compareHands(player, split, dealer);

            if (player.getHandTotal() == BLACKJACK)
            {
                System.out.print("Player has BLACKJACK!\n\n");
                isPlayerDone = true;
                System.out.print("Dealer uncovers card...\n\n");
                showHands(player, split, dealer);
                System.out.print("Dealer's move...\n\n");
                deck = dealerDraw(deck, player, split, dealer);
                showHands(player, split, dealer);
                compareHands(player, split, dealer);
            } // end if()
            else if(player.getHandTotal() > BLACKJACK)
            {
                System.out.print("Player Busted!\n\n");
                isPlayerDone = true;
                System.out.print("Dealer uncovers card...\n\n");
                showHands(player, split, dealer);
                compareHands(player, split, dealer);
            }
        } // end else

        return deck;
    } // end hit()

    private static Deck dealerDraw(Deck deck, Hand player, Hand split, Hand dealer)
    {
        if(player.getHandTotal() <= BLACKJACK)
        {
            // Dealer takes a precaution and only draws 
            // if hand total is less than or equal to 16.
            while(dealer.getHandTotal() <= 16 && 
                    (dealer.getHandTotal() <= player.getHandTotal() || 
                    (split != null  && dealer.getHandTotal() <= split.getHandTotal())))
                deck = drawFromDeck(deck, dealer);

            // Player has reached BLACKJACK!
            // There's no or little chance to win, 
            // dealer risks and draws even if total is high.
            if (player.getHandTotal() == BLACKJACK || (split != null  && 
                    split.getHandTotal() == BLACKJACK))
                while(dealer.getHandTotal() < BLACKJACK)
                    deck = drawFromDeck(deck, dealer);
        } // end if()

        return deck;
    } // dealerDraw()

    private static Deck drawFromDeck(Deck deck, Hand hand)
    {
        deck = checkDeck(deck);

        Card temp = new Card(deck.pop());

        if (hand.getName().equals("Dealer") && !isPlayerDone)
        {
            if(hand.getHandSize() < 1)
                System.out.print("Drawing Dealer's card... X_X");
            else
                System.out.print("Drawing Dealer's card... " + temp.toString());
        } // end if()
        else
        {
            if(hand.getName().equals("Dealer"))
                System.out.print("Drawing Dealer's card... " + temp.toString() + "\n");
            else
                System.out.print("Drawing Player's card... " + temp.toString());
        } // end else

        System.out.print("\n");

        hand.insert(temp);

        return deck;
    } // end drawFromDeck()

    private static void compareHands(Hand player, Hand split, Hand dealer)
    {
        if (isPlayerDone)
        {
            if(player.getHandTotal() > BLACKJACK || 
                    (split != null && split.getHandTotal() > BLACKJACK))
            {
                System.out.print("Player Busted!\n");
                if(dealer.getHandTotal() <= BLACKJACK)
                    System.out.print("Dealer Wins!\n\n");
            } // end if()
            else if(dealer.getHandTotal() > BLACKJACK)
            {
                System.out.print("Dealer Busted!\n");
                if(player.getHandTotal() <= BLACKJACK || 
                        (split != null && split.getHandTotal() <= BLACKJACK))
                    System.out.print("Player Wins!\n\n");
            } // end else if()
            else if(dealer.getHandTotal() > BLACKJACK && 
                    (player.getHandTotal() > BLACKJACK || 
                    (split != null && split.getHandTotal() > BLACKJACK)))
            {
                System.out.print("Both Busted!\n");
            } // end else if()
            else
            {
                if((player.getHandTotal() > dealer.getHandTotal() && 
                        player.getHandTotal() <= BLACKJACK) || 
                        (split != null && (split.getHandTotal() > dealer.getHandTotal() && 
                        player.getHandTotal() <= BLACKJACK)))
                    System.out.print("Player Wins!\n\n");
                else if((player.getHandTotal() < dealer.getHandTotal() && 
                        dealer.getHandTotal() <= BLACKJACK) || 
                        (split != null && (split.getHandTotal() < dealer.getHandTotal() && 
                        dealer.getHandTotal() <= BLACKJACK)))
                    System.out.print("Dealer Wins!\n\n");

                if(player.getHandTotal() == BLACKJACK || 
                    (split != null && split.getHandTotal() == BLACKJACK))
                    System.out.print("Player has BLACKJACK!\n\n");
                if(dealer.getHandTotal() == BLACKJACK)
                    System.out.print("Dealer has BLACKJACK!\n\n");
            } // end else
        } // end if()
    } // end compareHands()

    private static Deck checkDeck(Deck deck)
    {
        if(deck == null)
            deck = createDeck();
        else if(deck.isEmpty())
        {
            System.out.print("\nDeck is empty! You must create and shuffle new deck of cards!\n\n");
            deck = createDeck();
        } // end else if()

        return deck;
    } // end checkDeck()

    private static Deck createDeck()
    {
        System.out.println("Creating deck...");
        Deck deck = new Deck(DECKSIZE);
        deck.createDeck();
        System.out.println("Shuffling deck...");
        deck.shuffleDeck();
        System.out.print("\n");

        return deck;
    } // end createDeck()

    private static Deck initialDraw(Deck deck, Hand player, Hand split, Hand dealer)
    {
        deck = drawFromDeck(deck, player);
        deck = drawFromDeck(deck, dealer);
        deck = drawFromDeck(deck, player);
        deck = drawFromDeck(deck, dealer);

        System.out.print("\n");

        showHands(player, split, dealer);
        compareHands(player, split, dealer);

        return deck;
    } // end initialDraw()

    private static void showHands(Hand player, Hand split, Hand dealer)
    {
        System.out.print("Dealers Hand:");

        if(!isPlayerDone)
        {
            dealer.peek();
            System.out.print(" X_X = " + dealer.peekValue() + "\n");
        } // end if()
        else
        {
            dealer.displayHand();
            System.out.print(" = " + (dealer.getHandTotal() == BLACKJACK ? 
                    dealer.getHandTotal() + " : BLACKJACK!" : 
                    ((dealer.getHandTotal() > BLACKJACK) ? 
                    dealer.getHandTotal() + " : BUSTED!" : 
                    dealer.getHandTotal())) + "\n");
        } // end else

        System.out.print("Players Hand:");
        player.displayHand();
        System.out.print(" = " + (player.getHandTotal() == BLACKJACK ? 
                player.getHandTotal() + " : BLACKJACK!" : 
                ((player.getHandTotal() > BLACKJACK) ? 
                player.getHandTotal() + " : BUSTED!" : 
                player.getHandTotal())) + "\n");

        if (split != null)
        {
            System.out.print("Players Hand:");
            split.displayHand();
            System.out.print(" = " + (split.getHandTotal() == BLACKJACK ? 
                    split.getHandTotal() + " : BLACKJACK!" : 
                    ((split.getHandTotal() > BLACKJACK) ? 
                    split.getHandTotal() + " : BUSTED!" : 
                    split.getHandTotal())) + "\n\n");
        } // end if()
        else
            System.out.print("\n");
    } // end showHands()

    private static String options() throws IOException
    {
        System.out.print("deal, hit, split, stand, exit: ");
        InputStreamReader isr = new InputStreamReader(System.in);
        BufferedReader br = new BufferedReader(isr);
        String s = br.readLine();
        System.out.print("\n"); 
        return s;
    } // end options()
} // end BlackJack

Hand.java

class Hand
{
    private Card first;
    private int cardTotal;
    private String name;
    private int handSize;

    public Hand(String name)
    {
        first = null;
        this.name = name;
        cardTotal = 0;
        handSize = 0;
    } // end Hand()

    public void insert(Card card)
    {
        Card newLink = new Card(card);
        newLink.next = first;

        if (card.getRank() == 1 && cardTotal + card.getValue() > 21)
            cardTotal = cardTotal + (card.getValue() - 10);
        else
            cardTotal = cardTotal + card.getValue();

        handSize = handSize + 1;

        first = newLink;
    } // end insert()

    public Card deleteFirst()
    {
        Card temp = first;
        first = first.next;
        cardTotal = cardTotal - temp.getValue();
        handSize = handSize - 1;
        return temp;
    } // end deleteFirst()

    public void displayHand()
    {
        Card current = first;
        while(current != null)
        {
            current.showCard();
            current = current.next;
        } // end while()
    } // end displayHand()

    public boolean isEmpty()
    {
        return first == null;
    } // end isEmpty()

    public boolean bothEqual()
    {
        Card temp = first;
        return temp != null && (temp.getValue() == temp.next.getValue());
    } // end bothEqual()

    public void peek()
    {
        first.showCard();
    } // end peek()

    public int peekValue()
    {
        return first.getValue();
    } // end peekValue()

    public int getHandSize()
    {
        return handSize;
    } // end getHandSize()

    public String getName()
    {
        return name;
    } // end getName()

    public int getHandTotal()
    {
        return cardTotal;
    } // end getHandTotal()
} // end Hand

Deck.java

class Deck
{
    private int maxSize;
    private Card[] stackArray;
    private int top;

    public Deck(int s)
    {
        maxSize = s;
        stackArray = new Card[maxSize];
        top = -1;
    } // end Deck()

    private void push(Card card)
    {
        stackArray[++top] = new Card(card);
    } // end push()

    public Card pop()
    {
        return stackArray[top--];
    } // end pop()

    public boolean isEmpty()
    {
        return top == -1;
    } // end isEmpty()

    public void shuffleDeck()
    {
        Card swap;

        for (int i = 0; i < stackArray.length; i++) 
        {
            int r = i + (int) (Math.random() * (stackArray.length - i));
            swap = stackArray[i];
            stackArray[i] = stackArray[r];
            stackArray[r] = swap;
        } // end for()
    } // end shuffleDeck()

    public void createDeck()
    {
        String[] suit = {"\u2663", "\u2666", "\u2665", "\u2660"};
        int[] rank = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13};

        for (int i = 0; i < rank.length; i ++)
        {
            for (int j = 0; j < suit.length; j++)
            {
                push(new Card(suit[j], rank[i]));
            } // end for()
        } // end for()
    } // end createDeck()
} // end Deck

Card.java

class Card
{
    public Card next;
    private String suit;
    private int rank;

    Card(String suit, int rank)
    {
        this.suit = suit;
        this.rank = rank;
    } // end Card()

    Card(Card card)
    {
        suit = card.suit;
        rank = card.rank;
    } // end Card()

    private String getRankName()
    {
        if (rank == 1)
            return "A";
        else if (rank == 11)
            return "J";
        else if (rank == 12)
            return "Q";
        else if (rank == 13)
            return "K";
        else
            return String.valueOf(rank);
    } // end getRankName()

    public int getValue()
    {
        if (rank == 1)
            return 11;
        else if (rank == 11 || rank == 12 || rank == 13)
            return 10;

        return rank;
    } // end getValue()

    public String getSuit()
    {
        return suit;
    } // end getSuit()

    public int getRank()
    {
        return rank;
    } // end getRank()

    public void showCard()
    {
        System.out.print(" " + getRankName() + "_" + suit);
    } // end showCard()

    @Override
    public String toString()
    {
        return getRankName() + "_" + suit;
    } // end toString()
} // end Card
share|improve this question
3  
If you ever have a class with only static methods, it is should only be a Utility class (such as for static operations, extracting variables etc). This class is not OO! –  Tom Cammann Dec 19 '12 at 18:58
add comment

2 Answers

up vote 8 down vote accepted
+50

A few notes:

  1. I don't think you're utilizing OOP to its full potential in your BlackJack class; all its methods are static and you're passing around too many variables. A cleaner alternative would be to make deck, playersHand, splitHand, and dealersHand class-level variables, change the methods to be non-static, and then you won't have to pass them all around. So something like this:

    public class BlackJack {
    
      ...
    
      private Deck deck;
      private Hand splitHand;
      private Hand playersHand;
      private Hand dealersHand;
      private boolean isPlayerDone;
    
      public static void main(String[] args) throws IOException {
        BlackJack blackjack = new BlackJack();
        blackjack.start();
      }
    
      public void start() {
        while(runGame) {
          switch(options()) {
            case "deal":
              deal();
              break;
            case "hit":
              hit();
              break;
            case "stand":
              stand();
              break;
            ...
          }
        }
      }
    
      public void deal() {
        playersHand = new Hand("Player");
        dealersHand = new Hand("Dealer");
        splitHand = null;
        isPlayerDone = false;
    
        ...
      }
    
      ...
    
    }
    
  2. The Hand class doesn't really need a name because there are only 2 types of hands: dealer and player. So you can just pass in a boolean for drawFromDeck():

    private Deck drawFromDeck(boolean drawForPlayer) {
      Hand hand = drawForPlayer ? playersHand
                                : dealersHand;
    
      ...
    }
    
  3. You have several different places where you're checking for blackjack, and I can't easily follow the logic. compareHands() checks for blackjack, but there are a couple other places with some checks too. These might be necessary (I don't know the rules of Blackjack that well), but you should try to minimize duplicate logic as much as possible. For example, this block of code is in both main() and hit():

    if (player.getHandTotal() == BLACKJACK)
    {
        System.out.print("Player has BLACKJACK!\n\n");
        isPlayerDone = true;
        System.out.print("Dealer uncovers card...\n\n");
        showHands(player, split, dealer);
        System.out.print("Dealer's move...\n\n");
        deck = dealerDraw(deck, player, split, dealer);
        showHands(player, split, dealer);
        compareHands(player, split, dealer);
    } // end if()
    

I think fixing those (mostly points 1 & 3) would go a long way to making the code easier to read and maintain. I skimmed over your other classes and they seemed fine at a glance, having good separation of concerns.

share|improve this answer
    
Thanks for yourr feedback. I was wondering with con/pros for global class variables but then decided to try to have as little global variables as possible. I know they would have reduced amount of variables. Overall I guess you are right with your comments. Thanks. –  HelpNeeder Dec 19 '12 at 21:35
    
@HelpNeeder: is there anything else you wanted me to critique specifically or are you just looking for other opinions? –  seand Dec 21 '12 at 15:19
add comment

Some remarks:

  1. Your main method is HUGE, consider splitting it
  2. This code:

    deck = drawFromDeck(deck, player);
    deck = drawFromDeck(deck, dealer);
    deck = drawFromDeck(deck, player);
    deck = drawFromDeck(deck, dealer);
    

    can be reduced to this:

    for(int i = 0; i < 4; i++)
        deck = drawFromDeck(deck, dealer);
    
  3. This code:

    System.out.print(" = " + (player.getHandTotal() == BLACKJACK ? 
            player.getHandTotal() + " : BLACKJACK!" : 
            ((player.getHandTotal() > BLACKJACK) ? 
            player.getHandTotal() + " : BUSTED!" : 
            player.getHandTotal())) + "\n");
    

    is duplicated the lines below, you can create a method that accepts an Hand and does its calculations

  4. getRankName() in Card.java could be rewritten more cleanly with a switch
share|improve this answer
1  
#2, how would you rewrite this? I mean first we must draw player's card, then dealer's, then player's, then dealer's. The for loop does not do this... –  HelpNeeder Dec 25 '12 at 22:33
1  
#2 could be done like so (typed without a compiler): for(hand : new Hand[]{player,dealer,player,dealer}) deck = drawFromDeck(deck,hand); . Not sure if that is actually better. –  Brian Dec 26 '12 at 18:26
    
@HelpNeeder oh, I'm sorry, I misread that code :| –  miniBill Dec 26 '12 at 19:25
1  
@Brian, this is interesting. I never used an array within for loop like you did. Thanks. But I wouldn't do this, in this situation, like this because it's unnecessary to allocate more memory space for such array. –  HelpNeeder Dec 26 '12 at 22:17
1  
@HelpNeeder: This is a pretty inexpensive allocation. Keep in mind that it is an array of Hand references, not an array of Hand values. As a general rule, you should ignore minor inefficiencies in favor of cleaner code. Though I am not sure if my proposal is actually cleaner; I am merely extending miniBill's answer to handle your situation. –  Brian Dec 26 '12 at 22:33
show 1 more comment

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.