Null Pointer exception error with no obvious code errors - java

I have an error here and I don't know where it is coming from. I am in a beginner's java class is High School so I don't yet have much experience here. I have 3 programs that incorporate each other.
I have a card class that creates a playing card
//********************************************************************
// Card.java Author: Lewis and Loftus
//
// Solution to Programming Project 4.5
//********************************************************************
import java.util.*;
public class Card
{
public final static int ACE = 1;
public final static int TWO = 2;
public final static int THREE = 3;
public final static int FOUR = 4;
public final static int FIVE = 5;
public final static int SIX = 6;
public final static int SEVEN = 7;
public final static int EIGHT = 8;
public final static int NINE = 9;
public final static int TEN = 10;
public final static int JACK = 11;
public final static int QUEEN = 12;
public final static int KING = 13;
public final static int CLUBS = 1;
public final static int DIAMONDS = 2;
public final static int HEARTS = 3;
public final static int SPADES = 4;
private final static int NUM_FACES = 13;
private final static int NUM_SUITS = 4;
private int face, suit;
private String faceName, suitName;
private int myInt1, myInt2;
Random rand = new Random();
//-----------------------------------------------------------------
// Creates a random card.
//-----------------------------------------------------------------
public Card ()
{
face = rand.nextInt(4) + 1;
setFaceName();
suit = rand.nextInt(13) + 1;
setSuitName();
}
//-----------------------------------------------------------------
// Sets the string representation of the face using its stored
// numeric value.
//-----------------------------------------------------------------
private void setFaceName()
{
switch (face)
{
case 1:
faceName = "Ace";
break;
case 2:
faceName = "Two";
break;
case 3:
faceName = "Three";
break;
case 4:
faceName = "Four";
break;
case 5:
faceName = "Five";
break;
case 6:
faceName = "Six";
break;
case 7:
faceName = "Seven";
break;
case 8:
faceName = "Eight";
break;
case 9:
faceName = "Nine";
break;
case 10:
faceName = "Ten";
break;
case 11:
faceName = "Jack";
break;
case 12:
faceName = "Queen";
break;
case 13:
faceName = "King";
break;
}
}
//-----------------------------------------------------------------
// Sets the string representation of the suit using its stored
// numeric value.
//-----------------------------------------------------------------
private void setSuitName()
{
switch (suit)
{
case 1:
suitName = "Clubs";
break;
case 2:
suitName = "Diamonds";
break;
case 3:
suitName = "Hearts";
break;
case 4:
suitName = "Spades";
break;
}
}
//-----------------------------------------------------------------
// Determines if this card is higher than the passed card. The
// second parameter determines if aces should be considered high
// (beats a King) or low (lowest of all faces). Uses the suit
// if both cards have the same face.
//-----------------------------------------------------------------
public boolean isHigherThan (Card card2, boolean aceHigh)
{
boolean result = false;
if (face == card2.getFace())
{
if (suit > card2.getSuit())
result = true;
}
else
{
if (aceHigh && face == ACE)
result = true;
else
if (face > card2.getFace())
result = true;
}
return result;
}
//-----------------------------------------------------------------
// Determines if this card is higher than the passed card,
// assuming that aces should be considered high.
//-----------------------------------------------------------------
public boolean isHigherThan (Card card2)
{
return isHigherThan (card2, true);
}
//-----------------------------------------------------------------
// Returns the face (numeric value) of this card.
//-----------------------------------------------------------------
public int getFace ()
{
return face;
}
//-----------------------------------------------------------------
// Returns the suit (numeric value) of this card.
//-----------------------------------------------------------------
public int getSuit ()
{
return suit;
}
//-----------------------------------------------------------------
// Returns the face (string value) of this card.
//-----------------------------------------------------------------
public String getFaceName ()
{
return faceName;
}
//-----------------------------------------------------------------
// Returns the suit (string value) of this card.
//-----------------------------------------------------------------
public String getSuitName ()
{
return suitName;
}
//-----------------------------------------------------------------
// Returns the string representation of this card, including
// both face and suit.
//-----------------------------------------------------------------
public String toString ()
{
return faceName + " of " + suitName;
}
}
I have a Deck Of cards file that creates 52 cards
import java.util.*;
public class DeckOfCards
{
private Card deckOfCards[];
private int currentCardUsed;
private final int NumberOfCards = 52;
private int nextCard;
private Random rand;
String myString = "All Cards have been dealt.";
public DeckOfCards()
{
deckOfCards = new Card[NumberOfCards];
currentCardUsed = 0;
Random rand = new Random();
for(int index = 0; index < deckOfCards.length; index ++)
{
deckOfCards[index] = new Card();
}
}
public void shuffleCards()
{
currentCardUsed = 0;
for(int newCard = 0; newCard < deckOfCards.length; newCard ++)
{
int nextCard = rand.nextInt(NumberOfCards);
Card temporaryDeck = deckOfCards[newCard];
deckOfCards[newCard] = deckOfCards[nextCard];
deckOfCards[nextCard] = temporaryDeck;
}
}
public Card dealCard()
{
if(currentCardUsed < deckOfCards.length)
{
return deckOfCards[currentCardUsed ++];
}
else
{
return null;
}
}
}
And finally I have a driver class
public class DeckTester
{
public static void main(String [] args)
{
DeckOfCards deck = new DeckOfCards();
deck.shuffleCards();
System.out.println(deck.dealCard());
System.out.println(deck.dealCard());
System.out.println(deck.dealCard());
System.out.println(deck.dealCard());
System.out.println(deck.dealCard());
System.out.println(deck.dealCard());
}
}
None of them have errors. But when I run the driver I get this for the output
Exception in thread "main" java.lang.NullPointerException
at DeckOfCards.shuffleCards(DeckOfCards.java:39)
at DeckTester.main(DeckTester.java:8)
I have tried changing the Null in the deal Method to no avail.

