Assigning values to cards in a deck

1.2k Views Asked by At

I am working on building a blackjack game for my first programming class. I have zero programming experience other than the bit I've had in this class. Our major project is to build and run a blackjack program using java.

I have built my deck, shuffled it and dealt a card. Where I am at a stand still is assigning a value to the card that was pulled. I am not sure how to implement this. Can anyone provide any direction for me? Any help would be greatly appreciated!

Below are my Card and Deck class:

public class Card
{
    private int suit;
    private int rank;   
    private int value;
    private String [] suits = {"Spades", "Hearts", "Clubs", "Diamonds"};
    private String [] ranks = {"Ace", "2", "3", "4", "5", "6", "7", "8", "9", "10", "Jack", "Queen", "King"};

    public Card (int cardSuit, int cardRank)
    {
        suit = cardSuit;
        rank = cardRank;
    }

    public int getSuit()
    {
        return suit;
    }

    public int getRank()
    {
        return rank;
    }

    public String toString()
    {
        return ranks[rank] + " of " + suits[suit];
    }
}

import java.util.ArrayList;
import java.util.*;

public class Deck
{   
    private ArrayList<Card> deck;
    private int suitCounter;
    private int rankCounter;
    private int randomIndex;
    private Card card;
    private ArrayList<Card> temp;
    private Card removeCard;

    public Deck()    //building deck with 52 cards
    {
        deck = new ArrayList<Card>();
        
        for(suitCounter = 0; suitCounter < 4; suitCounter++)
        {
            for(rankCounter = 0; rankCounter < 13; rankCounter++)
            {
                deck.add(new Card(suitCounter, rankCounter));
            }
        }
    } 
    
    public void shuffle()    //shuffling the deck
    {
        Collections.shuffle(deck);
    }

    public Card deal()      //picking the first card out of the deck
    {
        card = deck.get(0);
        return card;
    }

    public void remove()     //removing the dealt card from the deck and putting it in a new  temporary deck
    {
        temp = new ArrayList<Card>();

        removeCard = deck.remove(0);
        temp.add(removeCard);
    }   
    
    public String toString()      //display the choosen card
    {
        return "Card: " + card;
    }
}
 

I do not have an official blackjackgame class with a main method as of yet. My hope was to get the deck built, deal a card and get the value and then I'd start building the main method. But here is what I have to help to know if I printed out the card with a value or not.

public class Game
{
    public static void main(String[] args)
    {   
        Deck playingDeck = new Deck();      //creating deck object
        playingDeck.shuffle();              //shuffling deck
        playingDeck.deal();                 //dealing card 1 of player's hand
        playingDeck.remove();               //removing card 1 from deck 
        System.out.println(playingDeck);    //printing out card 1 
        playingDeck.deal();                 //dealing card 2 of player's hand
        playingDeck.remove();               //removing card 2 from deck 
        System.out.println(playingDeck);    //printing out card 2 
    }

Thank you in advance!

1

There are 1 best solutions below

1
Mushroomator On

Some issues

Although you are generally on the right track, your implementation has some design decisions that you should reconsider. This is part of the reason why you find it hard to assign a value to the card that is drawn. In your design a card has a value attribute but if you think about it, a card's value in BlackJack is determined by its rank and its rank only. Additionally, it will not be changing over time, that is why you should keep the value closely coupled to the rank. Another thing to consider for both, ranks and suits, is there is only a limited set of allowed and well-defined values which is far smaller than the range of values an int or String could take so I would propose you use an enum to type those values. Always keep in mind enums are just special classes so they can have attributes as well.

Here how I would implement Rank and Suit.

Rank.java

public enum Rank {
    // the value is determined only by rank so assign the value here!
    TWO(2, "2"),
    THREE(3, "3"),
    FOUR(4, "4"),
    FIVE(5, "5"),
    SIX(6, "6"),
    SEVEN(7, "7"),
    EIGHT(8, "8"),
    NINE(9, "9"),
    TEN(10, "10"),
    JACK(10, "J"),
    QUEEN(10, "Q"),
    KING(10, "K"),
    ACE(11, "A");

    private final int value;
    private final String display;

    /**
     * Each rank has a value and a String which determines how the rank is displayed in the console.
     * @param value value assigned to a rank
     * @param display String displayed to represent this rank
     */
    Rank(int value, String display) {
        this.value = value;
        this.display = display;
    }

    public int getValue() {
        return value;
    }

    public String getDisplay() {
        return display;
    }

