Re: Design decision for a game
On May 21, 12:50 am, "Peter Duniho" <NpOeStPe...@nnowslpianmk.com>
wrote:
On Tue, 20 May 2008 14:25:22 -0700, pek <kimwl...@gmail.com> wrote:
I am currently developing the House Of Cards card game (I can't seem
to find any relative info on the internet), but it doesn't really
matter. My problem is a design problem.
Say you have a class that holds a set of cards. When a card is added
it has to obey certain rules. For example, if it is full and no other
cards can be added, an exception should be thrown. If the card is not
the correct suite, again, an exception should be thrown etc.
Now, depending on the options of the game, some rules apply and some
don't. So clearly, the rules aren't inside the class itself, but they
are added by the engine using an addRule method.
I don't know about the "clearly" part...no doubt someone could come up
with a design in which the card container (the "House") actually knows all
the rules and handles them accordingly. :)
Yeap, you're right. Someone could actually hard code the Rules in the
House class. But personally I try to abstract things as much as
possible. And detaching the rules is "clearly" a step of
scalability. ;)
So I thought that I
could create an interface called Rule with a method called addCard.
The class holds a list of rules and every time a card is added it
calls them in the added order.
I think you're on the right track, but instead of the Rule interface
having a specific method to be called, I think you could abstract further
by having your Rule interface implementation implement a listener
interface, and add itself as a listener to the container class to be
notified of changes to the container state.
You could do it the other way, but if you're going to go for the
listener-driven API, why not go whole-hog? :)
But, in order for this to work, I found two problems:
A) the class that implements the Rule has to have a lot of information
(the list of cards, if it is already closed etc.). How do I work
around this? Should the method of the interface have a list of the
needed information in parameters? Should the class pass itself in all
rules and provide getters for all attributes?
It seems to me that the rule interface should _be_ a listener for the
container's listener interface, and provide a listener interface for
monitoring changes in the rule-centric state of the game.
Then, you'd create the container, create one or more rules that are added
to the container as listeners, and then attach your GUI to the rules as
listeners, and optionally also to the container (if you want to present to
the user container state as well as rule state...which seems like a
natural thing to do).
The container's listener interface could as simple as "hey, the container
state changed" and then let the listeners query the container for its
current state. Alternatively, the listener interface could report exactly
what change happened. If you want to get really fancy, you could have the
listener interface manage things like thresholds for certain state
changes. For example, the container could have an "add listener" method
that also takes a point threshold so that the listener is only notified
when the points in the container meet the threshold requirement.
The container would provide methods for managing its state, such as adding
a card, emptying the container, closing the container, etc. Rule
implementations would use these methods to manage their own reactions to
the events that occur. Of course, you need some mechanism for dealing
with conflicts between rule implementations; it's not clear from your post
how this would be dealt with, though you seem to imply that you could have
multiple rule implementations attached at any given moment.
OK.. I think I understand some things here. I won't be fancy, so
threshold is a little overkill (although I did like the idea). What I
see problematic here is this:
class House {
...
public void addCard(Card card) {
// do some things, or probably nothing because the rules would
take care of it
...
// inform listeners
for (HouseListener lstnr : listeners) {
lstnr.cardAdded(this, card);
}
}
...
}
class SixCardsRule implements HouseListener {
...
public void cardAdded(House house, Card card) {
if ( house.getCards().size() == 5 ) {
// then adding this card satisfies this Rule. So,
house.setCards(new ArrayList<Card>());
// Now notify everyone. The 50 is for the score to be added
for (RuleListener lstnrs : listeners) {
lstnrs.ruleSatisfied("Six cards in total",50);
}
}
}
...
}
Does this look OK? Because I think it does, although I think that
adding the score to the RuleListener is a little odd. What if nobody
is listening and the score isn't added anywhere? What if the rule,
although satisfied, has nothing to do with scoring?
:S
Am I complicating things more than they should be or is it my idea?
B) because different rules have different reactions, I also create a
Listener for that class. Every time something important happens, the
registered listeners get informed. But I have no idea how the class
that implements the Rule can inform the other class to fire an event
to the registered listeners.
It seems to me that the rule implementation simply manages the container
state as appropriate, and the container provides the appropriate listener
interface so that listeners can be notified of changes to the container.
The classes that implement the rule interface don't so much "inform" the
container to "fire an event" as they simply do what they need to do to the
container, and the container itself recognizes the specific changes in
state that require notification to listeners added to the container.
Just in case I'm not clear at all, let me explain a little more.
The class I am talking about is called House. This holds a set of
cards. A card has points depending on it's suite. The house can hold
up to 31 points. When a card is added and the sum of the points are
EXACTLY 31, the player wins 20 points in his total score and the house
empties so cards can be added once again. If the sum of points exceed
31, the house closes and no more cards can be added. The game has four
houses.
Those are the standard rules. Additionally:
a) if a house has a total of six cards (not so common), the player
wins 50 points in his total score and the house empties.
What happens if the sixth card brings the total to exactly 31? How do you
decide which rule implementation deals with the points? Or does the
player get 70 points total in that case? (In which case there wouldn't be
a conflict).
Great question! I have no idea! :S What do you suggest? I also find it
easier to simply add 70 points, but finding a way to prioritize the
rules sounds more challenging. Any suggestions?
b) each house is of a particular suite. If a joker of the same suite
(the jokers have suites in this game) is added, any card in the house
are erased and the player wins 50 points in his total score. But if it
doesn't the game is over.
What's the difference between "any card in the house are erased" and "the
house empties"?
Sorry for my english. It is the exact same thing. The list of cards in
a house is simply emptied in both cases.
How does the game end other than adding a joker of the
wrong suit to a house?
The game starts with a standard 52 card deck and 4 jokers. So it also
ends when there are no other cards to deal.
Is it possible for this other game-ending
condition to occur at the same time a joker of the correct suit is added
to a house?
And similar to above, what happens if a joker rule is
satisfied at the same time as another (adding up to 31 points, reaching
six cards)?
The joker never stays in a house. Either the game is over or the house
is cleared and no cards are left (adding 50 to the score).
Great questions by the way! Very good feedback! That's more than I
expected! Thank you for this too ;)
Now, the rule with the six cards fires an event to the listeners that
the rule was satisfied. One of the listeners is the GUI and it reacts
by displaying information. The same thing goes for the 31 points rule
and any rule that others need to know about.
These are events that are fired and listeners react to. But these also
are the rules for the house.
So everything comes down to design decision. Because the rules vary,
and could be expanded, they need to be outside of the House. And
because the events depend on the rules, there has to be a bridge
between them.
It seems to me that you need a way of managing multiple controllers of the
house container class, to impose some structure on the precedence of
operations and of rules. Once you do that, then the "rules for the house"
are really just operations that the rule implementations perform based on
events raised by the container. That'd then be your bridge right there.
Pete
So what do you think about the code I posted previously with the
Listeners? Am I doing it OK?
Thank you very much for your help!