summaryrefslogtreecommitdiff
path: root/src/bcdd/DDTable.py
diff options
context:
space:
mode:
authorMichał Klichowicz <emkael@tlen.pl>2023-09-30 12:40:40 +0200
committerMichał Klichowicz <emkael@tlen.pl>2023-09-30 12:40:40 +0200
commit518bdba985a913044e84e82713a8e76f5ddd3301 (patch)
tree6757ec4d785c07e2c3f34e70b5bd7326fe7c7a2c /src/bcdd/DDTable.py
Initial import script
Diffstat (limited to 'src/bcdd/DDTable.py')
-rw-r--r--src/bcdd/DDTable.py98
1 files changed, 98 insertions, 0 deletions
diff --git a/src/bcdd/DDTable.py b/src/bcdd/DDTable.py
new file mode 100644
index 0000000..651aec3
--- /dev/null
+++ b/src/bcdd/DDTable.py
@@ -0,0 +1,98 @@
+from ctypes import c_char_p
+from copy import copy
+
+from .BCalcWrapper import BCalcWrapper
+from .Exceptions import DDTableInvalidException, FieldNotFoundException
+
+
+class DDTable(object):
+
+ def _get_empty_table(self):
+ table = []
+ row = [-1] * 5
+ for col in range(0, 4):
+ table.append(copy(row))
+ return table
+
+ def _validate_table(self, table):
+ for row in table:
+ for t in row:
+ if (t > 13) or (t < 0):
+ raise DDTableInvalidException(
+ 'Invalid number of tricks: %d' % (t)
+ )
+ 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()
+ ability = self._board.get_ability()
+ abilities = self._board.validate_ability(ability)
+ for player_ability in abilities:
+ player = player_ability[0]
+ player_id = BCalcWrapper.PLAYERS.index(player)
+ denom_id = 4
+ for tricks in player_ability[1]:
+ result[player_id][denom_id] = int(tricks, 16)
+ denom_id -= 1
+ return self._validate_table(result)
+
+ def get_pbn_table(self):
+ table = self._board.get_optimum_result_table()
+ parsed_table = self._board.validate_optimum_result_table(table)
+ result = self._get_empty_table()
+ for line_match in parsed_table:
+ 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)
+ result[player_id][denom_id] = tricks
+ return self._validate_table(result)
+
+ def get_dd_table(self, show_banner=True):
+ try:
+ return self.get_jfr_table()
+ except FieldNotFoundException:
+ try:
+ return self.get_pbn_table()
+ except FieldNotFoundException:
+ return self.get_bcalc_table(show_banner)
+
+ def print_table(self, dd_table):
+ print('\t' + '\t'.join(BCalcWrapper.DENOMINATIONS))
+ for i in range(0, 4):
+ print('%s%s' % (
+ self._wrapper.PLAYERS[i],
+ ''.join(['\t' + str(tricks) for tricks in dd_table[i]])))