patternpythonMinor
Finds the winning hand at showdown in texas holdem for an arbitrary number of players
Viewed 0 times
handtheholdemnumberarbitraryshowdownplayerstexaswinningfinds
Problem
I've read about half of a pretty extensive and popular Python book (I'm a complete beginner) and I decided to get my hands dirty with this little exercise to crystallize all the concepts I've taken in.
The program does as explained above in the title, it creates a showdown situation with random community cards and players in Hold-em and establishes the winner or winners and their hand ranking.
I would appreciate if you can criticise the style in every aspect and spot any non-idiomatic Python there may be. At least it seems to work as intended.
I have a Java background, by the way.
```
'''
This program finds the winning hand at showdown in texas holdem for
an arbitrary number of players.
For time and development complexity constraints we make the following simplification:
eg. any pair ties against another pair, regardless of the rank of the pair and kickers.
'''
import random
import enum
import itertools
import collections
CARD_RANK = ['2', '3', '4', '5', '6', '7', '8', '9', 'T', 'J', 'Q', 'K', 'A']
CARD_SUIT = ['heart', 'diamond', 'spade', 'club']
CARD_COUNT = 52
COMMUNITY_CARDS_COUNT = 5
MIN_NUMBER_OF_PLAYERS = 2
MAX_NUMBER_OF_PLAYERS = 10
class HandRanking (enum.Enum):
HIGH_CARD = 1
ONE_PAIR = 2
TWO_PAIR = 3
THREE_OF_A_KIND = 4
STRAIGHT = 5
FLUSH = 6
FULL_HOUSE = 7
FOUR_OF_A_KIND = 8
STRAIGHT_FLUSH = 9
class Showdown (object):
"""A class that represents a showdown situation."""
def __init__(self, community_cards, players):
self.community_cards = community_cards
self.players = players
'''
Creates the showdown situation with an arbitrary number of players between
two and ten and the five cards on the board
'''
def create_random_showdown (shuffled_deck):
number_of_players = random.randint (MIN_NUMBER_OF_PLAYERS, MAX_NUMBER_OF_PLAYERS)
players = {player_position : {s
The program does as explained above in the title, it creates a showdown situation with random community cards and players in Hold-em and establishes the winner or winners and their hand ranking.
I would appreciate if you can criticise the style in every aspect and spot any non-idiomatic Python there may be. At least it seems to work as intended.
I have a Java background, by the way.
```
'''
This program finds the winning hand at showdown in texas holdem for
an arbitrary number of players.
For time and development complexity constraints we make the following simplification:
- Any two players or more players with the same hand ranking tie for the pot, regardless of the strength of their hand.
eg. any pair ties against another pair, regardless of the rank of the pair and kickers.
'''
import random
import enum
import itertools
import collections
CARD_RANK = ['2', '3', '4', '5', '6', '7', '8', '9', 'T', 'J', 'Q', 'K', 'A']
CARD_SUIT = ['heart', 'diamond', 'spade', 'club']
CARD_COUNT = 52
COMMUNITY_CARDS_COUNT = 5
MIN_NUMBER_OF_PLAYERS = 2
MAX_NUMBER_OF_PLAYERS = 10
class HandRanking (enum.Enum):
HIGH_CARD = 1
ONE_PAIR = 2
TWO_PAIR = 3
THREE_OF_A_KIND = 4
STRAIGHT = 5
FLUSH = 6
FULL_HOUSE = 7
FOUR_OF_A_KIND = 8
STRAIGHT_FLUSH = 9
class Showdown (object):
"""A class that represents a showdown situation."""
def __init__(self, community_cards, players):
self.community_cards = community_cards
self.players = players
'''
Creates the showdown situation with an arbitrary number of players between
two and ten and the five cards on the board
'''
def create_random_showdown (shuffled_deck):
number_of_players = random.randint (MIN_NUMBER_OF_PLAYERS, MAX_NUMBER_OF_PLAYERS)
players = {player_position : {s
Solution
Comparing hand rankings
At a high level, this code is incorrect.
Repetitions
Several of the hand ranking formulas are based on determining how many of what card you have. Your logic for
Card representation
Having
This way, you can refer to
Finding the best ranking
I said before the
to this one:
Although...
Finding the best ranking, II
Instead of taking all 5-card combinations of 7 cards and determining the max of that (which would involve 21 iterations), just determine the best ranking looking at all 7 cards together. That's the really the same amount of work as finding the rank for 5 cards at a time, but instead - solve the whole problem.
Spacing
There's a lot of places where you put spaces in front of parentheses or brackets (e.g.
DO put a space after colons: so
There's a lot more to say, but for now this code is fundamentally wrong.
At a high level, this code is incorrect.
determine_hand_ranking yields you some enum, and then you compare those values - but hand ranking alone is insufficient to do a comparison. For instance, AAKKQ beats 7755A, but you would only get that correct if the aces up hand went first. You need more info than simply the rankings.Repetitions
Several of the hand ranking formulas are based on determining how many of what card you have. Your logic for
_is_four_of_a_kind is incorrect - that would be the logic for five of a kind. But rather than having lots of independent functions for this, determine_hand_ranking should just figure that all out internally. Using Counter for that is good - but just do it once, in one spot.Card representation
Having
(rank, suit) is not very helpful. Especially when you reference those things by index. Instead, add names to those tuples with namedtuple:Card = collections.namedtuple('Card', 'rank suit')This way, you can refer to
card.rank or card.suit directly. The card suit should be an enum.Enum as well, instead of just an arbitrary string - it'll be easier to compare against. Also it'll make your life easier if you make the ranks just the values 2 thru 14. For example:def _is_straight(cards):
ranks = sorted(c.rank for c in ranks)
# two cases: the wheel and not the wheel
return ranks in (list(range(ranks[0], ranks[0]+5)), [2, 3, 4, 5, 14])Finding the best ranking
I said before the
determine_hand_ranking should yield something more complex than just an enum. But it should yield something comparable. This will let you reduce this loop:hand_ranking = HandRanking.HIGH_CARD
all_cards = hole_cards.union (showdown.community_cards)
cards_combinations_for_a_hand = itertools.combinations (all_cards, 5)
for cards in cards_combinations_for_a_hand:
tmp_hand_ranking = determine_hand_ranking (cards)
if tmp_hand_ranking.value > hand_ranking.value:
hand_ranking = tmp_hand_rankingto this one:
max_hand_ranking = max(
itertools.combinations(all_cards, 5),
key=determine_hand_ranking
)Although...
Finding the best ranking, II
Instead of taking all 5-card combinations of 7 cards and determining the max of that (which would involve 21 iterations), just determine the best ranking looking at all 7 cards together. That's the really the same amount of work as finding the rank for 5 cards at a time, but instead - solve the whole problem.
Spacing
There's a lot of places where you put spaces in front of parentheses or brackets (e.g.
random.shuffle (deck) or dict () or player_and_hand_rankings [position], etc.). Don't put spaces there.DO put a space after colons: so
main() should be on its own line. There's a lot more to say, but for now this code is fundamentally wrong.
Code Snippets
Card = collections.namedtuple('Card', 'rank suit')def _is_straight(cards):
ranks = sorted(c.rank for c in ranks)
# two cases: the wheel and not the wheel
return ranks in (list(range(ranks[0], ranks[0]+5)), [2, 3, 4, 5, 14])hand_ranking = HandRanking.HIGH_CARD
all_cards = hole_cards.union (showdown.community_cards)
cards_combinations_for_a_hand = itertools.combinations (all_cards, 5)
for cards in cards_combinations_for_a_hand:
tmp_hand_ranking = determine_hand_ranking (cards)
if tmp_hand_ranking.value > hand_ranking.value:
hand_ranking = tmp_hand_rankingmax_hand_ranking = max(
itertools.combinations(all_cards, 5),
key=determine_hand_ranking
)Context
StackExchange Code Review Q#114115, answer score: 4
Revisions (0)
No revisions yet.