summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMichał Klichowicz <emkael@tlen.pl>2023-10-01 23:42:10 +0200
committerMichał Klichowicz <emkael@tlen.pl>2023-10-01 23:42:10 +0200
commitb3f00e26049c990f06ab4b1b37345d64f0b5c2ae (patch)
tree820ce5d325e6427645d5af8b160d574b6c453b56
parent7abe5062e8a9cbd41316090e8891fdcbc026696d (diff)
Removing double-dummy code from bcdd PBN parsing code
-rw-r--r--src/bcdd/BCalcWrapper.py45
-rw-r--r--src/bcdd/DDTable.py44
-rw-r--r--src/bcdd/Exceptions.py8
-rw-r--r--src/bcdd/PBNBoard.py12
-rw-r--r--src/bcdd/ParContract.py22
-rw-r--r--src/bcdd/ParScore.py22
6 files changed, 36 insertions, 117 deletions
diff --git a/src/bcdd/BCalcWrapper.py b/src/bcdd/BCalcWrapper.py
deleted file mode 100644
index a6bed27..0000000
--- a/src/bcdd/BCalcWrapper.py
+++ /dev/null
@@ -1,45 +0,0 @@
-'''
-Wrapper class for libbcalcDDS.dll
-'''
-
-import os
-from ctypes import cdll, c_char_p, c_void_p
-
-from .Exceptions import DllNotFoundException
-
-
-class BCalcWrapper(object):
-
- DENOMINATIONS = [ 'C', 'D', 'H', 'S', 'N' ]
- PLAYERS = [ 'N', 'E', 'S', 'W' ]
-
- def __init__(self):
- dllPath = os.path.join(
- os.path.dirname(__file__),
- '..')
- try:
- self.libbcalcdds = cdll.LoadLibrary(os.path.join(
- dllPath, 'libbcalcdds.dll'))
- except OSError:
- try:
- self.libbcalcdds = cdll.LoadLibrary(os.path.join(
- dllPath, 'libbcalcdds.so'))
- except OSError:
- self.libbcalcdds = None
- if self.libbcalcdds is None:
- raise DllNotFoundException()
-
- def __getattr__(self, attrname):
- def _dynamic_method(*args):
- function = getattr(self.libbcalcdds, 'bcalcDDS_' + attrname)
- if attrname == 'new':
- function.restype = c_void_p
- else:
- if attrname == 'getLastError':
- function.restype = c_char_p
- function.argtypes = [c_void_p]
- return function(*args)
- return _dynamic_method
-
- def declarerToLeader(self, player):
- return (player + 1) % 4
diff --git a/src/bcdd/DDTable.py b/src/bcdd/DDTable.py
index 651aec3..a97af48 100644
--- a/src/bcdd/DDTable.py
+++ b/src/bcdd/DDTable.py
@@ -1,7 +1,6 @@
-from ctypes import c_char_p
-from copy import copy
+from copy import copy
-from .BCalcWrapper import BCalcWrapper
+from .PBNBoard import PBNBoard
from .Exceptions import DDTableInvalidException, FieldNotFoundException
@@ -23,37 +22,8 @@ class DDTable(object):
)
return table
- _banner_displayed = False
-
def __init__(self, board):
self._board = board;
- self._wrapper = BCalcWrapper()
-
- def _check_for_error(self, solver):
- error = self._wrapper.getLastError(solver)
- if error:
- raise DDTableInvalidException(
- 'BCalc error: %s' % (c_char_p(error).value.decode('ascii')))
-
- def get_bcalc_table(self, show_banner=True):
- if not DDTable._banner_displayed and show_banner:
- print('Double dummy analysis provided by BCalc.')
- print('BCalc is awesome, check it out: http://bcalc.w8.pl')
- DDTable._banner_displayed = True
- result = self._get_empty_table()
- deal = self._board.get_layout()
- solver = self._wrapper.new(b"PBN", deal.encode(), 0, 0)
- self._check_for_error(solver)
- for denom in range(0, 5):
- self._wrapper.setTrumpAndReset(solver, denom)
- for player in range(0, 4):
- leader = self._wrapper.declarerToLeader(player)
- self._wrapper.setPlayerOnLeadAndReset(solver, leader)
- result[player][denom] = 13 - self._wrapper.getTricksToTake(
- solver)
- self._check_for_error(solver)
- self._wrapper.delete(solver);
- return self._validate_table(result)
def get_jfr_table(self):
result = self._get_empty_table()
@@ -61,7 +31,7 @@ class DDTable(object):
abilities = self._board.validate_ability(ability)
for player_ability in abilities:
player = player_ability[0]
- player_id = BCalcWrapper.PLAYERS.index(player)
+ player_id = PBNBoard.PLAYERS.index(player)
denom_id = 4
for tricks in player_ability[1]:
result[player_id][denom_id] = int(tricks, 16)
@@ -76,8 +46,8 @@ class DDTable(object):
player = line_match.group(1)[0]
denom = line_match.group(2)[0]
tricks = int(line_match.group(3))
- player_id = BCalcWrapper.PLAYERS.index(player)
- denom_id = BCalcWrapper.DENOMINATIONS.index(denom)
+ player_id = PBNBoard.PLAYERS.index(player)
+ denom_id = PBNBoard.DENOMINATIONS.index(denom)
result[player_id][denom_id] = tricks
return self._validate_table(result)
@@ -91,8 +61,8 @@ class DDTable(object):
return self.get_bcalc_table(show_banner)
def print_table(self, dd_table):
- print('\t' + '\t'.join(BCalcWrapper.DENOMINATIONS))
+ print('\t' + '\t'.join(PBNBoard.DENOMINATIONS))
for i in range(0, 4):
print('%s%s' % (
- self._wrapper.PLAYERS[i],
+ PBNBoard.PLAYERS[i],
''.join(['\t' + str(tricks) for tricks in dd_table[i]])))
diff --git a/src/bcdd/Exceptions.py b/src/bcdd/Exceptions.py
index fd81dd7..dc2d00b 100644
--- a/src/bcdd/Exceptions.py
+++ b/src/bcdd/Exceptions.py
@@ -1,11 +1,3 @@
-class FileNotFoundError(Exception):
- pass
-
-
-class DllNotFoundException(Exception):
- pass
-
-
class FieldNotFoundException(Exception):
pass
diff --git a/src/bcdd/PBNBoard.py b/src/bcdd/PBNBoard.py
index 87105f2..6e1abf6 100644
--- a/src/bcdd/PBNBoard.py
+++ b/src/bcdd/PBNBoard.py
@@ -1,6 +1,5 @@
import re
-from .BCalcWrapper import BCalcWrapper
from .Exceptions import FieldNotFoundException, DDTableInvalidException
@@ -19,6 +18,9 @@ class PBNBoard(object):
optimum_result_table_pattern = re.compile(
r'^([NESW])\s+([CDHSN])T?\s+(\d+)$')
+ DENOMINATIONS = [ 'C', 'D', 'H', 'S', 'N' ]
+ PLAYERS = [ 'N', 'E', 'S', 'W' ]
+
def __init__(self, lines, line_no=None):
self.line_number = line_no
self._has_optimum_result_table = None
@@ -98,7 +100,7 @@ class PBNBoard(object):
def write_ability(self, dd_table):
sb = ''
for i in range(0, 4):
- sb += BCalcWrapper.PLAYERS[i]
+ sb += PBNBoard.PLAYERS[i]
sb += ':'
sb += ''.join(['%X' % (j) for j in dd_table[i]])[::-1]
sb += ' '
@@ -206,9 +208,9 @@ class PBNBoard(object):
for j in range(0, 5):
self.fields.append(PBNField(
raw_data='%s %s%s %d' % (
- BCalcWrapper.PLAYERS[i],
- BCalcWrapper.DENOMINATIONS[j],
- 'T' if BCalcWrapper.DENOMINATIONS[j] == 'N' else '',
+ PBNBoard.PLAYERS[i],
+ PBNBoard.DENOMINATIONS[j],
+ 'T' if PBNBoard.DENOMINATIONS[j] == 'N' else '',
dd_table[i][j])))
def save_par_contract(self, contract, jfr_only=False):
diff --git a/src/bcdd/ParContract.py b/src/bcdd/ParContract.py
index 918efae..97ce0d9 100644
--- a/src/bcdd/ParContract.py
+++ b/src/bcdd/ParContract.py
@@ -1,6 +1,6 @@
import functools
-from .BCalcWrapper import BCalcWrapper as bcw
+from .PBNBoard import PBNBoard as pbb
from .Exceptions import ParScoreInvalidException
@@ -100,21 +100,21 @@ class ParContract(object):
return score
def __gt__(self, other):
- denomination = bcw.DENOMINATIONS.index(self.denomination) \
- if self.denomination in bcw.DENOMINATIONS \
+ denomination = pbb.DENOMINATIONS.index(self.denomination) \
+ if self.denomination in pbb.DENOMINATIONS \
else -1
- other_denomination = bcw.DENOMINATIONS.index(
+ other_denomination = pbb.DENOMINATIONS.index(
other.denomination) \
- if other.denomination in bcw.DENOMINATIONS else -1
+ if other.denomination in pbb.DENOMINATIONS else -1
return (self.level > other.level) \
or ((self.level == other.level) \
and (denomination > other_denomination))
def get_defense(self, dd_table, vulnerable):
- declarer_index = bcw.PLAYERS.index(self.declarer) \
- if self.declarer in bcw.PLAYERS else -1
- denomination_index = bcw.DENOMINATIONS.index(self.denomination) \
- if self.denomination in bcw.DENOMINATIONS else -1
+ declarer_index = pbb.PLAYERS.index(self.declarer) \
+ if self.declarer in pbb.PLAYERS else -1
+ denomination_index = pbb.DENOMINATIONS.index(self.denomination) \
+ if self.denomination in pbb.DENOMINATIONS else -1
if (self.level != 0) \
and (self.level + 6
<= dd_table[declarer_index][denomination_index]):
@@ -132,8 +132,8 @@ class ParContract(object):
if level + 6 > dd_table[defender][i]:
defense = ParContract(
level,
- bcw.DENOMINATIONS[i],
- bcw.PLAYERS[defender],
+ pbb.DENOMINATIONS[i],
+ pbb.PLAYERS[defender],
True,
0)
defense.score = defense.calculate_score(
diff --git a/src/bcdd/ParScore.py b/src/bcdd/ParScore.py
index e23a839..0fe28cf 100644
--- a/src/bcdd/ParScore.py
+++ b/src/bcdd/ParScore.py
@@ -1,6 +1,6 @@
import re
-from .BCalcWrapper import BCalcWrapper as bcw
+from .PBNBoard import PBNBoard as pbb
from .ParContract import ParContract
from .Exceptions import FieldNotFoundException
@@ -68,16 +68,16 @@ class ParScore(object):
or ((i % 2 == 1) and for_ew):
for j in range(0, 5):
level = dd_table[i][j] - 6
- denomination = bcw.DENOMINATIONS.index(
+ denomination = pbb.DENOMINATIONS.index(
contract.denomination) \
- if contract.denomination in bcw.DENOMINATIONS \
+ if contract.denomination in pbb.DENOMINATIONS \
else -1
if (level > contract.level) \
or ((level == contract.level) \
and (j > denomination)):
contract.level = level
- contract.denomination = bcw.DENOMINATIONS[j]
- contract.declarer = bcw.PLAYERS[i]
+ contract.denomination = pbb.DENOMINATIONS[j]
+ contract.declarer = pbb.PLAYERS[i]
tricks = dd_table[i][j]
vulnerability = self._board.get_vulnerable().upper()
vulnerable = self._determine_vulnerability(
@@ -104,11 +104,11 @@ class ParScore(object):
if highest_defense is not None:
# Highest contract has profitable defense
return highest_defense.validate()
- denomination_index = bcw.DENOMINATIONS.index(highest.denomination) \
- if highest.denomination in bcw.DENOMINATIONS \
+ denomination_index = pbb.DENOMINATIONS.index(highest.denomination) \
+ if highest.denomination in pbb.DENOMINATIONS \
else -1
- declarer_index = bcw.PLAYERS.index(highest.declarer) \
- if highest.declarer in bcw.PLAYERS else -1
+ declarer_index = pbb.PLAYERS.index(highest.declarer) \
+ if highest.declarer in pbb.PLAYERS else -1
player_indexes = [declarer_index, (declarer_index + 2) % 4]
vulnerable = self._determine_vulnerability(
vulnerability, highest.declarer)
@@ -122,8 +122,8 @@ class ParScore(object):
while level > 0:
contract = ParContract(
level,
- bcw.DENOMINATIONS[i],
- bcw.PLAYERS[player],
+ pbb.DENOMINATIONS[i],
+ pbb.PLAYERS[player],
False, 0)
contract.score = contract.calculate_score(
dd_table[player][i], vulnerable)