Designing a Card Game

P

pek

I'm about to create a simple card game and I'm not sure about what
classes should I create for the cards. I know a Card has a Rank (King,
Queen, Jack, 10....) and a Suit (Spades, Clubs, Hearts and Diamonds)
but the problem comes when I want to introduce Jokers and Tramp cards.
A Joker doesn't have a Suit (or a Rank, depending on how you look at
it).

Currently I have a Card class that takes two enumerations, a Suit
enumeration and a Rank enumeration. For the Joker I simply agreed to
always be a card with a Rank of Joker and a Suit of Spades. But that
just doesn't seem right. And I can't think of a way to create a
hierarchy of card classes.

Any suggestions?

As always, thank you in advanced.
Panagiotis
 
K

Knute Johnson

pek said:
I'm about to create a simple card game and I'm not sure about what
classes should I create for the cards. I know a Card has a Rank (King,
Queen, Jack, 10....) and a Suit (Spades, Clubs, Hearts and Diamonds)
but the problem comes when I want to introduce Jokers and Tramp cards.
A Joker doesn't have a Suit (or a Rank, depending on how you look at
it).

Currently I have a Card class that takes two enumerations, a Suit
enumeration and a Rank enumeration. For the Joker I simply agreed to
always be a card with a Rank of Joker and a Suit of Spades. But that
just doesn't seem right. And I can't think of a way to create a
hierarchy of card classes.

Any suggestions?

As always, thank you in advanced.
Panagiotis

How about a rank of Joker and suit of null?
 
P

pek

Well, you could just add another suit to your enumeration: "no suit", or
"joker suit" or whatever you want to call it.

I never heard of a "Tramp Card" so I don't know what would be most
appropriate for that. Even a "trump card" is usually some specific card
within the standard 52, and so is defined by the rules, not the card
itself.

Pete

Yes, I agree. trump card usually depends on the rules of a game. In my
game, a trump card is a card with a Rank of Trump (a special rank) and
a specific Suit (a standard suit - either spades or clubs etc.). So
creating a Trump card wasn't difficult.

As for Jokers. I changed it to always be Joker (Suit) Joker (Rank).
But I'm not sure if this is right. A stupid question crossed my mind:
What if I wanted to create a standard deck of 52 cards with 2 Red
Jokers and 2 Black Jokers (as is the case of a usual deck of cards)?
Does this mean that I have to have a Suit of RED_JOKER and
BLACK_JOKER? Isn't this a little overkill?

Thanks for your answers ;)
 
K

Knute Johnson

pek said:
Interestingly, you cannot pass null when a method requires an
enumeration! ;)

Sure you can!

public class test7 {
enum X { KING, QUEEN, JACK };

static void method(X x) {
System.out.println(x);
}

public static void main(String[] args) {
method((X)null);
}
}

C:\Documents and Settings\Knute Johnson>java test7
null
 
P

pek

[...]
As for Jokers. I changed it to always be Joker (Suit) Joker (Rank).
But I'm not sure if this is right. A stupid question crossed my mind:
What if I wanted to create a standard deck of 52 cards with 2 Red
Jokers and 2 Black Jokers (as is the case of a usual deck of cards)?
Does this mean that I have to have a Suit of RED_JOKER and
BLACK_JOKER? Isn't this a little overkill?

You can call the suit whatever you want. If you anticipate the need for a
"red" and "black" suit, separate from the existing four standard suits,
_and_ used for some kind of card other than a joker, you might just want
to call them "red" and "black", instead of including the word "joker".
The other suits -- hearts, diamonds, spades, clubs -- already have an
implied color for the name, so "red" and "black" by themselves seem to be
reasonably clear as "general purpose" as opposed to overlapping the four
standard suits.

If it really bothers you though, I certainly don't see anything wrong with
a somewhat redundant name, like "RED_JOKER" or "RED_GENERAL" (to
differentiate from the specific suits, for example).

An alternative would be to use a bitfield for the enumeration. Keeping in
mind that my limited attempts to use Java enumerations have resulted only
in clumsy code when trying to do anything but the most basic
declarations. But it seems to me that you could have an enumeration where
one bit signified the color, while one or two other bits signified the
specific suit (depending on how much redundancy you wanted). Then you
could just reuse the same "red" and "black" values already in use for the
regular suits for other cards that have a color for the suit but not a
specific suit per se.

