Order a list of objects - java

I am making a program that simulates a card game. I have a class Card that represents a playing card:
//suit types
public enum Suits{
SPADES,
CLUBS,
HEARTS,
DIAMONDS
}
//value types
public enum Values{
TWO,
THREE,
FOUR,
FIVE,
SIX,
SEVEN,
EIGHT,
NINE,
TEN,
JACK,
QUEEN,
KING,
ACE,
}
private Suits suit;
private Values value;
//Card constructor
public Card(Values value, Suits suit){
this.value = value;
this.suit = suit;
}
In my game class I have a players hand represented by a List of Cards:
public List<Card> playerHand = new ArrayList<Card>();
Is there a way that I can sort the players hand by my enum Values? Meaning if a players hand is:
TEN, ACE, TWO, FIVE
After ordering it would be:
TWO, FIVE, TEN, ACE
My conditions would be Ace is always high, and Suit doesn't matter. I know ordering Lists are trivial I just don't know how to do it for an Object based on an enum

Enums have a default ordering, i.e. they implement Comparable and thus you can just sort a list of enums or call ACE.compareTo( TEN ) in some Comparator. Note that the order of the enum values is the order of definition in the enum class (based on ordinal, see next paragraph), i.e. in your case TWO < THREE < FOUR etc.
Additionally enums have an ordinal, i.e. the index of the enum value inside the definition. In your case TWO would have the ordinal 0, THREE the ordinal 1 etc.
In order to sort your cards, you could use a Comparator (or implement Comparable<Card>) like this:
class CardComparator implements Comparator<Card> {
public int compare( Card lhs, Card rhs) {
//suit would be relevant otherwise you'd end up with spades 10, hearts 10, spades jack etc.
//thus sort by suit first to keep the cards of a suit together
int r = lhs.suit.compareTo( rhs.suit );
if( r == 0) { //same suit
r = lhs.value.compareTo( rhs.value );
}
return r;
}
}

Use
Collections.sort(list, comparator);
for sorting. As for comparator:
new Comparator<Card>() {
#Override
public int compare(Card x, Card y) {
return x.getValue().compareTo(y.getValue());
}
}

I would set a value for each of the Suits and Values, so that I can compare them to one another. For example:
public enum Suits{
SPADES(1),
CLUBS(2),
HEARTS(3),
DIAMONDS(4);
private int value;
private Suits(int value) {
this.value = value;
}
public int getValue() {
return value;
}
}
and the Values enum:
public enum Values{
TWO(2),
THREE(3),
FOUR(4),
FIVE(5),
SIX(6),
SEVEN(7),
EIGHT(8),
NINE(9),
TEN(10),
JACK(11),
QUEEN(12),
KING(13),
ACE(14);
private int value;
private Values(int value) {
this.value = value;
}
public int getValue() {
return value;
}
}
Then, you can implement a Comparator for the Cards:
public class CardsComparator implements Comparator<Card> {
public int compare(Card c1, Card c2) {
//implementation based on the values of Suits and Values.
if (c1.getSuits().getValue() == c2.getSuits.getValue()) {
return c1.getValue().compareTo(c2.getValue());
} else {
return c1.getSuits().compareTo(c2.getSuits());
}
}
}
and apply an instance of the Comparator to sort your List<Card>:
Collections.sort(listOfCards, new CardsComparator());

You can implement the interface Comparable. You would have to redefine the compareTo method for your Card class and it would then allow you to do something like :
Card a = ...;
Card b = ...;
if(a.compareTo(b) <= 0) {
...
}
Or even :
Collections.sort(list);
See http://docs.oracle.com/javase/7/docs/api/java/lang/Comparable.html

Related

Incompatible types error when iterating through enum elements to create new object