    @Override
    public String toString() {
        return display;
    }
}

Suit.java

public enum Suit {
    SPADES("Spades"),
    HEARTS("Hearts"),
    CLUBS("Clubs"),
    DIAMONDS("Diamonds");

    // display value in console
    private final String display;

    /**
     * Suit of a card
     * @param display display value for a suit
     */
    Suit(String display) {
        this.display = display;
    }

    public String getDisplay() {
        return display;
    }

    @Override
    public String toString() {
        return display;
    }
}

This has some nice benefits for the Card class. A card only needs a Suit and a Rank and that's it. With it's rank it will automatically have a value assigned as well now. The problem with your implementation of the card is that you had an array of all possible values for ranks and suits in every card. Again, think of the real world. A card certainly does not hold all values of possible ranks and suits, but only has one rank and one suit. Your class Card is a somewhat simplified model of the real-world card so it should have the properties of a real-world card. Designing it this way will also help you staying on top of your program because your design will be easier to comprehend.

Here my implementation of the card.

Card.java

public class Card {
    // value of a card is not going to change, so we can make it final
    private final Suit suit;
    private final Rank rank;

    public Card(Suit suit, Rank rank) {
        this.suit = suit;
        this.rank = rank;
    }

    public Suit getSuit() {
        return suit;
    }

    public Rank getRank() {
        return rank;
    }

    @Override
    public String toString() {
        // prints e.g. "2-Hearts"
        return rank.toString() + "-" + suit.toString();
    }
}

Now, let's move on to the Deck class. This war actually quite well designed by you as a deck is just a collection of cards. But there is a issue with your method deal() which hinders you from knowing which card was drawn. In your implementation you just remove the card on the top of the deck but you don't return it, so you're essentially just wiping a card from memory and it is lost and cannot be played in the game. The remove() method of an ArrayList does return the removed value by default so use that. Also, that's maybe splitting hairs, but I think you should rename deal() to drawTop() as dealing in the real-world involves first drawing a card then giving it to a player, who then accepts that card and puts it in his hand.

This is my Deck implementation. There are some changes required due to the previous changes I've made but this is essentially your Deck with the change described above.

Deck.java

import java.util.ArrayList;
import java.util.*;

public class Deck {
    private final ArrayList<Card> deck;

    /**
     * Building deck with 52 cards
     *
     * Try to use Java Doc syntax to like this to comment on methods.
     * These comments will be shown in IDEs and an HTML page can be auto-generated from them.
     */
    public Deck(){
        // get all possible value from the enums for Suit and Ranks
        var allSuits = Suit.values();
        var allRanks = Rank.values();
        // You can provide an initial capacity for how many card you are going to have
        // this will avoid unnecessary resizing of the array but that is not that important here
        this.deck = new ArrayList<Card>(allSuits.length * allRanks.length);
        // These are for-each loop that loop over all possible suits and ranks
        for (var suit : allSuits) {
            for (var rank : allRanks) {
                // add one card for each suit-rank combination
                deck.add(new Card(suit, rank));
            }
        }
    }

    /**
     * Shuffle the deck.
     */
    public void shuffle()    //shuffling the deck
    {
        Collections.shuffle(deck);
    }

    public Card drawTop()      //picking the first card out of the deck
    {
        // check if Deck is empty or not!
        if(this.isEmpty()) return null;
        // remove removes the card and returns the removed card
        return deck.remove(0);
    }

    /**
     * Checks if deck is empty i. e. no cards left in the deck.
     * @return true, if no cards are left in deck, false otherwise
     */
    public boolean isEmpty(){
        return deck.isEmpty();
    }

    /**
     * Gets number of remaining cards in the deck. This is helpful for debugging purposes.
     * @return number of remaining cards in the deck
     */
    public int getNoRemainingCars(){
        return deck.size();
    }