Personally, I'd go with the simpler version. We're only talking a handful
of constants one way or the other and context is always going to give you
all the information you need. But if you're really worried about
"overkill", sure...there are other ways to do it.

Like so many things, "overkill" is in the eye of the beholder. :)

Pete

Hahahhaa.. I totally agree with your last statement. ;)
I finally decided to keep it a simple as possible. I didn't add the
color of a joker since a red or a black joker doesn't have any
difference other than graphically.

Thanks again. ;)
 
E

Ed Kirwan

pek said:
I'm about to create a simple card game and I'm not sure about what
classes should I create for the cards. I know a Card has a Rank (King,
Queen, Jack, 10....) and a Suit (Spades, Clubs, Hearts and Diamonds)
but the problem comes when I want to introduce Jokers and Tramp cards.
A Joker doesn't have a Suit (or a Rank, depending on how you look at
it).

Currently I have a Card class that takes two enumerations, a Suit
enumeration and a Rank enumeration. For the Joker I simply agreed to
always be a card with a Rank of Joker and a Suit of Spades. But that
just doesn't seem right. And I can't think of a way to create a
hierarchy of card classes.

Any suggestions?

Perhaps it doesn't seem right to have a Joker defined with a suit (or a
null-suit of some kind, as Peter and Knute suggest), but the alternative is
to introduce a class-based distinction between cards with suits and cards
without suits and that would surely raise the complexity of the program for
little gain.

The simplicity of the class you describe sounds good, especially as it may
be re-used across games:
http://www.edmundkirwan.com/servlet/fractal/cs1/code/package132.html

That the Joker will involve rules different from other cards will not
trigger behaviour of the Joker itself, but of some evaluation of a hand of
cards:
http://www.edmundkirwan.com/servlet/fractal/cs1/code/package101.html

Though, admittedly, cards should be able to at least compare themselves with
other cards, yet even this comparison may be game-dependent. That will be
an interesting problem for you.
 
T

Tom Anderson

Perhaps it doesn't seem right to have a Joker defined with a suit (or a
null-suit of some kind, as Peter and Knute suggest), but the alternative is
to introduce a class-based distinction between cards with suits and cards
without suits and that would surely raise the complexity of the program for
little gain.

I think the null suit is the way to go. Either an actual 'physical' null,
or a Suit object that means 'no suit'. The latter might be more sensible,
as you avoid having to deal with physical nulls - this is actually a
design pattern called 'Null Object' (or 'Active Nothing' if you're my
dad). Another one for Pek to tick off!

I'd put some behaviour in Rank that can be used to ensure that Cards are
in valid combinations. Say:

// WARNING: i haven't tried to compile or run any of this code!

public enum Suit {SPADES, HEARTS, CLUBS, DIAMONDS, NONE}

public enum Rank {

KING (true), QUEEN (true), JACK (true), TEN (true), NINE (true),
EIGHT (true), SEVEN (true), SIX (true), FIVE (true), FOUR (true),
THREE (true), TWO (true), ACE (true),
JOKER (false) ;

private boolean hasSuit ;

private Rank(boolean hasSuit) {
this.hasSuit = hasSuit ;
}

public boolean canBeOfSuit(Suit suit) {
return hasSuit ^ (suit == Suit.NONE) ;
}

}

Alternatively, a constant-specific method might work:

public enum Rank {

KING, QUEEN, JACK, TEN, NINE, EIGHT, SEVEN, SIX, FIVE, FOUR,
THREE, TWO, ACE,
JOKER {public boolean canBeOfSuit(Suit suit) {return suit == Suit.NONE}} ;

public boolean canBeOfSuit(Suit suit) {
return suit != Suit.NONE ;
}
}

Either way, Card looks like this:

public class Card {
private Suit suit ;
private Rank rank ;
public Card(Suit suit, Rank rank) {
if (!rank.canBeOfSuit(suit)) throw new IllegalArgumentException("etc") ;
this.rank = rank ;
this.suit = suit ;
}
}
Though, admittedly, cards should be able to at least compare themselves
with other cards, yet even this comparison may be game-dependent. That
will be an interesting problem for you.