I am trying to use two enums to create a deck of cards via a 2d array, then print all of the created cards.
public class Deck {
private Card[][] cards;
public Deck() {
cards = new Card[4][13];
for (Card.Suit suit : Card.Suit.values()) {
for (Card.Rank rank : Card.Rank.values()) {
//This is where my problem begins
cards[suit][rank] = new Card(suit, rank);
}
}
}
public Card getCard(Card.Suit s, Card.Rank r) {
//The error appears here as well.
return cards[suit][rank];
}
Here are the enums I'm trying to use.
public class Card {
private Rank rank;
private Suit suit;
// Kinds of suits
public enum Suit {
CLUBS,
DIAMONDS,
HEARTS,
SPADES;
}
// Kinds of ranks
public enum Rank {
ACE,
DEUCE,
THREE,
FOUR,
FIVE,
SIX,
SEVEN,
EIGHT,
NINE,
TEN,
JACK,
QUEEN,
KING;
}
public Card(Suit s, Rank r) {
rank = r;
suit = s;
}
...}
What I think should be happening is that the for loop is going through each element via Card.Suit.values() and then Card.Rank.values() and creating a new Card(Suit suit, Rank rank), according to it's index in the array cards[s][r].
When I compile my code it gives the error.
"Deck.java:42: error: incompatible types: Suit cannot be converted to int
cards[suit][rank] = new Card(suit, rank);"
Indexes in arrays need to be integers, but at line cards[suit][rank] = ... you are passing enum constant held by suit (like CLUBS) and rank. You can get int representing position of current enum constant with Enum#ordinal() method.
Change your
cards[suit][rank] = new Card(suit, rank);
to
cards[suit.ordinal()][rank.ordinal()] = new Card(suit, rank);
// ^^^^^^^^^^ ^^^^^^^^^^

Java Blackjack OOP

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.

How to loop using enum just for certain portions(face cards) in a deck of cards

I am creating a deck of poker cards (52 cards). I want to be able to print it out for example:
2 of Club, 3 of Club......
2 of Diamond, 3 of Diamond....
Queen of Diamond, King of Diamond
and so on for the 52 cards.
I am able to do this now but having problem when it comes to the face cards which are the Jack, Queen, King and Ace. Currently I am using numbers to represent them. so instead of printing Jack of Clubs, it shows as 11 of Clubs which is wrong. I tried to store the face cards in an enum and tried to loop them but can't really get around to do it.
Can I get some advice on how I could get my face cards in instead of representing them as numbers. I have attached my main method and the class below. Thanks for help.
//Card Class
import java.util.Objects;
public class Card {
public enum Suits{
CLUBS, DIAMONDS, HEARTS, SPADES;
}
public enum Faces{
JACK, QUEEN, KING, ACE;
}
private int rank;
private String suit;
public Card(int rank, String suit){
this.rank = rank;
this.suit = suit;
}
public int getRank(){
return rank;
}
public String getSuit(){
return suit;
}
public String format(){
return String.format("%d of %s, ", getRank(), getSuit());
}
}
//Main method
public class CardTester {
public static void main(String[] args) {
Card[] cards = new Card[52];
int i = 0;
for (Card.Suits suit : Card.Suits.values()) {
for (int y = 2; y < 15; y++) {
cards[i] = new Card(y, suit.name());
i++;
}
}
for(Card p : cards){
System.out.print(p.format() + " ");
}
}
}
Change your format() method to:
public String format(){
if (getRank() < 11) {
return String.format("%d of %s, ", getRank(), getSuit());
}
else {
Faces face = Faces.values()[getRank() - 11];
return String.format("%s of %s, ", face, getSuit());
}
}
Alternatively, here's a better implementation for Card:
import java.util.Objects;
public class Card {
public enum Suit {
CLUBS, DIAMONDS, HEARTS, SPADES;
}
public enum Rank {
TWO, THREE, FOUR, FIVE, SIX, SEVEN, EIGHT, NINE, TEN,
JACK, QUEEN, KING, ACE;
}
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() {
return rank + " of " + suit;
}
#Override
public int hashCode() {
int hash = 5;
hash = 97 * hash + Objects.hashCode(this.suit);
hash = 97 * hash + Objects.hashCode(this.rank);
return hash;
}
#Override
public boolean equals(Object obj) {
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
final Card other = (Card) obj;
if (this.suit != other.suit) {
return false;
}
if (this.rank != other.rank) {
return false;
}
return true;
}
}
You can use it like this:
import java.util.ArrayList;
import java.util.List;
public class CardTester {
public static void main(String[] args) {
List<Card> cardList = new ArrayList<>();
Card lastCard = new Card(Card.Suit.SPADES, Card.Rank.ACE);
for (Card.Suit suit : Card.Suit.values()) {
for (Card.Rank rank : Card.Rank.values()) {
Card card = new Card(suit, rank);
cardList.add(card);
if (!card.equals(lastCard)) {
System.out.print(card + ", ");
}
else {
System.out.print(card);
}
}
}
// use cardList
}
}
Your modelling of cards is flawed, and your problem highlights this. Rank is not numeric and should not be an 'int'. You might want to use an enum for the rank.
You might be tempted to give your rank an integer value, but this is probably not a good idea. The value is generally defined by the game you are playing, and not by the deck of cards. For example in poker a king is ranked higher than a jack, but in blackjack they are ranked the same.
May be source code shown below will help you:
public enum Faces
{
JACK( 1, "Jack" ), QUEEN( 2, "Queen" ), KING( 3, "King" ), ACE( 4, "Ace" );
public int GetNumber()
{
return m_number;
}
public String GetName()
{
return m_name;
}
private Faces( int number_, String name_ )
{
m_number = number_;
m_name = name_;
}
private int m_number;
private String m_name;
}
you may delete the enum Faces you already have and add the following method to class Card:
public String getFace(int rank){
String result = "";
switch (rank){
case 11: result = "Jack"; break;
case 12: result = "QUEEN"; break;
case 13: result = "KING"; break;
case 14: result = "ACE"; break;
default:{
result = Integer.toString(rank);
break;
}
}
return result;
}
and modify your format() method to look like this:
public String format(){
return String.format("%s of %s, ", getFace(getRank()), getSuit());
}
This will do the job ;-)
A card consists of RANK and SUIT. Common poker software convention is to represent cards as SUIT+RANK Strings, as in 3c for 3 of clubs and Kd for king of diamonds. (see pokerstove or most hand history producers and consumers)
You need to expand the concept of RANK beyond faces. I've been using the Steve Brecher poker eval library, and his RANK class looks like this:
A simple lookup of the string "23456789TJQKA" converts between the enum and the RANK. There is a similar SUIT enum and a pretty reasonable Comparable class that combines the two.
public static enum Rank {
TWO, THREE, FOUR, FIVE, SIX, SEVEN, EIGHT, NINE, TEN, JACK, QUEEN, KING, ACE;
/**
* #return the character in {#link #RANK_CHARS} denoting this rank.
*/
public char toChar() {
return RANK_CHARS.charAt(this.ordinal());
}
/**
* #param c
* a character present in {#link #RANK_CHARS} (case
* insensitive)
* #return the Rank denoted by character.
* #throws IllegalArgumentException
* if c not in {#link #RANK_CHARS}
*/
public static Rank fromChar(char c) {
int i = RANK_CHARS.indexOf(Character.toUpperCase(c));
if (i >= 0)
return Rank.values()[i];
throw new IllegalArgumentException("'" + c + "'");
}
/**
* #return the pip value of this Rank, ranging from 2 for a
* <code>TWO</code> (deuce) to 14 for an <code>ACE</code>.
*/
public int pipValue() {
return this.ordinal() + 2;
}
public static final String RANK_CHARS = "23456789TJQKA";
}
You can find his old but still perfectly serviceable libraries here: http://www.brecware.com/Software/software.html or http://pokersource.sourceforge.net/
U can try this solution which uses enum. First of all you need to create two enums. One for suits and the other for cards.
The enum for suits is:
public enum suits
{
club, diamond,heart,spades
}
The enum for cards is:
public enum cards
{
two(2), three(3), four(4), five(5), six(6), seven(7), eight(8),nine(9),
ten(10), jack(11), queen(12), king(13),ace(14);
int number;
private cards(int number)
{
this.number=number;
}
public int getNumber()
{
return this.number;
}
}
THen in your main class you can loop through both the enums to get your result as follows:
public class Javaclass
{
public static void main(String[] args)
{
int count=2;
for(suits suit:suits.values())
{
for(cards card: cards.values())
{
if(count<=10)
System.out.println(card.getNumber()+" of "+suit);
else
System.out.println(card+" of "+suit);
count++;
}
count=2;
System.out.println("");
}
}
}
Hope this helps.

