My actual Project, I think.

From CSSEMediaWiki
Jump to: navigation, search

Ok, time to get this off the ground for real.

Since someone did my first choice of project, poker, last year I am going to attempt to combine at least one other non-poker game with poker. I will probably use a fair bit of last years design: Poker Simulator Design Log by Elliot Fisher. The first game I shall try to integrate will be 'Kings and Bastards'. You may know this be another name as there are many variations. It is quite different to poker, as it is a card shedding game rather than a bidding and pattern matching game. Should this prove overly simply (can't see that being the case, but you never know) I will add another game to the mix, possibly Gin Rummy. I also intend to feature wild-cards, including jokers, just as soon as I can adequately fix the joker problem.



Contents

Game Rules

Kings & Bastards

Objective:

Dispose of all of one's cards as soon as possible and gain the title of King.

This game is for 4-6 players, using a single deck. For higher numbers of players, extra decks are added as appropriate. I am not likely to implement that feature though.

Deck:

The deck ranks like this: 3,4,5,6,7,8,9,10,Jack,Queen,King and Ace is the highest card. The 2 card is a trump card. It plays as a single card and trumps (wins) any trick/play that has not already been trumped. It trumps single cards and sets alike. The joker functions as a single wildcard and can represent any card except the 2. It can not represent a whole set of cards, but can play as a single card in a set. Suits have no effect in this version.

Sets:

Cards can be played in singles, pairs, triples and quads. Any single/set can only be beaten by a another single/set of higher rank and the same quantity of cards. For example: a single 7 beats a single 6, but cannot be played on a pair of 6's. Similarly, triple Kings cannot be played on a pair of 4s' but beats a triple of Jacks.

Basics of Play:

For the first round a dealer is chosen and that player deals out all the cards in a clockwise direction, one at a time, starting on their left. Some players may end up with 1 less card than others, this isn't a huge problem in this game.

The player with the 3 of clubs begins with this card as a single or part of a set, placing this card/set on the table to begin the trick. Play proceeds in a clockwise direction, each player attempts to beat the last played card/set with one of a higher value (never equal) or they can choose to trump with a 2 and immediately win that trick. If a player either cannot play a card/set or chooses to skip that turn (eg: to hang onto a set or trump) then they 'pass' and do not play again until their turn comes back around.

If no other players can beat a trick/play then the player who played the last cards in the trick (ie the cards that cannot be beaten) wins that trick and starts the next one. If that player has disposed of all their cards then the next player to their left starts the next trick.

Victory:

The first player to dispose of all their cards wins the title of King and the round. They now wait out of play until all the other players dispose of their cards. The last player left gains the title of Bastard and becomes the dealer for the next round. At the start of each subsequent round the Bastard must exchange their two highest ranked cards with any two cards of the King's choice from the King's hand, before play begins.

The game has no specific end point, the rounds can be repeated as many times as desired. I will implement a scoring mechanism to record a player's number of rounds as King, but I will not add any particular limit to the number of rounds, that is something the actual players should agree on.



Poker: 5 Card Draw, No limit

Objective:

Win as much money from the other players as possible.

This game is for 4-8 players, using a single deck. For higher numbers of players, extra decks are added as appropriate. I am still not likely to implement that feature.

Deck:

The deck ranks like this: Ace,2,3,4,5,6,7,8,9,10,Jack,Queen,King and Ace again. Ace plays high and low. The joker functions as a wildcard and can represent any other card as required.

Basics of Play:

A player maybe chosen as the dealer or an individual not playing may deal.

Before any cards are dealt the Ante is set and collected. This is just a small amount of money/chips collected from each player that is used to start the Pot.

Cards are dealt clockwise from the dealer's left. Players get 5 cards each.

The first betting stage commences with the player to the dealers left. This is no limit poker so there is no cap on the value of the bets or Pot. The first player can 'bet' (into the pot) or 'fold' (decide their hand is too rubbish to play) (there is no 'checking' in this version). The next player can 'call' (place the same bet as the first player), 'fold' or 'raise' (place the same bet as the first plus another bet on top of that). This stage continues in that fashion until the bet against has been called and all players (that haven't folded) have had the same number of turns.

Provided not less than 2 players remain, the game moves to the Draw stage, where players may try to improve their hand by exchanging up to 3 cards with the remaining deck.

Next another betting stage begins, behaving in exactly the same manner as the first. Players bet, raise or fold until all but one have folded, or the bet against has been called, or all players have gone 'all in' (bet all their remaining money/chips).

Once the betting stage concludes all players reveal their hands, with the best hand winning everything in the Pot. This sequence can repeat until all players except one have run out of money/chips (bankrupt). In my version I intend to just declare the player with the most money/chips to be the overall winner when a game is stopped.

Victory:

Is achieved by forcing all other players to fold or by having the best (highest ranked) hand of cards at the table. See: [1] for a list of poker hands and their arrangement.


BEHOLD, THE HORROR:

This is a partial diagram, showing not a hell of a lot really. A certain individual shot this one down like an Airliner approaching a tall building. The main thing I had going on in this was that each player would advise the next player when it was their turn, a kind of chain of responsibility, but not really. The person advised using a more central class or classes to encapsulate the turn based behaviour away from the players. Grudgingly, I must agree.

Partial attempt


Yet another (large) incomplete diagram, much further along than the last one though:

Partial attempt 2

I think I have most of the elements now, though there is much in the way of connections, fields and methods missing. Card will probably use the card design piece that I had been toying with on the previous page. WildCard is something I intend to implement, but I can't figure out how to bolt it in yet.

Problem: Decorator used for K&bPlayer. Decorator seems appropriate, since during a Kings and Bastards game a player can be awarded the King or Bastard title, as well as being neither. However, using the decorator pattern straight from the wiki results in a lazy class, BasicKbPlayer. It adds nothing that the abstract does not already have. The King/bastard behaviour could be implemented with booleans, but then some other class has to manage that responsibility, which seems incorrect. A player, if King should have the responsibility of exchanging cards with the Bastard, in some manner. Other patterns like strategy don't fit outright. Since strategy works for methods that change their behaviour, the KbPlayer would end up with a no-op override when they are not the King or Bastard. I will leave this alone for now unless another solution presents itself.

Problem: God class in the form of Game and it's subclasses. This class currently ties most of the show together, and it is only going to get larger. I am not sure just how I can shed some of it's responsibilities without causing a sharp increase in coupling between other classes.

Problem: Dealer subclasses. The only significant difference between a dealer in Poker and in K&B is that one deals only 5 cards per player and the other distributes the entire deck. Hence the override of dealCards(). I am not sure this is sufficiently different to warrant the subclasses. This could be handled during initialisation, but doesn't seem OO like.

Problem: There seems to be some potential to pull out the "Stages" from the Game subclasses, making them in to some sort of strategy perhaps. This would allow customisation of the game sequences and rules.

Problem: The "Play" class. It is intended to be a wrapper for a Set of cards as played during K&B, a sort of 'playable unit'. However, I can't see any specific behaviour it could encapsulate so I may abandon it.

Problem: Lack of factories. There are classes that could use factories for construction, such as player, card (maybe), Dealer.

Problem: Ranking the poker hands. This has been causing me some grief, I am damned if I know how to make this work nicely.

Problem: I am not identifying the King and Bastard in a positive fashion. This failure could support using a boolean field to provide id of the condition, which in turn may eliminate/alter the need for a wrapper for the King or Bastard behaviour.


The long and the (very) short of it

Damn. I have run out of time to come anywhere near complete with this design. To that end I shall abandon any attempts at working code and leave the design diagram as it is below. It is still very much incomplete and lacks a great many things.

Partial attempt 3


Class tour

Game and Subclasses

This class is abstract and is supposed to contain methods and fields that are common to both Poker and Kings and Bastards. It's responsibilities (when instantiated through one of it's subclasses) are:

  • Instantiate and maintain the specific dealer object for the game type (Poker or K&B).
  • Instantiate and hold a list of all players in the game.
  • Maintain a reference to the current player who's turn it is.
  • Track the round number.
  • Determine the round and overall winners.

It also has methods to start off the game, end it, continue (when possible) after a player has completed some sort of action. The nextRound method is essentially a reset for most of the games mechanics like the deck, which rather than gather all cards back to it at the end of game is simply dumped and a new deck created. See my comment about shuffle().

Holds the bulk of the logic for each game type and controls the sequence of game stages.

Dealer and Subclasses

Naturally it makes no sense in a computer game like this that the dealer is a human player, hence the dealer is an automated process. Owns the deck.

There was going to be more here, but I am out of time. Most of this design is fairly self explanatory anyway, I hope.

Broken Stuff and Improvements

Shuffle() I realise that if I am disposing of the deck after each game then I really don't need the independent shuffle method, just randomize the deck on creation. This would however affect extensibility, as only games that required a shuffled deck could immediately use it. This is only a minor issue though and if I was continuing further I would remove the shuffle command for simplicity.

K&B Decorator. This refers to the previously noted decorator pattern applied to the K&BPlayer class to add in the behaviour of either the King or Bastard classes at runtime. There are several things wrong with the design as it stands. There is the lazy class smell in the basicKbPlayer. This can be fixed however, as I have broken the principle of having a lightweight abstract class in a decorator. Still, the object is vulnerable to being decorated multiple times and by the same subclass more than once, which clearly breaks the model that the player may be a King or a Bastard or neither at anytime. Two potential fixes have been brought to light: Decorate the basicKbPlayer, rather than the abstract super class. While that would work and provide a limit of one level of decoration, it does leave the abstract superclass just hanging around with little reason to exist. The alternative is: do away with the basicKbPlayer and just decorate K&BPlayer directly. That should work, (I haven't actually tried it) but then it is almost a composite pattern anyway, which is not completely suitable since it doesn't contain instances of itself.

K&B Player strategy option

An alternative pattern to a decorator could be a Strategy, as in the above example. However, the problem here is that the basicKbPlayer strategy has a no-op override on the exchangeCards() method. This is because only the King and Bastard exchange cards. One suggestion to combat this problem was to use a Null Object pattern to supply just that, a null object when the operation is called.

While reviewing that option I realised that I have provided no way for the K&BGame class to directly identify the King or Bastard players anyway. That could be fixed by simply holding a reference to each within the K&BGame class, which leads me to think that the K&B player wouldn't need to know anything about being King or Bastard. The card exchange functionality could be embedded into the K&BGame class. But this causes a separation of concerns issue, as well as adding to the GOD class (more on that later). Surely a K&bPlayer should know if they are king or bastard? That is how it is in reality after all. A compromise could be reached with boolean fields for king or bastard within K&bPlayer, which would encapsulate that knowledge away from the K&BGame class, however the behaviour of exchanging cards would still be outside of the K&bPlayer.

This separation of concerns problem brings up another issue: how to instigate the exchange of cards. I had in mind that at the appropriate stage the K&BGame class would (applying Tell Don't Ask) instruct the King and Bastard to exchange cards. This could actually work with the strategy pattern, as the instruction could be a kind of 'broadcast' to all players and only those with non null methods would act on it. A sort of flat version of the 'chain of responsibility' pattern or reverse observer. More accurately it is an implementation of event based functionality, it might be acceptable here on a small scale, but it does feel inefficient. That approach would hide all the responsibility for exchanging cards inside the K&bPlayer class, where I believe it should be. Boolean fields within K&bPlayer, accessed only by a boolean check method would be an adequate compromise for determining king or bastard. I say adequate because it is not an elegant solution, if it was, the nature of king or bastard would somehow be an emergent property of the design.

Yet another problem is that the King and Bastard need to know about each other to pass cards between them. Ideally, this should also be hidden from outside classes. It would present only a minor amount of coupling, but since each player doesn't natively know about the others, an outside class like K&BGame would have to setup the references.

I have also considered the possibility of a State pattern for the king and bastard behaviour. It is not much different to Strategy, you still end up with some sort 'basic' state plus it adds many more objects that may not be necessary. Also the State object is best shared, adding some more complexity. It does have some potential though.

AwardKing() This is related closely to the above problems. I have not determined just how the K&BGame class sets the king condition on a K&bPlayer. As soon as the first player dumps all their cards they should be crowned king. While the K&BGame can easily find out when that happens, I am not sure how it then updates a player to be a King or Bastard. This would have quite an effect on how the King and Bastard conditions are implemented, and vice versa.

WildCard This is potentially another conflicted class/behaviour. It wasn't high on the list so I haven't given it much thought. I get the impression it should involve some sort of wrapping/adapting of each card that is a special card for each game type. This is only the Joker currently, but it has slightly different behaviour between the games. Somehow this adaptation is carried over into determining poker hands and play values. Eg: in K&B, a Joker can play as a 7 in a pair of sevens, but in the next play another Joker is a equivalent to an Ace. WildCard should serve as an extensible platform for special cards in many games.

GOD class Referring to Game and it's subclasses. These classes, which are just one class at runtime, are looking mighty fat already. Some duplication could be pulled out and perhaps some interim card servicing class could be created to move some of the responsibility away. Still, I don't see much of an option to break up this very core like class without adding unnecessary complication.

Stages As noted earlier the Game Subclasses have some duplication in the form of dealStage() and other -Stage() methods. These are the sequence methods that control the flow of major game events, like dealing, betting etc. These could be dragged out and up to form a strategy of the abstract Game class. This would allow for easy customisation of the game sequences. It is not a significant complication either.

Ace and Joker Oh the humanity! I have struggled and ultimately given up on creating a slick solution for these two cards. As I have outlined on my user page, the current configuration of 'cards' is the best I can do without committing too many sins. Clearly the Joker and Ace are cards and must therefore exist, yet they have no obvious behaviour that warrants having them as subclasses. As it is they just generate lazy class smells, with their behaviour only emerging at runtime. The only fields that they would have as unique to them is some way to identify them, which would may have to be implemented as some sort of enumerated object or worse, a string! Ahhhh!! Perhaps some kind of Identity Object could be developed, come to think of it, doesn't Java already do something like that?

HandRank Another class that I struggled to find a home for. Obviously there needs to be some sort of system to pattern match the poker hands and assign some sort of value to determine the winner. In last years Poker simulator by Elliot Fisher there was a CardAnalyser class which contained many methods to test for each type of poker hand. While it does the pattern matching as required, it has an odour about it that I can't put my finger on. There is something not right about it, and I didn't want to make mine smell the same. I have worked along the theory that somehow the PokerHand class could be asked to work out it's own rank, since that seems like a reasonable place to park that responsibility. An alternative location might be the PokerPlayer class, but this may generate further coupling that I would like to avoid. This class would also be linked with WildCard, somehow.

Principles and Maxims

General principles and maxims/idioms I have attempted follow:

  • Do the simplest thing possible. For the most part I try to balance this with separation of concerns and extensibility.
  • Tell don't ask. I have tried to think along these lines, eliminating get and set, and instructing classes to do things.
  • Avoid Get and Set. See above.
  • No concrete base classes. Keep those super-classes abstract soldier!
  • Separation of concerns. My biggest concern!
  • Design by Contract. Suspect I may have actually broken this a couple of times.
  • Eliminate duplication. Go Kent.
  • Law of Demeter. This goes in hand with tell don't ask, which I have done my best to maintain.
  • Coupling and cohesion. Tried to keep the coupling down.

Hark! I doth spy a conclusion on the horizon!

The key conflicting forces within this design are simplicity, extensibility and separation of concerns. If I were to continue on I expect that I would be making many compromises between all three. This design could be made to be wildly extensible, but it would likely be a swarm of classes, abstraction and encapsulation to the nth degree. Simplicity would be reduced and the actual semantics of the objects could be 'washed out'. Likewise, simplicity could be enhanced, perhaps resulting in a tighter design, but with limited scope for extension or re-use. Ensuring responsibility for actions were assigned to reasonable, logical objects played a significant factor in the development of this design.

Another issue has been reality vs design. With this study I have found things that in reality make clear and obvious sense to the user are often blurred and difficult to fathom in a design context. The Joker card is the prime example of a simple, logical object in reality that has flummoxed all my attempts to integrate it into sensible, reasonable OO software design. It just breaks things by its very nature. I find that to be very frustrating and yet sublime at the same time. Perhaps this suggests that there is another aspect of software engineering that needs exploring, some perspective that allows such objects to comfortably exist within design.

Personal tools