    @Override
    public String toString() {
        return deck.toString();
    }
}

You are now able to call var topCard = new Deck().drawTop() and it will remove a card from the top of the deck and this card will be stored in topCard. You can just simply call System.out.println(topCard) or use topCard.getRank() and topCard.getSuit() to see which card was drawn. Remember with the rank you also will get the value of the card.

See this main method on how you can now interact with all those classes.

public static void main(String[] args) {
        // create the deck with all 52 cards
        var deck = new Deck();
        // print the deck
        System.out.printf("Newly created deck: %s\n", deck);
        // shuffle the cards
        deck.shuffle();
        System.out.printf("Shuffled deck: %s\n", deck);
        // remove the first card
        var dealtCard = deck.drawTop();
        // print the car that was dealt
        System.out.printf("Removed card: %s\n", dealtCard);
        // print info about that card
        System.out.printf("The card that was removed from the deck is the %s of %s\n", dealtCard.getRank(), dealtCard.getSuit());
        // print deck again to show that the card is no longer in the deck
        System.out.printf("The deck after a card was removed: %s\n", deck);
}

Expected output:

Newly created deck: [2-Spades, 3-Spades, 4-Spades, 5-Spades, 6-Spades, 7-Spades, 8-Spades, 9-Spades, 10-Spades, J-Spades, Q-Spades, K-Spades, A-Spades, 2-Hearts, 3-Hearts, 4-Hearts, 5-Hearts, 6-Hearts, 7-Hearts, 8-Hearts, 9-Hearts, 10-Hearts, J-Hearts, Q-Hearts, K-Hearts, A-Hearts, 2-Clubs, 3-Clubs, 4-Clubs, 5-Clubs, 6-Clubs, 7-Clubs, 8-Clubs, 9-Clubs, 10-Clubs, J-Clubs, Q-Clubs, K-Clubs, A-Clubs, 2-Diamonds, 3-Diamonds, 4-Diamonds, 5-Diamonds, 6-Diamonds, 7-Diamonds, 8-Diamonds, 9-Diamonds, 10-Diamonds, J-Diamonds, Q-Diamonds, K-Diamonds, A-Diamonds]
Shuffled deck: [6-Diamonds, 9-Hearts, Q-Diamonds, 2-Hearts, 7-Hearts, A-Clubs, J-Clubs, A-Hearts, 3-Clubs, 4-Hearts, 3-Hearts, Q-Clubs, 9-Clubs, 6-Hearts, K-Clubs, 8-Clubs, 2-Diamonds, J-Hearts, J-Spades, 4-Diamonds, A-Spades, 7-Clubs, 3-Diamonds, 4-Clubs, Q-Spades, 5-Spades, 5-Hearts, 10-Clubs, 2-Spades, 5-Clubs, K-Hearts, K-Diamonds, 4-Spades, A-Diamonds, 6-Spades, 9-Spades, 3-Spades, 10-Spades, 7-Diamonds, K-Spades, J-Diamonds, 7-Spades, 6-Clubs, 5-Diamonds, 8-Spades, 10-Diamonds, 9-Diamonds, Q-Hearts, 10-Hearts, 8-Hearts, 2-Clubs, 8-Diamonds]
Removed card: 6-Diamonds
The card that was removed from the deck is the 6 of Diamonds
The deck after a card was removed: [9-Hearts, Q-Diamonds, 2-Hearts, 7-Hearts, A-Clubs, J-Clubs, A-Hearts, 3-Clubs, 4-Hearts, 3-Hearts, Q-Clubs, 9-Clubs, 6-Hearts, K-Clubs, 8-Clubs, 2-Diamonds, J-Hearts, J-Spades, 4-Diamonds, A-Spades, 7-Clubs, 3-Diamonds, 4-Clubs, Q-Spades, 5-Spades, 5-Hearts, 10-Clubs, 2-Spades, 5-Clubs, K-Hearts, K-Diamonds, 4-Spades, A-Diamonds, 6-Spades, 9-Spades, 3-Spades, 10-Spades, 7-Diamonds, K-Spades, J-Diamonds, 7-Spades, 6-Clubs, 5-Diamonds, 8-Spades, 10-Diamonds, 9-Diamonds, Q-Hearts, 10-Hearts, 8-Hearts, 2-Clubs, 8-Diamonds]

The next steps

Now, this is it as far as your first question goes. But as you are a beginner and you wanted some pointers I've decided to give you some pointers on what to do next and how you could do it :) I know it's quite a lot to take in, but I am sure you will get your head around it. In the next few sections, I will show you can you can actually draw cards from the deck and deal them to players.

A few sentences ago I mentioned

dealing in the real-world involves first drawing a card then giving it to a player, who then accepts that card and puts it in his hand

The nouns in this sentence should be your next classes and the verbs should be your next methods. So that would be:

  • Hand
    • a collection of cards
    • ability to add and remove cards from the hand
  • Player
    • ability to accecpt a card an put it into his/ her hand
    • should have a Hand which is again a collection of Cards
  • Croupier (for the dealing cards part in the sentence)
    • Croupier is a player and has additional properties and abilities
    • needs a Deck of cards which he/ she can deal to players
    • ability to draw a card from the Deck and deal it to a player

Hand

A hand is a set of cards (I am using the Java data structure HashSet here) and there is a total value of all cards, so this should be the attributes. Additionally a Hand should allow to add and remove cards to it, this will be our methods. When adding and removing we need to adjust the total value of the hand accordingly.

Hand.java

mport java.util.HashSet;
import java.util.Set;

public class Hand {
    private final Set<Card> cards;
    private int totalValue;

