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