Poker Simulator Design Log

From CSSEMediaWiki
(Difference between revisions)
Jump to: navigation, search
m (Poker Simulator Design)
 
(7 intermediate revisions by one user not shown)
Line 4: Line 4:
  
 
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.  
 
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.  
 +
 +
== Initial attempt ==
  
 
Here is my initial (incomplete) class diagram. Right now it seems to be a mess of compositions. I'm wondering if this idea will be any good for showing design patterns, hmmm.
 
Here is my initial (incomplete) class diagram. Right now it seems to be a mess of compositions. I'm wondering if this idea will be any good for showing design patterns, hmmm.
  
[[Image:Poker Class Diagram.png|thumb|400px|centre|'''Original attempt''']]
+
[[Image:Poker Class Diagram.png|thumb|200px|centre|'''Original attempt''']]
  
 
I have greatly improved on the original design, here is take 2:
 
I have greatly improved on the original design, here is take 2:
  
[[Image: Poker Class Diamgram 2.png|thumb|400px|centre|'''Take two''']]
+
== Take two ==
 +
 
 +
[[Image: Poker Class Diagram 2.png|thumb|200px|centre|'''Take two''']]
  
 
I'm hoping this design will allow me to utilize more design patterns. I have a [[Strategy]] pattern for the different DealStyles - for a HoldEm game the dealPlayers() methods deals each player only 2 cards, one at a time, and for a Draw game each player is dealt 5 cards one at a time. The dealPlayers() method can be overridden by any future implementations of other poker styles that may be dealt in a different way. I'm not sure if the DrawTable and HoldEmPlayer classes are [[Eliminate irrelevant classes|irrelevant classes]] yet.
 
I'm hoping this design will allow me to utilize more design patterns. I have a [[Strategy]] pattern for the different DealStyles - for a HoldEm game the dealPlayers() methods deals each player only 2 cards, one at a time, and for a Draw game each player is dealt 5 cards one at a time. The dealPlayers() method can be overridden by any future implementations of other poker styles that may be dealt in a different way. I'm not sure if the DrawTable and HoldEmPlayer classes are [[Eliminate irrelevant classes|irrelevant classes]] yet.
  
 +
 +
== Take three ==
  
 
Here is my latest version:
 
Here is my latest version:
  
[[Image: Poker Class Diamgram 3.png]]
+
[[Image: Poker Class Diagram 3.png|thumb|200px|centre|'''Take three''']]
  
Ok so I realised my DealStyle strategy was bullocks and wasn't a proper Strategy pattern. I have used inheritance to solve this problem instead. I have made CardDeck and PlayerHand store a Set of cards because each Card is unique. Using a TreeSet to store Cards can come in handy for my CardAnalyser class for ordering the cards to determine a "straight" hand. I have also fixed it so that a HoldEmDealer can only be at a HoldEmTable. I'm unsure about all the parallel hierarchies at the moment. I think next I will have a look at the bigger picture and see if there is a better way to model the interaction between different types of Dealers, Tables, and Players.
+
Ok so I realised my DealStyle strategy was bullocks and wasn't a proper Strategy pattern, because the abstract Dealer shouldn't have a method for adding community cards as this is only for HoldEm. I have used inheritance to solve this problem instead. I have made CardDeck and PlayerHand store a Set of cards because each Card is unique. Using a TreeSet to store Cards can come in handy for my CardAnalyser class for ordering the cards to determine a "straight" hand. I have also fixed it so that a HoldEmDealer can only be at a HoldEmTable. I'm unsure about all the parallel hierarchies at the moment. I think next I will have a look at the bigger picture and see if there is a better way to model the interaction between different types of Dealers, Tables, and Players.
  
== Design notes ==
+
== Take four ==
 +
 
 +
I have done a huge redesign and I think it is finally approaching a finished form. There is still a bit of functionality I need to implement however.
 +
 
 +
[[Image: Poker Class Diagram v3.png|thumb|200px|centre|'''Take four''']]
 +
 
 +
There is an [[Abstract Factory]] method for creating different types of poker games (HoldEm, Draw etc). This ensures that only HoldEm Dealers can sit with HoldEm Players. This also gets rid of the need for [[Parallel hierarchies problem|parallel inheritance hierarchies]] that reference each other.
 +
 
 +
