This file is indexed.

/usr/share/pyshared/pybridge/bridge/bidding.py is in pybridge-common 0.3.0-7.2.

This file is owned by root:root, with mode 0o644.

The actual contents of the file can be viewed below.

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
# PyBridge -- online contract bridge made easy.
# Copyright (C) 2004-2007 PyBridge Project.
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.


from pybridge.network.error import GameError

from call import Bid, Pass, Double, Redouble
from symbols import Direction, Level, Strain


class Bidding(object):
    """This class models the bidding (auction) phase of a game of bridge.
    
    A bidding session is a list of Call objects and the dealer.
    """


    def __init__(self, dealer):
        if dealer not in Direction:
            raise TypeError, "Expected Direction, got %s" % type(dealer)
        self.calls  = []
        self.dealer = dealer


    def isComplete(self):
        """Bidding is complete if 4 or more calls have been made,
        and the last 3 calls are Pass calls.

        @return: True if bidding is complete, False if not.
        @rtype: bool
        """
        passes = len([c for c in self.calls[-3:] if isinstance(c, Pass)])
        return len(self.calls) >= 4 and passes == 3


    def isPassedOut(self):
        """Bidding is passed out if each player has passed on their first turn.
        In this case, the bidding is complete, but no contract is established.
        
        @return: True if bidding is passed out, False if not.
        @rtype: bool
        """
        passes = len([call for call in self.calls if isinstance(call, Pass)])
        return len(self.calls) == 4 and passes == 4


    def getContract(self):
        """When the bidding is complete, the contract is the last and highest
        bid, which may be doubled or redoubled.
        
        Hence, the contract represents the "final state" of the bidding.
        
        @return: a dict containing the keywords:
        @keyword bid: the last and highest bid.
        @keyword declarer: the partner who first bid the contract strain.
        @keyword doubleBy: the opponent who doubled the contract, or None.
        @keyword redoubleBy: the partner who redoubled an opponent's double
                             on the contract, or None.
        """
        if self.isComplete() and not self.isPassedOut():
            bid = self.getCurrentCall(Bid)
            double = self.getCurrentCall(Double)
            redouble = self.getCurrentCall(Redouble)
            # Determine partnership.
            caller = self.whoCalled(bid)
            partnership = (caller, Direction[(caller.index + 2) % 4])
            # Determine declarer.
            for call in self.calls:
                if isinstance(call, Bid) and call.strain == bid.strain \
                and self.whoCalled(call) in partnership:
                    declarerBid = call
                    break
            
            return {'bid'        : bid,
                    'declarer'   : self.whoCalled(declarerBid),
                    'doubleBy'   : double and self.whoCalled(double),
                    'redoubleBy' : redouble and self.whoCalled(redouble) }
        return None  # Bidding passed out or not complete, no contract.


    def getCurrentCall(self, calltype):
        """Returns most recent current call of specified type, or None.
        
        @param calltype: call type, in (Bid, Pass, Double, Redouble).
        @return: most recent call matching type, or None.
        """
        if calltype not in (Bid, Pass, Double, Redouble):
            raise GameError, "Expected call type, got %s" % type(calltype)

        for call in self.calls[::-1]:
            if isinstance(call, calltype):
                return call
            elif isinstance(call, Bid):
                break
        return None


    def makeCall(self, call, player=None):
        """Appends call from player to the calls list.
        
        @param call: the Call object representing player's call.
        @param player: the player making call, or None.
        """
        if not isinstance(call, (Bid, Pass, Double, Redouble)):
            raise GameError, "Expected call type, got %s" % type(call)
        if not self.isValidCall(call, player):
            raise GameError, "Invalid call"

        self.calls.append(call)


    def isValidCall(self, call, player=None):
        """Check that specified call is available to player, with respect to
        current state of bidding. If specified, player's turn will be checked.
        
        @param call: the Call object to be tested for validity.
        @param player: the player attempting to call, or None.
        @return: True if call is available, False if not.
        """
        if not isinstance(call, (Bid, Pass, Double, Redouble)):
            raise GameError, "Expected call type, got %s" % type(call)
        assert player in Direction or player is None
        
        # The bidding must not be complete.
        if self.isComplete():
            return False
        
        # It must be player's turn to call.
        if player and player != self.whoseTurn():
            return False
        
        # Bidding is not complete; a pass is always available.
        elif isinstance(call, Pass):
            return True
        
        currentBid = self.getCurrentCall(Bid)
        
        # A bid must be greater than the current bid.
        if isinstance(call, Bid):
            return not currentBid or call > currentBid
        
        # Doubles and redoubles only when a bid has been made.
        if currentBid:
            bidder = self.whoCalled(currentBid)
            
            # A double must be made on the current bid from opponents,
            # with has not been already doubled by partnership.
            if isinstance(call, Double):
                opposition = (Direction[(self.whoseTurn().index + 1) % 4],
                              Direction[(self.whoseTurn().index + 3) % 4])
                return bidder in opposition and not self.getCurrentCall(Double)
            
            # A redouble must be made on the current bid from partnership,
            # which has been doubled by an opponent.
            elif isinstance(call, Redouble):
                partnership = (self.whoseTurn(),
                               Direction[(self.whoseTurn().index + 2) % 4])
                return bidder in partnership and self.getCurrentCall(Double) \
                       and not self.getCurrentCall(Redouble)
        
        return False  # Otherwise unavailable.


    def whoCalled(self, call):
        """Returns the player who made the specified call.
        
        @param call: a Call.
        @return: the player who made call, or False.
        """
        if not isinstance(call, (Bid, Pass, Double, Redouble)):
            raise GameError, "Expected call type, got %s" % type(call)

        if call in self.calls:
            return Direction[(self.calls.index(call) + self.dealer.index) % 4]
        return False  # Call not made by any player.


    def whoseTurn(self):
        """Returns position of player who is next to make a call.
        
        @return: the current turn.
        @rtype: Direction
        """
        if self.isComplete():
            raise GameError, "Bidding complete"
        return Direction[(len(self.calls) + self.dealer.index) % 4]