Reputation: 9
I am currently working on a CS project that classifies player's hands. I solved the first half of the project to print out the deck, shuffled deck and the hands of player1, player2 and remaining deck. The problem comes up when I have to evaluate the hands. My code has to somehow evaluate which classification the hands are, and print out whether player1 or player2 wins. I have three classes so far:
public class Card {
static String[] card_suit = {"hearts", "diamonds", "clubs", "spades"};
static int[] card_rank = {2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14};// 11 is Jack, 12 is Queen, 13 is King and 14 is Ace
public int[] getRank() {
return card_rank;
}
public String[] getSuit() {
return card_suit;
}
}
public class Driver {
public static void main(String[] args) {
Card card = new Card();
Deck deck = new Deck();
deck.getDeck();
System.out.print("ORIGINAL DECK: ");
deck.printDeck();
deck.shuffleDeck();
System.out.print("SHUFFLED DECK: ");
deck.printDeck();
System.out.println();
System.out.print("PLAYER ONE: ");
System.out.println(java.util.Arrays.toString(deck.playerOneHands()));
System.out.print("PLAYER TWO: ");
System.out.println(java.util.Arrays.toString(deck.playerTwoHands()));
System.out.print("REMAINING DECK: ");
System.out.println(java.util.Arrays.toString(deck.remainingDeckCards()));
}
}
import java.util.Arrays;
import java.util.Collections;
public class Deck extends Card {
Card card = new Card();
private String[] deck_card = new String[52];
public String[] getDeck() {
int i = 0;
for(int s = 0; s < 4; s++) {
for(int r = 0; r < 13; r++) {
deck_card[i]=(card_suit[s] + " of " + card_rank[r]);
i++;
}
}
return deck_card;
}
public void printDeck() {
System.out.println (java.util.Arrays.toString (deck_card));
}
public void shuffleDeck() {
Collections.shuffle(Arrays.asList(deck_card));
}
public String[] playerOneHands() {
String [] firsthand = new String[5];
for(int a = 0; a < 5; a++) {
firsthand[a] = deck_card[a];
}
return firsthand;
}
public String[] playerTwoHands() {
String[] secondhand = new String[5];
for(int a = 0; a < 5; a++) {
secondhand[a] = deck_card[a+5];
}
return secondhand;
}
public String[] remainingDeckCards() {
String[] remainDeck = new String[42];
for(int a = 0; a < 42; a++){
remainDeck[a] = deck_card[a+10];
}
return remainDeck;
}
}
What I thought it would work is because the Deck class extends from the Card class, I can use the getRank method to compare each hand, but I am not sure how to construct the conditionals.
Any help is greatly appreciated. Thanks.
Upvotes: 0
Views: 1696
Reputation: 9650
It seems that you class Card
only has static fields; I would change it so that an instance of Card
would represent a single card from a Deck
. I would also make the suites an enum
type. You can also add integer constants for the figures and aces. The class can implement Comparable<Card>
:
public class Card implements Comparable<Card> {
public enum Suite {CLUBS, DIAMONDS, HEARTS, SPADES};
public static final int JACK = 11;
public static final int QUEEN = 12;
public static final int KING = 13;
public static final int ACE = 14;
public final Suite suite;
public final int rank;
public Card(Suite suite, int rank) {
if (suite == null) {
throw new IllegalArgumentException("Suite cannot be null");
}
if (rank < 2 || rank > 14) {
throw new IllegalArgumentException(
"Value must be between 2 and 14");
}
this.suite = suite;
this.rank = rank;
}
public Suite getSuite() {
return suite;
}
public int getRank() {
return rank;
}
@Override
public String toString() {
StringBuilder buf = new StringBuilder();
if (rank >= 2 && rank <= 10) {
buf.append(rank);
} else {
switch (rank) {
case JACK:
buf.append("jack");
break;
case QUEEN:
buf.append("queen");
break;
case KING:
buf.append("king");
break;
case ACE:
buf.append("ace");
break;
}
}
buf.append(" of ");
buf.append(suite.toString().toLowerCase());
return buf.toString();
}
@Override
public int compareTo(Card other) {
if (rank > other.rank) {
return 1;
} else if (rank < other.rank) {
return -1;
} else {
return suite.compareTo(other.suite);
}
}
}
Note that you could also have two subclasses of Card
: one for the numbers and one for the figures.
The Deck
is a collection of 52 cards. It is initialised by adding each card to a list. One can shuffle
a deck, or take
a card from the deck:
public class Deck {
private final List<Card> cards = new ArrayList<>();
public Deck() {
for (Card.Suite suite: Card.Suite.values()) {
for (int i = 2; i <= 14; ++i) {
cards.add(new Card(suite,i));
}
}
}
public void shuffle() {
Collections.shuffle(cards);
}
public boolean isEmpty() {
return cards.isEmpty();
}
public Card take() {
if (cards.isEmpty()) {
throw new IllegalStateException("Deck is empty");
}
return cards.remove(0);
}
}
You can take shuffle and take 5 cards from the deck like this:
Deck deck = new Deck();
deck.shuffle();
for (int i = 0; i < 5; ++i) {
Card card = deck.take();
System.out.println(card);
}
Now a Hand
is a set of five cards taken from a Deck
. We can declare Hand
as implementing Comparable<Hand>
so that we can know which of two hands have the highest value:
public class Hand implements Comparable<Hand> {
private final Card[] cards = new Card[5];
public Hand(Deck deck) {
for (int i = 0; i < 5; ++i) {
cards[i] = deck.take();
}
Arrays.sort(cards);
}
@Override
public int compareTo(Hand other) {
...
}
}
Now here comes the fun part: you must identify the hand type as one of the following (enum type):
public enum HandType {
SINGLE, PAIR, TWO_PAIRS, THREE, STRAIGHT, FLUSH, FULL_HOUSE, FOUR,
STRAIGHT_FLUSH, ROYAL_FLUSH;
}
Note that the constants are arranged from the lowest to the highest. In addition, the cards must be arranges so that in case of a tie, you can compare the cards to identify the winner.
I would suggest you make groups of cards of same rank; in the case where you have five different groups, it still can be a flush or a straight.
Another approach would consist in declaring a subclass of Hand
for each HandType
, but I don't think you would gain much by doing this.
public class Hand implements Comparable<Hand> {
public enum HandType {
SINGLE, PAIR, TWO_PAIRS, THREE, STRAIGHT, FLUSH, FULL_HOUSE, FOUR,
STRAIGHT_FLUSH, ROYAL_FLUSH;
}
private final Card[] cards = new Card[5];
private final int[] groupSize;
private final HandType type;
public Hand(Deck deck) {
for (int i = 0; i < 5; ++i) {
cards[i] = deck.take();
}
groupSize = group(cards);
type = identifyType(groupSize, cards);
}
@Override
public int compareTo(Hand other) {
int r = type.compareTo(other.type);
if (r != 0) {
return r;
}
for (int i = cards.length; --i >= 0; ) {
int r1 = cards[i].getRank();
int r2 = other.cards[i].getRank();
if (r1 < r2) {
return -1;
} else if (r1 > r2) {
return 1;
}
}
return 0;
}
@Override
public String toString() {
StringBuilder buf = new StringBuilder();
buf.append(type);
buf.append(": ");
buf.append(cards[0]);
for (int i = 1; i < 5; ++i) {
buf.append(", ");
buf.append(cards[i]);
}
return buf.toString();
}
private static int[] group(Card[] cards) {
Arrays.sort(cards);
List<List<Card>> groups = new ArrayList<>();
int val = -1; // invalid rank
List<Card> currentGroup = null;
for (Card card: cards) {
if (val == card.getRank()) {
currentGroup.add(card);
} else {
if (currentGroup != null) {
groups.add(currentGroup);
}
currentGroup = new ArrayList<>();
currentGroup.add(card);
val = card.getRank();
}
}
if (currentGroup != null) {
groups.add(currentGroup);
}
// identify groups of cards of same value
// sort groups by size and highest card
Collections.sort(groups, (List<Card> group1, List<Card> group2) -> {
int s1 = group1.size();
int s2 = group2.size();
if (s1 < s2) {
return -1;
} else if (s1 > s2) {
return 1;
} else {
return group1.get(s1-1).compareTo(group2.get(s2-1));
}
});
int[] groupSize = new int[groups.size()];
int g = 0;
int i = 0;
for (List<Card> group: groups) {
groupSize[g++] = group.size();
for (Card card: group) {
cards[i++] = card;
}
}
assert sum(groupSize) == 5;
return groupSize;
}
private static HandType identifyType(int[] groupSize, Card[] cards) {
switch (groupSize.length) {
case 2:
// can be a full house or four cards
if (groupSize[0] == 1) {
return HandType.FOUR;
} else if (groupSize[0] == 2) {
return HandType.FULL_HOUSE;
} else {
assert false;
return null;
}
case 3:
if (groupSize[0] == 1) {
// three cards or double pair
if (groupSize[1] == 1) {
return HandType.THREE;
} else {
assert groupSize[1] == 2 && groupSize[2] == 2;
return HandType.TWO_PAIRS;
}
} else {
assert false;
return null;
}
case 4:
// one pair
return HandType.PAIR;
case 5:
// all different values: check for flush
Card prev = cards[0];
boolean sameSuite = true;
boolean straight = true;
for (int i = 1; i < 5; ++i) {
Card card = cards[i];
straight &= card.getRank() == prev.getRank()+1;
sameSuite &= card.getSuite() == prev.getSuite();
}
if (sameSuite) {
if (straight) {
if (cards[4].getRank() == Card.ACE) {
return HandType.ROYAL_FLUSH;
}
return HandType.STRAIGHT_FLUSH;
} else {
return HandType.FLUSH;
}
} else {
if (straight) {
return HandType.STRAIGHT;
} else {
return HandType.SINGLE;
}
}
default:
assert false;
return null;
}
}
private static int sum(int[] groupSize) {
int sum = 0;
for (int s: groupSize) {
sum += s;
}
return sum;
}
}
Upvotes: 0
Reputation: 425003
Without wanting to do your homework for you...
This is a problem:
class Deck extends Card
A deck isn’t a subtype of a card. A deck has cards, so:
class Deck {
List<Card> cards;
}
is a better choice.
Also, the following code does nothing to the deck:
public void shuffleDeck() {
Collections.shuffle(Arrays.asList(deck_card));
}
It shuffles a copy of the deck, leaving the deck untouched.
Also, you shouldn’t be building strings in a loop. Instead, implement (override) a toString() method on Card and Deck.
Also, make suit an enum
.
Also, delete card_rank
entirely - it serves no purpose. Instead, add a int rank;
field to Card, or better make rank an enum
.
Fix these things first, then re-attack the problem by writing a method that is passed a Hand (a new class) that has a List and a method that returns a HandType (another enum) by evaluating if the hand is a straight flush, else four of a kind, else ... all the way down to high card - highest to lowest.
Upvotes: 1
Reputation: 418
Upvotes: 1