Players are now separated from the Table they are playing at, instead this relationship is modelled with a PlayerHand class. A Dealer deals cards to PlayerHands by sending a Card to the AddCardToHand() method of Table, and it is up to the Table to add the Card to the correct PlayerHand.
 +
 
 +
Removed the different types of Tables, as I feel it makes more sense to not differentiate according to game type, as in real life any table can be used for any type of Poker. I have gotten rid of the community cards being dealt to the table, and instead made the community cards be dealt straight to the PlayerHands. This duplicates the Card data as every player has a copy of each community card, however I feel this is an acceptable trade-off for a cleaner design.
 +
 
 +
CardAnalyser is implemented as a Singleton, because only one should ever be required. However it will not hurt if more than one is created - maybe because of this it doesn't have to be Singleton?
 +
 
 +
In order to determine the winner of a hand, PlayerHands are compared by calling CardAnalyser.analyseCards(), which is a [[Template Method]]. CardAnalyser returns a RankedHand object containing the rank and the Cards that are needed to compare to a RankedHand object of the same rank. PlayerHands use their equivalent RankedHands to compareTo each other.
 +
 
 +
I have just realised while typing this that I will have to change part of my implementation so that I can determine when two PlayerHands are tied. I was planning to store them in a TreeSet and the winner would be the first entry, however if there is a tie then only the first one will be added to the Set.
 +
 
 +
I still need to model the difference between a Cash Game and a Tournament Game. There is also blind levels, in a Tournament the blinds gradually increase - but I think I will leave this out until later.
 +
 
 +
== Take five ==
 +
 
 +
Wow, this diagram suddenly got HUGE. I have added all the information I was missing and have made this diagram up to a standard to include in my submission. I have been creating a crude GUI so a few methods have been added for that. I have also decided to implement some user interaction, so that instead of just dealing out the cards and seeing who wins, the user can check/bet/fold for each player as they go.
 +
 
 +
[[Image: Poker Class Diagram v4.png]]
 +
 
 +
The GUI class is not shown, I wanted to make an MVC design but ran out of time, so have created a simple GUI which is badly coupled with the rest of the design. Because of this I have excluded it from the diagram, the GUI is only for demonstration purposes.
 +
 
 +
Got rid of the Singleton CardAnalyser as it was unnecessary, but have made PokerGame a Singleton, as only one PokerGame should exist at a time.
 +
 
 +
Allowed for Tournaments and CashGames, however only CashGames are implemented, the Tournament class only exists right now to show how the design can be extended. Same for the DrawDealer, DrawPlayer, and WildCardAnalyser.
 +
 
 +
In order for players to be able to bet/raise/call etc I have added the classes TablePot and PotContribution. A Player bet is a PotContribution, these are collected in the TablePot class. A Table can have several TablePots if a side pot needs to be created when a Player is all-in, however the side pot functionality isn't complete.
 +
 
 +
Changed a lot of the Dealer and Table classes. A lot of methods in these classes have been added so that the Law of Demeter is satisfied. The Dealer now has more responsibility with starting/ending rounds, dealing with bets, and determining the winner of a hand.
 +
 
 +
Dealer is an Observer and TablePot is Observable. The Dealer is notified of changes to the TablePot and takes the appropriate action when all PotContributions in a TablePot are matched (ie a betting round is completed).
 +
 
 +
 
 +
== Design notes (from take four) ==
 
* A poker game can either be a Tournament or a Cash game.
 
* A poker game can either be a Tournament or a Cash game.
 
* Tournaments can consist of multiple tables (but are allowed to be only one table), and the tables are gradually removed as players are knocked out and get moved to balance the tables. Players cannot be added after the tournament starts.
 
* Tournaments can consist of multiple tables (but are allowed to be only one table), and the tables are gradually removed as players are knocked out and get moved to balance the tables. Players cannot be added after the tournament starts.
Line 30: Line 75:
 
* HoldEm poker has community cards which are dealt face up onto the table (5 in total), and players have to make the best poker hand possible out of any 5 card combination of the community cards and their own cards. Players can use 0 - 2 of their own cards in making a hand, but the combination that gives the highest ranked hand has to be used.
 
