OOD - blackjack design

这是一个使用Java编写的命令行二十一点游戏,作为高级编程课程的最终项目。游戏包括发牌、玩家选择等基本功能,并支持拆牌操作。本文探讨了面向对象编程的应用以及游戏设计的改进空间。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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
 
what happens if you draw an ace, then a 2, then a 10? (you are supposed to be at 13, but apparently your Hand.insert method puts you at 23, it does not appear to keep track of previous aces.) –  njzk2 Jan 21 '15 at 14:42
 
Njzk2, you are right. Never had a time to do this part. –  HelpNeeder  Jan 21 '15 at 16:22
 
I guess, we could solve this problem by checking the hands after each turn and do compare of the total with both upper and lower bound value of the aces. –  HelpNeeder  Jan 21 '15 at 16:24

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 deckplayersHandsplitHand, 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

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
2 
#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 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值