The thing i can't get my head round at all is how you deal with aces in
games where they can be both high and low (or either high or low, or
something). You can have methods for comparison and getting a point value,
but they'll need to know about the context around them to figure out which
value the card should take. Do you pass in that context? Messy! Do you let
the method somehow magically read it? Naughty! Do you have a toggle on the
card instance to tell it whether to be high or low? Ugly! Do you have two
rank values, ACE_HIGH and ACE_LOW? Kludgey!

tom
 
R

RedGrittyBrick

Tom said:
public enum Rank {

KING (true), QUEEN (true), JACK (true), TEN (true), NINE (true),
EIGHT (true), SEVEN (true), SIX (true), FIVE (true), FOUR (true),
THREE (true), TWO (true), ACE (true),
ick!

JOKER (false) ;

private boolean hasSuit ;

private Rank(boolean hasSuit) {
this.hasSuit = hasSuit ;
}

public boolean canBeOfSuit(Suit suit) {
return hasSuit ^ (suit == Suit.NONE) ;
}

}

public enum Rank {

KING, QUEEN, JACK, TEN, NINE, EIGHT, SEVEN,
SIX, FIVE, FOUR, THREE, TWO, ACE, JOKER (false) ;

private boolean hasSuit ;

private Rank() {
hasSuit = true;
}

private Rank(boolean hasSuit) {
this.hasSuit = hasSuit ;
}
 
T

Tom Anderson

public enum Rank {

KING, QUEEN, JACK, TEN, NINE, EIGHT, SEVEN,
SIX, FIVE, FOUR, THREE, TWO, ACE, JOKER (false) ;

private boolean hasSuit ;

private Rank() {
hasSuit = true;
}

private Rank(boolean hasSuit) {
this.hasSuit = hasSuit ;
}

Aha! I didn't realise you could do that. This enum stuff is all new to me!

tom
 
T

Tom Anderson

Tom Anderson wrote:


Sure, consider Omaha Hi/Low. The Ace can be used as both the high card
and the low card, in the same hand, even winning both times. How could
the card hold the logic for that comparison, let alone the variations in
games like Canasta or Baccarat,

Omaha Hi/Lo is a great example of why the approach of having
highness/lowness as part of the ace's type or state can't work. The basic
idea, of an ace being high or low, is the same as in many other games,
though.

I do wonder if there isn't a way to deal with aces within Rank. How about
- and this is really horrible, but i mention it for completeness - we say
that a Card will always try to claim it's greater than any card it's
compared to. So:

KING.compareTo(TEN) == 1
TEN.compareTo(KING) == -1
KING.compareTo(ACE) == 1
ACE.compareTo(KING) == 1 (!)

I think the common case in a program is asking whether some card beats
another, and this will work for that. Programs can also use this to ask if
a sequence of cards is in greatest-to-least order, dealing with aces
correctly. If you want to sort, though, you're stuffed!

Oh well, i suppose you could implement a Comparator<Rank>, perhaps
different for each game, to do this, so sorting works normally; you could
even have Comparators which could be fed context information from the rest
of the game before being applied.

I read that in Pinochle, not only is the ace high, but the 10 ranks right
after it, before the king!
let alone Crazy Eights?

Does Crazy Eights have an ordering on the cards?

tom
 
J

John B. Matthews

Tom Anderson said:
On Fri, 30 May 2008, RedGrittyBrick wrote: [fine example]
Aha! I didn't realise you could do that. This enum stuff is all new to me!

Same here; I liked this stuff from Sun:

<http://java.sun.com/j2se/1.5.0/docs/guide/language/enums.html>
<http://java.sun.com/docs/books/jls/third_edition/html/classes.html#8.9>

Here's one I came up with to manage keyCodes in a game:

<http://robotchase.sourceforge.net/org/gcs/robot/Key.html>
<http://robotchase.svn.sourceforge.net/viewvc/robotchase/trunk/src/org/gc
s/robot/Key.java?view=markup>

John
 
D

David Zimmerman

I read that in Pinochle, not only is the ace high, but the 10 ranks
right after it, before the king!

or Euchre where the Jack's can move from where you expect them to be
above the ace and even change suit. The jack of trump is the highest
card in that suit and the Jack of the same color is the second highest.
So if Hearts are trump then the rank of the trump suit is
Jack of hearts, Jack of Diamonds, Ace, King, Queen, Ten, Nine of Hearts
 
P

pek

Well, IMO, in games where ACES (or any card) that can be in more than
one places should be checked using rules. The links that Kriwan
provided had some evaluator classes which I think is how you can deal
with placing the cards correctly.

Here is another interesting question. Using comparable, you can find
whether one card is before or after another, but can you find how many
"steps" is it? I thought enum had an easy way of finding the index of
an element but it doesn't. So, what do you think about this solution:

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 index;
private Rank(int index) { this.index = index; }
public int getIndex() { return this.index; }
}

And yet another. Let's say that we have a Card class that other than
having a Rank and a Suit it has a Face (whether it is faced up or
down). Face.UP means that you can see the Rank and Suit of a Card
while Face.DOWN you cannot (think of cards in Klondike Solitaire).
Now, if a card is faced down and an object calls getRank() or
getSuit(), would throwing an Exception("The card is faced down, you
cannot see the rank or suit") be a good implementation or is this not
needed?

Sorry for extending this discussion further than it's original
topic. ;)
 
P

pek

[...]
Here is another interesting question. Using comparable, you can find
whether one card is before or after another, but can you find how many
"steps" is it? [...]
And yet another. Let's say that we have a Card class that other than
having a Rank and a Suit it has a Face (whether it is faced up or
down). Face.UP means that you can see the Rank and Suit of a Card
while Face.DOWN you cannot (think of cards in Klondike Solitaire).

I can't answer the enum question...haven't used Java enums enough yet.

But as far as adding the "face" as a property of the card, I'm not sure
I'd do that. Up to this point, your cards have had the very nice
characteristic of being immutable. They just "are", and can be members of
various collections, but any given card won't change. This keeps the
class nice and simple.

Adding a "face" property introduces mutability to the class, which may
open the door to other tempatations to weigh down the class with too much
functionality.
Now, if a card is faced down and an object calls getRank() or
getSuit(), would throwing an Exception("The card is faced down, you
cannot see the rank or suit") be a good implementation or is this not
needed?

I'd say this is a good example of what I'm talking about. Once you think
about having a "face" property, all of the sudden you're thinking of
making the card responsible for other stuff. The above suggestion seems
to me to go overboard. I mean, now the "face" property can turn the card
instance from being some useful description of the card to an object that
refuses to tell you anything at all about itself! Under that condition,
how would a rules implementation be able to properly manage the state of
the game, inasmuch as face-down cards still need to be considered?

No, I think for situations where a card's visibility is important, that's
probably better-managed by some other structural part of the game
implementation. For example, cards that are face-up would be found in
some specific collection, while cards that are face-down would be found in
some other collection. The rules implementation can tell from where the
cards are located whether they are visible to the player or not and can
manage the game that way.

Pete

Hmmm.. Seems fair.. I agree about the exception part. For "an object
that refuses to tell you anything at all about itself!" is surely a
bad thing. I didn't thought about it that way. And making to
collections of cards probably simplifies the problem. So I agree.

As for how many "steps" between enums, I completely forgot that enums
have the ordinal() method which return the index (exactly what I
wanted).

Thanks for your help and suggestions.
 
P

pek

Peter said:
[...]
Here is another interesting question. Using comparable, you can find
whether one card is before or after another, but can you find how many
"steps" is it? [...]
And yet another. Let's say that we have a Card class that other than
having a Rank and a Suit it has a Face (whether it is faced up or
down). Face.UP means that you can see the Rank and Suit of a Card
while Face.DOWN you cannot (think of cards in Klondike Solitaire).
I can't answer the enum question...haven't used Java enums enough yet.
But as far as adding the "face" as a property of the card, I'm not sure
I'd do that. Up to this point, your cards have had the very nice
characteristic of being immutable. They just "are", and can be members
of various collections, but any given card won't change. This keeps the
class nice and simple.
Adding a "face" property introduces mutability to the class, which may
open the door to other tempatations to weigh down the class with too
much functionality.

Pete's point applies to your suggested use of ordinal(), pek.
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);
}

