def test_deficit(self): p = Pot() p.new_round(10) p.bet(1, 10) assert p.deficit(2) == 10 p.bet(2, 25) assert p.deficit(1) == 15
def test_fold(self): p = Pot() p.new_round(10) p.bet(1, 10) p.bet(2, 25) p.bet(3, 50) assert p.deficit(2) == 25 assert p.deficit(1) == 40 p.bet(2, 25) # no #1 bet p.end_round() assert p.pots == [(110, [2, 3])]
class Game: NUM_PLAYERS = 8 def __init__(self, blinds_start, blinds_cap, blinds_timer, stack_start): self.state = {} # extra game state info.. server tracked self.action = None self.game_ended = False self.blinds_start = blinds_start self.blinds_cap = blinds_cap self.blinds_timer = blinds_timer self.blinds_sequence = generate_blinds(self.blinds_start, self.blinds_cap) self.starttime = None self.stack_start = stack_start self.players = {} for x in xrange(1, self.NUM_PLAYERS + 1): self.players[x] = None self.deck = Deck() self.dealer = None self.small_blind = None self.big_blind = None self.sb_amt = 0 self.bb_amt = 0 self.started = False self.community = [] self.minimum_bet = self.blinds_start self.pot = Pot() self.hand_over = False self.payments = None self.show = [] self.show_all = False self.places = [] self.payouts = [] def winners(self): s = set() for p in self.payouts: for w, amt in p: s.add(w) return s winners = property(winners) def add_player(self, player, position): player.sit_in() if position <= 0 or position > self.NUM_PLAYERS: player.stand_up() raise GameError, "Invalid seating position" if self.players[position]: player.stand_up() raise GameError, "A player is already sitting in that position" self.players[position] = player def _get_active_players(self): return dict((pos, p) for pos, p in self.players.iteritems() if p and p.sitting) active_players = property(_get_active_players) def _get_active_players_o(self): return [v for k, v in sorted(self.active_players.items(), key=lambda x: x[0])] active_players_o = property(_get_active_players_o) def _get_in_players(self): def from_dealer_sort(packed): x, _p = packed amt = x - self.dealer if amt <= 0: amt += self.NUM_PLAYERS return amt return sorted([(pos, p) for pos, p in self.active_players.iteritems() if p.playing], key=from_dealer_sort) in_players = property(_get_in_players) def ip_around(self, all=False): seen = set() while True: for pos, p in self.in_players: if all or p.purse > 0 or pos in seen: seen.add(pos) yield pos, p def _get_hands(self): return dict((pos, p.hand) for pos, p in self.in_players) hands = property(_get_hands) def call_around(self): rab = self.ip_around(all=True) out = rab.next() while out != self.called: out = rab.next() for x in xrange(len(self.in_players)): yield out out = rab.next() def evaluate(self): '''Return the payouts, shows, etc. ''' if not self.hand_over: raise GameError, "hand is not over yet" self.show = [] # fold to this player if len(self.in_players) == 1: winner_pos, winner_obj = self.in_players[0] tot = self.pot.total self.payouts = [[(winner_pos, tot)]] else: raw_evals = eval_hands( [(pos, p.hand) for pos, p in self.in_players], self.community) self.payouts = self.pot.payout(raw_evals, self.dealer) hand_key = dict((pos, (hand, cards)) for pos, hand, cards, place in raw_evals) shot_at_pot = {} best_hands = {} for amt, players in self.pot.pots: best_hands[amt] = () for p in players: shot_at_pot.setdefault(p, []).append(amt) show = [] for pos, p in self.call_around(): if self.show_all: show.append((pos, hand_key[pos])) else: show_it = False for amt in shot_at_pot[pos]: hand_score, cards = hand_key[pos] if hand_score > best_hands[amt]: show_it = True best_hands[amt] = hand_score if show_it: show.append((pos, hand_key[pos])) else: show.append((pos, None)) p.fold() self.show = show def make_payments(self): if self.payment_made: raise GameError, "payment already made" self.payment_made = True for pot in self.payouts: for pos, amt in pot: self.players[pos].fund(amt) self.payouts = [] def find_first_dealer(self): self.show_all = True self.deck.collect_shuffle() self.deck.deal(self.active_players_o, 1) highest = high_card([(pos, p.hand[0]) for pos, p in self.active_players.iteritems()]) self.dealer = highest[0] def start(self): # Find first dealer if len(self.active_players) == 0: raise GameError, "no players at table" if len(self.active_players) == 1: raise GameError, "only one player at table" self.started = True self.init_num_players = len(self.active_players) for p in self.active_players.itervalues(): p.fund(self.stack_start) self.find_first_dealer() self.starttime = time.time() self.advance_blinds() def advance_blinds(self): self.sb_amt, self.bb_amt = self.blinds_sequence.next() def _get_pot_amount(self): return sum(self.pot.itervalues()) pot_amount = property(_get_pot_amount) def clear_hands(self): for p in self.active_players.itervalues(): p.discard() p.playing = True def start_hand(self): self.pot = Pot() self.show = [] self.dealer, self.small_blind, self.big_blind = \ round_rotation(self.dealer, self.small_blind, self.big_blind, self.active_players.keys()) self.clear_hands() self.community = [] self.deck.collect_shuffle() self.first_round_done = False self.hand_over = False self.show_all = False self.payment_made = False def start_betting_round(self): self.dead_pot = False self.roundabout = self.ip_around() self.pot.new_round() self.action = None self.bet_made = False if not self.first_round_done: self.first_round_done = True # dealer bets first in heads-up play if len(self.active_players) == 2: self.roundabout.next() pos, p = self.roundabout.next() self.bet(pos, self.sb_amt) pos, p = self.roundabout.next() self.bet(pos, self.bb_amt) self.pot.set_minimum(self.bb_amt) self.taken_turn = set() self.action = self.roundabout.next() def deal_pockets(self): self.deck.deal(self.active_players_o, 2) # network.new_hand def deal_community(self): if not self.community: for x in xrange(3): self.community.append(self.deck.get_card()) else: self.community.append(self.deck.get_card()) def bet(self, pos, amt): player = self.players[pos] if self.dead_pot: raise GameError, "Player '%s': cannot bet out of turn!" % player.nick if self.action and self.action[0] != pos: raise GameError, "Player '%s': cannot bet out of turn!" % player.nick if amt >= 0: amt = min(amt, player.purse) player.deduct(amt) all_in = player.purse == 0 and True or False if self.bet_made and amt < self.pot.deficit(pos) and not all_in: raise GameError, "Player '%s': you must bet at least $%d to call" % (player.nick, self.pot.deficit(pos)) if amt > 0: if not all_in and amt > self.pot.deficit(pos) and len([p for _pos, p in self.in_players if p.purse > 0]) < 2: raise GameError, "Player '%s': all other players are all in; either call $%d or fold" % (player.nick, self.pot.deficit(pos)) try: typ = self.pot.bet(pos, amt, all_in=all_in) except GameError: player.fund(amt) # give the player back his money! raise if not self.bet_made: typ = Pot.BET_BET self.bet_made = True else: typ = Pot.BET_CHECK else: player.fold() typ = Pot.BET_FOLD if self.action: self.taken_turn.add(pos) self.action = self.roundabout.next() if len(self.in_players) == 1 or (self.action[0] in self.taken_turn and (self.pot.deficit(self.action[0]) == 0 or not self.bet_made)): # end of the betting round self.called = self.action self.action = None self.pot.end_round() self.dead_pot = True self.check_for_winners() return typ def check_for_winners(self): '''Called after a betting round.. is it time to showdown? ''' # everyone folded or all cards are dealt if len(self.in_players) == 1 or len(self.community) == 5: self.hand_over = True # everyone all-in (possibly except one player) if len(self.in_players) > 1 and len([p for pos, p in self.in_players if p.purse > 0]) <= 1: self.show_all = True def finish_hand(self): for pos, p in self.in_players: if p.purse == 0: p.discard() p.sitting = False p.playing = False self.places.append(( self.init_num_players - len(self.places), # places pos, p.nick, )) if len(self.active_players) == 1: self.game_ended = True pos, p = self.active_players.items()[0] p.discard() self.places.append(( self.init_num_players - len(self.places), # places pos, p.nick, ))