Spaces:
Running
Running
vincentlui
commited on
Commit
•
de49ebf
1
Parent(s):
1175a09
dds
Browse files- app.py +6 -5
- dds_util.py +120 -0
- hand_record.py +139 -67
- libdds.so +3 -0
- pbn_util.py +101 -6
app.py
CHANGED
@@ -36,7 +36,6 @@ css = ".output_img {display:block; margin-left: auto; margin-right: auto}"
|
|
36 |
model = CardDetectionModel()
|
37 |
|
38 |
def predict(image_path, top_hand_idx):
|
39 |
-
print(top_hand_idx)
|
40 |
start = timer()
|
41 |
df = None
|
42 |
try:
|
@@ -84,8 +83,8 @@ def save_file(df, cache_dir, files, board_no):
|
|
84 |
files.append(file_path)
|
85 |
return files, files, d
|
86 |
|
87 |
-
def create_hand_record(files):
|
88 |
-
file_path = create_hand_record_pdf(files)
|
89 |
return file_path
|
90 |
|
91 |
with gr.Blocks(css=custom_css) as demo:
|
@@ -144,12 +143,14 @@ with gr.Blocks(css=custom_css) as demo:
|
|
144 |
|
145 |
with gr.Tab('Hand Record'):
|
146 |
with gr.Row():
|
147 |
-
with gr.
|
148 |
tab2_upload_file = gr.Files(interactive=True)
|
|
|
|
|
149 |
tab2_submit_button = gr.Button('Create Hand Record',variant="primary")
|
150 |
tab2_download_file = gr.File(interactive=False)
|
151 |
|
152 |
-
tab2_submit_button.click(create_hand_record, tab2_upload_file, tab2_download_file)
|
153 |
|
154 |
a2.add([a1,b1])
|
155 |
a3.click(predict, [a1, a_top], [b1])
|
|
|
36 |
model = CardDetectionModel()
|
37 |
|
38 |
def predict(image_path, top_hand_idx):
|
|
|
39 |
start = timer()
|
40 |
df = None
|
41 |
try:
|
|
|
83 |
files.append(file_path)
|
84 |
return files, files, d
|
85 |
|
86 |
+
def create_hand_record(files, event, site):
|
87 |
+
file_path = create_hand_record_pdf(files, event=event, site=site)
|
88 |
return file_path
|
89 |
|
90 |
with gr.Blocks(css=custom_css) as demo:
|
|
|
143 |
|
144 |
with gr.Tab('Hand Record'):
|
145 |
with gr.Row():
|
146 |
+
with gr.Column():
|
147 |
tab2_upload_file = gr.Files(interactive=True)
|
148 |
+
tab2_event = gr.Textbox(max_lines=1, placeholder='Event name', label='Event')
|
149 |
+
tab2_site = gr.Textbox(max_lines=1, placeholder='Site name', label='Site')
|
150 |
tab2_submit_button = gr.Button('Create Hand Record',variant="primary")
|
151 |
tab2_download_file = gr.File(interactive=False)
|
152 |
|
153 |
+
tab2_submit_button.click(create_hand_record, [tab2_upload_file, tab2_event, tab2_site], tab2_download_file)
|
154 |
|
155 |
a2.add([a1,b1])
|
156 |
a3.click(predict, [a1, a_top], [b1])
|
dds_util.py
ADDED
@@ -0,0 +1,120 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import ctypes
|
2 |
+
from ctypes import POINTER, Structure, byref, c_char, c_int, c_uint
|
3 |
+
import os
|
4 |
+
import sys
|
5 |
+
|
6 |
+
|
7 |
+
"""
|
8 |
+
Adopted from Peter Ellington's PBNCreator https://github.com/Britwizard/PBNCreator
|
9 |
+
"""
|
10 |
+
DDS_HANDS = 4
|
11 |
+
DDS_SUITS = 5
|
12 |
+
DDS_STRAINS = 5
|
13 |
+
|
14 |
+
dcardSuit = ["S", "H", "D", "C", "NT"]
|
15 |
+
convert_table1={'a':'10','b':'11','c':'12','d':'13'}
|
16 |
+
convert_table2={'10':'a','11':'b','12':'c','13':'d'}
|
17 |
+
vulnerability= ["None","NS","EW","All","NS","EW","All","None","EW","All","None","NS","All","None","NS","EW"]
|
18 |
+
dealerlist=["N","E","S","W"]
|
19 |
+
dd_dealer_point = ["N","S","E","W"]
|
20 |
+
dd_suit=["NT","S","H","D","C"]
|
21 |
+
dd_suit_wide=["NT"," S"," H"," D"," C"]
|
22 |
+
|
23 |
+
|
24 |
+
class ddTableDeal(Structure):
|
25 |
+
_fields_ = [("cards", c_uint * DDS_HANDS * DDS_SUITS)]
|
26 |
+
|
27 |
+
class ddTableResults(Structure):
|
28 |
+
# _fields_ = [("resTable", c_int * DDS_STRAINS * DDS_HANDS)]
|
29 |
+
_fields_ = [("resTable", c_int * DDS_HANDS * DDS_STRAINS)]
|
30 |
+
|
31 |
+
class ddTableDealPBN(Structure):
|
32 |
+
_fields_ = [("cards", c_char * 80)]
|
33 |
+
|
34 |
+
class ddTableResults(Structure):
|
35 |
+
# _fields_ = [("resTable", c_int * DDS_STRAINS * DDS_HANDS)]
|
36 |
+
_fields_ = [("resTable", c_int * DDS_HANDS * DDS_STRAINS)]
|
37 |
+
|
38 |
+
tableDealPBN = ddTableDealPBN()
|
39 |
+
table = ddTableResults()
|
40 |
+
|
41 |
+
dll_name = DLL = None
|
42 |
+
if os.name == "posix":
|
43 |
+
dll_name = "libdds.so"
|
44 |
+
DLL = ctypes.CDLL
|
45 |
+
|
46 |
+
if dll_name:
|
47 |
+
#dll_path = os.path.join(os.path.dirname(os.path.abspath(__file__)),dll_name)
|
48 |
+
dll_path = os.path.join(os.getcwd(),dll_name)
|
49 |
+
#dll_path='C:\\Users\\peter\\Python\\PBNGenerator\\dds-64.dll'
|
50 |
+
if dll_name and os.path.exists(dll_path):
|
51 |
+
dll = DLL(dll_path)
|
52 |
+
dll.CalcDDtable.argtypes = [ddTableDeal, POINTER(ddTableResults)]
|
53 |
+
dll.ErrorMessage.argtypes = [c_int, POINTER(c_char)]
|
54 |
+
|
55 |
+
if os.name == "posix":
|
56 |
+
dll.SetMaxThreads(0)
|
57 |
+
def _check_dll(name):
|
58 |
+
return
|
59 |
+
|
60 |
+
else:
|
61 |
+
def _check_dll(name):
|
62 |
+
raise Exception(f"Unable to load DDS; {name} is not available")
|
63 |
+
|
64 |
+
|
65 |
+
def errorMessage(res):
|
66 |
+
msg = ctypes.create_string_buffer(80)
|
67 |
+
dll.ErrorMessage(res, msg)
|
68 |
+
result_len = ctypes.c_size_t(len(msg))
|
69 |
+
return msg[:result_len.value]
|
70 |
+
|
71 |
+
def calcDDtablePBN(tableDealPBN):
|
72 |
+
myTable = ctypes.pointer(table)
|
73 |
+
res = dll.CalcDDtablePBN(tableDealPBN, myTable)
|
74 |
+
if res != 1:
|
75 |
+
line = errorMessage(res)
|
76 |
+
raise Exception("DDS error: {}".format(line.decode("utf-8")))
|
77 |
+
return myTable
|
78 |
+
|
79 |
+
def get_ddstable(pbn):
|
80 |
+
tableDealPBN.cards = pbn
|
81 |
+
table = calcDDtablePBN(tableDealPBN)
|
82 |
+
all = { "N" : {}, "S" : {}, "E" : {}, "W" : {} }
|
83 |
+
# below doesn't work, why?
|
84 |
+
#all = dict.fromkeys(["N","S","E","W"], {})
|
85 |
+
# print(all)
|
86 |
+
for suit in range(0, DDS_SUITS):
|
87 |
+
all["N"][dcardSuit[suit]] = table.contents.resTable[suit][0]
|
88 |
+
all["S"][dcardSuit[suit]] = table.contents.resTable[suit][2]
|
89 |
+
all["E"][dcardSuit[suit]] = table.contents.resTable[suit][1]
|
90 |
+
all["W"][dcardSuit[suit]] = table.contents.resTable[suit][3]
|
91 |
+
return all
|
92 |
+
|
93 |
+
def get_dd_tricks(deal):
|
94 |
+
byte_board=deal.encode('utf-8') # ddstable requires the board description to be in byte format
|
95 |
+
dd_array = get_ddstable(byte_board)
|
96 |
+
double_dummy_tricks=''
|
97 |
+
for point in dd_array:
|
98 |
+
for suit in dd_suit:
|
99 |
+
temp = dd_array[point][suit]
|
100 |
+
value=str(temp)
|
101 |
+
if temp > 9:
|
102 |
+
value=convert_table2[value]
|
103 |
+
double_dummy_tricks = double_dummy_tricks + value
|
104 |
+
return(double_dummy_tricks)
|
105 |
+
|
106 |
+
"""
|
107 |
+
End
|
108 |
+
"""
|
109 |
+
|
110 |
+
DD_DIRECTIONS = ['N', 'S', 'E', 'W']
|
111 |
+
DD_SUITS = ['NT', 'S', 'H', 'D', 'C']
|
112 |
+
def get_result_table(pbn_deal_string):
|
113 |
+
dd_tricks_string = get_dd_tricks(pbn_deal_string)
|
114 |
+
s = '[OptimumResultTable "Declarer;Denomination\\2R;Result\\2R"]\n'
|
115 |
+
for i, char in enumerate(dd_tricks_string):
|
116 |
+
direction = DD_DIRECTIONS[i//5]
|
117 |
+
suit = DD_SUITS[i%5]
|
118 |
+
tricks = int(char, 16)
|
119 |
+
s += f'{direction} {suit} {tricks}\n'
|
120 |
+
return s
|
hand_record.py
CHANGED
@@ -2,84 +2,156 @@ from fpdf import FPDF
|
|
2 |
import tempfile
|
3 |
import os
|
4 |
import bridgebots
|
5 |
-
from pbn_util import merge_pbn, parse_pbn
|
6 |
|
7 |
DEALER_LIST = ['N', 'E', 'S', 'W']
|
8 |
VULNERABILITY_LIST = ["None","NS","EW","All","NS","EW","All","None","EW","All","None","NS","All","None","NS","EW"]
|
9 |
SUITS = [bridgebots.Suit.SPADES, bridgebots.Suit.HEARTS, bridgebots.Suit.DIAMONDS, bridgebots.Suit.CLUBS]
|
10 |
SUIT_SYMBOLS = ['♠','♥','♦','♣']
|
11 |
|
12 |
-
def create_hand_record_pdf(pbn_paths):
|
13 |
-
filepath_merged_pbn = merge_pbn(pbn_paths)
|
14 |
-
results = parse_pbn(filepath_merged_pbn)
|
15 |
-
fd,fn = tempfile.mkstemp(".pdf")
|
16 |
-
pdf = FPDF()
|
17 |
-
|
18 |
-
pdf.add_page()
|
19 |
-
pdf.add_font('times2', style='', fname='times.ttf')
|
20 |
-
pdf.set_font("times2", "", 8)
|
21 |
-
pdf.c_margin = 0.1
|
22 |
-
table_config = {
|
23 |
-
'borders_layout':'NONE',
|
24 |
-
'col_widths':3,
|
25 |
-
'line_height':pdf.font_size + 0.5,
|
26 |
-
'align': 'L',
|
27 |
-
'text_align': 'L',
|
28 |
-
'first_row_as_headings': False,
|
29 |
-
}
|
30 |
-
|
31 |
-
start_x,start_y = 10,10
|
32 |
-
table_size = 45, 48
|
33 |
-
table_margin = 2
|
34 |
-
for i, result in enumerate(results):
|
35 |
-
deal = result[0]
|
36 |
-
board_no = int(result[1][0]['Board'])
|
37 |
-
page_i = i % 20
|
38 |
-
if (i % 20 == 0) and (i != 0):
|
39 |
-
pdf.add_page()
|
40 |
-
row_idx = page_i // 4
|
41 |
-
col_idx = page_i % 4
|
42 |
-
x = start_x + (table_size[0] + 2 * table_margin + 1) * col_idx
|
43 |
-
y = start_y + (table_size[1] + 2 * table_margin + 1) * row_idx
|
44 |
-
|
45 |
-
top_left = x - table_margin, y - table_margin
|
46 |
-
top_right = x + table_size[0] + table_margin, y - table_margin
|
47 |
-
bottom_left = x - table_margin, y + table_size[1] + table_margin
|
48 |
-
bottom_right = x + table_size[0] + table_margin, y + table_size[1] + table_margin
|
49 |
-
pdf.set_xy(x,y)
|
50 |
-
|
51 |
-
pdf.line(*top_left, *bottom_left)
|
52 |
-
pdf.line(*top_left, * top_right)
|
53 |
-
pdf.line(*top_right, *bottom_right)
|
54 |
-
pdf.line(*bottom_left, *bottom_right)
|
55 |
-
|
56 |
-
dealer = DEALER_LIST[(board_no-1) % 4]
|
57 |
-
vul = VULNERABILITY_LIST[(board_no-1) % 16]
|
58 |
-
with pdf.table(**table_config) as table:
|
59 |
-
row = table.row()
|
60 |
-
row.cell(f'{board_no}\n{dealer}/{vul}', colspan=6, rowspan=4, align='C', v_align='C')
|
61 |
-
for i, (suit, values) in enumerate(zip(SUIT_SYMBOLS, get_values(deal, 'N'))):
|
62 |
-
if i!=0:
|
63 |
-
row = table.row()
|
64 |
-
print_suit_values(pdf,row,suit,values)
|
65 |
|
66 |
-
|
67 |
-
|
68 |
-
|
69 |
-
|
70 |
-
|
71 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
72 |
|
73 |
-
|
|
|
74 |
|
75 |
-
|
76 |
-
|
77 |
-
|
78 |
-
|
79 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
80 |
row = table.row()
|
81 |
-
|
|
|
|
|
|
|
|
|
82 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
83 |
pdf.output(fn)
|
84 |
return fn
|
85 |
|
|
|
2 |
import tempfile
|
3 |
import os
|
4 |
import bridgebots
|
5 |
+
from pbn_util import merge_pbn, parse_pbn, parse_dds_table
|
6 |
|
7 |
DEALER_LIST = ['N', 'E', 'S', 'W']
|
8 |
VULNERABILITY_LIST = ["None","NS","EW","All","NS","EW","All","None","EW","All","None","NS","All","None","NS","EW"]
|
9 |
SUITS = [bridgebots.Suit.SPADES, bridgebots.Suit.HEARTS, bridgebots.Suit.DIAMONDS, bridgebots.Suit.CLUBS]
|
10 |
SUIT_SYMBOLS = ['♠','♥','♦','♣']
|
11 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
12 |
|
13 |
+
suit_symbols = ['♠','♥','♦','♣']
|
14 |
+
|
15 |
+
class PDF(FPDF):
|
16 |
+
def __init__(self, event, site, *args, **kwargs):
|
17 |
+
super().__init__(*args, **kwargs)
|
18 |
+
self.event = event
|
19 |
+
self.site = site
|
20 |
+
|
21 |
+
def header(self) -> None:
|
22 |
+
# Setting font: helvetica bold 15
|
23 |
+
self.set_font("helvetica", "B", 12)
|
24 |
+
# Moving cursor to the right:
|
25 |
+
self.cell(80, 5, self.event)
|
26 |
+
# Printing title:
|
27 |
+
self.cell(80, 5, self.site, align="C", new_x="LMARGIN",
|
28 |
+
new_y="NEXT",)
|
29 |
+
# Performing a line break:
|
30 |
+
self.ln(5)
|
31 |
+
|
32 |
+
def footer(self) -> None:
|
33 |
+
return super().footer()
|
34 |
+
|
35 |
+
def print_boards(self, results):
|
36 |
+
self.add_page()
|
37 |
+
self.add_font('times2', style='', fname='times.ttf')
|
38 |
+
self.set_font("times2", "", 10)
|
39 |
+
self.c_margin = 0.1
|
40 |
+
table_config = {
|
41 |
+
'borders_layout':'NONE',
|
42 |
+
'col_widths':3,
|
43 |
+
'line_height':self.font_size + 0.4,
|
44 |
+
'align': 'L',
|
45 |
+
'text_align': 'L',
|
46 |
+
'first_row_as_headings': False,
|
47 |
+
}
|
48 |
+
|
49 |
+
start_x,start_y = 9, self.y
|
50 |
+
table_size = 45, 46
|
51 |
+
table_margin = 2
|
52 |
+
for i, result in enumerate(results):
|
53 |
+
deal = result[0]
|
54 |
+
board_no = int(result[1][0]['Board'])
|
55 |
+
dds_tricks_table = result[1][0]['dds_tricks_table']
|
56 |
+
|
57 |
+
page_i = i % 20
|
58 |
+
if (i % 20 == 0) and (i != 0):
|
59 |
+
self.add_page()
|
60 |
+
row_idx = page_i // 4
|
61 |
+
col_idx = page_i % 4
|
62 |
+
x = start_x + (table_size[0] + 2 * table_margin + 1) * col_idx
|
63 |
+
y = start_y + (table_size[1] + 2 * table_margin + 1) * row_idx
|
64 |
+
|
65 |
+
top_left = x - table_margin, y - table_margin
|
66 |
+
top_right = x + table_size[0] + table_margin, y - table_margin
|
67 |
+
bottom_left = x - table_margin, y + table_size[1] + table_margin
|
68 |
+
bottom_right = x + table_size[0] + table_margin, y + table_size[1] + table_margin
|
69 |
+
self.set_xy(x,y)
|
70 |
+
|
71 |
+
self.line(*top_left, *bottom_left)
|
72 |
+
self.line(*top_left, * top_right)
|
73 |
+
self.line(*top_right, *bottom_right)
|
74 |
+
self.line(*bottom_left, *bottom_right)
|
75 |
+
|
76 |
+
dds_top_left = x - table_margin, y + 35 - 1
|
77 |
+
dds_top_right = x - table_margin + 18, y + 35 - 1
|
78 |
+
dds_bottom_right = x - table_margin + 18, y + table_size[1] + table_margin
|
79 |
+
self.line(*dds_top_left, *dds_top_right)
|
80 |
+
self.line(*dds_top_right, *dds_bottom_right)
|
81 |
|
82 |
+
dealer = DEALER_LIST[(board_no-1) % 4]
|
83 |
+
vul = VULNERABILITY_LIST[(board_no-1) % 16]
|
84 |
|
85 |
+
self.set_font_size(10)
|
86 |
+
with self.table(**table_config) as table:
|
87 |
+
row = table.row()
|
88 |
+
self.set_font_size(16)
|
89 |
+
row.cell(str(board_no), colspan=6, rowspan=2, align='C', v_align='C', padding=(2,0,0,0))
|
90 |
+
self.set_font_size(10)
|
91 |
+
for i, (suit, values) in enumerate(zip(suit_symbols, get_values(deal, 'N'))):
|
92 |
+
if i!=0:
|
93 |
+
row = table.row()
|
94 |
+
if i == 2:
|
95 |
+
row.cell(f'{dealer} / {vul}', colspan=6, rowspan=2, align='C', v_align='C')
|
96 |
+
print_suit_values(self,row,suit,values)
|
97 |
+
|
98 |
+
# row = table.row()
|
99 |
+
# row = table.row()
|
100 |
+
for i, (suit, values1, values2) in enumerate(zip(suit_symbols, get_values(deal, 'W'), get_values(deal, 'E'))):
|
101 |
row = table.row()
|
102 |
+
row.cell('', colspan=1, rowspan=1)
|
103 |
+
print_suit_values(self, row, suit, values1)
|
104 |
+
row.cell('',colspan=3)
|
105 |
+
|
106 |
+
print_suit_values(self, row, suit, values2)
|
107 |
|
108 |
+
# row = table.row()
|
109 |
+
row = table.row()
|
110 |
+
row.cell('', colspan=6, rowspan=4)
|
111 |
+
for i, (suit, values) in enumerate(zip(suit_symbols, get_values(deal, 'S'))):
|
112 |
+
if i!=0:
|
113 |
+
row = table.row()
|
114 |
+
print_suit_values(self, row, suit, values)
|
115 |
+
|
116 |
+
self.set_xy(x-1,y+34)
|
117 |
+
self.set_font_size(7)
|
118 |
+
dds_table_config = {
|
119 |
+
'borders_layout':'NONE',
|
120 |
+
'col_widths':2.8,
|
121 |
+
'line_height':self.font_size + 0.1,
|
122 |
+
'align': 'L',
|
123 |
+
'text_align': 'L',
|
124 |
+
'first_row_as_headings': False,
|
125 |
+
'padding': 0.1
|
126 |
+
}
|
127 |
+
with self.table(**dds_table_config) as table:
|
128 |
+
row = table.row()
|
129 |
+
row.cell('')
|
130 |
+
self.set_font_size(5)
|
131 |
+
row.cell('NT', align='C')
|
132 |
+
self.set_font_size(7)
|
133 |
+
row.cell(suit_symbols[0], align='C')
|
134 |
+
row.cell(suit_symbols[1], align='C')
|
135 |
+
row.cell(suit_symbols[2], align='C')
|
136 |
+
row.cell(suit_symbols[3], align='C')
|
137 |
+
row = table.row()
|
138 |
+
for i in range(4):
|
139 |
+
if i!=0:
|
140 |
+
row = table.row()
|
141 |
+
row.cell(dds_tricks_table[i*5][0], align='C')
|
142 |
+
row.cell(dds_tricks_table[i*5][2], align='C')
|
143 |
+
row.cell(dds_tricks_table[i*5+1][2], align='C')
|
144 |
+
row.cell(dds_tricks_table[i*5+2][2], align='C')
|
145 |
+
row.cell(dds_tricks_table[i*5+3][2], align='C')
|
146 |
+
row.cell(dds_tricks_table[i*5+4][2], align='C')
|
147 |
+
|
148 |
+
|
149 |
+
def create_hand_record_pdf(pbn_paths, event, site):
|
150 |
+
filepath_merged_pbn = merge_pbn(pbn_paths)
|
151 |
+
results = parse_pbn(filepath_merged_pbn)
|
152 |
+
fd,fn = tempfile.mkstemp(".pdf")
|
153 |
+
pdf = PDF(event, site)
|
154 |
+
pdf.print_boards(results)
|
155 |
pdf.output(fn)
|
156 |
return fn
|
157 |
|
libdds.so
ADDED
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
1 |
+
version https://git-lfs.github.com/spec/v1
|
2 |
+
oid sha256:f38db5d391d6d1f9046af221e517b6359bbe536ad55907eb37253ef7eccfaaa0
|
3 |
+
size 495992
|
pbn_util.py
CHANGED
@@ -6,18 +6,95 @@ from datetime import datetime
|
|
6 |
import pandas as pd
|
7 |
import tempfile
|
8 |
from collections import Counter
|
|
|
|
|
9 |
|
10 |
|
11 |
DEALER_LIST = ['N', 'E', 'S', 'W']
|
12 |
VULNERABILITY_LIST = ["None","NS","EW","All","NS","EW","All","None","EW","All","None","NS","All","None","NS","EW"]
|
13 |
SUITS = [bridgebots.Suit.SPADES, bridgebots.Suit.HEARTS, bridgebots.Suit.DIAMONDS, bridgebots.Suit.CLUBS]
|
14 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
15 |
def parse_single_pbn_record(record_strings):
|
16 |
"""
|
17 |
:param record_strings: One string per line of a single PBN deal record
|
18 |
:return: Deal and BoardRecord corresponding to the PBN record
|
19 |
"""
|
20 |
-
record_dict =
|
21 |
try:
|
22 |
deal = bridgebots.pbn.from_pbn_deal(record_dict["Dealer"], record_dict["Vulnerable"], record_dict["Deal"])
|
23 |
except KeyError as e:
|
@@ -69,9 +146,10 @@ def create_single_pbn_string(
|
|
69 |
deal += ' '.join(
|
70 |
['.'.join(data[col]) for col in data.columns[1:]]
|
71 |
) # sss.hhh.ddd.ccc sss.hhh.ddd.ccc......
|
|
|
72 |
|
73 |
file = ''
|
74 |
-
file += ("%This
|
75 |
file += f'[Event "{event}"]\n'
|
76 |
file += f'[Site "{site}"]\n'
|
77 |
file += f'[Date "{date_print}"]\n'
|
@@ -79,7 +157,8 @@ def create_single_pbn_string(
|
|
79 |
file += f'[Dealer "{dealer}"]\n'
|
80 |
file += f'[Vulnerable "{vulnerability}"]\n'
|
81 |
file += f'[Deal "{deal}"]\n'
|
82 |
-
|
|
|
83 |
return file
|
84 |
|
85 |
def merge_pbn(pbn_paths):
|
@@ -96,7 +175,7 @@ def merge_pbn(pbn_paths):
|
|
96 |
with open(fd, 'w') as f:
|
97 |
for i, (k,v) in enumerate(ordered_board_dict.items()):
|
98 |
if i != 0:
|
99 |
-
f.write('\n
|
100 |
f.write(v)
|
101 |
return fn
|
102 |
|
@@ -107,7 +186,6 @@ def validate_pbn(pbn_string):
|
|
107 |
except AssertionError:
|
108 |
raise ValueError('Everyone should have 13 cards')
|
109 |
except Exception as e:
|
110 |
-
print('test')
|
111 |
raise Exception(e)
|
112 |
hands = deal.hands
|
113 |
duplicated = set()
|
@@ -134,4 +212,21 @@ def validate_pbn(pbn_string):
|
|
134 |
err_msg += '{direction.name} has {num_cards} cards. '
|
135 |
|
136 |
if err_msg:
|
137 |
-
raise ValueError(err_msg)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
6 |
import pandas as pd
|
7 |
import tempfile
|
8 |
from collections import Counter
|
9 |
+
from dds_util import get_result_table
|
10 |
+
from typing import List, Dict
|
11 |
|
12 |
|
13 |
DEALER_LIST = ['N', 'E', 'S', 'W']
|
14 |
VULNERABILITY_LIST = ["None","NS","EW","All","NS","EW","All","None","EW","All","None","NS","All","None","NS","EW"]
|
15 |
SUITS = [bridgebots.Suit.SPADES, bridgebots.Suit.HEARTS, bridgebots.Suit.DIAMONDS, bridgebots.Suit.CLUBS]
|
16 |
|
17 |
+
|
18 |
+
def _build_record_dict(record_strings: List[str]) -> Dict:
|
19 |
+
"""
|
20 |
+
Parse the pbn line by line. When a block section like "Auction" or "Play" is encountered, collect all the content of
|
21 |
+
the block into a single entry
|
22 |
+
:param record_strings: List of string lines for a single board
|
23 |
+
:return: A dictionary mapping keys from the pbn to strings or other useful values (e.g. list of strings for the
|
24 |
+
bidding record)
|
25 |
+
"""
|
26 |
+
record_dict = {}
|
27 |
+
# Janky while loop to handle non bracketed lines
|
28 |
+
i = 0
|
29 |
+
while i < len(record_strings):
|
30 |
+
record_string = record_strings[i]
|
31 |
+
if not (record_string.startswith("[") or record_string.startswith("{")):
|
32 |
+
i += 1
|
33 |
+
continue
|
34 |
+
if record_string.startswith("{"):
|
35 |
+
commentary = ""
|
36 |
+
while i < len(record_strings):
|
37 |
+
record_string = record_strings[i]
|
38 |
+
if record_string.startswith("["):
|
39 |
+
break
|
40 |
+
commentary += record_string + " "
|
41 |
+
i += 1
|
42 |
+
record_dict["Commentary"] = commentary.strip()
|
43 |
+
continue
|
44 |
+
if record_string.startswith("[") and "]" not in record_string:
|
45 |
+
while "]" not in record_string:
|
46 |
+
i += 1
|
47 |
+
record_string = record_string + record_strings[i]
|
48 |
+
record_string = record_string.replace("[", "").replace("]", "")
|
49 |
+
key, value = record_string.split(maxsplit=1)
|
50 |
+
value = value.replace('"', "")
|
51 |
+
if key == "Note":
|
52 |
+
number, message = value.split(":", maxsplit=1)
|
53 |
+
key = key + "_" + number
|
54 |
+
value = message
|
55 |
+
record_dict[key] = value
|
56 |
+
if key == "Auction":
|
57 |
+
auction_record = []
|
58 |
+
i += 1
|
59 |
+
while i < len(record_strings):
|
60 |
+
auction_str = record_strings[i]
|
61 |
+
if "[" in auction_str:
|
62 |
+
break
|
63 |
+
auction_record.extend(auction_str.split())
|
64 |
+
i += 1
|
65 |
+
record_dict["bidding_record"] = auction_record
|
66 |
+
|
67 |
+
elif key == "Play":
|
68 |
+
play_record = []
|
69 |
+
i += 1
|
70 |
+
while i < len(record_strings):
|
71 |
+
play_str = record_strings[i]
|
72 |
+
if "[" in play_str or play_str == "*":
|
73 |
+
break
|
74 |
+
play_record.append(play_str.split())
|
75 |
+
i += 1
|
76 |
+
record_dict["play_record"] = play_record
|
77 |
+
elif key == "OptimumResultTable":
|
78 |
+
dds_tricks_table = []
|
79 |
+
i += 1
|
80 |
+
while i < len(record_strings):
|
81 |
+
dds_str = record_strings[i]
|
82 |
+
if "[" in dds_str or dds_str == "*":
|
83 |
+
break
|
84 |
+
dds_tricks_table.append(dds_str.split())
|
85 |
+
i += 1
|
86 |
+
print(dds_tricks_table)
|
87 |
+
record_dict["dds_tricks_table"] = dds_tricks_table
|
88 |
+
else:
|
89 |
+
i += 1
|
90 |
+
return record_dict
|
91 |
+
|
92 |
def parse_single_pbn_record(record_strings):
|
93 |
"""
|
94 |
:param record_strings: One string per line of a single PBN deal record
|
95 |
:return: Deal and BoardRecord corresponding to the PBN record
|
96 |
"""
|
97 |
+
record_dict = _build_record_dict(record_strings)
|
98 |
try:
|
99 |
deal = bridgebots.pbn.from_pbn_deal(record_dict["Dealer"], record_dict["Vulnerable"], record_dict["Deal"])
|
100 |
except KeyError as e:
|
|
|
146 |
deal += ' '.join(
|
147 |
['.'.join(data[col]) for col in data.columns[1:]]
|
148 |
) # sss.hhh.ddd.ccc sss.hhh.ddd.ccc......
|
149 |
+
dd_tricks = get_result_table(deal)
|
150 |
|
151 |
file = ''
|
152 |
+
file += ("%This PBN was generated by Bridge Hand Scanner\n")
|
153 |
file += f'[Event "{event}"]\n'
|
154 |
file += f'[Site "{site}"]\n'
|
155 |
file += f'[Date "{date_print}"]\n'
|
|
|
157 |
file += f'[Dealer "{dealer}"]\n'
|
158 |
file += f'[Vulnerable "{vulnerability}"]\n'
|
159 |
file += f'[Deal "{deal}"]\n'
|
160 |
+
file += dd_tricks
|
161 |
+
file += '\n'
|
162 |
return file
|
163 |
|
164 |
def merge_pbn(pbn_paths):
|
|
|
175 |
with open(fd, 'w') as f:
|
176 |
for i, (k,v) in enumerate(ordered_board_dict.items()):
|
177 |
if i != 0:
|
178 |
+
f.write('\n')
|
179 |
f.write(v)
|
180 |
return fn
|
181 |
|
|
|
186 |
except AssertionError:
|
187 |
raise ValueError('Everyone should have 13 cards')
|
188 |
except Exception as e:
|
|
|
189 |
raise Exception(e)
|
190 |
hands = deal.hands
|
191 |
duplicated = set()
|
|
|
212 |
err_msg += '{direction.name} has {num_cards} cards. '
|
213 |
|
214 |
if err_msg:
|
215 |
+
raise ValueError(err_msg)
|
216 |
+
|
217 |
+
def parse_dds_table(raw_list):
|
218 |
+
table = []
|
219 |
+
row = []
|
220 |
+
tempchar = ''
|
221 |
+
for x in raw_list:
|
222 |
+
if len(x) > 0:
|
223 |
+
tempchar += x[0]
|
224 |
+
else:
|
225 |
+
if len(tempchar) > 0:
|
226 |
+
row.append(tempchar)
|
227 |
+
tempchar = ''
|
228 |
+
|
229 |
+
if len(row) == 3:
|
230 |
+
table.append(row)
|
231 |
+
row = []
|
232 |
+
return table
|