Ejemplo n.º 1
0
    def _state_init(self):
        # Active players are players remaining at table with remaining
        # chips.
        self.active = set()
        for pos in range(self.table.num_seats):
            ps = self.table.get(pos)
            if ps is not None and ps.chips > 0:
                self.active.add(ps)

        if len(self.active) < 2:
            raise GameError('game over - too few funded players left')

        # There can be multiple pots, per table-stakes rules.
        self.pots = Pots()

        self.bets = {} # ps -> attempted bet

        # The deck
        deck = []
        for suit in 'hscd':
            for rank in map(Rank, range(2, 15)):
                deck.append(Card(rank, suit))
        random.shuffle(deck)
        self.deck = deck

        # The community cards.
        self.flop = []
        self.turn = None
        self.river = None

        # We could advance the dealer when the game has ended, but
        # since that can happen at multiple places it's more
        # convienient to do it once, here. If we want a particular
        # seat as dealer, we must remember to subtract one when we set
        # it.
        self.dealer += 1
Ejemplo n.º 2
0
class Game(object):
    def __init__(self, table, rules):
        self.table = table
        self.rules = rules

        # The dealer button is a piece of state that changes between
        # successive games. Most other state is state for the actual
        # game. This state is set up in the state machine code.
        self.dealer = -1

        # Seed for the state machine.
        self.state = 'init'

    def set_blinds(self, big, small):
        self.bb = big
        self.sb = small

    def next(self):
        while True:
            p = self.table.get(self.pos)
            self.pos += 1
            if p is not None:
                return p

    def post(self, ps, chips):
        _chips = chips

        self.pots.post(ps, chips)

        # Update bets. This doesn't care about side pots etc, it's
        # just what the player attempted to match this betting round.
        try:
            self.bets[ps] += _chips
        except:
            self.bets[ps] = _chips

        print 'bets:', ' '.join(('%s=%s' % (k.player, v) for (k, v) in self.bets.iteritems()))

    # modifies: self.pots, self.bets, self.active,
    # self.deck, self.flop, self.turn, self.river,
    # self.dealer
    def _state_init(self):
        # Active players are players remaining at table with remaining
        # chips.
        self.active = set()
        for pos in range(self.table.num_seats):
            ps = self.table.get(pos)
            if ps is not None and ps.chips > 0:
                self.active.add(ps)

        if len(self.active) < 2:
            raise GameError('game over - too few funded players left')

        # There can be multiple pots, per table-stakes rules.
        self.pots = Pots()

        self.bets = {} # ps -> attempted bet

        # The deck
        deck = []
        for suit in 'hscd':
            for rank in map(Rank, range(2, 15)):
                deck.append(Card(rank, suit))
        random.shuffle(deck)
        self.deck = deck

        # The community cards.
        self.flop = []
        self.turn = None
        self.river = None

        # We could advance the dealer when the game has ended, but
        # since that can happen at multiple places it's more
        # convienient to do it once, here. If we want a particular
        # seat as dealer, we must remember to subtract one when we set
        # it.
        self.dealer += 1

    # modifies: self.deck
    def _state_deal_hole_cards(self):
        dealer = self.table.find(self.dealer)
        print '%s is dealer' % (self.table.get(dealer).player)

        for pos in range(dealer + 1,
                         dealer + 1 + self.table.num_seats):
            ps = self.table.get(pos)
            if ps is None:
                continue
            ps.hole, self.deck = self.deck[0:2], self.deck[2:]

            print 'dealt hole cards to %s' % (ps.player)

    # modifies: self.deck, self.flop
    def _state_deal_flop(self):
        burn, flop, self.deck = self.deck[0], \
            self.deck[1:4], self.deck[4:]

        self.flop = flop
        print 'flop is %s' % (' '.join(map(str, flop)))

    # modifies: self.deck, self.turn
    def _state_deal_turn(self):
        burn, turn, self.deck = self.deck[0], \
            self.deck[1], self.deck[2:]

        self.turn = turn
        print 'turn is %s' % (turn,)

    # modifies: self.deck, self.river
    def _state_deal_river(self):
        burn, river, self.deck = self.deck[0], \
            self.deck[1], self.deck[2:]

        self.river = river
        print 'river is %s' % (river,)

    # modifies: self.pos
    # calls: self.post
    def _state_post_blinds(self):
        pos = self.table.find(self.dealer)
        print '%s is dealer' % (self.table.get(pos).player)

        # If there are only two players, the dealer will post the
        # small blinds. Otherwise, it's the next player.
        if self.table.players() > 2:
            pos += 1

        player_sb_idx = self.table.find(pos)
        player_sb = self.table.get(player_sb_idx)

        print '%s posting small blind' % (player_sb.player,)
        self.post(player_sb, self.sb)

        player_bb_idx = self.table.find(player_sb_idx + 1)
        player_bb = self.table.get(player_bb_idx)

        print '%s posting big blind' % (player_bb.player,)
        self.post(player_bb, self.bb)

        self.pos = player_bb_idx + 1

    # modifies: self.pos, self.bets
    # calls: self.post
    def _state_betting(self):
        acted = set()

        while self.active:
            # betting round continues until everyone has acted and
            # bet the same amount (or all-in)

            if len(acted) == len(self.active):
                val = self.bets.values()
                # XXX: All in
                if all((v == val[0] for v in val)):
                    break

            i = self.table.find(self.pos)
            ps = self.table.get(i)

            print '%s to act' % (ps.player,)

            # Construct a setof valid options for the player.
            options = set(['fold'])
            if not self.bets or sum(self.bets.values()) == 0:
                options.add('bet') # open
                options.add('check')
            else:
                # TODO (bjorn): BB can check here (which is the same
                # as call).
                options.add('call')
                options.add('raise')

            # A context to execute the options.
            class Action(object):
                def action_check(iself):
                    if 'check' not in options:
                        raise GameError('invalid check')
                    print 'check'
                    self.post(ps, 0)
                def action_call(iself):
                    if 'call' not in options:
                        raise GameError('invalid call')
                    print 'call'
                    # XXX: All in etc
                    self.post(ps, max(self.bets.values()) - self.bets.get(ps, 0))
                def action_raise(iself, chips):
                    if 'raise' not in options or chips > ps.chips:
                        raise GameError('invalid raise')
                    print 'raise', chips
                    self.post(ps, chips)
                def action_bet(iself, chips):
                    if 'bet' not in options or chips > ps.chips:
                        raise GameError('invalid bet')
                    print 'bet', chips
                    self.post(ps, chips)
                def action_fold(iself):
                    print 'fold'
                    self.active.remove(ps)

            if ps.chips and len([pso for pso in self.active if pso.chips]) > 1:
                # Yield execution to the main loop.
                yield options, Action()
            else:
                if ps.chips == 0:
                    print '%s has no chips left' % (ps.player,)
                else:
                    print '%s is the sole player with chips left - skipping' % (ps.player,)
                    break

            acted.add(ps)
            self.pos = i + 1

        self.bets = {}
        self.pos = self.dealer + 1

    # modifies:
    def _state_showdown(self):
        if not self.active:
            return

        print 'community cards are %s' % ' '.join(map(str, self.flop + [self.turn, self.river]))

        relative = []
        for ps in self.active:
            best = Hand.best_from_seven(*(ps.hole + self.flop + [self.turn, self.river]))
            print '%s has %s, best hand is %s' % (ps.player, ' '.join(map(str, ps.hole)), best.classify())
            relative.append((best, ps))
        relative.sort(reverse=True)

        for stakes, total in self.pots.list():
            ordering = [(best, ps) for (best, ps) in relative if ps in stakes]
            winners = [w for w in ordering if w[0] == ordering[0][0]]
            won = total / len(winners)
            for _, ps in winners:
                print '%s won %s from a pot' % (ps.player, won)
                ps.chips += won

    def loop(self):
        while True:
            yield self.game()

    def game(self):
        ret = None
        # Transitions for the game state machine.
        if self.state == 'init':
            print 'INIT'
            self._state_init()
            self.state = 'deal-hole'
        elif self.state == 'deal-hole':
            print 'DEALING CARDS'
            self._state_deal_hole_cards()
            self.state = 'blinds'
        elif self.state == 'blinds':
            print 'POSTING BLINDS'
            self._state_post_blinds()
            self.state = 'betting'
            self.sub_state = 'preflop'
        elif self.state == 'betting':
            print 'BETTING', self.sub_state
            ret = self._state_betting()
            if self.sub_state == 'preflop':
                self.state = 'deal-flop'
            elif self.sub_state == 'flop':
                self.state = 'deal-turn'
            elif self.sub_state == 'turn':
                self.state = 'deal-river'
            elif self.sub_state == 'river':
                self.state = 'showdown'
        elif self.state == 'deal-flop':
            print 'DEALING FLOP'
            self._state_deal_flop()
            self.state = 'betting'
            self.sub_state = 'flop'
        elif self.state == 'deal-turn':
            print 'DEALING TURN'
            self._state_deal_turn()
            self.state = 'betting'
            self.sub_state = 'turn'
        elif self.state == 'deal-river':
            print 'DEALING RIVER'
            self._state_deal_river()
            self.state = 'betting'
            self.sub_state = 'river'
        elif self.state == 'showdown':
            print 'SHOWDOWN'
            self._state_showdown()
            self.state = 'init'
        else:
            raise AssertionError('invalid state %s' % (self.state,))

        yield ret