Comparing two objects in Java .. each object has two fields

I want to compare between two cards in poker hand .. each of them has a suit and a rank and I want to check if there are duplicates to create an exception .. need help with this piece of code please ! is it ok to use the equals method or it is wrong ?!
import java.util.ArrayList;
public class Pokerhand {
public final int CARDS_NUMBER = 5;
ArrayList<Card> cards = new ArrayList<Card>();
public Pokerhand (Card card1 , Card card2, Card card3, Card card4, Card card5)
{
cards.add(card1);
cards.add(card2);
cards.add(card3);
cards.add(card4);
cards.add(card5);
}
private boolean check(ArrayList<Card> cards)
{
if (cards.size() != CARDS_NUMBER)
throw new IllegalArgumentException("Incorrect number of cards!! ");
for ( int i=0 ; i<= cards.size(); i++)
{
if (cards.get(i).equals(cards.get(i+1)))
throw new IllegalArgumentException("Duplicat card");
}
}
}
You should consider implementing Comparable in your Card class. Say each card has rank (1-king) and suit (hearts, diamonds, etc.) The method compareTo would look like:
public int compareTo(Card anotherCard) {
if (this.suit.equals(anotherCard.suit) && this.rank.equals(anotherCard.rank)) {
return 0;
}
else if (this.suit.equals(anotherCard.suit)) {
return this.rank.compareTo(anotherCard.rank);
}
else {
return this.suit.compareTo(anotherCard.suit);
}
}
Then you should just use card1.compareTo(card2) == 0 instead of card1.equals(card2)
You should override the equals method, or it just compare the reference of two objects, rather than the value of the cards.
Here is a example:
public class Card {
private int rank;
private int suit;
#Override
public boolean equals(Object obj) {
if (!(obj instanceof Card)) {
return false;
}
Card c = (Card) obj;
return this.rank == c.rank && this.suit == c.suit;
}
}
Instead of
if (cards.get(i).equals(cards.get(i+1)))
throw new IllegalArgumentException("Duplicat card");
Try:
if(cards.get(i).getSuit() == cards.get(i+1).getSuit()
&& cards.get(i).getRank() == cards.get(i+1).getRank())
throw new IllegalArgumentException("Duplicat card");
This way you are comparing the values within the objects as opposed to comparing the object references are the same.
You could maybe model this better using enums. For example:
import java.util.EnumSet;
public class PokerGame {
public enum Suit {
SPADES, HEARTS, DIAMONDS, CLUBS
}
public enum Rank {
ACE, TWO, THREE, FOUR, FIVE, SIX, SEVEN, EIGH, NINE, TEN, JACK, QUEEN, KING
}
public enum Card {
ACE_OF_SPADES(Rank.ACE, Suit.SPADES),
TWO_OF_SPADES(Rank.TWO, Suit.SPADES),
THREE_OF_SPADES(Rank.THREE, Suit.SPADES),
FOUR_OF_SPADES(Rank.FOUR, Suit.SPADES),
FIVE_OF_SPADES(Rank.FIVE, Suit.SPADES);
// the other cards left as an exercise for the reader (-;
private Rank rank;
private Suit suit;
Card(Rank rank, Suit suit) {
this.rank = rank;
this.suit = suit;
}
public Rank rank() {
return rank;
}
public Suit suit() {
return suit;
}
}
public static void main(String[] args) {
EnumSet<Card> deck = EnumSet.allOf(Card.class);
System.out.println(deck.size());
}
}
You can then use EnumSet to create decks or hands of cards, always guaranteed to be unique.
Additional remark, the following logic is flawed:
for ( int i=0 ; i<= cards.size(); i++)
{
if (cards.get(i).equals(cards.get(i+1)))
throw new IllegalArgumentException("Duplicat card");
}
This will throw an IndexOutOfBoundsException because you are iterating up to cards.size() and acccessing cards.get(i + 1). Furthermore this will not detect non-adjacent duplicates.

