Poker Simulator Design Writeup

From CSSEMediaWiki
(Difference between revisions)
Jump to: navigation, search
Line 11: Line 11:
 
== A Brief Introduction to Poker ==
 
== A Brief Introduction to Poker ==
  
Poker is a card game where players make bets on the value of the cards in their hand, which accumulate in a central pot. The winner is the player with the highest ranked hand, or the last player to remain in the hand after all other players have folded. In order to stay in contention for the pot a player must match the bet of every other player in the pot. If a player doesn't wish to match a bet, they may fold their hand at any time, making them out of contention for the pot. Play is commenced clockwise around the table from the left of the player in the dealer position. After the hand is completed, the player to the left of the dealer assumes the dealer position for the next hand.
+
Poker is a card game where players make bets on the value of the cards in their hand, which accumulate in a central pot. The winner is the player with the highest ranked hand, or the last player to remain in the hand after all other players have folded. (Another word for folded is ''mucked''). In order to stay in contention for the pot a player must match the bet of every other player in the pot. If a player doesn't wish to match a bet, they may fold their hand at any time, making them out of contention for the pot. Play is commenced clockwise around the table from the left of the player in the dealer position. After the hand is completed, the player to the left of the dealer assumes the dealer position for the next hand.
  
 
The poker variant implemented in my design is called Texas Hold 'em, and is the most popular form of poker today. In a game of Texas Hold 'em, each player is dealt only two cards face down (the hole cards). A total of five community cards are dealt face up on the table, and each player must make the best possible five card hand using any combination of community cards and their own cards. The first betting round is performed after the players receive their two hole cards. Once this is complete, three community cards are dealt (the flop), and another betting round performed. After this a fourth community card is dealt (the turn), followed by a betting round, then the last community card is dealt (the river), followed by the last betting round.  
 
The poker variant implemented in my design is called Texas Hold 'em, and is the most popular form of poker today. In a game of Texas Hold 'em, each player is dealt only two cards face down (the hole cards). A total of five community cards are dealt face up on the table, and each player must make the best possible five card hand using any combination of community cards and their own cards. The first betting round is performed after the players receive their two hole cards. Once this is complete, three community cards are dealt (the flop), and another betting round performed. After this a fourth community card is dealt (the turn), followed by a betting round, then the last community card is dealt (the river), followed by the last betting round.  
Line 50: Line 50:
 
The PokerGame class is a [[Singleton]]. I required it to be subclassed to TournamentGame and CashGame, so have modified the Singleton pattern to allow for this. Note that TournamentGame has not been fully implemented. PokerGame contains the main() method of my program, which takes in command line arguments to determine the PokerGame subclass to create, and the style of poker (eg HoldEm or Draw). The getPokerGame() method uses [[Use lazy initialization pattern | lazy initialization]] to create the Singleton PokerGame instance, and is first called in main().
 
The PokerGame class is a [[Singleton]]. I required it to be subclassed to TournamentGame and CashGame, so have modified the Singleton pattern to allow for this. Note that TournamentGame has not been fully implemented. PokerGame contains the main() method of my program, which takes in command line arguments to determine the PokerGame subclass to create, and the style of poker (eg HoldEm or Draw). The getPokerGame() method uses [[Use lazy initialization pattern | lazy initialization]] to create the Singleton PokerGame instance, and is first called in main().
  
 +
PokerGame stores information about the blinds and ante amounts for the game, and also stores an instance of the [[Abstract Factory]] PokerStyleFactory. Subclasses override the init() method which uses the PokerStyleFactory to initialize a game. The run() method is also overridden by subclasses to start the game, and is called by the GUI creating a new Thread.
  
 +
=== PokerStyleFactory ===
  
 +
The PokerStyleFactory class is reasonably straightforward. It is used to create Players, Dealers, and Tables. The Table class is not abstract which is unusual for the [[Abstract Factory]] pattern, but I have included the Table creation as part of the entire creation process because the type of Dealer and type of Players that sit at a Table must be equivalent. Players do not sit directly at a Table, but instead each Player has one PlayerHand which is used to represent them at the Table. So when a Player is created, an equivalent PlayerHand is created for that Player and it is the PlayerHand that is added to the Table. When a Dealer is created they are added to the associated Table and vice versa.
  
=== CardAnalyser ===
+
The problem with using an Abstract Factory is that is is hard to later add more products (products are Player, Dealer, Table). If a new product was to be added, the abstract factory base class ''and'' all existing subclasses will have to be modified - this is obviously not ideal. However I feel that the benefits of using Abstract Factory outweigh this issue, as I think it is unlikely that more than the existing products will have to be added later.
  
