problems with List<Card> and sorting
Hi
I am building a card game, what I would like to do from both my Deck and Hand classes is be able to reorder the cards, by suit and rank. However, i am using a List's and though this works great with adding, removing etc however sorting seems to raise issues!
Is a list really the best thing to use, or should I just use an array, but I did read all the stuff online about enums and it recommended a List... anyway...
publicclass Card
{
privatefinal Rank rank;
privatefinal Suit suit;
publicenum Rank
{
Ace(14),Deuce(2),Three(3),Four(4),Five(5),Six(6),Seven(7), Eight(8),
Nine(9), Ten(10), Jack(11), Queen(12), King(13);
privatefinal Integer rankValue;
Rank(int rankValue)
{
this.rankValue = rankValue;
}
publicint facevalue(){return rankValue;};
}
publicenum Suit
{
Hearts (0), Clubs(1), Diamonds(2), Spades(3);
privatefinal Integer suitValue;
Suit(int suitValue)
{
this.suitValue = suitValue;
}
publicint suitValue(){return suitValue;};
}
public Card(Rank rank, Suit suit)
{
this.rank = rank;
this.suit = suit;
}
public Rank rank(){return rank;}
public Suit suit(){return suit;}
publicint suitValue(){return suit.suitValue;}
publicint rankValue(){return rank.rankValue;}
public String toString(){return rank +" of " + suit;}
}
import java.util.*;
publicclass Deck
{
privatestaticfinal List<Card> Deck =new ArrayList<Card>();
privateint deckPointer = 0;
// Initialize prototype deck
static
{
for (Card.Suit suit : Card.Suit.values())
for (Card.Rank rank : Card.Rank.values())
Deck.add(new Card(rank, suit));
}
public Card getCard(int index)
{
return Deck.get(index);
}
public String getCardStringValue(int index)
{
return Deck.get(index).toString();
}
public Card dealCard()
{
Card c = Deck.get(deckPointer);
deckPointer++;
return c;
}
publicvoid displayDeck()
{
System.out.println(Deck.toString());
}
publicvoid shuffle()
{
Collections.shuffle(Deck);
}
publicvoid orderDeck()
{
?
}
publicvoid deckSize()
{
System.out.println(Deck.size());
}
}
[5820 byte] By [
cakea] at [2007-10-2 8:21:14]

List vs array is completely irrelevant. The decision you have to make is Comparable vs Comparator.
HiIn this time of good will, would you care to enlighten me in regard to this subject?Many thanks, merry xmas, Ron
cakea at 2007-7-16 22:20:23 >

There are two overloads to Collections.sort:
public static <T extends Comparable><? super T>> void sort(List<T> list)
public static <T> void sort(List<T> list, Comparator<? super T> c)
As the documentation states, the first works according to the order defined by the element's implementation of Comparable. The second works accourding to the order specified by the passed Comparator.
You can implement your sorting with either of these; neither is strictly the best. In general, you decide the approach based on the answer to: "Is there one way of sorting for this type which is the only way that makes much sense?" If there's only one sensible way of sorting you make your class implement Comparable. If there isn't, or there is but you need to sort on different criteria in different places, you write a Comparator.
Lokoa at 2007-7-16 22:20:23 >

Hi
I have managed to implement comparable and can now reorder the deck, but what I would actually like to be able to do, using Collections.sort() is be able to sort the cards by either suit and rank (for when I want to be able to reorder the deck) or just by Rank (for when I am assessing the Hand). Below is my card class:
public class Card implements Comparable
{
private final Rank rank;
private final Suit suit;
public enum Rank
{
Ace(14),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 Integer rankValue;
Rank(int rankValue)
{
this.rankValue = rankValue;
}
public Integer facevalue(){ return rankValue; };
}
public enum Suit
{
Hearts (0), Clubs(1), Diamonds(2), Spades(3);
private final Integer suitValue;
Suit(int suitValue)
{
this.suitValue = suitValue;
}
public Integer suitValue(){ return suitValue; };
}
//private int binaryvalue;//value of the card as a 6 bit number, first two suit, second four rank value
public Card(Rank rank, Suit suit)
{
this.rank = rank;
this.suit = suit;
}
public int compareTo(Object o)
{
Card c = (Card) o;
int suitCompare = suit.compareTo(c.suit);
return suitCompare != 0 ? suitCompare : rank.compareTo(c.rank);
}
public Rank rank() { return rank; }
public Suit suit() { return suit; }
public Integer suitValue(){return suit.suitValue;}
public Integer rankValue(){return rank.rankValue;}
//public int getBinaryValue(){ return (rank.facevalue << 4)| suit.suitvalue;}
public String toString() { return rank + " of " + suit; }
}
Thanks for your help, Ron
cakea at 2007-7-16 22:20:23 >