Since the rank of a card is a function of the game, and not the card (and
isn't ACE usually the *highest* ranking card?), a name like "ACE" doesn't
actually communicate Rank, at least not in the sense a Game would mean it.

Relative ranking is an attribute of the game, not the card, true? An Ace
doesn't know if it's the highest, lowest, both at once or something in between
except for what game is under way.

So why model the relative ranking as an attribute of the card, then?

It is not.

Hmmm.. While I totally agree with that, I believe it is really
convinient to use ordinal() to do some basic calculations.. Of couse,
if you need a more complicated way (such as finding which card to
place to the top or bottom) then I would likely use an object to do
that.

What would you use for calculating this other than ordinal()? Create a
static method that would do the job maybe?
 
S

Stanimir Stamenkov

Sat, 31 May 2008 10:30:41 -0700, /Peter Duniho/:
Sure. But on what does the Comparator base its decision?

Unless I'm missing something here, the Comparator would base its
decision on the specific game logic, wouldn't it? Example:

class SpecificGame implements Game {

static Map<Card, Integer> cardRanks;
static {
// init cardRanks
}

private Comparator<Card> rankComparator = new Comparator<Card>() {
public int compare(Card c1, Card c2) {
// Game state could also be accounted here.
return cardRanks.get(c1) - cardRanks.get(c2);
}
};

public Comparator<Card> getComparator() {
return rankComparator;
}

}
 
P

pek

pek said:
Hmmm.. While I totally agree with that, I believe it is really
convinient to use ordinal() to do some basic calculations.. [...]
What would you use for calculating this other than ordinal()? Create a
static method that would do the job maybe?
I tend to prefer instance methods.
public interface Game
{
public java.util.Comparator<Card> getComparator();
// other methods
}
The Comparator is free to account for game state in its logic.

Sure. But on what does the Comparator base its decision?

We actually discussed this earlier in the thread. I believe we already
came to the agreement that the card itself shouldn't be implementing game
rule evaluations. This is exactly what you're arguing for, so we agree
with you.

But it's a huge convenience for the card to have _some_ numerical value
that is representative of the usual comparison for game rules. Special
cases can be, well...special-cased. :)