I have provided a Wildcard class in the diagram as an example (but not implemented it in my code). Wildcard is a rule variation where specific cards are "wild" which means they can be designated as any card that the player chooses in making a hand.
+
An alternative implementation would use the [[Prototype]] pattern to create products which gives more flexibility if more products need to be added. A single "make()" method would be used to create the products. However this is not as safe as it requires down-casting in Java, which I did not want to use, especially since the added flexibility is not really needed.
  
 +
=== PlayerHand ===
  
 +
[[image: PlayerHand.png]]
 +
 +
Each PlayerHand represents a Player at a Table. PlayerHands consist of The Dealer deals cards to each PlayerHand. At the end of a hand, the Dealer collects all active PlayerHands (that haven't been mucked), and determines the winner. To determine the winner, PlayerHands are compared to each other, this is achieved by the PlayerHands implementing compareTo(). Within the compareTo() method, a PlayerHand determines its hand rank by sending its Cards to the CardAnalyser class. This returns a RankedHand object containing the rank (0 - 8), and the five cards that make the hand. Note that in Texas Hold 'em a Player has seven possible cards with which to make a five card hand, so CardAnalyser accepts more than five cards.
 +
 +
RankedHands also implement compareTo(), so the PlayerHand compareTo() method effectively returns the result of their equivalent RankedHands compareTo(). RankedHands need to be compared in different ways depending on what rank they are, hence I have used a [[State]] pattern to model this. This is because some hand ranks use all five cards (high card, straight, flush, and straight flush) - FullHand subclass, some hand ranks use a subset of the five cards (pair, 3 of a kind, 4 of a kind) - PartialHand subclass, and some hand ranks are combinations of lower hand ranks (two pair, full house) - CombinedHand subclass. For example when a CombinedHand is being compared to a CombinedHand of the same rank, the first part of the hand may be the same for both, but the second part of the hand will determine which is better. When a FullHand is being compared to another FullHand, the hand with the higher cards is better, even if the first four cards of each are equivalent.
 +
 +
Note that I have provided a WildCardAnalyser subclass of CardAnalyer (visible in the overall class diagram above) as an example of how my design can be extended, but it is not implemented it in my code. "Wildcard" is a rule variation where specific cards are "wild" which means they can be designated as any card that the player chooses in making a hand.
 +
 +
=== Dealer ===
 +
 +
[[image: Dealer.png]]
  
  
Line 171: Line 186:
  
 
[[Avoid concrete base classes]]
 
[[Avoid concrete base classes]]
 +
 +
Program to the interface, not to the implementation
  
 
[[Getters and setters]] -  
 
[[Getters and setters]] -  
 +
 +
[[Many to many association idiom]]
  
 
== Principles Violated ==
 
== Principles Violated ==

Revision as of 01:06, 1 October 2008

Contents

Intro

For my design study I have chosen to create a poker simulator of sorts. I have started this project specifically for the 427 design study. Cards are dealt to a number of players around a table, the hand played through and the winner of the hand determined. The game can continue for an arbitrary number of hands. Currently the user controls each player in turn and can see all the Player's cards, so it is more of a simulator than a full blown poker game. Ideally if I had more time it would be nice to make it multiplayer with some client server functionality, or to implement some AI to play against.

I have implemented the Texas Hold 'em variant of poker (see A Brief Introduction to Poker below), but have left the design open for the implementation of other poker variants and rules. To illustrate this there are classes for the Draw variant of poker and the Wildcard rule in the design, but without full functionality. I have also implemented the "cash game" type of poker, where the game consists of one table and players can come or go as they wish. Alternately a poker game can be "tournament" style, which consists of one or more tables and players aim to win the tournament by taking other players chips and knocking them out until there is one player left. As players are knocked out the tables are balanced and joined together until there is one table; the "final table." I have created a Tournament class to illustrate this possibility, but there is currently no functionality for it.


Haven't had time to implement: There are also different bet limits in poker, I am not sure how this might effect my design if they are to be implemented in the future. The most common is No Limit where you can bet however much you like at any time, even your entire chip stack. There are also Limit games where bets and raises are limited to a fixed amount, and Pot Limit where bets are limited to the size of the pot.


A Brief Introduction to Poker

Poker is a card game where players make bets on the value of the cards in their hand, which accumulate in a central pot. The winner is the player with the highest ranked hand, or the last player to remain in the hand after all other players have folded. (Another word for folded is mucked). In order to stay in contention for the pot a player must match the bet of every other player in the pot. If a player doesn't wish to match a bet, they may fold their hand at any time, making them out of contention for the pot. Play is commenced clockwise around the table from the left of the player in the dealer position. After the hand is completed, the player to the left of the dealer assumes the dealer position for the next hand.

The poker variant implemented in my design is called Texas Hold 'em, and is the most popular form of poker today. In a game of Texas Hold 'em, each player is dealt only two cards face down (the hole cards). A total of five community cards are dealt face up on the table, and each player must make the best possible five card hand using any combination of community cards and their own cards. The first betting round is performed after the players receive their two hole cards. Once this is complete, three community cards are dealt (the flop), and another betting round performed. After this a fourth community card is dealt (the turn), followed by a betting round, then the last community card is dealt (the river), followed by the last betting round.

Betting rounds consist of players either checking (opting to bet nothing if there is no current bet to match), calling an existing bet by putting out the equivalent amount of chips, or raising an existing bet by putting out a greater amount of chips than the existing bet. The winner of the hand wins the money in the pot, or in the case of a draw (two or more players having a hand of equivalent rank) the money is split between the players.

For more information I will refer the reader to Wikipedia. (I love Wikipedia, Wiki Wiki Pedia).

Source Code

My source code is written in Java and can be found here: media:pokerSimulatorCode.tgz

To run it from a shell, type: java PokerSimulator [-gameType] [-variant]

-gameType is either Cash Game (-c) or Tournament (-t). Currently only (-c) is implemented.

-variant specifies the poker variant. Currently only (-h) for HoldEm is implemented.


Class Diagram

Class Diagram

Here is my entire class diagram in all its glory. Note that I have excluded the GUI from the class diagram, as I had to hack it together very quickly before this project was due. Subsequently the GUI is badly designed and is tightly coupled with the rest of the design. In my class diagram $ means static, and # means protected. I have not differentiated between composition and aggregation relationships.

Design Breakdown

In this section I will present the different parts of my design and explain what they do.

PokerGame

PokerGame.png

The PokerGame class is a Singleton. I required it to be subclassed to TournamentGame and CashGame, so have modified the Singleton pattern to allow for this. Note that TournamentGame has not been fully implemented. PokerGame contains the main() method of my program, which takes in command line arguments to determine the PokerGame subclass to create, and the style of poker (eg HoldEm or Draw). The getPokerGame() method uses lazy initialization to create the Singleton PokerGame instance, and is first called in main().

PokerGame stores information about the blinds and ante amounts for the game, and also stores an instance of the Abstract Factory PokerStyleFactory. Subclasses override the init() method which uses the PokerStyleFactory to initialize a game. The run() method is also overridden by subclasses to start the game, and is called by the GUI creating a new Thread.

PokerStyleFactory

The PokerStyleFactory class is reasonably straightforward. It is used to create Players, Dealers, and Tables. The Table class is not abstract which is unusual for the Abstract Factory pattern, but I have included the Table creation as part of the entire creation process because the type of Dealer and type of Players that sit at a Table must be equivalent. Players do not sit directly at a Table, but instead each Player has one PlayerHand which is used to represent them at the Table. So when a Player is created, an equivalent PlayerHand is created for that Player and it is the PlayerHand that is added to the Table. When a Dealer is created they are added to the associated Table and vice versa.

The problem with using an Abstract Factory is that is is hard to later add more products (products are Player, Dealer, Table). If a new product was to be added, the abstract factory base class and all existing subclasses will have to be modified - this is obviously not ideal. However I feel that the benefits of using Abstract Factory outweigh this issue, as I think it is unlikely that more than the existing products will have to be added later.

An alternative implementation would use the Prototype pattern to create products which gives more flexibility if more products need to be added. A single "make()" method would be used to create the products. However this is not as safe as it requires down-casting in Java, which I did not want to use, especially since the added flexibility is not really needed.

PlayerHand

PlayerHand.png

Each PlayerHand represents a Player at a Table. PlayerHands consist of The Dealer deals cards to each PlayerHand. At the end of a hand, the Dealer collects all active PlayerHands (that haven't been mucked), and determines the winner. To determine the winner, PlayerHands are compared to each other, this is achieved by the PlayerHands implementing compareTo(). Within the compareTo() method, a PlayerHand determines its hand rank by sending its Cards to the CardAnalyser class. This returns a RankedHand object containing the rank (0 - 8), and the five cards that make the hand. Note that in Texas Hold 'em a Player has seven possible cards with which to make a five card hand, so CardAnalyser accepts more than five cards.

RankedHands also implement compareTo(), so the PlayerHand compareTo() method effectively returns the result of their equivalent RankedHands compareTo(). RankedHands need to be compared in different ways depending on what rank they are, hence I have used a State pattern to model this. This is because some hand ranks use all five cards (high card, straight, flush, and straight flush) - FullHand subclass, some hand ranks use a subset of the five cards (pair, 3 of a kind, 4 of a kind) - PartialHand subclass, and some hand ranks are combinations of lower hand ranks (two pair, full house) - CombinedHand subclass. For example when a CombinedHand is being compared to a CombinedHand of the same rank, the first part of the hand may be the same for both, but the second part of the hand will determine which is better. When a FullHand is being compared to another FullHand, the hand with the higher cards is better, even if the first four cards of each are equivalent.

Note that I have provided a WildCardAnalyser subclass of CardAnalyer (visible in the overall class diagram above) as an example of how my design can be extended, but it is not implemented it in my code. "Wildcard" is a rule variation where specific cards are "wild" which means they can be designated as any card that the player chooses in making a hand.

Dealer

Dealer.png


Critique

Some classes seem to exhibit the Large class smell, namely Table and TablePot. However it is important to note that they are large because of the number methods in them and not the number of fields, if they had too many fields I might have to apply the Extract Class refactoring method. The large number of methods in these classes are required partly because the GUI needed them and partly to satisfy the Law of demeter.

I do not like the relationship between Table, TablePot, PotContribution, Player and PlayerHand. There seems to be some duplicated data in that a Table contains a direct reference to PlayerHands, but also contains a reference to the same PlayerHands though the TablePot - PotContribution - Player - PlayerHand. This problem has been caused by the TablePot and PotContribution classes which have been tacked on last to add the betting/calling/folding functionality. If I had more time on this project my next step would be to refactor these classes, possibly adding a PlayerAction class to deal with betting, calling and folding.

Design Patterns

Observer

I have used the Observer pattern to model the relationship between the Dealer and the TablePot. The Dealer needs to know when the TablePot is modified so he can determine if the betting round is complete or not. Note that the Abstract Subject class is the Java Observable class which implements this pattern, and Dealer implements the Java Observer interface by defining an update() method.

Abstract Subject Observable
Concrete Subject TablePot
Observer Dealer
Concrete Observer HoldEmDealer

Template Method

The analyseCards() method in CardAnalyser is a Template Method. It defines the structure of the algorithm that determines the hand rank of a set of cards. The primitive operations correspond to a check for each possible rank, and subclasses of CardAnalyser can define these operations differently for different rule variations. The algorithms structure is set in the base class because the hand ranks must be checked in order from worst to best so that the best hand rank is returned. (ie a flush might also contain a pair, but it is the flush rank that counts).

Abstract Class CardAnalyser
Concrete Template Method analyseCards()
Abstract primitiveOperation1 checkHighCard()
Abstract primitiveOperation2 checkPair()
Abstract primitiveOperation3 checkTwoPair()
Abstract primitiveOperation4 checkTrips()
Abstract primitiveOperation5 checkStraight()
Abstract primitiveOperation6 checkFlush()
Abstract primitiveOperation7 checkFullHouse()
Abstract primitiveOperation8 checkFourOfKind()
Abstract primitiveOperation9 checkStraightFlush()
Concrete Class1 RegularCardAnalyser
Concrete Class2 WildCardAnalyser

State

The RankedHand class is a State pattern, with PlayerHand as the context. The behaviour of the compareTo() method varies depending on the type of RankedHand, as it is necessary to compare different types of hands in different ways. PlayerHand does not actually contain a RankedHand, but instead creates a new RankedHand and uses it within it's own compareTo() method to determine if another PlayerHand is better.

Context PlayerHand
Abstract State RankedHand
Abstract handle() compareTo()
Concrete State1 FullHand
Concrete State2 PartialHand
Concrete State3 CombinedHand

Abstract Factory

The PokerStyleFactory class provides an Abstract Factory pattern. It is used for creating the related objects for different poker styles - Dealer, Player and Table. I used it to enforce the constraint that only objects of one style of poker may be used at one time, so that, for example, a HoldEmDealer doesn't end up sitting at a Table with DrawPlayers. It is also useful in that it allows for several different styles of poker to be implemented.

Abstract Factory PokerStyleFactory
Concrete Factory1 HoldEmFactory
Concrete Factory2 DrawFactory
Abstract Product1 Dealer
Abstract Product2 Player
Concrete Product Table
Client PokerGame

Singleton

PokerGame is a Singleton class, and is used to ensure that only one instance of a PokerGame may exist. I have modified the Singleton pattern to allow PokerGame to be subclassed to a CashGame or a TournamentGame (not yet implemented). This is dicussed in more depth above in "Design Breakdown"

Iterator

I have used Iterators throughout RegularCardAnalyser in order to iterate though Sets of Cards.

Principles Followed

Avoid concrete base classes

Program to the interface, not to the implementation

Getters and setters -

Many to many association idiom

Principles Violated

Tell, don't ask - Used getters for a lot of fields.

Not always used Iterator to iterate though Collections - found it simpler to write for loops to iterate though Lists.

Beware singletons - I have one Singleton class: PokerGame. It has subclasses and this required making the constructors protected instead of private, so unfortunately in Java it is possible for more than one PokerGame to be created by another class in the same package.

Parting Comments

Personal tools