* HoldEm poker has community cards which are dealt face up onto the table (5 in total), and players have to make the best poker hand possible out of any 5 card combination of the community cards and their own cards. Players can use 0 - 2 of their own cards in making a hand, but the combination that gives the highest ranked hand has to be used.
 
* I thought about how the "suit" attribute of a Card might break the [[Beware value switches]] heuristic, in that when checking for a flush hand, the suit value will be used in case analysis. However I thought it was a bit extreme to decompose Card into an inheritance hierarchy of different suits, because there will only ever be 4 suits to test. (ie no further extension is possible)
 
* I thought about how the "suit" attribute of a Card might break the [[Beware value switches]] heuristic, in that when checking for a flush hand, the suit value will be used in case analysis. However I thought it was a bit extreme to decompose Card into an inheritance hierarchy of different suits, because there will only ever be 4 suits to test. (ie no further extension is possible)
* The CardAnalyser class is not implemented in the design yet, but it is a class that is necessary so I have included it in the diagram.
+
* Flyweight could be used for cards if there are going to be many Tables each with a CardDeck (ie in a Tournament situation). However I felt it was unnecessary. Because efficiency is not an issue and there would have to be an unrealistic number of Tables for it to be a problem.
 
+
 
+
Random idea that I needed to note: Have a HandHistory class that deals with storing history/information about each hand much like online poker software.
+
Another random idea: Draw Table probably doesn't need to be there, Table should be concrete and HoldEmTable a specific type of Table that has community cards. This goes against "Avoid concrete base classes", but as discussed in class recently this is an acceptable compromise.
+
Problem: how to ensure that, for example, a DrawPlayer cannot play at a HoldEmTable?
+
 
+
Another idea: Factory method, in that a player is created when they are added to a table. The type of table specifies the type of Player to create. This would involve re-adding separate classes for HoldEmTable and DrawTable, which creates [[Parallel hierarchies problem||parallel hierarchies]] which I am not happy about, as there seem to be a bunch of them in this design... hmmm.
+
 
+
Flyweight could be used for cards if there are going to be many Tables each with a CardDeck.
+

Latest revision as of 13:05, 30 September 2008

Contents

Poker Simulator Design

For my design study I have chosen to create a poker simulator. Cards are dealt to a number of players around a table, and the winner of the hand determined. The game can continue for an arbitrary number of hands. Initially there will be no player input or AI, but I will leave the design open for extending it to a full blown poker game. The two variants of poker I am initially implementing are Texas Hold'em and 5 Card Draw, but I am leaving the design open for other variants, eg Omaha, 5 Card Stud etc.

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.

Initial attempt

Here is my initial (incomplete) class diagram. Right now it seems to be a mess of compositions. I'm wondering if this idea will be any good for showing design patterns, hmmm.

Original attempt

I have greatly improved on the original design, here is take 2:

Take two

Take two

I'm hoping this design will allow me to utilize more design patterns. I have a Strategy pattern for the different DealStyles - for a HoldEm game the dealPlayers() methods deals each player only 2 cards, one at a time, and for a Draw game each player is dealt 5 cards one at a time. The dealPlayers() method can be overridden by any future implementations of other poker styles that may be dealt in a different way. I'm not sure if the DrawTable and HoldEmPlayer classes are irrelevant classes yet.


Take three

Here is my latest version:

Take three

Ok so I realised my DealStyle strategy was bullocks and wasn't a proper Strategy pattern, because the abstract Dealer shouldn't have a method for adding community cards as this is only for HoldEm. I have used inheritance to solve this problem instead. I have made CardDeck and PlayerHand store a Set of cards because each Card is unique. Using a TreeSet to store Cards can come in handy for my CardAnalyser class for ordering the cards to determine a "straight" hand. I have also fixed it so that a HoldEmDealer can only be at a HoldEmTable. I'm unsure about all the parallel hierarchies at the moment. I think next I will have a look at the bigger picture and see if there is a better way to model the interaction between different types of Dealers, Tables, and Players.

Take four

I have done a huge redesign and I think it is finally approaching a finished form. There is still a bit of functionality I need to implement however.

Take four

There is an Abstract Factory method for creating different types of poker games (HoldEm, Draw etc). This ensures that only HoldEm Dealers can sit with HoldEm Players. This also gets rid of the need for parallel inheritance hierarchies that reference each other.