    public Hand() {
        this.cards = new HashSet<>();
        this.totalValue = 0;
    }

    /**
     * Add a card to the hand
     * @param card card to be added
     */
    public void addCard(Card card){
        // add card to hand
        cards.add(card);
        // increase total value by value of the card
        totalValue += card.getRank().getValue();
    }

    public void removeCard(Suit suits, Rank rank){
        var searchedCard = new Card(suits, rank);
        // call other remove method which does the actual removal
        removeCard(searchedCard);
    }

    public void removeCard(Card card){
        // remove card if it is present
        cards.remove(card);
        // remove card from total value
        totalValue -= card.getRank().getValue();
    }

    @Override
    public String toString() {
        return "Hand{" +
                "cards=" + cards +
                ", totalValue=" + totalValue +
                '}';
    }
}

Player

Let's move on to the player. A player will have a name and a Hand. Additionally he/ she (a gender is also be possible if you want to assign one btw) should have the ability to accept a card and put it in their hand when a card is dealt to them. A player will also have other abilities like making a decision whether to take on another card or not and other stuff, but that is up to you to extend on that, I will just focus on how accepting/ receiving a card.

Player.java

public class Player {
    // protected is a necessary access qualifier so subclasses of Player (Croupier will also have the property name and hand without specifically stating that, see Croupier.java) 
    protected String name;
    protected Hand hand;

    public Player(String name) {
        this.name = name;
        this.hand = new Hand();
        System.out.printf("Player %s has joined the game.\n", name);
    }

    /**
     * Receive a card and add it to the hand
     * @param card card to be received
     */
    public void receiveCard(Card card){
        // add card to hand
        hand.addCard(card);
    }

    @Override
    public String toString() {
        return "Player{" +
                "name='" + name + '\'' +
                ", hand=" + hand +
                '}';
    }

    public String getName() {
        return name;
    }
}

Croupier

Now it gets interesting as a croupier is a player with all the abilities and properties of a player but he/ she also has more abilities and properties. To reflect that in Java we use a feature called inheritance. So a Croupier has everything a Player has and more so it extends the Player. Inheritance will allow us to simply define that and we don't have to re-implement everything we have done for the Player again for the Croupier. So we only need to focus on the additional properties and abilities of a Croupier i. e. he/ she gets a deck of cards which he/ she can shuffle and deal to players. To shuffle we just use the shuffle() method of the Deck. To deal a card we use the drawTop() method of the Deckto draw a card from the deck (and get that card returned) and the receiveCard() method of the Player which we pass that drawn card to. This way the top card from the deck will be removed and added to the hand of the player. All this happens in the dealCardTo() method.

Croupier.java

/**
 * Croupier extends Player as he/she is also a Player and should have the same possibilities as a player, but also has additional functionality.
 * Google for Java inheritence for more information
 */
public class Croupier extends Player{
    private final Deck deck;

    public Croupier(String name, Deck deck) {
        // call constructor of superclass player
        super(name);
        this.deck = deck;
    }

    public void dealCardTo(Player player){
        // get one card of the deck
        var cardToDeal = deck.drawTop();
        // deal the card to the player
        player.receiveCard(cardToDeal);
        System.out.printf("%s is dealing a card to %s.\n", name, player.getName());
    }

    // this is just for demo purposes
    public String showDeck(){
        return deck.toString();
    }

    public void shuffleCards(){
        deck.shuffle();
        System.out.printf("Croupier %s has shuffled the cards.\n", name);
    }