Otherwise, you wind up having to implement each possible comparison
explicitly. Who wants to do that?

I don't think that "pek" is proposing that the game rules be embedded in
the Card class. That's contrary to where the discussion has taken us so
far.

Pete

Of course I'm not proposing to embed rules in the card class. I had a
very sucessful discussion in another thread (http://groups.google.com/
group/comp.lang.java.programmer/browse_thread/thread/
6d8224fb08979586/) that I implemented it. It's an easy way of adding
rules (I believe we did talk about this somewhere in this thread too)
and currently I'm testing it even more while developing a simple
Klondike Solitaire game. I started this discussion mostly to see how
other people would design cards games with jokers, ordering and
generally any other design decissions that a card game will need.

I started a wiki for java game developing (http://treazy.com/wiki/
index.php?title=The_Definitive_Java_Game_Programming_Open-Source_Book)
and I'm focusing mostly on design. Your help was excelent! I'm trying
to create an article. Any help or suggestions is welcome.
 
P

pek

OK, I know this is the wrong place to post my question, but this is
where I first wrote about it. In this discussion (Designing a Card
Game) I was wondering how to create a Joker Card because a Joker
doesn't have a Suit and the Card class does. Anyway, one of the
answers was:

How about a rank of Joker and suit of null?
by Knute Johnson

At that time, I tested his suggestion and replied:
Interestingly, you cannot pass null when a method requires an
enumeration! ;)

In which he replied:
Sure you can!
[snip]
method((X)null);
[snip]

I swear that when I first tested new Card(Rank.JOKER, null) eclipse
displayed a compile-time error. I can't remember what was the error,
but now I can't reproduce it! Now new Card(Rank.JOKER, null) doesn't
yell anything! Assuming Knute also had seen this error (since he
provided a solution instead of calling me names), what's happening?

Just in case this may help, when I first tested that code, I was under
Windows XP while now I'm using Ubuntu. Although I seriously doubt it.

So what's the big deal? Is it, or is it not a compile-time error when
passing null in a method that expects an Enum?
 
R

Roedy Green

How about a rank of Joker and suit of null?

I would use enum Card

with rank and suit as properties (i.e getRank())
that return yet another enum, with JOKER being one of the possible
values.

Treat JOKER like HEARTS or like TWO
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,764
Messages
2,569,567
Members
45,041
Latest member
RomeoFarnh

Latest Threads

Top