From: <umg...@us...> - 2007-07-16 15:28:34
|
Revision: 483 http://svn.sourceforge.net/pybridge/?rev=483&view=rev Author: umgangee Date: 2007-07-16 08:28:30 -0700 (Mon, 16 Jul 2007) Log Message: ----------- Refactor Auction class (previously Bidding) as a subclass of list. Modified Paths: -------------- trunk/pybridge/pybridge/games/bridge/game.py trunk/pybridge/pybridge/games/bridge/scoring.py trunk/pybridge/pybridge/games/bridge/ui/window_bidbox.py trunk/pybridge/pybridge/games/bridge/ui/window_bridgetable.py trunk/pybridge/pybridge/ui/vocabulary.py trunk/pybridge/tests/bridge/test_game.py trunk/pybridge/tests/monkey.py Added Paths: ----------- trunk/pybridge/pybridge/games/bridge/auction.py trunk/pybridge/tests/bridge/test_auction.py Removed Paths: ------------- trunk/pybridge/pybridge/games/bridge/bidding.py trunk/pybridge/tests/bridge/test_bidding.py Copied: trunk/pybridge/pybridge/games/bridge/auction.py (from rev 480, trunk/pybridge/pybridge/games/bridge/bidding.py) =================================================================== --- trunk/pybridge/pybridge/games/bridge/auction.py (rev 0) +++ trunk/pybridge/pybridge/games/bridge/auction.py 2007-07-16 15:28:30 UTC (rev 483) @@ -0,0 +1,194 @@ +# 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 call import Bid, Pass, Double, Redouble +from symbols import Direction + + +class Contract(object): + """Represents the result of an auction.""" + + + def __init__(self, auction): + """ + @param auction: a completed, but not passed out, auction. + @type auction: Auction + """ + assert auction.isComplete() and not auction.isPassedOut() + + # The contract is the last (and highest) bid. + self.bid = auction.currentBid + + # The declarer is the first partner to bid the contract denomination. + caller = auction.whoCalled(self.bid) + partnership = (caller, Direction[(caller.index + 2) % 4]) + # Determine which partner is declarer. + for call in auction: + if isinstance(call, Bid) and call.strain == self.bid.strain: + bidder = auction.whoCalled(call) + if bidder in partnership: + self.declarer = bidder + break + + self.doubleBy, self.redoubleBy = None, None + if auction.currentDouble: + # The opponent who doubled the contract bid. + self.doubleBy = auction.whoCalled(auction.currentDouble) + if auction.currentRedouble: + # The partner who redoubled an opponent's double. + self.redoubleBy = auction.whoCalled(auction.currentRedouble) + + + + +class Auction(list): + """The auction (bidding phase) of a game of bridge.""" + + + def __init__(self, dealer): + """ + @param dealer: who distributes the cards and makes the first call. + @type dealer: Direction + """ + self.dealer = dealer + self.contract = None + + currentBid = property(lambda self: self._getCurrentCall(Bid)) + currentDouble = property(lambda self: self._getCurrentCall(Double)) + currentRedouble = property(lambda self: self._getCurrentCall(Redouble)) + + + def isComplete(self): + """Auction is complete if all players have called (ie. 4 or more calls) + 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[-3:] if isinstance(c, Pass)]) + return len(self) >= 4 and passes == 3 + + + def isPassedOut(self): + """Auction 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 if isinstance(call, Pass)]) + return len(self) == 4 and passes == 4 + + + def makeCall(self, call): + """Appends call from position to the calls list. + + Please note that call validity should be checked with isValidCall() + before calling this method! + + @param call: a candidate call. + """ + assert call not in self # Calls must be distinct. + assert self.isValidCall(call) + + self.append(call) + if self.isComplete() and not self.isPassedOut(): + self.contract = Contract(self) + + + def isValidCall(self, call, position=None): + """Check that call can be made, according to the rules of bidding. + + @param call: the candidate call. + @param position: if specified, the position from which the call is made. + @return: True if call is available, False if not. + """ + # The bidding must not be complete. + if self.isComplete(): + return False + + # Position's turn to play. + if position and position != self.whoseTurn(): + return False + + # A pass is always available. + if isinstance(call, Pass): + return True + + # A bid must be greater than the current bid. + if isinstance(call, Bid): + return not self.currentBid or call > self.currentBid + + # Doubles and redoubles only when a bid has been made. + if self.currentBid: + bidder = self.whoCalled(self.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.currentDouble + + # 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.currentDouble \ + and not self.currentRedouble + + return False # Otherwise unavailable. + + + def whoCalled(self, call): + """Returns the position from which the specified call was made. + + @param call: a call made in the auction. + @return: the position of the player who made call, or None. + """ + if call in self: + return Direction[(self.dealer.index + self.index(call)) % 4] + return None # Call not made by any player. + + + def whoseTurn(self): + """Returns the position from which the next call should be made. + + @return: the next position to make a call, or None. + """ + if self.isComplete(): + return + return Direction[(self.dealer.index + len(self)) % 4] + + + def _getCurrentCall(self, callclass): + """Returns most recent current call of specified class, or None. + + @param callclass: call class, in (Bid, Pass, Double, Redouble). + @return: most recent call matching type, or None. + """ + assert callclass in (Bid, Pass, Double, Redouble) + + for call in reversed(self): + if isinstance(call, callclass): + return call + elif isinstance(call, Bid): + break # Bids cancel all preceding calls. + return None + Deleted: trunk/pybridge/pybridge/games/bridge/bidding.py =================================================================== --- trunk/pybridge/pybridge/games/bridge/bidding.py 2007-07-13 17:56:13 UTC (rev 482) +++ trunk/pybridge/pybridge/games/bridge/bidding.py 2007-07-16 15:28:30 UTC (rev 483) @@ -1,201 +0,0 @@ -# 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] - Modified: trunk/pybridge/pybridge/games/bridge/game.py =================================================================== --- trunk/pybridge/pybridge/games/bridge/game.py 2007-07-13 17:56:13 UTC (rev 482) +++ trunk/pybridge/pybridge/games/bridge/game.py 2007-07-16 15:28:30 UTC (rev 483) @@ -23,7 +23,7 @@ from pybridge.interfaces.observer import ISubject from pybridge.network.error import GameError -from bidding import Bidding +from auction import Auction from board import Board from play import Trick, TrickPlay from scoring import scoreDuplicate @@ -34,7 +34,7 @@ class Bridge(object): - """A bridge game models the bidding and play sequence. + """A bridge game sequences the auction and trick play. The methods of this class comprise the interface of a state machine. Clients should only use the class methods to interact with the game state. @@ -59,18 +59,18 @@ def __init__(self): self.listeners = [] - # TODO: are these necessary? self.board = None - self.bidding = None - self.contract = None - self.trumpSuit = None + self.auction = None self.play = None self.boardQueue = [] # Boards for successive games. self.visibleHands = {} # A subset of deal, containing revealed hands. self.players = {} # One-to-one mapping from BridgePlayer to Direction. + contract = property(lambda self: self.auction and self.auction.contract or None) + trumpSuit = property(lambda self: self.play and self.play.trumpSuit or None) + # Implementation of ICardGame. @@ -85,9 +85,7 @@ else: # Create a board. self.board = Board() self.board.nextDeal() - self.bidding = Bidding(self.board['dealer']) # Start bidding. - self.contract = None - self.trumpSuit = None + self.auction = Auction(self.board['dealer']) # Start auction. self.play = None self.visibleHands.clear() @@ -101,8 +99,8 @@ def inProgress(self): if self.play is not None: return not self.play.isComplete() - elif self.bidding: - return not self.bidding.isPassedOut() + elif self.auction is not None: + return not self.auction.isPassedOut() else: return False @@ -120,8 +118,8 @@ visibleBoard['deal'] = self.visibleHands state['board'] = visibleBoard - if self.bidding: - state['calls'] = self.bidding.calls + if self.auction: + state['auction'] = list(self.auction) if self.play is not None: state['play'] = [dict(trick) for trick in self.play] @@ -132,15 +130,23 @@ if state.get('board'): self.start(state['board']) - for call in state.get('calls', []): + # Perform validation on game provided by server. + # Better to encounter errors earlier than later. + + for call in state.get('auction', []): turn = self.getTurn() self.makeCall(call, position=turn) for trick in state.get('play', []): - turn = self.play.whoseTurn() - trickobj = Trick(leader=turn, trumpSuit=self.trumpSuit) - trickobj.update(trick) # Populate with cards. - self.play.append(trickobj) + # TODO: clean this up. + leader = self.getTurn() + for turn in Direction[leader.index:] + Direction[:leader.index]: + if turn in trick: + card = trick[turn] + if turn == self.play.dummy: + self.playCard(card, position=self.play.declarer) + else: + self.playCard(card, position=turn) def updateState(self, event, *args, **kwargs): @@ -200,7 +206,7 @@ def makeCall(self, call, player=None, position=None): - """Make a call in the current bidding session. + """Make a call in the current auction. This method expects to receive either a player argument or a position. If both are given, the position argument is disregarded. @@ -222,19 +228,19 @@ raise TypeError, "Expected Direction, got %s" % type(position) # Validate call according to game state. - if not self.bidding or self.bidding.isComplete(): - raise GameError, "No game in progress, or bidding complete" + if self.auction is None or self.auction.isComplete(): + raise GameError, "No game in progress, or auction complete" if self.getTurn() != position: raise GameError, "Call made out of turn" - if not self.bidding.isValidCall(call, position): + if not self.auction.isValidCall(call, position): raise GameError, "Call cannot be made" - self.bidding.makeCall(call, position) + self.auction.makeCall(call) - if self.bidding.isComplete() and not self.bidding.isPassedOut(): - self.contract = self.bidding.getContract() # TODO: make a property - self.trumpSuit = self.trumpMap[self.contract['bid'].strain] - self.play = TrickPlay(self.contract['declarer'], self.trumpSuit) + if self.auction.isComplete() and not self.auction.isPassedOut(): + declarer = self.auction.contract.declarer + trumpSuit = self.trumpMap[self.contract.bid.strain] + self.play = TrickPlay(declarer, trumpSuit) self.notify('makeCall', call=call, position=position) @@ -349,10 +355,10 @@ def getTurn(self): if self.inProgress(): - if self.bidding.isComplete(): # In trick play. + if self.auction.isComplete(): # In trick play. return self.play.whoseTurn() - else: # Currently in the bidding. - return self.bidding.whoseTurn() + else: # Currently in the auction. + return self.auction.whoseTurn() else: # Not in game. raise GameError, "No game in progress" @@ -360,16 +366,15 @@ def getScore(self): """Returns the integer score value for declarer/dummy if: - - bidding stage has been passed out, with no bids made. - - play stage is complete. + - auction has been passed out, with no bids made. + - trick play is complete. """ - if self.inProgress() or self.bidding is None: + if self.inProgress() or self.auction is None: raise GameError, "Game not complete" - if self.bidding.isPassedOut(): + if self.auction.isPassedOut(): return 0 # A passed out deal does not score. - contract = self.bidding.getContract() - declarer = contract['declarer'] + declarer = self.contract.declarer dummy = Direction[(declarer.index + 2) % 4] if declarer in (Direction.North, Direction.South): @@ -379,7 +384,7 @@ declarerWon, defenceWon = self.play.wonTrickCount() - result = {'contract': contract, 'tricksMade': declarerWon, + result = {'contract': self.contract, 'tricksMade': declarerWon, 'vulnerable': vulnerable} return scoreDuplicate(result) Modified: trunk/pybridge/pybridge/games/bridge/scoring.py =================================================================== --- trunk/pybridge/pybridge/games/bridge/scoring.py 2007-07-13 17:56:13 UTC (rev 482) +++ trunk/pybridge/pybridge/games/bridge/scoring.py 2007-07-16 15:28:30 UTC (rev 483) @@ -31,13 +31,13 @@ """ score = 0 - isDoubled = result['contract']['doubleBy'] - isRedoubled = result['contract']['redoubleBy'] + isDoubled = bool(result['contract'].doubleBy) + isRedoubled = bool(result['contract'].redoubleBy) isVulnerable = result['vulnerable'] - contractLevel = result['contract']['bid'].level.index + 1 + contractLevel = result['contract'].bid.level.index + 1 tricksMade = result['tricksMade'] - tricksRequired = result['contract']['bid'].level.index + 7 - trumpSuit = result['contract']['bid'].strain + tricksRequired = result['contract'].bid.level.index + 7 + trumpSuit = result['contract'].bid.strain if tricksMade >= tricksRequired: # Contract fulfilled. Modified: trunk/pybridge/pybridge/games/bridge/ui/window_bidbox.py =================================================================== --- trunk/pybridge/pybridge/games/bridge/ui/window_bidbox.py 2007-07-13 17:56:13 UTC (rev 482) +++ trunk/pybridge/pybridge/games/bridge/ui/window_bidbox.py 2007-07-16 15:28:30 UTC (rev 483) @@ -26,7 +26,7 @@ class WindowBidbox(object): - """The bidding box is presented to a player, during bidding. + """The bidding box is presented to the playing user, during an auction. Each call (bid, pass, double or redouble) is displayed as a button. When it is the player's turn to bid, a call is made by clicking the @@ -104,9 +104,9 @@ def setTable(self, table, position): - """Monitor the state of bidding in game at specified table. + """Monitor the state of auction in game at specified table. - @param table: the BridgeGame for which to observe bidding session. + @param table: the BridgeGame for which to observe auction session. @param: """ if self.table: @@ -134,7 +134,7 @@ if self.position == self.table.game.getTurn(): self.window.set_property('sensitive', True) for call, button in self.callButtons.items(): - isvalid = self.table.game.bidding.isValidCall(call) + isvalid = self.table.game.auction.isValidCall(call) button.set_property('sensitive', isvalid) else: self.window.set_property('sensitive', False) Modified: trunk/pybridge/pybridge/games/bridge/ui/window_bridgetable.py =================================================================== --- trunk/pybridge/pybridge/games/bridge/ui/window_bridgetable.py 2007-07-13 17:56:13 UTC (rev 482) +++ trunk/pybridge/pybridge/games/bridge/ui/window_bridgetable.py 2007-07-16 15:28:30 UTC (rev 483) @@ -107,8 +107,8 @@ textContract = render_contract(game.contract) textMade = str(declarerWon) - if game.contract['declarer'] in (Direction.North, Direction.South) and score > 0 \ - or game.contract['declarer'] in (Direction.East, Direction.West) and score < 0: + if game.contract.declarer in (Direction.North, Direction.South) and score > 0 \ + or game.contract.declarer in (Direction.East, Direction.West) and score < 0: textNS, textEW = str(abs(score)), '' else: textNS, textEW = '', str(abs(score)) @@ -170,7 +170,7 @@ def set_trickcount(self, game): if game.play: declarerWon, defenceWon = game.play.wonTrickCount() - required = game.contract['bid'].level.index + 7 + required = game.contract.bid.level.index + 7 declarerNeeds = max(0, required - declarerWon) defenceNeeds = max(0, 13 + 1 - required - defenceWon) else: @@ -292,12 +292,12 @@ self.setTurnIndicator() - for call in self.table.game.bidding.calls: - position = self.table.game.bidding.whoCalled(call) + for call in self.table.game.auction: + position = self.table.game.auction.whoCalled(call) self.biddingview.add_call(call, position) - # If user is a player and bidding in progress, open bidding box. - if self.player and not self.table.game.bidding.isComplete(): + # If user is a player and auction in progress, open bidding box. + if self.player and not self.table.game.auction.isComplete(): bidbox = self.children.open(WindowBidbox, parent=self) bidbox.setCallSelectHandler(self.on_call_selected) bidbox.setTable(self.table, self.position) @@ -340,7 +340,7 @@ self.scoreview.add_score(self.table.game) declarerWon, defenceWon = self.table.game.play.wonTrickCount() - required = self.table.game.contract['bid'].level.index + 7 + required = self.table.game.contract.bid.level.index + 7 offset = declarerWon - required score = self.table.game.getScore() @@ -519,7 +519,7 @@ self.biddingview.add_call(call, position) self.setTurnIndicator() - if self.table.game.bidding.isComplete(): + if self.table.game.auction.isComplete(): self.dashboard.set_contract(self.table.game) if self.children.get(WindowBidbox): # If a player. self.children.close(self.children[WindowBidbox]) @@ -578,8 +578,8 @@ d = self.player.callRemote('getHand') d.addCallbacks(self.table.game.revealHand, self.errback, callbackKeywords={'position' : self.position}) - # If game is running and bidding is active, open bidding box. - if not self.table.game.bidding.isComplete(): + # If game is running and auction is active, open bidding box. + if not self.table.game.auction.isComplete(): bidbox = self.children.open(WindowBidbox, parent=self) bidbox.setCallSelectHandler(self.on_call_selected) bidbox.setTable(self.table, self.position) Modified: trunk/pybridge/pybridge/ui/vocabulary.py =================================================================== --- trunk/pybridge/pybridge/ui/vocabulary.py 2007-07-13 17:56:13 UTC (rev 482) +++ trunk/pybridge/pybridge/ui/vocabulary.py 2007-07-16 15:28:30 UTC (rev 483) @@ -191,11 +191,11 @@ @return: a format string representing the contract. @rtype: str """ - doubled = contract['redoubleBy'] and CALLTYPE_SYMBOLS[Call.Redouble] \ - or contract['doubleBy'] and CALLTYPE_SYMBOLS[Call.Double] or '' + doubled = contract.redoubleBy and CALLTYPE_SYMBOLS[Call.Redouble] \ + or contract.doubleBy and CALLTYPE_SYMBOLS[Call.Double] or '' - fields = {'bid': render_call(contract['bid']), 'doubled': doubled, - 'declarer': DIRECTION_NAMES[contract['declarer']]} + fields = {'bid': render_call(contract.bid), 'doubled': doubled, + 'declarer': DIRECTION_NAMES[contract.declarer]} if doubled: return _('%(bid)s %(doubled)s by %(declarer)s') % fields Copied: trunk/pybridge/tests/bridge/test_auction.py (from rev 480, trunk/pybridge/tests/bridge/test_bidding.py) =================================================================== --- trunk/pybridge/tests/bridge/test_auction.py (rev 0) +++ trunk/pybridge/tests/bridge/test_auction.py 2007-07-16 15:28:30 UTC (rev 483) @@ -0,0 +1,113 @@ +import unittest + +from pybridge.games.bridge.auction import Auction +from pybridge.games.bridge.call import Bid, Pass, Double, Redouble +from pybridge.games.bridge.symbols import Direction, Level, Strain + + +class TestAuction(unittest.TestCase): + +# bids = [Bid(l, s) for l in Level for s in Strain] + + dealer = Direction.North + calls = [Pass(), Pass(), Bid(Level.One, Strain.Club), Double(), + Redouble(), Pass(), Pass(), Bid(Level.One, Strain.NoTrump), + Pass(), Bid(Level.Three, Strain.NoTrump), Pass(), Pass(), + Pass()] + + + def setUp(self): + self.auction = Auction(dealer=self.dealer) + + + def tearDown(self): + self.auction = None + + + def stepThroughAuction(self): + for call in self.calls: + yield call + self.auction.makeCall(call) + + + def testIsComplete(self): + """Checking isComplete() only when auction completed""" + s = self.stepThroughAuction() + self.assertEqual(self.auction.isComplete(), False) + try: + while s.next(): + self.assertEqual(self.auction.isComplete(), False) + except StopIteration: + self.assertEqual(self.auction.isComplete(), True) + + + def testIsPassedOut(self): + """Checking isPassedOut() when all players pass""" + for call in [Pass(), Pass(), Pass(), Pass()]: + self.assertEqual(self.auction.isPassedOut(), False) + self.auction.makeCall(call) + self.assertEqual(self.auction.isPassedOut(), True) + + + def testCurrentCalls(self): + """Checking currentBid/currentDouble/currentRedouble values""" + + def testCurrent(bid, double, redouble): + self.assertEqual(self.auction.currentBid, bid) + self.assertEqual(self.auction.currentDouble, double) + self.assertEqual(self.auction.currentRedouble, redouble) + + testCurrent(None, None, None) + + bid = Bid(Level.One, Strain.Diamond) + self.auction.makeCall(bid) + testCurrent(bid, None, None) # Ensure that currentBid is set. + + double = Double() + self.auction.makeCall(double) + testCurrent(bid, double, None) # Ensure currentBid/Double set. + + redouble = Redouble() + self.auction.makeCall(redouble) + testCurrent(bid, double, redouble) # Ensure currentBid/Double/Redouble set. + + bid = Bid(Level.One, Strain.Spade) + self.auction.makeCall(bid) + testCurrent(bid, None, None) # Ensure currentBid set, Double/Redouble reset. + + + def testWhoseTurn(self): + """Checking whoseTurn() returns position on turn""" + s = self.stepThroughAuction() + try: + turn = self.dealer + while s.next(): + self.assertEqual(self.auction.whoseTurn(), turn) + turn = Direction[(turn.index + 1) % 4] # Turn moves clockwise. + except StopIteration: + self.assertEqual(self.auction.whoseTurn(), None) + + + def testIsValidCall(self): + """Checking isValidCall() identifies valid and invalid calls.""" + s = self.stepThroughAuction() + try: + while True: + candidate = s.next() + self.assert_(self.auction.isValidCall(candidate)) + if self.auction.currentBid: + pass + #self.assert_(self.auction.isValidCall + except StopIteration: + pass + + + +def main(): + suite = unittest.makeSuite(TestAuction) + unittest.TextTestRunner(verbosity=2).run(suite) + + +if __name__ == '__main__': + main() + Deleted: trunk/pybridge/tests/bridge/test_bidding.py =================================================================== --- trunk/pybridge/tests/bridge/test_bidding.py 2007-07-13 17:56:13 UTC (rev 482) +++ trunk/pybridge/tests/bridge/test_bidding.py 2007-07-16 15:28:30 UTC (rev 483) @@ -1,67 +0,0 @@ -import unittest -import random - -from pybridge.games.bridge.bidding import Bidding -from pybridge.games.bridge.call import Bid, Pass, Double, Redouble -from pybridge.games.bridge.symbols import Direction, Level, Strain - - -class TestBidding(unittest.TestCase): - - - # Generate some bids. - bids = [Bid(l, s) for l in Level for s in Strain] - - - def setUp(self): - dealer = random.choice(Direction) - self.bidding = Bidding(dealer) - - - def tearDown(self): - self.bidding = None - - - def testGetCurrentCall(self): - """getCurrentCall""" - for calltype in [Bid, Pass, Double, Redouble]: - self.assertEqual(self.bidding.getCurrentCall(calltype), None) - - for call, calltype in [(Pass(), Pass), (Bid(Level.One, Strain.Club), Bid), - (Double(), Double), (Redouble(), Redouble)]: - self.assertEqual(self.bidding.getCurrentCall(calltype), None) - self.bidding.makeCall(call) - self.assertEqual(self.bidding.getCurrentCall(calltype), call) - - # A bid should clear types Pass, Double, Redouble. - bid = Bid(Level.One, Strain.Diamond) - self.bidding.makeCall(bid) - self.assertEqual(self.bidding.getCurrentCall(Bid), bid) - for calltype in [Pass, Double, Redouble]: - self.assertEqual(self.bidding.getCurrentCall(calltype), None) - - - def testWhoseTurn(self): - """whoseTurn""" - # Tests whoseTurn() before and after making calls. - turn = Direction[self.bidding.dealer.index] - for call in self.bids: - self.assertEqual(self.bidding.whoseTurn(), turn) - self.bidding.makeCall(call) - turn = Direction[(turn.index + 1) % 4] - self.assertEqual(self.bidding.whoseTurn(), turn) - - - def testIsValidCall(self): - """isValidCall""" - pass - - -def main(): - suite = unittest.makeSuite(TestBidding) - unittest.TextTestRunner(verbosity=2).run(suite) - - -if __name__ == '__main__': - main() - Modified: trunk/pybridge/tests/bridge/test_game.py =================================================================== --- trunk/pybridge/tests/bridge/test_game.py 2007-07-13 17:56:13 UTC (rev 482) +++ trunk/pybridge/tests/bridge/test_game.py 2007-07-16 15:28:30 UTC (rev 483) @@ -96,7 +96,7 @@ This does not attempt to test the integrity of Bidding and Play. """ - calls = [Bid(l, s) for l in Level for s in Strain] + [Pass()]*3 + calls = [Bid(l, s) for l in Level for s in Strain] + [Pass(), Pass(), Pass()] self.game.start(board) for call in calls: Modified: trunk/pybridge/tests/monkey.py =================================================================== --- trunk/pybridge/tests/monkey.py 2007-07-13 17:56:13 UTC (rev 482) +++ trunk/pybridge/tests/monkey.py 2007-07-16 15:28:30 UTC (rev 483) @@ -221,7 +221,7 @@ def loop(): if self.table.game.inProgress(): - if not self.table.game.bidding.isComplete(): + if not self.table.game.auction.isComplete(): self.chooseCall() else: self.chooseCard() This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |