From: <umg...@us...> - 2007-07-25 21:04:31
|
Revision: 495 http://pybridge.svn.sourceforge.net/pybridge/?rev=495&view=rev Author: umgangee Date: 2007-07-25 14:04:25 -0700 (Wed, 25 Jul 2007) Log Message: ----------- Split scoring down into component parts, to provide support for both duplicate and rubber scoring schemes. Fix a couple of bugs in scoring code as well! Modified Paths: -------------- trunk/pybridge/pybridge/games/bridge/game.py Added Paths: ----------- trunk/pybridge/pybridge/games/bridge/result.py Removed Paths: ------------- trunk/pybridge/pybridge/games/bridge/scoring.py Modified: trunk/pybridge/pybridge/games/bridge/game.py =================================================================== --- trunk/pybridge/pybridge/games/bridge/game.py 2007-07-24 19:30:16 UTC (rev 494) +++ trunk/pybridge/pybridge/games/bridge/game.py 2007-07-25 21:04:25 UTC (rev 495) @@ -13,7 +13,7 @@ # # 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. +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. from twisted.spread import pb @@ -26,7 +26,7 @@ from auction import Auction from board import Board from play import Trick, TrickPlay -from scoring import scoreDuplicate +from result import DuplicateResult, RubberResult from call import Bid, Pass, Double, Redouble from card import Card @@ -47,13 +47,13 @@ implements(ICardGame, ISubject) - # Valid positions. + # Valid positions (for Table). positions = Direction - # Mapping from Strain symbols (in bidding) to Suit symbols (in play). - trumpMap = {Strain.Club: Suit.Club, Strain.Diamond: Suit.Diamond, - Strain.Heart: Suit.Heart, Strain.Spade: Suit.Spade, - Strain.NoTrump: None} + # Mapping from Strain symbols (in auction) to Suit symbols (in play). + __trumpMap = {Strain.Club: Suit.Club, Strain.Diamond: Suit.Diamond, + Strain.Heart: Suit.Heart, Strain.Spade: Suit.Spade, + Strain.NoTrump: None} def __init__(self): @@ -239,7 +239,7 @@ if self.auction.isComplete() and not self.auction.isPassedOut(): declarer = self.auction.contract.declarer - trumpSuit = self.trumpMap[self.contract.bid.strain] + trumpSuit = self.__trumpMap[self.contract.bid.strain] self.play = TrickPlay(declarer, trumpSuit) self.notify('makeCall', call=call, position=position) @@ -374,23 +374,13 @@ if self.auction.isPassedOut(): return 0 # A passed out deal does not score. - declarer = self.contract.declarer - dummy = Direction[(declarer.index + 2) % 4] + tricksMade, _ = self.play.wonTrickCount() + result = DuplicateResult(self.contract, self.board['vuln'], tricksMade) + return result.score - if declarer in (Direction.North, Direction.South): - vulnerable = (self.board['vuln'] in (Vulnerable.NorthSouth, Vulnerable.All)) - else: # East or West - vulnerable = (self.board['vuln'] in (Vulnerable.EastWest, Vulnerable.All)) - declarerWon, defenceWon = self.play.wonTrickCount() - result = {'contract': self.contract, 'tricksMade': declarerWon, - 'vulnerable': vulnerable} - return scoreDuplicate(result) - - - class BridgePlayer(pb.Referenceable): """Actor representing a player's view of a BridgeGame object.""" Copied: trunk/pybridge/pybridge/games/bridge/result.py (from rev 492, trunk/pybridge/pybridge/games/bridge/scoring.py) =================================================================== --- trunk/pybridge/pybridge/games/bridge/result.py (rev 0) +++ trunk/pybridge/pybridge/games/bridge/result.py 2007-07-25 21:04:25 UTC (rev 495) @@ -0,0 +1,216 @@ +# 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 symbols import Direction, Strain, Vulnerable + + +class GameResult(object): + """Represents the result of a completed round of bridge.""" + + _getScore = NotImplemented # Expected to be implemented by subclasses. + + __vulnMapping = {Vulnerable.None: (), + Vulnerable.NorthSouth: (Direction.North, Direction.South), + Vulnerable.EastWest: (Direction.East, Direction.West), + Vulnerable.All: tuple(Direction)} + + + def __init__(self, contract, vuln, tricksMade=None): + """ + @type contract: Contract + @type vuln: Vulnerable + @type tricksMade: int or None + """ + self.contract = contract + self.tricksMade = tricksMade + self.isVulnerable = self.contract.declarer in self.__vulnMapping[vuln] + + self.score = self._getScore() + + + def _getScoreComponents(self): + """Compute the component values which contribute to the score. + Note that particular scoring schemes may ignore some of the components. + + Scoring values: http://en.wikipedia.org/wiki/Bridge_scoring + + @return: a dict of component values. + @rtype: dict + """ + components = {} + + isDoubled = bool(self.contract.doubleBy) + isRedoubled = bool(self.contract.redoubleBy) + isVulnerable = self.isVulnerable + contractLevel = self.contract.bid.level.index + 1 + tricksMade = self.tricksMade + tricksRequired = contractLevel + 6 + trumpSuit = self.contract.bid.strain + + if tricksMade >= tricksRequired: # Contract successful. + + #### Contract tricks (bid and made) #### + if trumpSuit in (Strain.Club, Strain.Diamond): + # Clubs and Diamonds score 20 for each odd trick. + components['odd'] = contractLevel * 20 + else: # Hearts, Spades and NT score 30 for each odd trick. + components['odd'] = contractLevel * 30 + if trumpSuit == Strain.NoTrump: + components['odd'] += 10 # For NT, add a 10 point bonus. + if isRedoubled: + components['odd'] *= 4 # Double the doubled score. + elif isDoubled: + components['odd'] *= 2 # Double score. + + + #### Overtricks #### + overTricks = tricksMade - tricksRequired + + if isRedoubled: + # 400 for each overtrick if vulnerable, 200 if not. + if isVulnerable: + components['over'] = overTricks * 400 + else: + components['over'] = overTricks * 200 + + elif isDoubled: + # 200 for each overtrick if vulnerable, 100 if not. + if isVulnerable: + components['over'] = overTricks * 200 + else: + components['over'] = overTricks * 100 + + else: # Undoubled contract. + if trumpSuit in (Strain.Club, Strain.Diamond): + # Clubs and Diamonds score 20 for each overtrick. + components['over'] = overTricks * 20 + else: + # Hearts, Spades and NT score 30 for each overtrick. + components['over'] = overTricks * 30 + + + #### Premium bonuses #### + + if tricksRequired == 13: + # 1500 for grand slam if vulnerable, 1000 if not. + if isVulnerable: + components['slambonus'] = 1500 + else: + components['slambonus'] = 1000 + + elif tricksRequired == 12: + # 750 for small slam if vulnerable, 500 if not. + if isVulnerable: + components['slambonus'] = 750 + else: + components['slambonus'] = 500 + + elif components['odd'] >= 100: # Game contract (non-slam). + # 500 for game if vulnerable, 300 if not. + if isVulnerable: + components['gamebonus'] = 500 + else: + components['gamebonus'] = 300 + + else: # Non-game contract. + components['partscore'] = 50 + + + #### Insult bonus #### + if isRedoubled: + components['insultbonus'] = 100 + elif isDoubled: + components['insultbonus'] = 50 + + + else: # Contract not successful. + + underTricks = tricksRequired - tricksMade + + if isRedoubled: + if isVulnerable: + # -400 for first, then -600 each. + components['under'] = -400 + (underTricks - 1) * -600 + else: + # -200 for first, -400 for second and third, then -600 each. + components['under'] = -200 + (underTricks - 1) * -400 + if underTricks > 3: + components['under'] += (underTricks - 3) * -200 + + elif isDoubled: + if isVulnerable: + # -200 for first, then -300 each. + components['under'] = -200 + (underTricks - 1) * -300 + else: + # -100 for first, -200 for second and third, then -300 each. + components['under'] = -100 + (underTricks - 1) * -200 + if underTricks > 3: + components['under'] += (underTricks - 3) * -100 + else: + if isVulnerable: + # -100 each. + components['under'] = underTricks * -100 + else: + # -50 each. + components['under'] = underTricks * -50 + + return components + + + + +class DuplicateResult(GameResult): + """Represents the result of a completed round of duplicate bridge.""" + + + def _getScore(self): + """Duplicate bridge scoring scheme. + + @return: score value: positive for declarer, negative for defenders. + """ + score = 0 + if self.contract and self.tricksMade: + for key, value in self._getScoreComponents().items(): + if key in ('odd', 'over', 'under', 'slambonus', 'gamebonus', + 'partscore', 'insultbonus'): + score += value + return score + + + + +class RubberResult(GameResult): + """Represents the result of a completed round of rubber bridge.""" + + + def _getScore(self): + """Rubber bridge scoring scheme. + + @return: 2-tuple of numeric scores (above the line, below the line): + positive for declarer, negative for defenders. + """ + above, below = 0, 0 + if self.contract and self.tricksMade: + for key, value in self._getScoreComponents().items(): + # Note: gamebonus/partscore are not assigned in rubber bridge. + if key in ('over', 'under', 'slambonus', 'insultbonus'): + above += value + elif key == 'odd': + below += value + return above, below + Deleted: trunk/pybridge/pybridge/games/bridge/scoring.py =================================================================== --- trunk/pybridge/pybridge/games/bridge/scoring.py 2007-07-24 19:30:16 UTC (rev 494) +++ trunk/pybridge/pybridge/games/bridge/scoring.py 2007-07-25 21:04:25 UTC (rev 495) @@ -1,145 +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 symbols import Strain - - -# There are undoubtedly many minor variations of the score values. -# In the future, score values may be stored in separate XML format files. - - -def scoreDuplicate(result): - """Scoring algorithm for duplicate bridge. - - This code includes the scoring values from: - http://www.ebu.co.uk/lawsandethics/the_laws/chapter8.asp - """ - score = 0 - - isDoubled = bool(result['contract'].doubleBy) - isRedoubled = bool(result['contract'].redoubleBy) - isVulnerable = result['vulnerable'] - contractLevel = result['contract'].bid.level.index + 1 - tricksMade = result['tricksMade'] - tricksRequired = result['contract'].bid.level.index + 7 - trumpSuit = result['contract'].bid.strain - - if tricksMade >= tricksRequired: - # Contract fulfilled. - - # Calculate scores for tricks bid and made. - if trumpSuit in (Strain.Club, Strain.Diamond): - # Clubs and Diamonds score 20 for each odd trick. - score += contractLevel * 20 - else: - # Hearts, Spades and NT score 30 for each odd trick. - score += contractLevel * 30 - if trumpSuit is Strain.NoTrump: - score += 10 # For NT, add a 10 point bonus. - - # Calculate scores for doubles. - if isDoubled: - score *= 2 # Multiply score by 2 for each isDoubled odd trick. - elif isRedoubled: - score *= 4 # Multiply score by 4 for each isRedoubled odd trick. - - # Calculate premium scores. - if score >= 100: - if isVulnerable: - score += 500 # Game, vulnerable. - else: - # Game, not vulnerable. - score += 300 # Game, not vulnerable. - if tricksRequired == 13: - if isVulnerable: - score += 1500 # Grand slam, vulnerable. - else: - score += 1000 # Grand slam, not vulnerable. - elif tricksRequired == 12: - if isVulnerable: - score += 750 # Small slam, vulnerable. - else: - score += 500 # Small slam, not vulnerable. - - else: - score += 50 # Any part score. - - # Calculate "for the insult" bonuses. - if isDoubled: - score += 50 - elif isRedoubled: - score += 100 - - # Calculate scores for overtricks. - overTricks = tricksMade - tricksRequired - if isDoubled: - if isVulnerable: - # Score 200 for each doubled and vulnerable overtrick. - score += overTricks * 200 - else: - # Score 100 for each doubled and not vulnerable overtrick. - score += overTricks * 100 - elif isRedoubled: - if isVulnerable: - # Score 400 for each redoubled and vulnerable overtrick. - score += overTricks * 400 - else: - score += overTricks * 200 - else: - if trumpSuit in (Strain.Club, Strain.Diamond): - # Clubs and Diamonds score 20 for each undoubled overtrick. - score += overTricks * 20 - else: - # Hearts, Spades and NT score 30 for each undoubled overtrick. - score += overTricks * 30 - - else: - # Contract not fulfilled. - - underTricks = tricksRequired - tricksMade - if isDoubled: - if isVulnerable: - # Score 200 for the first doubled and vulnerable undertrick. - # Score 300 for all other undertricks. - score -= 200 + (underTricks - 1) * 300 - else: - # Score 100 for the first doubled and non-vulnerable undertrick. - # Score 200 for all other undertricks. - # Score 100 extra for third and greater undertricks. - score -= 100 + (underTricks - 1) * 200 - if underTricks > 3: - score -= (underTricks - 3) * 100 - elif isRedoubled: - if isVulnerable: - score -= 400 + (underTricks - 1) * 600 - else: - score -= 200 + (underTricks - 1) * 400 - if underTricks > 3: - score -= (underTricks - 3) * 200 - else: - if isVulnerable: - score -= 100 + (underTricks - 1) * 100 - else: - score -= 50 + (underTricks - 1) * 50 - - return score - - -def scoreRubber(result): - pass # TODO: implement. - This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <umg...@us...> - 2007-07-26 19:35:35
|
Revision: 496 http://pybridge.svn.sourceforge.net/pybridge/?rev=496&view=rev Author: umgangee Date: 2007-07-26 12:03:38 -0700 (Thu, 26 Jul 2007) Log Message: ----------- Incorporate support for Result objects in board, make appropriate tweaks to dependencies. Modified Paths: -------------- trunk/pybridge/pybridge/games/bridge/board.py trunk/pybridge/pybridge/games/bridge/game.py trunk/pybridge/pybridge/games/bridge/result.py trunk/pybridge/pybridge/games/bridge/ui/window_bridgetable.py Modified: trunk/pybridge/pybridge/games/bridge/board.py =================================================================== --- trunk/pybridge/pybridge/games/bridge/board.py 2007-07-25 21:04:25 UTC (rev 495) +++ trunk/pybridge/pybridge/games/bridge/board.py 2007-07-26 19:03:38 UTC (rev 496) @@ -18,7 +18,9 @@ import random import time + from deal import Deal +from result import DuplicateResult, RubberResult from symbols import Direction, Vulnerable @@ -43,32 +45,68 @@ @type vuln: Vulnerable """ + __directionToVuln = {Direction.North: Vulnerable.NorthSouth, + Direction.East: Vulnerable.EastWest, + Direction.South: Vulnerable.NorthSouth, + Direction.West: Vulnerable.EastWest} - def nextDeal(self, result=None): - """Generates and stores a random deal for the board. + + @classmethod + def first(cls, deal=None): + """Build an initial board. - If result of a previous game is provided, the dealer and vulnerability - are rotated according to the rules of bridge. + @deal: if provided, the deal to be wrapped by board. + Otherwise, a randomly-generated deal is wrapped. + """ + board = cls() + board['deal'] = deal or Deal.fromRandom() + board['num'] = 1 + board['time'] = tuple(time.localtime()) + + board['dealer'] = Direction.North # Arbitary. + board['vuln'] = Vulnerable.None + + return board + + + def next(self, result, deal=None): + """Given the result for this board, builds the next board. - @param result: - @type result: + The dealer and vulnerability of the next board are calculated + from the result provided. + + @param result: the result of the this board. + @param deal: if provided, the deal to be wrapped by next board. + Otherwise, a randomly-generated deal is wrapped. """ - self['deal'] = Deal.fromRandom() + assert result.board == self - self['num'] = self.get('num', 0) + 1 - self['time'] = tuple(time.localtime()) + board = Board(self.copy()) # copy() returns a dict. + board['deal'] = deal or Deal.fromRandom() + board['num'] = board.get('num', 0) + 1 + board['time'] = tuple(time.localtime()) - if self.get('dealer'): # Rotate dealer. - self['dealer'] = Direction[(self['dealer'].index + 1) % 4] - else: # Select any player as the dealer. - self['dealer'] = random.choice(Direction) + # Dealer rotates clockwise. + board['dealer'] = Direction[(board['dealer'].index + 1) % 4] - if result: - # TODO: proper GameResult object. - # TODO: consider vulnerability rules for duplicate, rubber bridge. - #if result.bidding.isPassedOut(): - # self['vuln'] = result.board['vuln'] - #elif result.getScore() >= 0 - self['vuln'] = Vulnerable[(result.board['vuln'].index + 1) % 4] - else: - self['vuln'] = Vulnerable.None # The default value. + if isinstance(result, DuplicateResult): + # See http://www.d21acbl.com/References/Laws/node5.html#law2 + i = (board['num'] - 1) % 16 + # Map from duplicate board index range 1..16 to vulnerability. + board['vuln'] = Vulnerable[(i%4 + i/4)%4] + + elif isinstance(result, RubberResult): + # Determine vulnerability of board from result of previous board. + above, below = result.score + if below >= 100: # Game contract successful. + pair = __directionToVuln[result.contract.declarer] + # Vulnerability transitions. + if board['vuln'] == Vulnerable.None: + board['vuln'] = pair + elif board['vuln'] in (pair, Vulnerable.All): # Rubber won. + board['vuln'] = Vulnerable.None + else: # Pair not vulnerable, other pair are vulnerable. + board['vuln'] = Vulnerable.All + + return board + Modified: trunk/pybridge/pybridge/games/bridge/game.py =================================================================== --- trunk/pybridge/pybridge/games/bridge/game.py 2007-07-25 21:04:25 UTC (rev 495) +++ trunk/pybridge/pybridge/games/bridge/game.py 2007-07-26 19:03:38 UTC (rev 496) @@ -63,12 +63,14 @@ self.auction = None self.play = None - self.boardQueue = [] # Boards for successive games. + self.boardQueue = [] # Boards for successive rounds. + self.results = [] # Results of previous rounds. 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) + result = property(lambda self: not self.inProgress() and self.results[-1]) # Implementation of ICardGame. @@ -81,10 +83,12 @@ if board: # Use specified board. self.board = board elif self.board: # Advance to next deal. - self.board.nextDeal(result=self) # TODO: proper GameResult object. + tricksMade, _ = self.play.wonTrickCount() + result = DuplicateResult(self.board, self.contract, tricksMade) + self.board = self.board.next(result) else: # Create a board. - self.board = Board() - self.board.nextDeal() + self.board = Board.first() + self.auction = Auction(self.board['dealer']) # Start auction. self.play = None self.visibleHands.clear() @@ -242,10 +246,14 @@ trumpSuit = self.__trumpMap[self.contract.bid.strain] self.play = TrickPlay(declarer, trumpSuit) + # If bidding is passed out, game is complete. + if not self.inProgress() and self.board['deal']: + self.results.append(DuplicateResult(self.board, contract=None)) + self.notify('makeCall', call=call, position=position) - # If bidding is passed out, reveal all hands. if not self.inProgress() and self.board['deal']: + # Reveal all unrevealed hands. for position in Direction: hand = self.board['deal'].get(position) if hand and position not in self.visibleHands: @@ -302,7 +310,6 @@ raise GameError, "Card cannot be played from hand" self.play.playCard(card, playfrom) - self.notify('playCard', card=card, position=position) # Dummy's hand is revealed when the first card of first trick is played. if len(self.play[0]) == 1: @@ -310,12 +317,21 @@ if dummyhand: # Reveal hand only if known. self.revealHand(dummyhand, self.play.dummy) - # If play is complete, reveal all hands. + # If play is complete, game is complete. if not self.inProgress() and self.board['deal']: + tricksMade, _ = self.play.wonTrickCount() + result = DuplicateResult(self.board, self.contract, tricksMade) + self.results.append(result) + + self.notify('playCard', card=card, position=position) + + if not self.inProgress() and self.board['deal']: + # Reveal all unrevealed hands. for position in Direction: hand = self.board['deal'].get(position) if hand and position not in self.visibleHands: self.revealHand(hand, position) + def revealHand(self, hand, position): @@ -363,24 +379,8 @@ raise GameError, "No game in progress" - def getScore(self): - """Returns the integer score value for declarer/dummy if: - - auction has been passed out, with no bids made. - - trick play is complete. - """ - if self.inProgress() or self.auction is None: - raise GameError, "Game not complete" - if self.auction.isPassedOut(): - return 0 # A passed out deal does not score. - tricksMade, _ = self.play.wonTrickCount() - result = DuplicateResult(self.contract, self.board['vuln'], tricksMade) - return result.score - - - - class BridgePlayer(pb.Referenceable): """Actor representing a player's view of a BridgeGame object.""" Modified: trunk/pybridge/pybridge/games/bridge/result.py =================================================================== --- trunk/pybridge/pybridge/games/bridge/result.py 2007-07-25 21:04:25 UTC (rev 495) +++ trunk/pybridge/pybridge/games/bridge/result.py 2007-07-26 19:03:38 UTC (rev 496) @@ -30,14 +30,17 @@ Vulnerable.All: tuple(Direction)} - def __init__(self, contract, vuln, tricksMade=None): + def __init__(self, board, contract, tricksMade=None): """ + @type board: Board @type contract: Contract - @type vuln: Vulnerable @type tricksMade: int or None """ + self.board = board self.contract = contract self.tricksMade = tricksMade + + vuln = self.board.get('vuln', Vulnerable.None) self.isVulnerable = self.contract.declarer in self.__vulnMapping[vuln] self.score = self._getScore() Modified: trunk/pybridge/pybridge/games/bridge/ui/window_bridgetable.py =================================================================== --- trunk/pybridge/pybridge/games/bridge/ui/window_bridgetable.py 2007-07-25 21:04:25 UTC (rev 495) +++ trunk/pybridge/pybridge/games/bridge/ui/window_bridgetable.py 2007-07-26 19:03:38 UTC (rev 496) @@ -103,7 +103,7 @@ def add_score(self, game): declarerWon, defenceWon = game.play.wonTrickCount() - score = game.getScore() + score = game.result.score textContract = render_contract(game.contract) textMade = str(declarerWon) @@ -339,13 +339,13 @@ if self.table.game.contract: self.scoreview.add_score(self.table.game) - declarerWon, defenceWon = self.table.game.play.wonTrickCount() - required = self.table.game.contract.bid.level.index + 7 - offset = declarerWon - required - score = self.table.game.getScore() + tricksMade = self.table.game.result.tricksMade + tricksRequired = self.table.game.contract.bid.level.index + 7 + offset = tricksMade - tricksRequired + score = self.table.game.result.score fields = {'contract': render_contract(self.table.game.contract), - 'offset': abs(offset) } + 'offset': abs(offset)} if offset > 0: if offset == 1: resultText = _('Contract %(contract)s made by 1 trick.') % fields This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <umg...@us...> - 2007-07-27 19:05:05
|
Revision: 498 http://pybridge.svn.sourceforge.net/pybridge/?rev=498&view=rev Author: umgangee Date: 2007-07-27 12:05:07 -0700 (Fri, 27 Jul 2007) Log Message: ----------- Since determination of vulnerability in rubber bridge requires knowledge of previous results, pass list of results to board.next(), instead of just the most recent result. Modified Paths: -------------- trunk/pybridge/pybridge/games/bridge/board.py trunk/pybridge/pybridge/games/bridge/game.py Modified: trunk/pybridge/pybridge/games/bridge/board.py =================================================================== --- trunk/pybridge/pybridge/games/bridge/board.py 2007-07-27 19:04:18 UTC (rev 497) +++ trunk/pybridge/pybridge/games/bridge/board.py 2007-07-27 19:05:07 UTC (rev 498) @@ -45,12 +45,7 @@ @type vuln: Vulnerable """ - __directionToVuln = {Direction.North: Vulnerable.NorthSouth, - Direction.East: Vulnerable.EastWest, - Direction.South: Vulnerable.NorthSouth, - Direction.West: Vulnerable.EastWest} - @classmethod def first(cls, deal=None): """Build an initial board. @@ -69,17 +64,20 @@ return board - def next(self, result, deal=None): - """Given the result for this board, builds the next board. + def next(self, results, deal=None): + """Given the results for this board (and all previous boards), + builds the next board. The dealer and vulnerability of the next board are calculated - from the result provided. + from the results provided. - @param result: the result of the this board. + @param result: a list of all previous results, ordered from earliest + to most recent, ie. this board's result is last in list. @param deal: if provided, the deal to be wrapped by next board. Otherwise, a randomly-generated deal is wrapped. """ - assert result.board == self + boardresult = results[-1] + assert boardresult.board == self board = Board(self.copy()) # copy() returns a dict. board['deal'] = deal or Deal.fromRandom() @@ -89,24 +87,33 @@ # Dealer rotates clockwise. board['dealer'] = Direction[(board['dealer'].index + 1) % 4] - if isinstance(result, DuplicateResult): + if isinstance(boardresult, DuplicateResult): # See http://www.d21acbl.com/References/Laws/node5.html#law2 i = (board['num'] - 1) % 16 # Map from duplicate board index range 1..16 to vulnerability. board['vuln'] = Vulnerable[(i%4 + i/4)%4] - elif isinstance(result, RubberResult): - # Determine vulnerability of board from result of previous board. - above, below = result.score - if below >= 100: # Game contract successful. - pair = __directionToVuln[result.contract.declarer] - # Vulnerability transitions. - if board['vuln'] == Vulnerable.None: - board['vuln'] = pair - elif board['vuln'] in (pair, Vulnerable.All): # Rubber won. - board['vuln'] = Vulnerable.None - else: # Pair not vulnerable, other pair are vulnerable. - board['vuln'] = Vulnerable.All + elif isinstance(boardresult, RubberResult): + belowNS, belowEW = 0, 0 # Running totals of below-the-line scores. + board['vuln'] = Vulnerable.None + # Only consider rounds which score below-the-line. + for result in (r for r in results if r.score.below > 0): + if result.contract.declarer in (Direction.North, Direction.South): + belowNS += result.score.below + pair = Vulnerable.NorthSouth + else: + belowEW += result.score.below + pair = Vulnerable.EastWest + # If either score exceeds 100, pair has made game. + if belowNS >= 100 or belowEW >= 100: + belowNS, belowEW = 0, 0 # Reset totals for next game. + # Vulnerability transitions. + if board['vuln'] == Vulnerable.None: + board['vuln'] = pair + elif board['vuln'] in (pair, Vulnerable.All): + board['vuln'] = Vulnerable.None + else: # Pair was not vulnerable, but other pair was. + board['vuln'] = Vulnerable.All return board Modified: trunk/pybridge/pybridge/games/bridge/game.py =================================================================== --- trunk/pybridge/pybridge/games/bridge/game.py 2007-07-27 19:04:18 UTC (rev 497) +++ trunk/pybridge/pybridge/games/bridge/game.py 2007-07-27 19:05:07 UTC (rev 498) @@ -82,10 +82,8 @@ if board: # Use specified board. self.board = board - elif self.board: # Advance to next deal. - tricksMade, _ = self.play.wonTrickCount() - result = DuplicateResult(self.board, self.contract, tricksMade) - self.board = self.board.next(result) + elif self.board: # Advance to next round. + self.board = self.board.next(self.results) else: # Create a board. self.board = Board.first() This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <umg...@us...> - 2007-08-03 18:42:16
|
Revision: 499 http://pybridge.svn.sourceforge.net/pybridge/?rev=499&view=rev Author: umgangee Date: 2007-08-03 11:42:14 -0700 (Fri, 03 Aug 2007) Log Message: ----------- New Rubber class (for rubber bridge), move some logic from Board to BridgeGame. I think this implements rubber bridge scoring/vulnerability rules accurately, but some testing is still required. Modified Paths: -------------- trunk/pybridge/pybridge/games/bridge/board.py trunk/pybridge/pybridge/games/bridge/game.py trunk/pybridge/pybridge/games/bridge/result.py Modified: trunk/pybridge/pybridge/games/bridge/board.py =================================================================== --- trunk/pybridge/pybridge/games/bridge/board.py 2007-07-27 19:05:07 UTC (rev 498) +++ trunk/pybridge/pybridge/games/bridge/board.py 2007-08-03 18:42:14 UTC (rev 499) @@ -48,7 +48,7 @@ @classmethod def first(cls, deal=None): - """Build an initial board. + """Builds an initial board. @deal: if provided, the deal to be wrapped by board. Otherwise, a randomly-generated deal is wrapped. @@ -58,27 +58,22 @@ board['num'] = 1 board['time'] = tuple(time.localtime()) - board['dealer'] = Direction.North # Arbitary. + # Convention for duplicate bridge. + board['dealer'] = Direction.North board['vuln'] = Vulnerable.None return board - def next(self, results, deal=None): - """Given the results for this board (and all previous boards), - builds the next board. + def next(self, deal=None): + """Builds and returns a successor board to this board. - The dealer and vulnerability of the next board are calculated - from the results provided. + The dealer and vulnerability of the successor board are determined from + the board number, according to the rotation scheme for duplicate bridge. - @param result: a list of all previous results, ordered from earliest - to most recent, ie. this board's result is last in list. @param deal: if provided, the deal to be wrapped by next board. Otherwise, a randomly-generated deal is wrapped. """ - boardresult = results[-1] - assert boardresult.board == self - board = Board(self.copy()) # copy() returns a dict. board['deal'] = deal or Deal.fromRandom() board['num'] = board.get('num', 0) + 1 @@ -87,33 +82,10 @@ # Dealer rotates clockwise. board['dealer'] = Direction[(board['dealer'].index + 1) % 4] - if isinstance(boardresult, DuplicateResult): - # See http://www.d21acbl.com/References/Laws/node5.html#law2 - i = (board['num'] - 1) % 16 - # Map from duplicate board index range 1..16 to vulnerability. - board['vuln'] = Vulnerable[(i%4 + i/4)%4] + # Map from duplicate board index range 1..16 to vulnerability. + # See http://www.d21acbl.com/References/Laws/node5.html#law2 + i = (board['num'] - 1) % 16 + board['vuln'] = Vulnerable[(i%4 + i/4)%4] - elif isinstance(boardresult, RubberResult): - belowNS, belowEW = 0, 0 # Running totals of below-the-line scores. - board['vuln'] = Vulnerable.None - # Only consider rounds which score below-the-line. - for result in (r for r in results if r.score.below > 0): - if result.contract.declarer in (Direction.North, Direction.South): - belowNS += result.score.below - pair = Vulnerable.NorthSouth - else: - belowEW += result.score.below - pair = Vulnerable.EastWest - # If either score exceeds 100, pair has made game. - if belowNS >= 100 or belowEW >= 100: - belowNS, belowEW = 0, 0 # Reset totals for next game. - # Vulnerability transitions. - if board['vuln'] == Vulnerable.None: - board['vuln'] = pair - elif board['vuln'] in (pair, Vulnerable.All): - board['vuln'] = Vulnerable.None - else: # Pair was not vulnerable, but other pair was. - board['vuln'] = Vulnerable.All - return board Modified: trunk/pybridge/pybridge/games/bridge/game.py =================================================================== --- trunk/pybridge/pybridge/games/bridge/game.py 2007-07-27 19:05:07 UTC (rev 498) +++ trunk/pybridge/pybridge/games/bridge/game.py 2007-08-03 18:42:14 UTC (rev 499) @@ -25,8 +25,8 @@ from auction import Auction from board import Board -from play import Trick, TrickPlay -from result import DuplicateResult, RubberResult +from play import TrickPlay +from result import DuplicateResult, Rubber, RubberResult from call import Bid, Pass, Double, Redouble from card import Card @@ -56,7 +56,7 @@ Strain.NoTrump: None} - def __init__(self): + def __init__(self, **options): self.listeners = [] self.board = None @@ -68,6 +68,11 @@ self.visibleHands = {} # A subset of deal, containing revealed hands. self.players = {} # One-to-one mapping from BridgePlayer to Direction. + self.options = options + if self.options.get('RubberScoring'): # Use rubber scoring? + self.rubbers = [] # Group results into Rubber objects. + + contract = property(lambda self: self.auction and self.auction.contract or None) trumpSuit = property(lambda self: self.play and self.play.trumpSuit or None) result = property(lambda self: not self.inProgress() and self.results[-1]) @@ -83,10 +88,27 @@ if board: # Use specified board. self.board = board elif self.board: # Advance to next round. - self.board = self.board.next(self.results) - else: # Create a board. + self.board = self.board.next() + else: # Create an initial board. self.board = Board.first() + if self.options.get('RubberScoring'): + # Vulnerability determined by number of games won by each pair. + if len(self.rubbers) == 0 or self.rubbers[-1].winner: + self.board['vuln'] = Vulnerable.None # First round, new rubber. + else: + games = self.rubbers[-1].games + if len(games[(Direction.North, Direction.South)]) > 0: + if len(games[(Direction.East, Direction.West)]) > 0: + self.board['vuln'] = Vulnerable.All + else: + self.board['vuln'] = Vulnerable.NorthSouth + else: + if len(games[(Direction.East, Direction.West)]) > 0: + self.board['vuln'] = Vulnerable.EastWest + else: + self.board['vuln'] = Vulnerable.None + self.auction = Auction(self.board['dealer']) # Start auction. self.play = None self.visibleHands.clear() @@ -246,7 +268,7 @@ # If bidding is passed out, game is complete. if not self.inProgress() and self.board['deal']: - self.results.append(DuplicateResult(self.board, contract=None)) + self._addResult(self.board, contract=None) self.notify('makeCall', call=call, position=position) @@ -318,8 +340,7 @@ # If play is complete, game is complete. if not self.inProgress() and self.board['deal']: tricksMade, _ = self.play.wonTrickCount() - result = DuplicateResult(self.board, self.contract, tricksMade) - self.results.append(result) + self._addResult(self.board, self.contract, tricksMade) self.notify('playCard', card=card, position=position) @@ -329,9 +350,22 @@ hand = self.board['deal'].get(position) if hand and position not in self.visibleHands: self.revealHand(hand, position) - + def _addResult(self, board, contract=None, tricksMade=None): + if self.options.get('RubberScoring'): + result = RubberResult(board, contract, tricksMade) + if len(self.rubbers) > 0 and self.rubbers[-1].winner is None: + rubber = self.rubbers[-1] + else: # Instantiate new rubber. + rubber = Rubber() + self.rubbers.append(rubber) + rubber.append(result) + else: + result = DuplicateResult(board, contract, tricksMade) + self.results.append(result) + + def revealHand(self, hand, position): """Reveal hand to all observers. Modified: trunk/pybridge/pybridge/games/bridge/result.py =================================================================== --- trunk/pybridge/pybridge/games/bridge/result.py 2007-07-27 19:05:07 UTC (rev 498) +++ trunk/pybridge/pybridge/games/bridge/result.py 2007-08-03 18:42:14 UTC (rev 499) @@ -24,10 +24,10 @@ _getScore = NotImplemented # Expected to be implemented by subclasses. - __vulnMapping = {Vulnerable.None: (), - Vulnerable.NorthSouth: (Direction.North, Direction.South), - Vulnerable.EastWest: (Direction.East, Direction.West), - Vulnerable.All: tuple(Direction)} + __vulnMap = {Vulnerable.None: (), + Vulnerable.NorthSouth: (Direction.North, Direction.South), + Vulnerable.EastWest: (Direction.East, Direction.West), + Vulnerable.All: tuple(Direction)} def __init__(self, board, contract, tricksMade=None): @@ -42,7 +42,7 @@ if self.contract: vuln = self.board.get('vuln', Vulnerable.None) - self.isVulnerable = self.contract.declarer in self.__vulnMapping[vuln] + self.isVulnerable = self.contract.declarer in self.__vulnMap[vuln] self.score = self._getScore() @@ -218,3 +218,55 @@ below += value return above, below + + + +class Rubber(list): + """A rubber set, in which pairs compete to make two consecutive games. + + A game is made by accumulation of 100+ points from below-the-line scores + without interruption from an opponent's game. + """ + + games = property(lambda self: self._getGames()) + winner = property(lambda self: self._getWinner()) + + + def _getGames(self): + """Returns, for each pair, a list of completed 'games' won by the pair + in this rubber. + + A game is represented as the list of consecutive results in this rubber, + with below-the-line scores that count towards the game. + """ + gamesNS, gamesEW = [], [] + + game = [] + belowNS, belowEW = 0, 0 # Cumulative totals for results. + for result in self: + game.append(result) + + if result.contract.declarer in (Direction.North, Direction.South): + belowNS += result.score[1] + if belowNS >= 100: + gamesNS.append(game) + else: + belowEW += result.score[1] + if belowEW >= 100: + gamesEW.append(game) + + # If either accumulated total exceeds 100, proceed to next game. + if belowNS >= 100 or belowEW >= 100: + game = [] + belowNS, belowEW = 0, 0 + + return {(Direction.North, Direction.South): gamesNS, + (Direction.East, Direction.West): gamesEW} + + + def _getWinner(self): + """The rubber is won by the pair which have completed two games.""" + for pair, games in self.games.items(): + if len(games) >= 2: + return pair + This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <umg...@us...> - 2007-08-31 13:45:22
|
Revision: 500 http://pybridge.svn.sourceforge.net/pybridge/?rev=500&view=rev Author: umgangee Date: 2007-08-31 06:45:20 -0700 (Fri, 31 Aug 2007) Log Message: ----------- Refactor rubber scoring, to facilitate rubber scoresheet display. Modified Paths: -------------- trunk/pybridge/pybridge/games/bridge/game.py trunk/pybridge/pybridge/games/bridge/result.py trunk/pybridge/pybridge/games/bridge/ui/window_bridgetable.py Added Paths: ----------- trunk/pybridge/pybridge/games/bridge/ui/window_scoresheet.py Modified: trunk/pybridge/pybridge/games/bridge/game.py =================================================================== --- trunk/pybridge/pybridge/games/bridge/game.py 2007-08-03 18:42:14 UTC (rev 499) +++ trunk/pybridge/pybridge/games/bridge/game.py 2007-08-31 13:45:20 UTC (rev 500) @@ -97,14 +97,14 @@ if len(self.rubbers) == 0 or self.rubbers[-1].winner: self.board['vuln'] = Vulnerable.None # First round, new rubber. else: - games = self.rubbers[-1].games - if len(games[(Direction.North, Direction.South)]) > 0: - if len(games[(Direction.East, Direction.West)]) > 0: + pairs = [pair for game, pair in self.rubbers[-1].games] + if pairs.count((Direction.North, Direction.South)) > 0: + if pairs.count((Direction.East, Direction.West)) > 0: self.board['vuln'] = Vulnerable.All else: self.board['vuln'] = Vulnerable.NorthSouth else: - if len(games[(Direction.East, Direction.West)]) > 0: + if pairs.count((Direction.East, Direction.West)) > 0: self.board['vuln'] = Vulnerable.EastWest else: self.board['vuln'] = Vulnerable.None Modified: trunk/pybridge/pybridge/games/bridge/result.py =================================================================== --- trunk/pybridge/pybridge/games/bridge/result.py 2007-08-03 18:42:14 UTC (rev 499) +++ trunk/pybridge/pybridge/games/bridge/result.py 2007-08-31 13:45:20 UTC (rev 500) @@ -233,40 +233,41 @@ def _getGames(self): - """Returns, for each pair, a list of completed 'games' won by the pair - in this rubber. + """Returns a list of completed (ie. won) 'games' in this rubber, in the + order of their completion. - A game is represented as the list of consecutive results in this rubber, - with below-the-line scores that count towards the game. + A game is represented as a list of consecutive results from this rubber, + coupled with the identifier of the scoring pair. """ - gamesNS, gamesEW = [], [] + games = [] - game = [] - belowNS, belowEW = 0, 0 # Cumulative totals for results. + thisgame = [] + belowNS, belowEW = 0, 0 # Cumulative totals for results in this game. + for result in self: - game.append(result) - + thisgame.append(result) if result.contract.declarer in (Direction.North, Direction.South): belowNS += result.score[1] if belowNS >= 100: - gamesNS.append(game) + games.append((thisgame, (Direction.North, Direction.South))) else: belowEW += result.score[1] if belowEW >= 100: - gamesEW.append(game) + games.append((thisgame, (Direction.East, Direction.West))) - # If either accumulated total exceeds 100, proceed to next game. - if belowNS >= 100 or belowEW >= 100: - game = [] - belowNS, belowEW = 0, 0 + # If either total for this game exceeds 100, proceed to next game. + if belowNS >= 100 or belowEW >= 100: + thisgame = [] + belowNS, belowEW = 0, 0 # Reset accumulators. - return {(Direction.North, Direction.South): gamesNS, - (Direction.East, Direction.West): gamesEW} + return games def _getWinner(self): """The rubber is won by the pair which have completed two games.""" - for pair, games in self.games.items(): - if len(games) >= 2: + pairs = [pair for game, pair in self.games] + + for pair in ((Direction.North, Direction.South), (Direction.East, Direction.West)): + if pairs.count(pair) >= 2: return pair Modified: trunk/pybridge/pybridge/games/bridge/ui/window_bridgetable.py =================================================================== --- trunk/pybridge/pybridge/games/bridge/ui/window_bridgetable.py 2007-08-03 18:42:14 UTC (rev 499) +++ trunk/pybridge/pybridge/games/bridge/ui/window_bridgetable.py 2007-08-31 13:45:20 UTC (rev 500) @@ -29,6 +29,7 @@ from pybridge.ui.window_gametable import WindowGameTable from window_bidbox import WindowBidbox +from window_scoresheet import ScoreSheet class BiddingView(gtk.TreeView): @@ -83,41 +84,6 @@ -class ScoreView(gtk.TreeView): - """A display of contracts bid, their results and their scores.""" - - - def __init__(self): - gtk.TreeView.__init__(self) - self.set_rules_hint(True) - - self.store = gtk.ListStore(str, str, str, str) - self.set_model(self.store) - self.clear = self.store.clear - renderer = gtk.CellRendererText() - - for index, title in enumerate([_('Contract'), _('Made'), _('N/S'), _('E/W')]): - column = gtk.TreeViewColumn(title, renderer, markup=index) - self.append_column(column) - - - def add_score(self, game): - declarerWon, defenceWon = game.play.wonTrickCount() - score = game.result.score - - 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: - textNS, textEW = str(abs(score)), '' - else: - textNS, textEW = '', str(abs(score)) - - self.store.prepend([textContract, textMade, textNS, textEW]) - - - - class BridgeDashboard(gtk.VBox): """An at-a-glance display of the state of a bridge game.""" @@ -242,7 +208,7 @@ exp = gtk.Expander(_('Bidding')) exp.set_expanded(True) exp.add(frame) - self.sidebar.pack_start(exp) + self.sidebar.pack_start(exp, expand=True) self.trickarea = TrickArea(positions=Direction) self.trickarea.set_size_request(-1, 180) @@ -253,10 +219,10 @@ exp.add(frame) self.sidebar.pack_start(exp, expand=False) - self.scoreview = ScoreView() + self.scoresheet = ScoreSheet() sw = gtk.ScrolledWindow() sw.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC) - sw.add(self.scoreview) + sw.add(self.scoresheet) frame = gtk.Frame() frame.add(sw) exp = gtk.Expander(_('Score Sheet')) @@ -265,11 +231,6 @@ self.sidebar.pack_start(exp, expand=False) - def errback(self, failure): - print "Error: %s" % failure.getErrorMessage() - print failure.getBriefTraceback() - - def setTable(self, table): """Changes display to match the table specified. @@ -337,12 +298,11 @@ # Determine and display score in dialog box and score sheet. if self.table.game.contract: - self.scoreview.add_score(self.table.game) + self.scoresheet.add_result(self.table.game.result) tricksMade = self.table.game.result.tricksMade tricksRequired = self.table.game.contract.bid.level.index + 7 offset = tricksMade - tricksRequired - score = self.table.game.result.score fields = {'contract': render_contract(self.table.game.contract), 'offset': abs(offset)} @@ -359,6 +319,10 @@ else: resultText = _('Contract %(contract)s made exactly.') % fields + score = self.table.game.result.score + if isinstance(score, tuple): # Rubber scoring. + score = sum(score) # TODO: display above, below separately. + pair = (score >= 0 and _('declarer')) or _('defence') scoreText = _('Score %(points)s points for %(pair)s.') % {'points': abs(score), 'pair': pair} Added: trunk/pybridge/pybridge/games/bridge/ui/window_scoresheet.py =================================================================== --- trunk/pybridge/pybridge/games/bridge/ui/window_scoresheet.py (rev 0) +++ trunk/pybridge/pybridge/games/bridge/ui/window_scoresheet.py 2007-08-31 13:45:20 UTC (rev 500) @@ -0,0 +1,140 @@ +# 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. + + +import gtk + +from pybridge.games.bridge.result import DuplicateResult, RubberResult +from pybridge.games.bridge.symbols import Direction + +from pybridge.ui.eventhandler import SimpleEventHandler +import pybridge.ui.vocabulary as voc + + +class ScoreSheet(gtk.TreeView): + """A score sheet widget, which presents GameResult information.""" + + # TODO: display total scores for N/S and E/W. + + + def __init__(self): + gtk.TreeView.__init__(self) + self.set_rules_hint(True) + + self.store = gtk.ListStore(int, str, str, str, str) + self.set_model(self.store) + + renderer = gtk.CellRendererText() + for index, title in enumerate([_('Board'), _('Contract'), _('Made'), _('N/S'), _('E/W')]): + column = gtk.TreeViewColumn(title, renderer, markup=index) + self.append_column(column) + + + def add_result(self, result): + if isinstance(result, RubberResult): + # Rubber results are split into 'above' and 'below' scores. + score = sum(result.score) + else: + score = result.score + + if result.contract is None: # Bidding passed out. + row = (result.board['num'], _('Passed out'), '-', '-', '', '') + + else: + if result.contract.declarer in (Direction.North, Direction.South) and score > 0 \ + or result.contract.declarer in (Direction.East, Direction.West) and score < 0: + scoreNS, scoreEW = str(abs(score)), '' + else: + scoreNS, scoreEW = '', str(abs(score)) + + row = (result.board['num'], voc.render_contract(result.contract), + #voc.DIRECTION_NAMES[result.contract.declarer], + str(result.tricksMade), scoreNS, scoreEW) + + self.store.append(row) + + + + +class RubberScoreSheet(gtk.TreeView): + """A score sheet widget, which presents a Rubber object.""" + + + def __init__(self): + gtk.TreeView.__init__(self) + self.set_rules_hint(True) + self.set_row_separator_func(self._row_separator) + + # Set bool field in a row to display as separator. + self.store = gtk.ListStore(str, str, bool) + self.set_model(self.store) + + renderer = gtk.CellRendererText() + for index, title in enumerate([_('N/S'), _('E/W')]): + column = gtk.TreeViewColumn(title, renderer, markup=index) + self.append_column(column) + + + def set_rubber(self, rubber): + self.store.clear() + self.store.append(('', '', True)) # The initial dividing line. + + for game, pair in rubber.games: + #aboveiters, belowiters = [], [] + for result in rubber: + above, below = result.score + if result.contract.declarer in (Direction.North, Direction.South) and below > 0 \ + or result.contract.declarer in (Direction.East, Direction.West) and score < 0: + self.store.prepend((str(above), '', False)) + self.store.append((str(below), '', False)) + else: + self.store.prepend(('', str(above), False)) + self.store.append(('', str(below), False)) + + + def _row_separator(self, model, iter, data): + print model, iter, data + return True + + + + +class WindowScoreSheet(object): + """""" + + + def __init__(self, parent=None): + self.window = gtk.Window() + if parent: + self.window.set_transient_for(parent.window) + self.window.set_title(_('Score Sheet')) + self.window.connect('delete_event', self.on_delete_event) + #self.window.set_resizable(False) + + self.eventHandler = SimpleEventHandler(self) + self.table = None + + + def tearDown(self): + pass + + + + def on_delete_event(self, widget, *args): + # TODO: call wm.close(self) + return True # Stops window deletion taking place. + This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <umg...@us...> - 2007-10-04 16:37:17
|
Revision: 505 http://pybridge.svn.sourceforge.net/pybridge/?rev=505&view=rev Author: umgangee Date: 2007-10-04 09:37:16 -0700 (Thu, 04 Oct 2007) Log Message: ----------- Make Contract and Result objects copyable, send results over-the-wire for display on clients. Modified Paths: -------------- trunk/pybridge/pybridge/games/bridge/auction.py trunk/pybridge/pybridge/games/bridge/game.py trunk/pybridge/pybridge/games/bridge/result.py trunk/pybridge/pybridge/games/bridge/ui/window_scoresheet.py Modified: trunk/pybridge/pybridge/games/bridge/auction.py =================================================================== --- trunk/pybridge/pybridge/games/bridge/auction.py 2007-09-26 13:02:19 UTC (rev 504) +++ trunk/pybridge/pybridge/games/bridge/auction.py 2007-10-04 16:37:16 UTC (rev 505) @@ -16,11 +16,13 @@ # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +from twisted.spread import pb + from call import Bid, Pass, Double, Redouble from symbols import Direction -class Contract(object): +class Contract(object, pb.Copyable, pb.RemoteCopy): """Represents the result of an auction.""" @@ -54,8 +56,19 @@ self.redoubleBy = auction.whoCalled(auction.currentRedouble) + def getStateToCopy(self): + return self.bid, self.declarer, self.doubleBy, self.redoubleBy + def setCopyableState(self, state): + self.bid, self.declarer, self.doubleBy, self.redoubleBy = state + + +pb.setUnjellyableForClass(Contract, Contract) + + + + class Auction(list): """The auction (bidding phase) of a game of bridge.""" Modified: trunk/pybridge/pybridge/games/bridge/game.py =================================================================== --- trunk/pybridge/pybridge/games/bridge/game.py 2007-09-26 13:02:19 UTC (rev 504) +++ trunk/pybridge/pybridge/games/bridge/game.py 2007-10-04 16:37:16 UTC (rev 505) @@ -87,6 +87,8 @@ if board: # Use specified board. self.board = board + elif self.boardQueue: # Use pre-specified board. + self.board = self.boardQueue.pop(0) elif self.board: # Advance to next round. self.board = self.board.next() else: # Create an initial board. @@ -136,6 +138,9 @@ def getState(self): state = {} + state['options'] = self.options + state['results'] = self.results + if self.inProgress(): # Remove hidden hands from deal. visibleBoard = self.board.copy() @@ -151,6 +156,8 @@ def setState(self, state): + self.options = state.get('options', {}) + if state.get('board'): self.start(state['board']) @@ -172,7 +179,9 @@ else: self.playCard(card, position=turn) + self.results = state.get('results', []) # Overwrites current game result. + def updateState(self, event, *args, **kwargs): allowed = ['start', 'makeCall', 'playCard', 'revealHand'] if event in allowed: Modified: trunk/pybridge/pybridge/games/bridge/result.py =================================================================== --- trunk/pybridge/pybridge/games/bridge/result.py 2007-09-26 13:02:19 UTC (rev 504) +++ trunk/pybridge/pybridge/games/bridge/result.py 2007-10-04 16:37:16 UTC (rev 505) @@ -16,6 +16,8 @@ # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +from twisted.spread import pb + from symbols import Direction, Strain, Vulnerable @@ -40,6 +42,7 @@ self.contract = contract self.tricksMade = tricksMade + self.isVulnerable = None if self.contract: vuln = self.board.get('vuln', Vulnerable.None) self.isVulnerable = self.contract.declarer in self.__vulnMap[vuln] @@ -47,6 +50,16 @@ self.score = self._getScore() + def getStateToCopy(self): + return (self.board.copy(), self.contract, self.tricksMade, + self.isVulnerable, self.score) + + + def setCopyableState(self, state): + self.board, self.contract, self.tricksMade, self.isVulnerable, self.score = state + # assert self.score == self._getScore() + + def _getScoreComponents(self): """Compute the component values which contribute to the score. Note that particular scoring schemes may ignore some of the components. @@ -178,7 +191,7 @@ -class DuplicateResult(GameResult): +class DuplicateResult(GameResult, pb.Copyable, pb.RemoteCopy): """Represents the result of a completed round of duplicate bridge.""" @@ -196,9 +209,12 @@ return score +pb.setUnjellyableForClass(DuplicateResult, DuplicateResult) -class RubberResult(GameResult): + + +class RubberResult(GameResult, pb.Copyable, pb.RemoteCopy): """Represents the result of a completed round of rubber bridge.""" @@ -219,8 +235,11 @@ return above, below +pb.setUnjellyableForClass(RubberResult, RubberResult) + + class Rubber(list): """A rubber set, in which pairs compete to make two consecutive games. Modified: trunk/pybridge/pybridge/games/bridge/ui/window_scoresheet.py =================================================================== --- trunk/pybridge/pybridge/games/bridge/ui/window_scoresheet.py 2007-09-26 13:02:19 UTC (rev 504) +++ trunk/pybridge/pybridge/games/bridge/ui/window_scoresheet.py 2007-10-04 16:37:16 UTC (rev 505) @@ -52,7 +52,7 @@ score = result.score if result.contract is None: # Bidding passed out. - row = (result.board['num'], _('Passed out'), '-', '-', '', '') + row = (result.board['num'], _('Passed out'), '-', '', '') else: if result.contract.declarer in (Direction.North, Direction.South) and score > 0 \ @@ -122,19 +122,53 @@ if parent: self.window.set_transient_for(parent.window) self.window.set_title(_('Score Sheet')) - self.window.connect('delete_event', self.on_delete_event) - #self.window.set_resizable(False) + #self.window.connect('delete_event', self.on_delete_event) self.eventHandler = SimpleEventHandler(self) self.table = None + self.window.show() + def tearDown(self): - pass + self.table.game.detach(self.eventHandler) + self.table = None # Dereference table. + def setTable(self, table): + self.table = table + self.table.game.attach(self.eventHandler) - def on_delete_event(self, widget, *args): - # TODO: call wm.close(self) - return True # Stops window deletion taking place. + if hasattr(self.table.game, 'rubbers'): + self.scoresheet = RubberScoreSheet() + if self.table.game.rubbers: + rubber = self.table.game.rubbers[-1] + self.scoresheet.set_rubber(rubber) + else: # Duplicate-style list of results. + self.scoresheet = ScoreSheet() + for result in self.table.game.results: + self.scoresheet.add_result(result) + + self.window.add(self.scoresheet) + self.scoresheet.show() + + + def update(self): + if self.table.game.results and not self.table.game.inProgress(): + if isinstance(self.scoresheet, RubberScoreSheet): + rubber = self.table.game.rubbers[-1] + self.scoresheet.set_rubber(rubber) + else: + result = self.table.game.results[-1] + self.scoresheet.add_result(result) + + + + def event_makeCall(self, call, position): + self.update() + + + def event_playCard(self, card, position): + self.update() + This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |