Duplicate Bridge Scoring in OpenOffice Calc
This module contains functions written in OpenOffice Basic, to support scoring duplicate
bridge in the OpenOffice Calc spreadsheet application. I'm currently using these
functions in spreadsheets like this:
OpenOffice also supports JavaScript, BeanShell and Python; this is just the method that I
happened to stumble upon first. In 38 years of programming, this is certainly the
first--and hopefully the last--piece of BASIC I will ever write.
REM Bridge.bas -- OpenOffice.org Basic module for scoring duplicate bridge.
REM
REM This module contains two functions: DuplicateScore() and IsVulnerable().
REM
REM They may be used together in a spreadsheet formula like this:
REM
REM DUPLICATESCORE(<Contract>;<Made>;<Down>;ISVULNERABLE(<Board>;<Declarer>))
REM
Option Explicit
REM A test procedure may be placed here.
REM DuplicateScore -- return the score for declarer's side, given a contract and result
REM
REM usage:
REM DuplicateScore(<Contract>,<Made>,<Down>,<Vuln>)
REM
REM in Calc:
REM DUPLICATESCORE(<Contract>;<Made>;<Down>;<Vuln>)
REM
REM arguments:
REM <Contract> is a string giving the level, strain, and whether doubled or redoubled.
REM Values range from "1C", "1D", "1H", "1S", "1NT" through "7C", "7D", "7H", "7S", "7NT"
REM and may be suffixed "1Cx" .. "7NTx" for doubled, or "1Cxx" .. "7NTxx" for redoubled.
REM
REM <Made> is an integer in the range 0..7, representing the number of tricks over
REM book (i.e., over 6) that the declarer's side took, if the contract was made.
REM If the declarer's side did not make their contract, <Made> should be 0.
REM
REM <Down> is an integer in the range 0..13, representing the number of tricks by which
REM the declarer's side fell short of making their contract. If the contract was made,
REM <Down> should be 0.
REM
REM <Vuln> is a boolean value, representing whether the declarer's side was vulnerable
REM on the hand.
REM
REM return:
REM Returns a non-zero integer in the range -7600 .. 2980; returns 0 if any of the
REM arguments are out of range, or the combination of arguments is invalid.
REM
Function DuplicateScore(Contract As String, Made As Integer, Down As Integer, Vuln As Boolean) As Integer
Dim Level As Integer
Dim Strain As String
Dim Doubled As Boolean
Dim Redoubled As Boolean
Dim IsMinor As Boolean
Dim IsMajor As Boolean
Dim IsNotrump As Boolean
Dim ContractPoints As Integer
Dim OvertrickPoints As Integer
Dim GamePoints As Integer
Dim SlamPoints As Integer
Dim PenaltyPoints As Integer
Contract = Trim(Contract) ' remove leading and trailing spaces, if any
Level = CInt(Left(Contract, 1))
If (Level < 1 OR Level > 7) Then ' Invalid contract string, return 0
Made = 0
Down = 0
End If
Strain = Mid(Contract, 2, 1) ' "S", "H", "D", "C" or "N"
If (InStr(Contract, "x") = Len(Contract) - 1) Then
Redoubled = True
ElseIf (InStr(Contract, "x") = Len(Contract)) Then
Doubled = True
End If
If (Strain = "S" OR Strain = "H") Then
IsMajor = True
ElseIf (Strain = "D" OR Strain = "C") Then
IsMinor = True
ElseIf (Strain = "N") Then
IsNotrump = True
Else ' Invalid contract string, return 0
Made = 0
Down = 0
End If
If (Made < 0 OR Made > 7) Then ' Invalid hand result, return 0
Made = 0
Down = 0
End If
If (Down < 0 OR Down > Level + 6) Then ' Invalid hand result, return 0
Made = 0
Down = 0
End If
If (Made > 0 AND Down > 0) Then ' Invalid hand result, return 0
Made = 0
Down = 0
End If
If (Made > 0) Then
REM CONTRACT POINTS
If (IsNotrump) Then
If (Redoubled) Then
ContractPoints = 160 + 120 * (Level - 1)
ElseIf (Doubled) Then
ContractPoints = 80 + 60 * (Level - 1)
Else
ContractPoints = 40 + 30 * (Level - 1)
End If
ElseIf (IsMajor) Then
If (Redoubled) Then
ContractPoints = 120 * Level
ElseIf (Doubled) Then
ContractPoints = 60 * Level
Else
ContractPoints = 30 * Level
End If
ElseIf (IsMinor) Then
If (Redoubled) Then
ContractPoints = 80 * Level
ElseIf (Doubled) Then
ContractPoints = 40 * Level
Else
ContractPoints = 20 * Level
End If
End If
If (Made > Level) Then
REM OVERTRICK POINTS
If (Redoubled) Then
If (Vuln) Then
OvertrickPoints = 400 * (Made - Level)
Else
OvertrickPoints = 200 * (Made - Level)
End If
ElseIf (Doubled) Then
If (Vuln) Then
OvertrickPoints = 200 * (Made - Level)
Else
OvertrickPoints = 100 * (Made - Level)
End If
ElseIf (IsMinor) Then
OvertrickPoints = 20 * (Made - Level)
Else
OvertrickPoints = 30 * (Made - Level)
End If
End If
If (Made >= Level) Then
REM GAME BONUS
If (ContractPoints >= 100) Then
If (Vuln) Then
GamePoints = 500
Else
GamePoints = 300
End If
Else
GamePoints = 50
End If
REM DOUBLED OR REDOUBLED BONUS
If (Doubled) Then
GamePoints = GamePoints + 50
ElseIf (Redoubled) Then
GamePoints = GamePoints + 100
End If
REM SLAM BONUS
If (Level > 6) Then
If (Vuln) Then
SlamPoints = 1500
Else
SlamPoints = 1000
End If
ElseIf (Level > 5) Then
If (Vuln) Then
SlamPoints = 750
Else
SlamPoints = 500
End If
End If
End If
End If
REM PENALTY POINTS
If (Down > 0) Then
If (Vuln) Then
If (Redoubled) Then
If (Down > 3) Then
PenaltyPoints = -1600 - (Down - 3) * 600
ElseIf (Down > 1) Then
PenaltyPoints = -400 - (Down - 1) * 600
Else
PenaltyPoints = -400
End If
ElseIf (Doubled) Then
If (Down > 3) Then
PenaltyPoints = -800 - (Down - 3) * 300
ElseIf (Down > 1) Then
PenaltyPoints = -200 - (Down - 1) * 300
Else
PenaltyPoints = -200
End If
Else
PenaltyPoints = Down * -100
End If
Else
If (Redoubled) Then
If (Down > 3) Then
PenaltyPoints = -1000 - (Down - 3) * 600
ElseIf (Down > 1) Then
PenaltyPoints = -200 - (Down - 1) * 400
Else
PenaltyPoints = -200
End If
ElseIf (Doubled) Then
If (Down > 3) Then
PenaltyPoints = -500 - (Down - 3) * 300
ElseIf (Down > 1) Then
PenaltyPoints = -100 - (Down - 1) * 200
Else
PenaltyPoints = -100
End If
Else
PenaltyPoints = Down * -50
End If
End If
End If
DuplicateScore = ContractPoints + OvertrickPoints + GamePoints + SlamPoints + PenaltyPoints
End Function
REM IsVulnerable -- return a value indicating whether declarer's side is vulnerable
REM
REM usage:
REM IsVulnerable(<Board>,<Declarer>)
REM
REM arguments:
REM <Board> is a positive integer in the range 1..32767.
REM
REM <Declarer> is a string starting with "N", "S", "E" or "W".
REM
REM return:
REM Returns 1 if declarer's side is vulnerable on the given board, 0 otherwise.
REM (The OO BASIC Boolean type is returned to Calc as a string; an Integer is
REM easier to handle in spreadsheet formulas, and will be converted correctly to
REM a Boolean when passed as the fourth argument to the DUPLICATESCORE() function).
REM
Function IsVulnerable(Board As Integer, Declarer As String) As Integer
Dim Row As Integer
Dim Col As Integer
Dim Vuln(15, 3) As Integer
Vuln( 0, 0) = 1 : Vuln( 0, 1) = 0 : Vuln( 0, 2) = 1 : Vuln( 0, 3) = 0
Vuln( 1, 0) = 0 : Vuln( 1, 1) = 0 : Vuln( 1, 2) = 0 : Vuln( 1, 3) = 0
Vuln( 2, 0) = 0 : Vuln( 2, 1) = 1 : Vuln( 2, 2) = 0 : Vuln( 2, 3) = 1
Vuln( 3, 0) = 1 : Vuln( 3, 1) = 0 : Vuln( 3, 2) = 1 : Vuln( 3, 3) = 0
Vuln( 4, 0) = 1 : Vuln( 4, 1) = 1 : Vuln( 4, 2) = 1 : Vuln( 4, 3) = 1
Vuln( 5, 0) = 0 : Vuln( 5, 1) = 1 : Vuln( 5, 2) = 0 : Vuln( 5, 3) = 1
Vuln( 6, 0) = 1 : Vuln( 6, 1) = 0 : Vuln( 6, 2) = 1 : Vuln( 6, 3) = 0
Vuln( 7, 0) = 1 : Vuln( 7, 1) = 1 : Vuln( 7, 2) = 1 : Vuln( 7, 3) = 1
Vuln( 8, 0) = 0 : Vuln( 8, 1) = 0 : Vuln( 8, 2) = 0 : Vuln( 8, 3) = 0
Vuln( 9, 0) = 1 : Vuln( 9, 1) = 0 : Vuln( 9, 2) = 1 : Vuln( 9, 3) = 0
Vuln(10, 0) = 1 : Vuln(10, 1) = 1 : Vuln(10, 2) = 1 : Vuln(10, 3) = 1
Vuln(11, 0) = 0 : Vuln(11, 1) = 0 : Vuln(11, 2) = 0 : Vuln(11, 3) = 0
Vuln(12, 0) = 0 : Vuln(12, 1) = 1 : Vuln(12, 2) = 0 : Vuln(12, 3) = 1
Vuln(13, 0) = 1 : Vuln(13, 1) = 1 : Vuln(13, 2) = 1 : Vuln(13, 3) = 1
Vuln(14, 0) = 0 : Vuln(14, 1) = 0 : Vuln(14, 2) = 0 : Vuln(14, 3) = 0
Vuln(15, 0) = 0 : Vuln(15, 1) = 1 : Vuln(15, 2) = 0 : Vuln(15, 3) = 1
Row = Board MOD 16
REM "W", "N", "E", "S" -> 0, 1, 2, 3
If (Left(Declarer, 1) = "W") Then
Col = 0
ElseIf (Left(Declarer, 1) = "N") Then
Col = 1
ElseIf (Left(Declarer, 1) = "E") Then
Col = 2
ElseIf (Left(Declarer, 1) = "S") Then
Col = 3
End If
IsVulnerable = Vuln(Row, Col)
End Function