difference method java using enum types

I wish to write a method that can return the difference in value of 2 cards.
Im confused as I'm learning enums and not sure of the most efficient way to implement it.
public class Card implements Comparable<Card> {
public enum Rank {TWO, THREE, FOUR, FIVE, SIX,SEVEN, EIGHT, NINE, TEN,
JACK, QUEEN, KING, ACE}
public enum Suit {CLUBS, DIAMONDS, HEARTS, SPADES}
public static int difference(Card c){
}
Any help or guidance would be appreciated.
public enum Rank {ACE(1), TWO(2); /* obviously add the other values*/
private int value ;
Rank(int value){
this.value = value;
};
public int getValue(){
return this.value;
}
public int calcDifference(Rank rank){
return getValue() - rank.getValue();
}
};
which can then be called like so :
Rank rankAce = Rank.ACE;
System.out.println(rankAce.calcDifference(Rank.TWO));
You could remove the int value and just use ordinal, but this way gives you a bit of flexibility.
The simplest way to define a value of a card is to add thirteen times the suit to the rank, or to add four times the rank to the suit:
public int faceValue(Card c) {
return 13*c.getSuit().ordinal()+c.getRank().ordinal();
}
or
public int faceValue(Card c) {
return 4*c.getRank().ordinal()+c.getSuit().ordinal();
}
Both ways produce numbers from 0 to 51, inclusive.
With faceValue in hand, you can define the difference function as follows:
public static int faceValueDifference(Card left, Card right){
return left.faceValue()-right.faceValue();
}

Categories

Resources