    @Override
    public String toString() {
        return "Croupier{" +
                "name='" + name + '\'' +
                ", hand=" + hand +
                ", deck=" + deck +
                '}';
    }
}

Putting it all together

Now we have all necessary features to deal cards to players so here an quick example of how we would do that.

Application.java

public class Application {
    public static void main(String[] args) {               
        System.out.println("""
                +-------------------------------------------------------------------
                | Example: Create players, a deck and deal 1 card to each player
                +-------------------------------------------------------------------
                """);
        // create two players, and the croupier
        var john = new Player("John");
        System.out.println(john);
        var elton = new Player("Elton");
        System.out.println(elton);
        // create a croupier; a groupie is a player and deals cards, so give him a new deck of cards
        var croupierTom = new Croupier("Tom", new Deck());
        System.out.println(croupierTom);
        // a list of all players
        var allPlayers = new ArrayList<Player>();
        allPlayers.add(john);
        allPlayers.add(elton);
        // remember a croupier is also a player because it is a subclass
        allPlayers.add(croupierTom);
        // let the croupier shuffle the cards
        croupierTom.shuffleCards();
        // tell croupier to deal one card to each player (including himself)
        for (var player : allPlayers) {
            croupierTom.dealCardTo(player);
        }
        // now let's see who got which cards
        for (var player : allPlayers) {
            System.out.println(player);
        }
    }
}

Expected output:

+-------------------------------------------------------------------
| Example: Create players, a deck and deal 1 card to each player
+-------------------------------------------------------------------

Player John has joined the game.
Player{name='John', hand=Hand{cards=[], totalValue=0}}
Player Elton has joined the game.
Player{name='Elton', hand=Hand{cards=[], totalValue=0}}
Player Tom has joined the game.
Croupier{name='Tom', hand=Hand{cards=[], totalValue=0}, deck=[2-Spades, 3-Spades, 4-Spades, 5-Spades, 6-Spades, 7-Spades, 8-Spades, 9-Spades, 10-Spades, J-Spades, Q-Spades, K-Spades, A-Spades, 2-Hearts, 3-Hearts, 4-Hearts, 5-Hearts, 6-Hearts, 7-Hearts, 8-Hearts, 9-Hearts, 10-Hearts, J-Hearts, Q-Hearts, K-Hearts, A-Hearts, 2-Clubs, 3-Clubs, 4-Clubs, 5-Clubs, 6-Clubs, 7-Clubs, 8-Clubs, 9-Clubs, 10-Clubs, J-Clubs, Q-Clubs, K-Clubs, A-Clubs, 2-Diamonds, 3-Diamonds, 4-Diamonds, 5-Diamonds, 6-Diamonds, 7-Diamonds, 8-Diamonds, 9-Diamonds, 10-Diamonds, J-Diamonds, Q-Diamonds, K-Diamonds, A-Diamonds]}
Croupier Tom has shuffled the cards.
Tom is dealing a card to John.
Tom is dealing a card to Elton.
Tom is dealing a card to Tom.
Player{name='John', hand=Hand{cards=[7-Spades], totalValue=7}}
Player{name='Elton', hand=Hand{cards=[4-Diamonds], totalValue=4}}
Croupier{name='Tom', hand=Hand{cards=[2-Spades], totalValue=2}, deck=[8-Hearts, 5-Hearts, 9-Hearts, 3-Spades, Q-Hearts, 8-Clubs, 2-Diamonds, 6-Diamonds, 10-Hearts, 2-Hearts, 8-Spades, Q-Clubs, 2-Clubs, Q-Spades, 5-Diamonds, 5-Clubs, 7-Clubs, 4-Hearts, K-Spades, 3-Hearts, 9-Clubs, K-Clubs, Q-Diamonds, A-Clubs, 10-Spades, 4-Clubs, A-Spades, J-Diamonds, 10-Diamonds, 7-Diamonds, J-Hearts, A-Hearts, 5-Spades, A-Diamonds, 4-Spades, 7-Hearts, 6-Hearts, J-Clubs, 9-Diamonds, 6-Spades, J-Spades, 8-Diamonds, 10-Clubs, 6-Clubs, 3-Diamonds, K-Diamonds, 3-Clubs, 9-Spades, K-Hearts]}

Keep in mind that through shuffling your results will probably be slightly different but each player (including the croupier) should have one card in his/ her hand and the deck should have all those cards removed from it.

What to do next?

Now that should give you a good skeleton to implement the rest of the game. There is still a lot to do. The next thing should probably be to create a Game class which represents one game of BlackJack. Again start from the real-world and keep in mind that your design should be a model of that. Always think in terms of abilities (= methods) and properties (= attributes) an instance has. A Game will certainly have a collection of players, a croupier and the ability to start the game. Think about what should happen on the start of the game: The croupier will have to deal cards (we've seen how to do that already) but it won't be just one and there will be decisions being made by the players involved whether they would like to take on another card. And you will need to implement the rules of BlackJack as well as win conditions to determine a winner. You could also implement some stakes for the players and so on and so on.

I hope this has helped you understand what and how you can start and what to do next :) Happy coding :)

Disclaimer: My design is not a gold standard or anything like this, it's just one possible design, there might be other (better) designs. But it's one relatively simple yet powerful enough approach I think.