It looks like you are declaring a local Random object in your constructor.
Try changing line 25 to be:
rand = new Random();

The line Random rand = new Random(); in your constructor creates a local object rand whose scope is limited to constructor only. You have two options to fix this issue :-
1) Change line in your constructor to
Random rand = new Random(); ---> this.rand = new Random()
2) Initialize your instance object at the time of declaration only. Change line
private Random rand; ---> private Random rand = new Random();

Related

Shuffling Deck of Cards Using Card object

Have been attempting to create a class called CardDeck to shuffle card objects created in a different class (called DeckOfCard, sorry for the confusion, not well named) but have run out of ideas of how to accomplish this.
here is what I have come up with, I've included the original class DeckOfCards as well, any help/ advice is welcomed and appreciated!
//CODE FOR CARD OBJECT//
public final static int ace = 1, two= 2,three=3, four=4, five=5, six=6, seven =7, eight=8, nine=9, ten=10, jack= 11, queen=12, king=13;
public final static int diamonds= 1, clubs= 2, spades= 3, hearts=4;
private final static int numberOfFaces = 13;
private final static int numberOfSuits = 4;
private int face, suit;
private String faceValue, suitName;
//create a random card
public DeckOfCards()
{
face= (int)(Math.random() * numberOfFaces);
setFaceValue();
suit= (int) (Math.random() * numberOfSuits);
setSuitName();
}
//sets the string representation of each face value to its coorspdoing numeric value
private void setFaceValue()
{
switch(face)
{
case ace:
faceValue= "Ace";
break;
case two:
faceValue= "Two";
break;
case three:
faceValue= "Three";
break;
case four:
faceValue= "Four";
break;
case five:
faceValue = "Five";
break;
case six:
faceValue = "Six";
break;
case seven:
faceValue= "Seven";
break;
case eight:
faceValue= "Eight";
break;
case nine:
faceValue= "Nine";
break;
case ten:
faceValue= "Ten";
break;
case jack:
faceValue= "Jack";
break;
case queen:
faceValue= "Queen";
break;
case king:
faceValue= "King";
break;
}
}
//set the string representation of each suit
private void setSuitName()
{
switch(suit)
{
case diamonds:
suitName = "Diamonds";
break;
case clubs:
suitName= "Clubs";
break;
case spades:
suitName = "Spades";
break;
case hearts:
suitName = "Hearts";
break;
}
}
public String getFaceValue()
{
return faceValue;
}
public String getSuitName()
{
return suitName;
}
public String toString()
{
return faceValue+ " of " +suitName;
}
}
And Here is my current code... It's not much but this is as close as I have been able to get thus far:
import java.util.Random;
public class CardDeck
{
private DeckOfCards[] cards;
//create new deck of cards
public CardDeck()
{
cards = new DeckOfCards[52];
int index= 0;
int[] cardTypes = {DeckOfCards.ace, DeckOfCards.diamonds, DeckOfCards.spades, DeckOfCards.hearts};
for(int cardType : cardTypes)
{
for(int i = 1; i<=13; i++)
{
DeckOfCards card = new DeckOfCards();
cards[index++]= card;
}
}
}
//create shuffle method, use loop to generate random suit and random faceValue
public void shuffle()
{
System.out.println("Suffuling cards");
int loopCount = 53;
while (loopCount > 0) {
double index1 = Math.random();
double index2 = Math.random();
DeckOfCards temp = cards[index1];
cards[index1] = cards[index2];
cards[index2] = temp;
loopCount--;
}
}
}
Define a enum types for the rank and the suit. This provides type safety so that you don't accidentally pass a rank for a suit parameter or vice versa. The enumerated values also make good keys to use in maps to define scoring systems for different games and so on. Note also that enums can have properties and methods, so you can add user friendly names for values that way.
Then create a Card type that has a rank and a suit.
Iterate over the ranks, and for each rank, iterate over the suits. Create a new Card for each combination of rank and suit, and add it to a List; this is your deck. When your deck is built, you can shuffle it with a convenience method in Collections.
public final class Card {
public enum Rank {
ACE, DEUCE, THREE, FOUR, FIVE, SIX, SEVEN, EIGHT, NINE, TEN, JACK, QUEEN, KING
}
public enum Suit {
SPADES, HEARTS, DIAMOND, CLUBS
}
private final Rank rank;
private final Suit suit;
public Card(Rank rank, Suit suit) {
this.rank = Objects.requireNonNull(rank);
this.suit = Objects.requireNonNull(suit);
}
public Rank getRank() {
return rank;
}
public Suit getSuit() {
return suit;
}
#Override
public boolean equals(Object obj) {
if (obj == this) return true;
if (!(obj instanceof Card)) return false;
Card that = (Card) obj;
return (getRank() == that.getRank()) && (getSuit() == that.getSuit());
}
#Override
public int hashCode() {
return getRank().hashCode() * 31 + getSuit().hashCode();
}
#Override
public String toString() {
return getRank() + " of " + getSuit();
}
}
public class Deck {
private final List<? extends Card> cards;
public Deck(Collection<? extends Card> cards) {
this.cards = new ArrayList<>(cards);
}
public void shuffle(Random random) {
if (random == null) random = ThreadLocalRandom.current();
Collections.shuffle(cards, random);
}
public static Deck newStandardDeck() {
List<Card> cards = new ArrayList<>();
for (Card.Rank rank : Card.Rank.values()) {
for (Card.Suit suit : Card.Suit.values()) {
cards.add(new Card(rank, suit));
}
}
return new Deck(cards);
}
}
Your issue lies here:
double index1 = Math.random();
double index2 = Math.random();
Math.random() returns a double float between 0 and 1.
Try this instead:
Random r = new Random();
while(loopCount > 0) {
int index1 = r.nextInt(cards.length);
int index2 = r.nextInt(cards.length);
So there's some big issues with your code structure but i salvaged this.
The easiest solution is to create a sorted list of cards, one for each suite and value and then set each card to a random index. This will perform that on a field 'cards'
// create shuffle method, use loop to generate random suit and random faceValue
public void shuffle() {
System.out.println("Suffuling cards");
int loopCount = 0;
ArrayList<DeckOfCards> inOrder = new ArrayList<DeckOfCards>();
for(int i = 0; i < 54; i++)
{
inOrder.add(cards[i]);
}
DeckOfCards[] shuffled = new DeckOfCards[54];
for(int i = 0; i < 54; i++)
{
//Math.random()*size of inOrder will give you a double between 0 and size of in order.
//Round this down and convert to an int and you have a random index!
int randCardIndex = (int) (Math.floor(Math.random()*inOrder.size()));
//Grab the card and add it to the shuffled deck in the current position
shuffled[i] = inOrder.get(randCardIndex);
//Remove this card so it can no longer be grabbed
inOrder.remove(randCardIndex);
}
cards = shuffled;
}
Please change the name to Card instead of DeckOfCards. A list of Card objects becomes a deck of cards, so you don't want to call the object itself the deck.
The random function should follow a similar approach as the constructor of DeckOfCards in which your multiplier should be applied on top of the random number and be casted into an int. After that, your code should work fine. Card size 52 should be stored as a global private variable. Never use "magic numbers" without storing them in a variable and calling that variable. Both index1 and index2 should be the following:
double index1 = Math.random()*NUM_CARDS
Below your private array have the following variable: private final int NUM_CARDS = 52
Otherwise you are on the right track and the shuffling mechanism is good. It's just the random index generation gets a decimal number between 0 to 1.
When you have to debug, use the printing your variables technique. In this case, print out every value of index1 and index2 and check whether those values are being generated as intended. You can also try a less beginner friendly method called debug mode (cockroach icon) and double click on a line number to set a breakpoint at which you want the execution to pause and you can view all the variables stored in memory at that point of your code.

ArrayList Novice Error in Java

I am trying to get more proficient with Java. I am making a simple card game to test my basic skills. I have come across a problem in my Deck class. The for-loops are not creating new card objects in my arraylist even though they should. When I call getTotalCards the size of the arraylist is always 0. Any ideas of what I am doing wrong?
Main Class:
package gameofcards;
import java.util.Random;
import static gameofcards.Card.*;
public class GameOfCards {
public static void main(String[] args) {
Deck d1 = new Deck();
System.out.println(d1.getTotalCards());
}
}
Deck Class:
package gameofcards;
import java.util.ArrayList;
public class Deck {
private ArrayList<Card> cards;
public Deck(){
cards = new ArrayList<Card>();
for(int i = 1; i >=4; i++){
for(int j = 1; j >=13; j++){
cards.add(new Card(i,j));
}
}
}
public int getTotalCards(){
return cards.size();
}
}
Card Class:
package gameofcards;
public class Card {
private int Suite;
private int Rank;
public static final int Club = 1;
public static final int Diamond = 2;
public static final int Hearts = 3;
public static final int Spade = 4;
public static final int Jack = 10;
public static final int Queen = 11;
public static final int King = 12;
public static final int Ace = 13;
public void setSuite(int cardSuite){
Suite=cardSuite;
}
public int getSuite(){
return Suite;
}
public void setRank(int cardRank){
Rank=cardRank;
}
public int getRank(){
return Rank;
}
public Card(int Suite, int Rank){
this.Suite = Suite;
this.Rank = Rank;
}
public String cardSuite(){
switch(Suite){
case Club: return "Clubs";
case Diamond: return "Diamonds";
case Hearts: return "Hearts";
case Spade: return "Spades";
default: return "Joker";
}
}
public String cardRank() {
switch(Rank){
case 2: return "2";
case 3: return "3";
case 4: return "4";
case 5: return "5";
case 6: return "6";
case 7: return "7";
case 8: return "8";
case 9: return "9";
case 10: return "Jack";
case 11: return "Queen";
case 12: return "King";
case 13: return "Ace";
default: return "Joker";
}
}
}
Your loop conditions are wrong.
Instead of:
for(int i = 1; i >=4; i++){
It should be:
for(int i = 1; i <=4; i++){
And a similar problem with the inner loop condition.
You want to loop while i is less than or equal to 4.
It wasn't adding cards because it never entered the loop in the first place.
An easy way to have figured this out would have been to put a println inside the loop and see what happens. You would have noticed that it never printed.
The conditions in for loop are wrong. should be i<=4 and j<=13
for(int i = 1; i <=4; i++){
for(int j = 1; j <=13; j++){
cards.add(new Card(i,j));
}
}

Highest value card found in deck

In a Java program I am trying to return the largest value card in a deck.
Diamonds are the lowest valued suit. Then clubs, then hearts and finally spades have the largest value. As you can see I have a section of code that simply says largest = in the findLargest methods. I am not sure where to go with the rest of this method.
public class Card {
private int number;
private String suit;
/*
* Randomly creates a card numbered 1 to 13 (ace = 1!) and labelled "Hearts","Clubs","Diamonds" or "Spades".
*/
public Card() {
double randomNum = Math.random() * 4.0;
if (randomNum < 1.0)
suit = "Hearts";
else if (randomNum < 2.0)
suit = "Clubs";
else if (randomNum < 3.0)
suit = "Diamonds";
else
suit = "Spades";
randomNum = Math.random() * 13.0;
number = (int) randomNum + 1;
}
/*
* Creates a card with specified number and suit
*/
public Card (int n, String s) {
number = n;
suit = s;
}
public int getNumber() {
return number;
}
public String getSuit () {
return suit;
}
public String cardString() {
// System.out.println(number + " " + suit);
String stringNum = "";
switch (number) {
case 1:
stringNum = "Ace";
break;
case 2:
stringNum = "Two";
break;
case 3:
stringNum = "Three";
break;
case 4:
stringNum = "Four";
break;
case 5:
stringNum = "Five";
break;
case 6:
stringNum = "Six";
break;
case 7:
stringNum = "Seven";
break;
case 8:
stringNum = "Eight";
break;
case 9:
stringNum = "Nine";
break;
case 10:
stringNum = "Ten";
break;
case 11:
stringNum = "Jack";
break;
case 12:
stringNum = "Queen";
break;
case 13:
stringNum = "King";
break;
default:
System.out.println("Error in Card - illegal number");
}
return stringNum + " of " + suit;
}
}
public class PackCards {
private ArrayList<Card> pack;
/*
* Create a random pack of size n
*/
public PackCards(int n) {
Card c;
pack = new ArrayList<Card>();
for (int i = 1; i <= n; i++) {
c = new Card();
pack.add(c);
}
}
public void printPack() {
for (Card c : pack) {
System.out.println(c.cardString());
}
}
public Card findLargest() {
if ( c.getNumber() > largest.getNumber() )
largest =
else if (c.getNumber() == largest.getNumber() ) {
if (largest.getSuit().equals("Diamonds"))
largest = largest.getNumber;
}
else if (c.getNumber() == largest.getNumber() ) {
if (largest.getSuit().equals("Clubs"))
largest = largest.getNumber;
}
else if (c.getNumber() == largest.getNumber() ) {
if (largest.getSuit().equals("Hearts"))
largest = largest.getNumber;
}
else return;
}
I would simplify the code to make it easier to see what you are trying to achieve. In Java 8 you can do.
import java.util.Random;
public class Card {
enum Suit {
// must be in increasing order.
Spades, Diamonds, Clubs, Hearts
}
static final Suit[] SUITS = Suit.values();
private final int number;
private final Suit suit;
/*
* Randomly creates a card numbered 1 to 13 (ace = 1!) and labelled "Hearts","Clubs","Diamonds" or "Spades".
*/
public Card() {
Random rand = new Random();
suit = SUITS[rand.nextInt(SUITS.length)];
number = rand.nextInt(13) + 1;
}
public int getNumber() {
return number;
}
public Suit getSuit() {
return suit;
}
static final String[] NAMES = ",Ace,Two,Three,Four,Five,Six,Seven,Eight,Nine,Ten,Jack,Queen,King".split(",");
public String cardString() {
assert number > 1 && number < NAMES.length;
return NAMES[number] + " of " + suit;
}
}
This simplifies your pack as well
public class PackOfCards {
private final List<Card> cards;
public PackOfCards(int size) {
// note duplicates are possible, i.e. ever card could be the same.
cards = IntStream.range(0, size)
.mapToObj(n -> new Card())
.collect(Collectors.toList());
}
public Card findLargest() {
return cards.stream().max(Comparator.comparing(Card::getNumber)
.thenComparing(Card::getSuit)).get();
}
}
You can change the Card class to implements the Comparable interface. For example:
public class Card implements Comparable<Card> {
....// your current implementation for the Card class
public int compareTo(Card another) {
if (this.suit.compareTo(another.suit) == 0) {
if (this.number < another.number) {
return -1;
} else if (this.number > another.number) {
return 1;
} else {
return 0;
}
} else if (this.suit.compareTo("Diamonds") == 0) {
// Diamonds being the lowest valued suit ...
return -1;
} else if (this.suit.compareTo("Clubs") == 0) {
// ... then Clubs ...
if (another.suit.compareTo("Diamonds") == 0) {
return 1;
} else {
return -1;
}
} else if (this.suit.compareTo("Hearts") == 0) {
// ... Hearts ...
if (another.suit.compareTo("Spades") == 0) {
return -1;
} else {
return 1;
}
} else {
// ... and largest values, Spades
return 1;
}
}
}
Then, you can modify the PackCards to store the cards in an ordered collection. For example:
pack = new TreeSet<Card>();
That way, the cards in the pack are ordered, and the last one is the largest.
If you want to mantain the order in wich the cards was added to the list, you can obtain the largest one in this way:
largest = Collections.max(pack);
For this sort of work, you want to leverage the object-oriented nature of Java and utilize Enums for both the FaceValue and the Suit. Enums are 'naturally ordered' in the order you list them, making your construction and string output a LOT easier:
public class Card implements Comparable<Card> {
private FaceValue faceValue;
private Suit suit;
/*
* Randomly creates a card numbered 1 to 13 (ace = 1!)
* labelled "Hearts","Clubs","Diamonds" or "Spades".
*/
public Card() {
int randomSuit = (int)Math.floor(Math.random() * 4.0); //Number between 0 and 3, inclusive.
int randomFace = (int)Math.floor(Math.random() * 13.0);
this.faceValue = FaceValue.values()[randomFace];
this.suit = Suit.values()[randomSuit];
}
public Card(FaceValue value, Suit suit) {
this.faceValue = value;
this.suit = suit;
}
// TODO Constructor that convert ints and strings to proper enums and call previous constructor
public int compareTo(Card that) {
int comparison = this.suit.compareTo(that.suit);
return (comparison != 0) ? comparison : this.faceValue.compareTo(that.faceValue);
}
public String cardString() {
return this.faceValue + " of " + this.suit;
}
public enum Suit{
DIAMOND, CLUB, HEART, SPADE;
}
public enum FaceValue {
ACE, ONE, TWO, THREE, FOUR, FIVE, SIX, SEVEN, EIGHT, NINE, TEN, JACK, QUEEN, KING;
} // You can modify this enum if you need to give back something more specific as the string value.
}
Then for your Deck you can simplify things as well by choosing a good data structure, in this case a TreeSet, which will automatically use the compareTo function you've provided for cards to sort the cards.:
public class Deck {
//By using a Set we eliminate duplicates
private final TreeSet<Card> cards;
public Deck(int size) {
cards = Sets.newTreeSet(IntStream.range(0, size)
.mapToObj(n -> new Card())
.collect(Collectors.toList());
}
//Because this is a TreeSet, finding the largest value is trivial.
public Card findLargest() {
return this.cards.last();
}
}
If you need special strings for the cards in your deck, you would do the following modification to FaceValue:
public enum FaceValue {
ACE {
public String toString() { return "Ace"; }
},
ONE {
public String toString() { return "One"; }
},
... // Fill out the rest in the same manner.
KING {
public String toString() { return "King"; }
}
}
Remember that in Object-Oriented programming, the number one rule is that an object should know how behave. Thus, if you find yourself writing a switch statement, chances are you can defer the behavior to the objects themselves. In this case, you need to create sub-objects (the FaceValue and Suit enums) that you assign to a given card. The card asks those sub parts how they are ordered, and how they should respond to a toString request.

Modify a .java file to change constants to enums

I'm trying to finish this homework but it has been a little bit challenging so far, I'm quite new at java:
1). I have to create enums to replace the named constants for rank and suit in Card.java.
2).Make the changes necessary in Deck.java and DisplayDeck.java so they will work with the new Card.java.
This is my code so far for my three classes: Card.java, Deck.java and DisplayDeck.java
Original Card.java
public class Card {
private final int rank;
private final int suit;
// Kinds of suits
public final static int DIAMONDS = 1;
public final static int CLUBS = 2;
public final static int HEARTS = 3;
public final static int SPADES = 4;
// Kinds of ranks
public final static int ACE = 1;
public final static int DEUCE = 2;
public final static int THREE = 3;
public final static int FOUR = 4;
public final static int FIVE = 5;
public final static int SIX = 6;
public final static int SEVEN = 7;
public final static int EIGHT = 8;
public final static int NINE = 9;
public final static int TEN = 10;
public final static int JACK = 11;
public final static int QUEEN = 12;
public final static int KING = 13;
public Card(int rank, int suit) {
assert isValidRank(rank);
assert isValidSuit(suit);
this.rank = rank;
this.suit = suit;
}
public int getSuit() {
return suit;
}
public int getRank() {
return rank;
}
public static boolean isValidRank(int rank) {
return ACE <= rank && rank <= KING;
}
public static boolean isValidSuit(int suit) {
return DIAMONDS <= suit && suit <= SPADES;
}
public static String rankToString(int rank) {
switch (rank) {
case ACE:
return "Ace";
case DEUCE:
return "Deuce";
case THREE:
return "Three";
case FOUR:
return "Four";
case FIVE:
return "Five";
case SIX:
return "Six";
case SEVEN:
return "Seven";
case EIGHT:
return "Eight";
case NINE:
return "Nine";
case TEN:
return "Ten";
case JACK:
return "Jack";
case QUEEN:
return "Queen";
case KING:
return "King";
default:
//Handle an illegal argument. There are generally two
//ways to handle invalid arguments, throwing an exception
//(see the section on Handling Exceptions) or return null
return null;
}
}
public static String suitToString(int suit) {
switch (suit) {
case DIAMONDS:
return "Diamonds";
case CLUBS:
return "Clubs";
case HEARTS:
return "Hearts";
case SPADES:
return "Spades";
default:
return null;
}
}
public static void main(String[] args) {
// must run program with -ea flag (java -ea ..) to
// use assert statements
assert rankToString(ACE) == "Ace";
assert rankToString(DEUCE) == "Deuce";
assert rankToString(THREE) == "Three";
assert rankToString(FOUR) == "Four";
assert rankToString(FIVE) == "Five";
assert rankToString(SIX) == "Six";
assert rankToString(SEVEN) == "Seven";
assert rankToString(EIGHT) == "Eight";
assert rankToString(NINE) == "Nine";
assert rankToString(TEN) == "Ten";
assert rankToString(JACK) == "Jack";
assert rankToString(QUEEN) == "Queen";
assert rankToString(KING) == "King";
assert suitToString(DIAMONDS) == "Diamonds";
assert suitToString(CLUBS) == "Clubs";
assert suitToString(HEARTS) == "Hearts";
assert suitToString(SPADES) == "Spades";
}
}
My modified Card.java so far:
public class Card {
public enum Suit
{
DIAMONDS(1),
CLUBS(2),
HEARTS(3),
SPADES(4);
private final int suit;
private Suit(int suit)
{
this.suit = suit;
}
public int getSuit()
{
return suit;
}
} //end enum suit
public enum Rank
{
ACE(1),
DEUCE(2),
THREE(3),
FOUR(4),
FIVE(5),
SIX(6),
SEVEN(7),
EIGHT(8),
NINE(9),
TEN(10),
JACK(11),
QUEEN(12),
KING(13);
private final int rank;
private Rank(int rank)
{
this.rank = rank;
}
public int getRank()
{
return rank;
}
}//end enum rank
public static String rankToString(Rank rank) {
switch (rank) {
case ACE:
return "Ace";
case DEUCE:
return "Deuce";
case THREE:
return "Three";
case FOUR:
return "Four";
case FIVE:
return "Five";
case SIX:
return "Six";
case SEVEN:
return "Seven";
case EIGHT:
return "Eight";
case NINE:
return "Nine";
case TEN:
return "Ten";
case JACK:
return "Jack";
case QUEEN:
return "Queen";
case KING:
return "King";
default:
//Handle an illegal argument. There are generally two
//ways to handle invalid arguments, throwing an exception
//(see the section on Handling Exceptions) or return null
return null;
}
}
public static String suitToString(Suit suit) {
switch (suit) {
case DIAMONDS:
return "Diamonds";
case CLUBS:
return "Clubs";
case HEARTS:
return "Hearts";
case SPADES:
return "Spades";
default:
return null;
}
}
}//end class
And this are the original Deck.java and DisplayDeck.java:
Original Deck.java
import java.util.*;
public class Deck {
public static int numSuits = 4;
public static int numRanks = 13;
public static int numCards = numSuits * numRanks;
private Card[][] cards;
public Deck() {
cards = new Card[numSuits][numRanks];
for (int suit = Card.DIAMONDS; suit <= Card.SPADES; suit++) {
for (int rank = Card.ACE; rank <= Card.KING; rank++) {
cards[suit-1][rank-1] = new Card(rank, suit);
}
}
}
public Card getCard(int suit, int rank) {
return cards[suit-1][rank-1];
}
}
Modified Deck.java:
import java.util.*;
public class Deck {
public static int numSuits = 4;
public static int numRanks = 13;
public static int numCards = numSuits * numRanks;
private Card[][] cards;
public Deck() {
cards = new Card[numSuits][numRanks];
for (Card.Suit suit : Card.Suit.values()) {
for (Card.Rank rank : Card.Rank.values()) {
// I dont know how to change this to work with Card.java
cards[suit-1][rank-1] = new Card(rank, suit);
}
}
}
public Card getCard(int suit, int rank) {
return cards[suit-1][rank-1];
}
}
and DisplayDeck.java
import java.util.*;
public class DisplayDeck {
public static void main(String[] args) {
Deck deck = new Deck();
/// how can i modify this code to work with both Card.java and Deck.java?
for (int suit = Card.DIAMONDS; suit <= Card.SPADES; suit++) {
for (int rank = Card.ACE; rank <= Card.KING; rank++) {
Card card = deck.getCard(suit, rank);
System.out.format("%s of %s%n",
card.rankToString(card.getRank()),
card.suitToString(card.getSuit()));
}
}
}
}
I update my code on what I got so far.
Get rid of all your int representations for rank and suit. You can replace loops like this:
for (int suit = Card.DIAMONDS; suit <= Card.SPADES; suit++) { ...
with an enhanced for loop:
for (Suit suit : Card.Suit.values()) { ...
Replace mappings from int to String with EnumMap<Suit,String> or EnumMap<Rank,String> as appropriate. Methods for checking whether an int value is valid can be disposed of, as a non-null enum is guaranteed to be a legal value.
EDIT (in response to comment): You can define your deck as simply an array of Card objects:
public class Deck {
private List<Card> cards;
public Deck() {
cards = new ArrayList<>();
for (Card.Suit suit : Card.Suit.values()) {
for (Card.Rank rank : Card.Rank.values()) {
cards.add(new Card(suit, rank));
}
}
}
}
With this, you can use the Collections utility class to trivially implement shuffling:
public class Deck {
. . . // as above
public void shuffle() {
Collections.shuffle(cards);
}
}
Since every enum type comes with a built-in compareTo() method that tells you the relative order of two enum values (of the same type). You can build on this to make the Card class implement Comparable<Card>. (This is left as an exercise for the reader :-)). Then you can trivially sort the deck back into its "brand new" order or even sort hands drawn randomly from the deck.
I see that you are currently retrieving a card by suit and rank. That's a weird way to access cards in a deck, especially since you just turn around and use the Card object to retrieve its suit and rank. But if you really need that kind of retrieval, you can expand the Deck definition to include internally an EnumMap<Suit, EnumMap<Rank, Card>> to allow you to quickly retrieve a card that way. (Yes, each card would then be stored in two distinct data structures inside the Deck object.) A better approach, in my view, is to add a method to retrieve a card by offset into the deck (and another method to tell you the deck size):
public class Deck {
. . .
public Card getCard(int index) {
return cards.get(index);
}
public int cardCount() {
return cards.size();
}
}
To be fancier, you can define a method that returns an iterator over all the cards:
public class Deck {
. . .
public Iterator<Card> iterator() {
return cards.iterator();
}
}
Even fancier yet, you could declare Deck to implement Iterable<Card>. Then you can print the deck contents very simply:
Deck deck = new Deck();
for (Card card : deck) {
// print the card
}

Flow of control in java - multiple classes

I am a beginner to java and have been going through oracle's java tutorial. In those tutorials I came across a question involving multiple classes. The class called Deck uses class called Cards. I want to know if the class Deck ever uses the main method in class Cards. Does the line of flow ever go through the main method of Cards.
public class Card {
private final int rank;
private final int suit;
// Kinds of suits
public final static int DIAMONDS = 1;
public final static int CLUBS = 2;
public final static int HEARTS = 3;
public final static int SPADES = 4;
// Kinds of ranks
public final static int ACE = 1;
public final static int DEUCE = 2;
public final static int THREE = 3;
public final static int FOUR = 4;
public final static int FIVE = 5;
public final static int SIX = 6;
public final static int SEVEN = 7;
public final static int EIGHT = 8;
public final static int NINE = 9;
public final static int TEN = 10;
public final static int JACK = 11;
public final static int QUEEN = 12;
public final static int KING = 13;
public Card(int rank, int suit) {
assert isValidRank(rank);
assert isValidSuit(suit);
this.rank = rank;
this.suit = suit;
}
public int getSuit() {
return suit;
}
public int getRank() {
return rank;
}
public static boolean isValidRank(int rank) {
return ACE <= rank && rank <= KING;
}
public static boolean isValidSuit(int suit) {
return DIAMONDS <= suit && suit <= SPADES;
}
public static String rankToString(int rank) {
switch (rank) {
case ACE:
return "Ace";
case DEUCE:
return "Deuce";
case THREE:
return "Three";
case FOUR:
return "Four";
case FIVE:
return "Five";
case SIX:
return "Six";
case SEVEN:
return "Seven";
case EIGHT:
return "Eight";
case NINE:
return "Nine";
case TEN:
return "Ten";
case JACK:
return "Jack";
case QUEEN:
return "Queen";
case KING:
return "King";
default:
//Handle an illegal argument. There are generally two
//ways to handle invalid arguments, throwing an exception
//(see the section on Handling Exceptions) or return null
return null;
}
}
public static String suitToString(int suit) {
switch (suit) {
case DIAMONDS:
return "Diamonds";
case CLUBS:
return "Clubs";
case HEARTS:
return "Hearts";
case SPADES:
return "Spades";
default:
return null;
}
}
public static void main(String[] args) {
// must run program with -ea flag (java -ea ..) to
// use assert statements
assert rankToString(ACE) == "Ace";
assert rankToString(DEUCE) == "Deuce";
assert rankToString(THREE) == "Three";
assert rankToString(FOUR) == "Four";
assert rankToString(FIVE) == "Five";
assert rankToString(SIX) == "Six";
assert rankToString(SEVEN) == "Seven";
assert rankToString(EIGHT) == "Eight";
assert rankToString(NINE) == "Nine";
assert rankToString(TEN) == "Ten";
assert rankToString(JACK) == "Jack";
assert rankToString(QUEEN) == "Queen";
assert rankToString(KING) == "King";
assert suitToString(DIAMONDS) == "Diamonds";
assert suitToString(CLUBS) == "Clubs";
assert suitToString(HEARTS) == "Hearts";
assert suitToString(SPADES) == "Spades";
}
}
import java.util.*;
public class Deck {
public static int numSuits = 4;
public static int numRanks = 13;
public static int numCards = numSuits * numRanks;
private Card[][] cards;
public Deck() {
cards = new Card[numSuits][numRanks];
for (int suit = Card.DIAMONDS; suit <= Card.SPADES; suit++) {
for (int rank = Card.ACE; rank <= Card.KING; rank++) {
cards[suit-1][rank-1] = new Card(rank, suit);
}
}
}
public Card getCard(int suit, int rank) {
return cards[suit-1][rank-1];
}
}
No, it does not. The main method implemented in this example is intended for testing purposes (note the asserts).
It's very typical, however I'd say a very bad, custom to have the main method in all the possible tutorials and examples. And yes, it confuses beginners.
Testing code should always be separated from the tested code. In a "normal" code for the good design. In a "school" code for the didactical reason, so the learners are not confused and see the good design even in the school code :)

Categories