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. |