Players are now separated from the Table they are playing at, instead this relationship is modelled with a PlayerHand class. A Dealer deals cards to PlayerHands by sending a Card to the AddCardToHand() method of Table, and it is up to the Table to add the Card to the correct PlayerHand.

Removed the different types of Tables, as I feel it makes more sense to not differentiate according to game type, as in real life any table can be used for any type of Poker. I have gotten rid of the community cards being dealt to the table, and instead made the community cards be dealt straight to the PlayerHands. This duplicates the Card data as every player has a copy of each community card, however I feel this is an acceptable trade-off for a cleaner design.

CardAnalyser is implemented as a Singleton, because only one should ever be required. However it will not hurt if more than one is created - maybe because of this it doesn't have to be Singleton?

In order to determine the winner of a hand, PlayerHands are compared by calling CardAnalyser.analyseCards(), which is a Template Method. CardAnalyser returns a RankedHand object containing the rank and the Cards that are needed to compare to a RankedHand object of the same rank. PlayerHands use their equivalent RankedHands to compareTo each other.

I have just realised while typing this that I will have to change part of my implementation so that I can determine when two PlayerHands are tied. I was planning to store them in a TreeSet and the winner would be the first entry, however if there is a tie then only the first one will be added to the Set.

I still need to model the difference between a Cash Game and a Tournament Game. There is also blind levels, in a Tournament the blinds gradually increase - but I think I will leave this out until later.

Take five

Wow, this diagram suddenly got HUGE. I have added all the information I was missing and have made this diagram up to a standard to include in my submission. I have been creating a crude GUI so a few methods have been added for that. I have also decided to implement some user interaction, so that instead of just dealing out the cards and seeing who wins, the user can check/bet/fold for each player as they go.

Poker Class Diagram v4.png

The GUI class is not shown, I wanted to make an MVC design but ran out of time, so have created a simple GUI which is badly coupled with the rest of the design. Because of this I have excluded it from the diagram, the GUI is only for demonstration purposes.

Got rid of the Singleton CardAnalyser as it was unnecessary, but have made PokerGame a Singleton, as only one PokerGame should exist at a time.

Allowed for Tournaments and CashGames, however only CashGames are implemented, the Tournament class only exists right now to show how the design can be extended. Same for the DrawDealer, DrawPlayer, and WildCardAnalyser.

In order for players to be able to bet/raise/call etc I have added the classes TablePot and PotContribution. A Player bet is a PotContribution, these are collected in the TablePot class. A Table can have several TablePots if a side pot needs to be created when a Player is all-in, however the side pot functionality isn't complete.

Changed a lot of the Dealer and Table classes. A lot of methods in these classes have been added so that the Law of Demeter is satisfied. The Dealer now has more responsibility with starting/ending rounds, dealing with bets, and determining the winner of a hand.

Dealer is an Observer and TablePot is Observable. The Dealer is notified of changes to the TablePot and takes the appropriate action when all PotContributions in a TablePot are matched (ie a betting round is completed).


Design notes (from take four)

  • A poker game can either be a Tournament or a Cash game.
  • Tournaments can consist of multiple tables (but are allowed to be only one table), and the tables are gradually removed as players are knocked out and get moved to balance the tables. Players cannot be added after the tournament starts.
  • Cash games consist of only one table. Players can be added or removed at any time.
  • Players of Draw poker can choose to discard 0 - 5 cards of their hand after the first betting round, and draw new cards from the deck to make up 5 cards.
  • Players of HoldEm poker only get 2 cards of their own, which they have to keep.
  • HoldEm poker has community cards which are dealt face up onto the table (5 in total), and players have to make the best poker hand possible out of any 5 card combination of the community cards and their own cards. Players can use 0 - 2 of their own cards in making a hand, but the combination that gives the highest ranked hand has to be used.
  • I thought about how the "suit" attribute of a Card might break the Beware value switches heuristic, in that when checking for a flush hand, the suit value will be used in case analysis. However I thought it was a bit extreme to decompose Card into an inheritance hierarchy of different suits, because there will only ever be 4 suits to test. (ie no further extension is possible)
  • Flyweight could be used for cards if there are going to be many Tables each with a CardDeck (ie in a Tournament situation). However I felt it was unnecessary. Because efficiency is not an issue and there would have to be an unrealistic number of Tables for it to be a problem.
Personal tools