> I have managed to implement comparable and can now
> reorder the deck, but what I would actually like to
> be able to do, using Collections.sort() is be able to
> sort the cards by either suit and rank (for when I
> want to be able to reorder the deck) or just by Rank
> (for when I am assessing the Hand).
You could add a boolean compareBySuit in your Card class. Add a setCompareBySuit(boolean b) method in your Deck class which will set all your cards' compareBySuit-flags to true or false. Your compareTo-method will look like this:
public int compareTo(Object obj)
{
Card other = (Card)obj;
int difference;
if(compareBySuit)
difference = this.suit - other.suit;
else
difference = this.rank - other.rank;
return difference;
}
Good luck.
> > I have managed to implement comparable and can
> now
> > reorder the deck, but what I would actually like
> to
> > be able to do, using Collections.sort() is be able
> to
> > sort the cards by either suit and rank (for when I
> > want to be able to reorder the deck) or just by
> Rank
> > (for when I am assessing the Hand).
>
> You could add a boolean compareBySuit in your
> Card class. Add a setCompareBySuit(boolean b)
> method in your Deck class which will set all your
> cards' compareBySuit-flags to true or false. Your
> compareTo-method will look like this:
> public int compareTo(Object obj)
> {
>Card other = (Card)obj;
>int difference;
>
>if(compareBySuit)
> difference = this.suit - other.suit;
>else
> difference = this.rank - other.rank;
>
>return difference;
> }
> Good luck.
Or have a RankComparator and a SuitComparator. This becomes useful because if you want to sort by both Rank and Suit, you can make a combined Comparator that simply uses the other ones. Or you could sort by Rank and then Suit, because Collections.sort is stable.
The boolean method seems to be more of a hack. But, to each his own...
~Cheers
> Or have a RankComparator and a SuitComparator. Yes, definitely better.> The boolean method seems to be more of a hack.You put it mildly, thanks.; )
Hi
Everybody, thanks for the advice...
> The boolean method seems to be more of a hack.
it is all good.
My next question, what would the 'RankComparator' and SuitComparator' look like? And, how would I call them with the Collections.sort() method, as that was the problem I was originally having?
Again, many thanks with all this, Ron
cakea at 2007-7-16 22:20:23 >

So... carrying on from my last post, would I encapsulate the RankComparator and SuitComparator within the 'compareTo' function or would they in fact be two seperate methods?
public int compareTo(Object o)
{
Card c = (Card) o;
int comparisonResult;
suitComparator...
rankComparator...
comparisonResult = suit.compareTo(c.suit, suitComparator);
comparisonResult != 0 ? comparisonResult:rank.compareTo(c.rank, rankComparator);
return comparisonResult
}
I know this isn't right, but it would be building on something like this?
Many thanks, Ron
cakea at 2007-7-16 22:20:23 >

Since you want to sort in different ways in different places you should indeed implement different Comparators for your type. But you are using them the wrong way, by using them :)
What you have to do is write different classes RankComparator and SuitComparator (implement the actual logic in their compare method). You don't have to call their compare method, the Collections.sort method does this. At least this overload does:
public static <T> void sort(List<T> list, Comparator<? super T> c)
So you write your RankComparator, implementing the Comparator interface, and then you can
Collections.sort(yourList, new RankComparator());
or
Collections.sort(yourList, new SuitComparator());
as you please.
Lokoa at 2007-7-16 22:20:23 >

Hi
This is my new class, with hopefully the comparators implemented correctly, however, now when I call the method from within my Hand class, I am now getting this message:
Collections.sort(Hand, new RankComparator());
error: RankComparator cannot be resolved to a type?
What am I missing? Many thanks, Ron
import java.util.Comparator;
public class Card implements Comparable, java.io.Serializable
{
private final Rank rank;
private final Suit suit;
public enum Rank
{
Ace(14),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 Integer rankValue;
Rank(int rankValue)
{
this.rankValue = rankValue;
}
public Integer facevalue(){ return rankValue; };
}
public enum Suit
{
Hearts (0), Clubs(1), Diamonds(2), Spades(3);
private final Integer suitValue;
Suit(int suitValue)
{
this.suitValue = suitValue;
}
public Integer suitValue(){ return suitValue; };
}
public Card(Rank rank, Suit suit)
{
this.rank = rank;
this.suit = suit;
}
public int compareTo(Object o)
{
Card c = (Card) o;
int suitCompare = suit.compareTo(c.suit);
return suitCompare != 0 ? suitCompare : rank.compareTo(c.rank);
}
private class RankComparator implements Comparator
{
public final int compare ( Object fCard, Object sCard )
{
int rankDiff = ((Card)fCard).rank.rankValue - ((Card)sCard).rank.rankValue;
if ( rankDiff > 0 ) return 1;
if ( rankDiff < 0 ) return -1;
else return 0;
}
}
private class SuitComparator implements Comparator
{
public final int compare ( Object fCard, Object sCard )
{
int suitDiff = ((Card)fCard).suit.suitValue - ((Card)sCard).suit.suitValue;
if ( suitDiff > 0 ) return 1;
if ( suitDiff < 0 ) return -1;
else return 0;
}
}
public Rank rank() { return rank; }
public Suit suit() { return suit; }
public Integer suitValue(){return suit.suitValue;}
public Integer rankValue(){return rank.rankValue;}
public String toString() { return rank + " of " + suit; }
}
cakea at 2007-7-16 22:20:23 >

> What am I missing?You've made RankComparator a private inner class of Card, so Hand can't see it.
Where are you calling Collections.sort(hand, new RankComparator()) from? You have declared your Comparator implementations as private inner classes of Card, so these class names are only available from within class Card.
I would either make them public inner classes or more likely even just classes on their own outside of Card. If you do keep them as inner classes, you should declare them public static, and then you can use them as:
Collections.sort(hand, new Card.RankComparator());
But I'd recommend just doing it in separate classes.
Lokoa at 2007-7-16 22:20:23 >

HiI decided that I liked them as inner classes, but I might change that later as I work out what works best for the game!! :)Many thanks to you all, and happy new year, RonNext problem!!!!!
cakea at 2007-7-16 22:20:23 >
