Granted, this is probably something rather simple I have looked over by mistake, but I have to be sure.
I am working on getting a classic PlayingCard class running with a PlayingCard Tester to test it as an assignment. Now, when the whole thing is done, I will have a Pack class as well for producing a pack of cards and shuffling it, along with an equals method in the PlayingCard class, but for now, I just want to get basic PlayingCard class to work with the tester.
This is the current set up for the PlayingCard Class, ignore the int statement for i, as that was for a random numbers generator with the aid of a for loop, but that has been or rather, should be rendered redundent with the PlayingCard constructor.
public class PlayingCard
{
public PlayingCard(int rank, int suit)
{
this.rank = rank;
this.suit = suit;
}
// Values for rank
public final int TWO = 2;
public final int THREE = 3;
public final int FOUR = 4;
public final int FIVE = 5;
public final int SIX = 6;
public final int SEVEN = 7;
public final int EIGHT = 8;
public final int NINE = 9;
public final int TEN = 10;
public final int JACK = 11;
public final int QUEEN = 12;
public final int KING = 13;
public final int ACE = 14;
// Values for suits
public final int CLUBS = 0;
public final int DIAMONDS = 1;
public final int HEARTS = -1;
public final int SPADES = -2;
// Set random Rank and Suit values
public int rank;
public int suit;
public int i;
public int getRank()
{
return rank;
}
public int getSuit()
{
return suit;
}
#Override
public String toString()
{
return getClass().getName() + "[rank " + rank + "suit " + suit + "]";
}
public void format()
{
System.out.format(rank + " of " + suit);
}
}
And this is the current tester, note I have used a set of values based of the values for rank and suit in the class.
public class PlayingCardTester
{
public static void main(String[] args)
{
PlayingCard test;
test = new PlayingCard(14, 1);
test.getRank();
test.getSuit();
test.toString();
test.format();
System.out.println("");
}
}
The values put into the constructor should be being used in the methods of the class. The output I want from the actual tester and class is something like this:
Ace of Diamonds
Instead, I get this:
0 of 0
What I am asking is, how can I use the values given in the constructor in the methods implemented in the tester and class so I can get the output I want? The values used a meant to be related to those specified in the class, but something tells me they are effectivly useless or not being used in the actual class. It's probably something rather simple I have missed but unable to find, or myself being incredably stupid about it, but I want to be sure in case something more complex is required.
Advice?
EDIT: Added the details to the constructor, now I have the output:
14 of 1
You actually have two separate problems. The first issue is, as noted in another answer, that you do not assign the variables passed to the constructor of your class to the corresponding instance variables.
The second issue you have is that you are setting int values to represent the Rank and Suit but do not provide corresponding String representation in your PlayingCard class.
You could do this for ranks by creating an array like:
String ranks [] = {"TWO","THREE"...};
and using ranks[rank -2] in format();
As suit integer representations have non sequential values you would need to use another data structure (such as a Map).
An easier way of doing all of this however is to creating two enums as below. You would then create a new card like:
PlayingCard card = new PlayingCard(Rank.ACE, Suit.SPADES);
Enum for Suit:
public enum Suit {
HEARTS(0), SPADES(-2), CLUBS(-1), DIAMONDS(1);
private int value;
private Suit(int value){
this.value = value;
}
public int getValue(){
return value;
}
}
Enum For Rank:
public enum RANK {
TWO(2), THREE(3), ACE(14);
private int value;
private Suit(int value){
this.value = value;
}
public int getValue(){
return value;
}
}
Playing Card:
public class PlayingCard
{
private Rank rank;
private Suit suit;
public PlayingCard(Rank rank, Suit suit)
{
this.rank = rank;
this.suit = suit;
}
public Rank getRank()
{
return rank;
}
public Suit getSuit()
{
return suit;
}
#Override
public String toString()
{
return getClass().getName() + "[rank " + rank + "suit " + suit + "]";
}
public void format()
{
System.out.format(rank + " of " + suit);
}
}
You need to set parameters from constructor to your instance.
public PlayingCard(int rank, int suit)
{
this.rank = rank;
this.suit = suit;
}
cause this constructor takes two parameters but dont do anything with them.
public PlayingCard(int rank, int suit)
{
}
and because int is naturally set to 0 thats why you get two 0.
Related
I am trying to achieve the best potential of OOP in Java by creating a blackjack game. I have 5 java files so far.
Suit.java
enum Suit {
/*
* Initialize suit values
*/
HEARTS,
SPADES,
CLUBS,
DIAMONDS;
}
Rank.java
enum Rank {
/*
* Initialize rank and card value
*/
TWO(2),
THREE(3),
FOUR(4),
FIVE(5),
SIX(6),
SEVEN(7),
EIGHT(8),
NINE(9),
TEN(10),
JACK(10),
QUEEN(10),
KING(10),
ACE(11);
// Hold card value
private int cardValue;
/*
* Constructor set card value
* #param cardValue value of card
*/
private Rank(int cardValue) {
this.cardValue = cardValue;
}
/*
* This method obtains the card value
* #return This returns the value of card
*/
public int getCardValue() {
return cardValue;
}
}
Card.java
public class Card {
private Suit suit;
private Rank rank;
public Card(Rank rank, Suit suit) {
this.rank = rank;
this.suit = suit;
}
public Rank getCardValue() {
return rank;
}
public Suit getSuitValue() {
return suit;
}
}
deck.java
import java.util.ArrayList;
import java.util.Collections;
import java.util.Random;
public class Deck {
private ArrayList deck;
public Deck() {
this.deck = new ArrayList();
for(Suit suit : Suit.values()) {
for(Rank rank: Rank.values()) {
Card card = new Card(rank, suit);
this.deck.add(card);
}
}
Collections.shuffle(deck);
}
public ArrayList getDeck() {
return deck;
}
}
Game.java
import java.util.ArrayList;
import java.util.Iterator;
public class Game {
public static void main(String[] args) {
Deck deck = new Deck();
for(int i=0; i<deck.getDeck().size(); i++) {
System.out.println( ((Card) deck.getDeck().get(i)).getCardValue() + " of "
+ ((Card) deck.getDeck().get(i)).getSuitValue() );
}
}
}
Does my design make sense so far? Also how can I call (Rank.java) -> int getCardValue() instead of (Card.java) -> Rank getCardValue()? Do I need to do some polymorphism? extend? I want the int value not the text value.
Thanks!
To answer your direct question, all you need to do is to simply chain methods:
Card myCard = new Card(Rank.TWO, Suit.HEARTS);
int value = myCard.getCardValue().getCardValue();
The first getCardValue() call returns a Rank object, and the next getCardValue() get's the rank's value. But this is confusing to me, two methods with the same name that return different types. Myself, I'd rename Card's getCardValue() to be the more straightforward and logical getRank() and Rank's getCardValue() to the more simple getValue(). Then the code would look more logical:
int value = myCard.getRank().getValue();
As for your other question, "is the design OK", that's too broad for this site, but I think that you're using inheritance fine, that you don't want to over-use inheritance and to stick with composition as you're doing. And also I will say that you need to account for ACE having two possible values, 1 and 11.
For example, possibly something like:
public enum Rank {
TWO(2, 0, false),
THREE(3, 0, false),
FOUR(4, 0, false),
FIVE(5, 0, false),
SIX(6, 0, false),
SEVEN(7, 0, false),
EIGHT(8, 0, false),
NINE(9, 0, false),
TEN(10, 0, false),
JACK(10, 0, false),
QUEEN(10, 0, false),
KING(10, 0, false),
ACE(11, 1, true);
private int value;
private int value2;
private boolean twoValues;
private Rank(int value, int value2, boolean twoValues) {
this.value = value;
this.value2 = value2;
this.twoValues = twoValues;
}
public int getValue() {
return value;
}
public int getValue2() {
// TODO: consider throwing a custom exception if twoValues is false
return value2;
}
public boolean hasTwoValues() {
return twoValues;
}
}
Just to throw some alternatives out there: I would perhaps model the value of a rank differently. Since the ace doesn't really have a definite value. A Hand has a value, but even that actually has a "best value", that may count an ace 11 or 1.
So I would perhaps remove the value of a rank. And introduce a Hand:
public class Hand {
private Hand(Collection<Card> cards) {
...
}
private int calculateBestValue() {
...
}
}
I would also not make the value calculation public. The Hand could "present" itself, so it can also say whether you have a "soft" value or not.
I also think the Deck should not have a getDeck() method. That violates encapsulation. Instead it should a method called: nextCard(). Which gives you the next card from the top of the deck. It may need an isEmpty() method too, which returns whether the deck has cards or not.
I have been stuck on this for sometime now, the objective is to create an array to hold the cards in, once I do create the array print them out the only card that is printing is king of hearts. Why is it not iterating?
public class Card
{
// Card suits (provided for your convenience - use is optional)
public static final int SPADES = 0;
public static final int HEARTS = 1;
public static final int CLUBS = 2;
public static final int DIAMONDS = 3;
// Card faces (provided for your convenience - use is optional)
public static final int ACE = 1;
public static final int TWO = 2;
public static final int THREE = 3;
public static final int FOUR = 4;
public static final int FIVE = 5;
public static final int SIX = 6;
public static final int SEVEN = 7;
public static final int EIGHT = 8;
public static final int NINE = 9;
public static final int TEN = 10;
public static final int JACK = 11;
public static final int QUEEN = 12;
public static final int KING = 13;
// define fields here
private static int suit;
private static int val;
private String[] suits = {"Clubs", "Spades", "Diamonds", "Hearts"};
private String[] vals = {"Ace","2", "3", "4", "5", "6", "7",
"8", "9", "10", "Jack", "Queen", "King" };
// This constructor builds a card with the given suit and face, turned face down.
public Card(int suit, int val)
{
this.val = val;
this.suit = suit;
}
public #Override String toString()
{
return vals[val] + " Of " + suits[suit];
}
// This method retrieves the suit (spades, hearts, etc.) of this card.
public int getSuit()
{
return this.suit;
}
// This method retrieves the face (ace through king) of this card.
public int getFace()
{
// return this.val;
switch(val)
{
case 0 : return 1;
case 1 : return 2;
case 2 : return 3;
case 3 : return 4;
case 5 : return 6;
case 6 : return 7;
case 7 : return 8;
case 8 : return 9;
case 9 : return 10;
case 10 : return 10;
case 12 : return 10;
default : return 0;
}
}
}`
import java.util.ArrayList;
import java.util.Random;
// This class represents the deck of cards from which cards are dealt to players.
public class Deck
{
//array
public static Card[] cards;
// private static ArrayList<Card> cards;
int i;
int counter;
// This constructor builds a deck of 52 cards.
Deck()
{
i = 51;
//array implementation
cards = new Card[52];
int x = 0;
for(int i = 0; i < 4; i++)
{
for(int j = 0; j < 13;j++)
{
//Array implementation
cards[x] = new Card(i,j);
x++;
}
}
}
// This method takes the top card off the deck and returns it.
public Card deal()
{
//Array implementation
int index = 0;
Card temp = cards[index];
return temp;
}
// this method returns true if there are no more cards to deal, false otherwise
public boolean isEmpty()
{
if(cards.length == 0)
{
return true;
}
return false;
}
//this method puts the deck int some random order
// public void shuffle()
// {
// for(int i = 51; i > 0; i--)
// {
// int rand = (int) (Math.random() * (i+1));
// Card temp = this.cards[i];
// this.cards[i] = cards[rand];
// cards[rand] = temp;
// }
//
// }
}
public class BlackJack extends Deck
{
public static void main(String []args)
{
System.out.println("Ready to play a game of BlackJack?");
Deck deck = new Deck();
for(int i = 0; i < cards.length;i++)
{
System.out.println(cards[i]);
}
Problem is caused by the following:
// define fields here
private static int suit;
private static int val;
because they are static, all Card instances share these, so they will all have the last value they were set to. Remove the static.
To give you a little hint how you might make the code even easier to read/maintain:
use enums instead of constants. You can give them constructors/member variables just like classes
use for-each-loops, it's way easier to read and prevents the usual off-by-1 problems
type 'static' BEFORE all other modifiers and keep all static variables and members separate from any non-static content. this way you can never mix them up by accident
Bad / unusual sides of this code are:
I use static nested enums and classes. Usually each of those should go into its own file and be non-static
I like to prefix my variables according to their scope so I cannot mess em up: pXxx for Parameter, sXxx for static variables, mXxxx for members, no prefix for method variables
public class CardGame {
static public enum SuitType {
SPADES, HEARTS, CLUBS, DIAMONDS
}
static public enum FaceType {
ACE("Ace", 1), //
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("Jack", 11), //
QUEEN("Queen", 12), //
KING("King", 13), //
;
private final String mName;
private final int mValue;
private FaceType(final String pName, final int pValue) {
mName = pName;
mValue = pValue;
}
public String getName() {
return mName;
}
public int getValue() {
return mValue;
}
public int getValue_alternative() {
return ordinal() + 1; // ordinal is the index of the enum, starting at ace=0 ... king=12
// sorting and comparing of suits could also be done by .ordinal() if need be
}
}
static public class Card {
private final SuitType mSuit;
private final FaceType mFace;
public Card(final SuitType pSuit, final FaceType pFace) {
mSuit = pSuit;
mFace = pFace;
}
public SuitType getSuit() {
return mSuit;
}
public FaceType getFace() {
return mFace;
}
#Override public String toString() {
return mFace.getName() + " of " + mSuit + " - " + mFace.getValue() + (mFace.getValue() > 1 ? " points" : " point");
}
}
static public class Deck {
private final ArrayList<Card> mCards;
public Deck() {
mCards = createFullDeck();
}
private ArrayList<Card> createFullDeck() { // can also be static as it dowes not use member cariables => potential factory method
final ArrayList<Card> ret = new ArrayList<>();
for (final SuitType suit : SuitType.values()) {
for (final FaceType face : FaceType.values()) {
final Card c = new Card(suit, face);
ret.add(c);
}
}
return ret;
}
public void shuffleRandomly() {
final ArrayList<Card> tempList = new ArrayList<>();
tempList.addAll(mCards);
mCards.clear();
while (tempList.size() > 0) {
final int index = (int) (Math.random() * tempList.size());
final Card card = tempList.remove(index);
mCards.add(card);
}
}
public void print() {
System.out.println(" - - - DECK BEGINS - - - ");
int counter = 0;
for (final Card c : mCards) {
System.out.println("\t" + c);
++counter;
}
System.out.println("> Printed " + counter + " cards.");
System.out.println(" - - - DECK ENDS - - - ");
}
}
public static void main(final String[] args) {
final Deck d = new Deck();
d.print();
d.shuffleRandomly();
d.print();
}
}
My other question got flagged duplicate couse I didn't explain well enough so i try again only a lot better.
So I'm making this java Black Jack console game and have stumbled to a small wall here. I've made a class called Card which is ready. Now i'm stumbeling a bit in creating all 52 cards in to an array of cards. Well i made code that works but it seems ugly to me having three nested for loops. Is there a "real way" of doing what I did here.
So here would be part of the class Card
int value;
String suit;
String name;
public void Card(int value, int suitNumber)
this.value=value;
switch(suitNumber){
case 1 : this.suit = "Heart";
break;
.......
}
switch(value){
case 1 : this.name="Ace";
......
}
Part of Pack class have a nested for loop like this:
Card[] packOfCards = new Card[51];
for(int k=0; k<52; ){
for(int i=1; i<5; i++){
for(int j=1; j<14; j++){
packOfCards[k] = Card(j, i);
k++;
// it seems stupid for me to have 3 nested
//loops like that
}
}
}
You can get same result using one for loop by saving more time complexity instead of three for loop and repeatation. Try,
for (int k = 1; k <= 52; k++) {
int i = k % 5;
int j = k % 14;
packOfCards[k-1] = new Card(j, i);
}
You either need to loop over:
Every slot in your deck
Every possible card value (Which probably requires a nested loop; get each value for each suit, or vice-versa)
At present you seem to be looping over both.
The following code uses the first approach:
for(int i = 0; i < 52; i++) {
int suit = (i % 13) + 1;
int value = (i / 13) + 1;
packOfCards[i] = new Card(value, suit);
}
And this code uses the second (two-loop) approach:
for (int i = 1; i <= 4; i++) {
for (int j = 1; j <= 13; j++) {
int cardIndex = (13 * (i-1)) + j;
packOfCards[cardIndex] = new Card(j, i);
}
}
Also note that your Card class looks wrong. You've declared a void method when I think you wanted a constructor, and then you missed the new keyword when creating the new card (hint: the compiler will alert you to a lot of these mistakes).
As others have suggested, it would probably make more sense to use enums too as the type for your card value and suit, rather than juggling between int and String.
import java.util.ArrayList;
import java.util.List;
public class Card {
private final CardValue value;
private final Suit suit;
public Card(CardValue value, Suit suit) {
this.value = value;
this.suit = suit;
}
#Override
public String toString() {
return value + " of " + suit;
}
public static void main(String[] args) {
List<Card> deck = new ArrayList<Card>();
for (Suit s : Suit.values()) {
for (CardValue v : CardValue.values()) {
deck.add(new Card(v, s));
}
}
for (Card c : deck) {
System.out.println(c);
}
Card aceOfSpades = new Card(CardValue.withValue(1), Suit.SPADES);
System.out.println(aceOfSpades);
}
public enum Suit {
HEARTS ("Hearts"),
CLUBS ("Clubs"),
DIAMONDS ("Diamonds"),
SPADES ("Spades");
private final String name;
Suit(String name) {
this.name = name;
}
#Override
public String toString() {
return this.name;
}
}
public enum CardValue {
TWO(2, "Two"),
THREE(3, "Three"),
FOUR(4, "Four"),
FIVE(5, "Five"),
SIX(6, "Six"),
SEVEN(7, "Seven"),
EIGHT(8, "Eight"),
NINE(9, "Nine"),
TEN(10, "Ten"),
JACK(11, "Jack"),
QUEEN(12, "Queen"),
KING(13, "King"),
ACE(1, "Ace");
private final int value;
private final String name;
CardValue(int value, String name) {
this.value = value;
this.name = name;
}
public int getValue() {
return value;
}
public static CardValue withValue(int value) {
for (CardValue v : CardValue.values()) {
if (value == v.value) {
return v;
}
}
return null;
}
#Override
public String toString() {
return this.name;
}
}
}
You'll probably want to pull the enums out into seperate files, but for the sake of producing a relatively concisely working example I put them within the main class.
So, in my java class, we were given these card and deck classes, which will be used for a card game later on. Here is the card code:
public class Card {
// public constants:
public static final int ACE = 1;
public static final int DEUCE = 2;
public static final int TWO = 2;
public static final int THREE = 3;
public static final int FOUR = 4;
public static final int FIVE = 5;
public static final int SIX = 6;
public static final int SEVEN = 7;
public static final int EIGHT = 8;
public static final int NINE = 9;
public static final int TEN = 10;
public static final int JACK = 11;
public static final int KNAVE = 11;
public static final int QUEEN = 12;
public static final int KING = 13;
public static final int SPADES = 1;
public static final int HEARTS = 2;
public static final int DIAMONDS = 3;
public static final int CLUBS = 4;
// private instance data;
private int rank;
private int suit;
// public constructor:
public Card ( int rank, int suit ) {
if ( rank < Card.ACE | rank > Card.KING | suit < Card.SPADES | suit > Card.CLUBS ) {
throw new IllegalArgumentException();
} else {
this.rank = rank;
this.suit = suit;
}
}
/** Returns this card's suit. */
public int getSuit() {
return this.suit;
}
/** Returns this card's rank. */
public int getRank() {
return this.rank;
}
/** Returns a stringy version of this card. */
public String toString() {
// Replace the next instruction with your code:
throw new UnsupportedOperationException();
}
}
And here is deck class code:
public class Deck {
// private instance data;
private Card[] cards;
// public constructor:
public Deck() {
this.cards = new Card[52];
int i = 0;
for ( int suit = Card.SPADES; suit <= Card.CLUBS; suit++ ) {
for ( int rank = Card.ACE; rank <= Card.KING; rank++ ) {
this.cards[i] = new Card(rank,suit);
i++;
}
}
}
/** Returns a copy of the card at the specified index in this deck. */
public Card cardAt(int index) {
if ( index < 0 | index > 51 ) {
throw new IllegalArgumentException();
} else {
return new Card( this.cards[index].getRank(),this.cards[index].getSuit() );
}
}
/** Shuffles this deck. */
public void shuffle() {
// Replace the next instruction with your code:
throw new UnsupportedOperationException();
}
/** Returns a stringy version of this deck. */
public String toString() {
// Replace the next instruction with your code:
throw new UnsupportedOperationException();
}
}
My issue is that I am stuck on the toString method for both classes and the shuffle method for the deck class. Any help?
The shuffle can be implemented as follows
/** Shuffles this deck. */
public void shuffle() {
// Shuffle the elements in the array
Collections.shuffle(Arrays.asList(cards));
}
The toString simply builds a string and returns it. This can be anything you want to describe your class. Here is what I would do.
for deck
/** Returns a stringy version of this deck. */
public String toString() {
String output="Current Deck:\n";
for (int i=0; i <cards.length ; i++){
output+= cardAt(i).toString();
}
return output;
}
as for card, I'd change a few things. You assign a bunch of ints to represent the card, I would personally use an enum if you don't care about the values. This way cards that have values other than a number, ie a jack vs a 10, will be represented by their name, rather than some number. Who really ever says "i have a 12 of clubs". I'd change my stuff to this.
public enum Rank{ACE,TWO,THREE ....SO ON}
public enum Suit {HEARTS,CLUBS,SPADES,DIAMONDS}
you'd then change all of your get methods, but the real advantage comes in the fact you don't need a long switch statement to deduce the card NAME from the enum values. you can implement toString like this.
//change rank and suit type from int to the Enums
Rank rank;
Suit suit;
/*modify all your getters*/
/** Returns a stringy version of this deck. */
public String toString() {
//enum will be printed out!
return rank+" of " + suit +"\n";
}
My card class where the method is defined.
package blackjackgamemodel;
public class Card {
protected Rank rank;
protected Suit suit;
public Card(Card.Rank rank, Card.Suit suit) {
this.rank = rank;
this.suit = suit;
}
public String toString() {
return "" + rank + " of " + suit;
}
public Card.Rank rank() {
return rank;
}
public Card.Suit suit() {
return suit;
}
public enum Rank {
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);
private int value;
Rank(int value) {
this.value = value;
}
public int value() {
return value;
}
}
public enum Suit {
SPADES, HEARTS, CLUBS, DIAMONDS
}
}
The game class where the error is
package blackjackgamemodel;
import blackjackgamemodel.Card;
import blackjackgamemodel.Deck;
public class Game {
protected Deck deck;
protected int sum;
protected int aces;
public Game() {
// Initialize deck, sum and aces.
deck = new Deck();
sum = 0;
aces = 0;
}
public Card draw() {
// Draw a card from the deck
Card drawn_card = deck.draw();
// Calculate the value to add to sum
int v = drawn_card.value();
if (v == 1) {
v = 11;
// If the card is an ace, increase the count of aces.
aces++;
}
else if (v > 10) {
v = 10;
}
// Now v is the Blackjack value of the card.
// If the sum is greater than 21 and there are aces,
// Then decrease the sum by 10 and the aces by 1.
if (aces > 0 && sum > 21) {
sum = sum - 10;
aces = aces - 1;
}
// Return the card that was drawn.
return drawn_card;
}
public int sum() {
// Getter for sum.
return sum;
}
}
ERROR
Description Resource Path Location Type
The method value() is undefined for the type Card Game.java /BlackJack/src/blackjackgamemodel line 25 Java Problem
It's the Card.Rank enum that has the value() method, not the Card class.
Try:
int v = drawn_card.rank().value()
Indeed Card has no value method: It has a rank method and Card.Rank has a value method.
value() isn't a method on Card it is on Card.Rank in your included code.
The message is correct, Card does not have a value() method. Did you mean to get the card's rank and then get that value? That's looks like the intent.
Does your Card have a value method? It looks like Card's rank has a value method.
Try:
int v = drawn_card.rank().value();