From b3f00e26049c990f06ab4b1b37345d64f0b5c2ae Mon Sep 17 00:00:00 2001 From: Michał Klichowicz Date: Sun, 1 Oct 2023 23:42:10 +0200 Subject: Removing double-dummy code from bcdd PBN parsing code --- src/bcdd/BCalcWrapper.py | 45 --------------------------------------------- src/bcdd/DDTable.py | 44 +++++++------------------------------------- src/bcdd/Exceptions.py | 8 -------- src/bcdd/PBNBoard.py | 12 +++++++----- src/bcdd/ParContract.py | 22 +++++++++++----------- src/bcdd/ParScore.py | 22 +++++++++++----------- 6 files changed, 36 insertions(+), 117 deletions(-) delete mode 100644 src/bcdd/BCalcWrapper.py 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) -- cgit v1.2.3