Skip to content

Commit

Permalink
Squashed commit of the following:
Browse files Browse the repository at this point in the history
commit bb93107
Author: lucbellicaud <[email protected]>
Date:   Thu Apr 6 10:34:59 2023 +0200

    fix variable name in defensive claim

commit d3ad675
Author: Zhivko Draganov <[email protected]>
Date:   Wed Apr 5 11:56:47 2023 +0300

    feat: Add two more examples for alert bid

commit 99459aa
Merge: b623450 ed57bb8
Author: lucbellicaud <[email protected]>
Date:   Wed Apr 5 10:59:40 2023 +0200

    Merge branch 'staging' of https://github.com/Seven-of-Di/ben into staging

commit b623450
Author: lucbellicaud <[email protected]>
Date:   Wed Apr 5 10:59:37 2023 +0200

    manual alerte + None instead of ""

commit ed57bb8
Author: Zhivko Draganov <[email protected]>
Date:   Tue Apr 4 11:06:39 2023 +0300

    fix: Use the bang notation for displaying suits

commit 0618355
Author: lucbellicaud <[email protected]>
Date:   Mon Apr 3 18:13:11 2023 +0200

    Delete parenthesis and usually

commit 5e8bbeb
Author: Zhivko Draganov <[email protected]>
Date:   Mon Apr 3 17:20:47 2023 +0300

    feat: Change generation script to use emojis

commit 517ca61
Author: lucbellicaud <[email protected]>
Date:   Mon Apr 3 13:11:55 2023 +0200

    Squashed commit of the following:

    commit 759ae4d
    Author: lucbellicaud <[email protected]>
    Date:   Mon Apr 3 12:35:33 2023 +0200

        alert symbol instead of abbrevation

        Debug option for playing card
        sybol instead of !+suit for alert

    commit 0fab7b6
    Author: lucbellicaud <[email protected]>
    Date:   Fri Mar 31 15:45:12 2023 +0200

        count points

commit e81355f
Merge: c46fa5d 6c5f059
Author: Zhivko Draganov <[email protected]>
Date:   Fri Mar 31 11:13:22 2023 +0300

    Merge pull request #4 from Seven-of-Di/develop

    chore: Develop

commit c46fa5d
Merge: 23b7c8a 4b22d6f
Author: Zhivko Draganov <[email protected]>
Date:   Thu Mar 30 16:47:16 2023 +0300

    Merge pull request #3 from Seven-of-Di/develop

    chore: Develop
  • Loading branch information
lucbellicaud committed Apr 6, 2023
1 parent 759ae4d commit 2ba39b8
Show file tree
Hide file tree
Showing 8 changed files with 187 additions and 110 deletions.
5 changes: 5 additions & 0 deletions examples/alert_bid_1.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"dealer": "W",
"vuln": "None",
"auction": ["1D", "PASS", "1S", "PASS", "2H", "PASS", "3S"]
}
5 changes: 5 additions & 0 deletions examples/alert_bid_2.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"dealer": "W",
"vuln": "None",
"auction": ["1D", "PASS", "1S", "PASS", "2H", "PASS", "4C"]
}
6 changes: 6 additions & 0 deletions src/bots.py
Original file line number Diff line number Diff line change
Expand Up @@ -309,6 +309,12 @@ def lead(self, auction):
expected_tricks=np.mean(tricks[:, i, 0]),
p_make_contract=np.mean(tricks[:, i, 1])
))
print(Card_.get_from_52(deck52.card32to52(card_i)))
print((tricks[:, i, 0]))
print(np.mean((tricks[:, i, 0])))
print(tricks[:, i, 1])
print(np.mean(tricks[:, i, 1]))
pass
candidate_cards = sorted(
candidate_cards, key=lambda c: c.p_make_contract)

Expand Down
4 changes: 2 additions & 2 deletions src/claim_dds.py
Original file line number Diff line number Diff line change
Expand Up @@ -149,11 +149,11 @@ def check_declarer_claim(dd_solved, claim_direction: Direction, trick_leader: Di
def check_defensive_claim(dd_solved, claim_direction: Direction, trick_leader: Direction, claim: int, current_trick: List[Card_], possible_tricks_left: int) -> bool:
claimer_in_hand = claim_direction == trick_leader.offset(
len(current_trick))
claimer_partner__in_hand = claim_direction == trick_leader.offset(
claimer_partner_in_hand = claim_direction == trick_leader.offset(
len(current_trick)+2)
if claimer_in_hand:
return True if any(all([i >= claim for i in card_res]) for card_res in dd_solved.values()) else False
if claimer_is_partner:
if claimer_partner_in_hand:
return True if all(all([i >= claim for i in card_res]) for card_res in dd_solved.values()) else False
else:
return True if all(all([i <= possible_tricks_left-claim for i in card_res]) for card_res in dd_solved.values()) else False
Expand Down
9 changes: 7 additions & 2 deletions src/create_alerts_database.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,9 @@
import hashlib
from typing import Dict
from alert_utils import BidPosition, BidExplanations
from generate_alerts import generate_alert_from_bid_explanation
from generate_alerts import generate_alert_from_bid_explanation,manual_alert
import pickle5 as pickle
# import pickle
import numpy as np
import json
from itertools import islice
Expand Down Expand Up @@ -32,8 +33,12 @@ def chunks(data):

for chunk in chunks(dict_of_alerts):
for bid_position in chunk:
alert = generate_alert_from_bid_explanation(chunk[bid_position])
alert = manual_alert(bid_position.sequence)
if alert != None:
db[bid_position.to_hex()] = alert
continue

alert = generate_alert_from_bid_explanation(chunk[bid_position])
if alert != None:
db[bid_position.to_hex()] = alert

Expand Down
122 changes: 77 additions & 45 deletions src/generate_alerts.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,9 @@
from nn.models import Models
import conf
import os
from utils import Direction, PlayerHand, Suit, Diag
from utils import Direction, PlayerHand, Suit, Diag, BiddingSuit
from alert_utils import BidExplanations, BidPosition
from SequenceAtom import Bid
import pickle


Expand All @@ -20,6 +21,48 @@
diags = [Diag.generate_random() for _ in range(100)]


def manual_alert(seq_str: List[str]) -> str | None:
if len(seq_str) == seq_str.count("PASS"):
return "0-11 hcp"
if not(seq_str[-1] != "PASS" and len(seq_str)-seq_str.count("PASS") == 1):
return None
bid = Bid.from_str(seq_str[-1])
if not bid <= Bid.from_str("3S"):
return None
if bid == Bid(1, BiddingSuit.CLUBS):
return "Better minor, 12+hcp,3+!C"
if bid == Bid(1, BiddingSuit.DIAMONDS):
return "Better minor, 12+hcp,3+!D"
if bid == Bid(1, BiddingSuit.HEARTS):
return "5th major, 12+hcp,5+!H"
if bid == Bid(1, BiddingSuit.SPADES):
return "5th major, 12+hcp,5+!S"
if bid == Bid(1, BiddingSuit.NO_TRUMP):
return "15-17 hcp balanced"
if bid == Bid(2, BiddingSuit.CLUBS):
return "Any strong hand, 21+hcp"
if bid == Bid(2, BiddingSuit.DIAMONDS):
return "Natural preempt, 6-10 hcp, 6!D"
if bid == Bid(2, BiddingSuit.HEARTS):
return "Natural preempt, 6-10 hcp, 6!H"
if bid == Bid(2, BiddingSuit.SPADES):
return "Natural preempt, 6-10 hcp, 6!S"
if bid == Bid(2, BiddingSuit.NO_TRUMP):
return "20-21 hcp balanced"
if bid == Bid(2, BiddingSuit.SPADES):
return "Natural preempt, 6-10 hcp, 6!S"
if bid == Bid(3, BiddingSuit.CLUBS):
return "Natural preempt, 6-10 hcp, 7!C"
if bid == Bid(3, BiddingSuit.DIAMONDS):
return "Natural preempt, 6-10 hcp, 7!D"
if bid == Bid(3, BiddingSuit.HEARTS):
return "Natural preempt, 6-10 hcp, 7!H"
if bid == Bid(3, BiddingSuit.SPADES):
return "Natural preempt, 6-10 hcp, 7!S"

raise Exception


def bid_and_extract_hand(diag: Diag, dict_of_alerts: Dict[BidPosition, BidExplanations], verbose=False):
# vuls = [bool(random.getrandbits(1)), bool(random.getrandbits(1))]
vuls = [False, False]
Expand Down Expand Up @@ -54,8 +97,8 @@ def bid_and_extract_hand(diag: Diag, dict_of_alerts: Dict[BidPosition, BidExplan
def generate_alerts(check_point: int):
dict_of_alerts: Dict[BidPosition, BidExplanations] = {}

# with open('dev_alerts', 'rb') as f:
# dict_of_alerts = pickle.load(f)
with open('C:/Users/lucbe/OneDrive/Documents/Bridge/alerts.pickle', 'rb') as f:
dict_of_alerts = pickle.load(f)

while True:
start = time.time()
Expand All @@ -65,7 +108,7 @@ def generate_alerts(check_point: int):
end = time.time()
print('{} diags alerts saved in {} seconds. Current number of sequence {}'.format(
check_point, end-start, len(dict_of_alerts)))
with open('dev_alerts', 'wb') as f:
with open('C:/Users/lucbe/OneDrive/Documents/Bridge/alerts.pickle', 'wb') as f:
pickle.dump(dict_of_alerts, f, pickle.HIGHEST_PROTOCOL)


Expand All @@ -82,16 +125,12 @@ def generate_usual_alert_from_dict(dic: Dict, ascending: bool) -> int:


def generete_hcp_alert(bid_explanation: BidExplanations) -> str:
strict_minimum_hcp = bid_explanation.min_hcp
strict_maximum_hcp = bid_explanation.max_hcp
usual_minimum_hcp = generate_usual_alert_from_dict(
bid_explanation.hcp_distribution, ascending=True)
usual_maximum_hcp = generate_usual_alert_from_dict(
bid_explanation.hcp_distribution, ascending=False)
minimum_text = str(strict_minimum_hcp) if strict_minimum_hcp == usual_minimum_hcp else "({}){}".format(
strict_minimum_hcp, usual_minimum_hcp)
maximum_text = str(strict_maximum_hcp) if strict_maximum_hcp == usual_maximum_hcp else "{}({})".format(
usual_maximum_hcp, strict_maximum_hcp)
minimum_text = str(usual_minimum_hcp)
maximum_text = str(usual_maximum_hcp)
return "{}-{}hcp".format(minimum_text, maximum_text)


Expand All @@ -112,26 +151,12 @@ def generate_suit_length_alert_as_dict(bid_explanation: BidExplanations, suit: S
}


def print_suit_max_length(usual_max_length: int, strict_max_length: int, usual_min_length: int, strict_min_length: int, suit: Suit) -> str:
strict_min_length = 0 if strict_min_length == 1 else strict_min_length
def print_suit_max_length(usual_max_length: int, usual_min_length: int, suit: Suit) -> str:
usual_min_length = 0 if usual_min_length == 1 else usual_min_length
max_parenthesis = usual_max_length != strict_max_length
min_parenthesis = usual_min_length != strict_min_length
if min_parenthesis and max_parenthesis:
return "({}){}-{}({}){}|".format(strict_min_length, usual_min_length, usual_max_length, strict_max_length, suit.symbol())
if min_parenthesis:
return "({}){}-{}{}|".format(strict_min_length, usual_min_length, usual_max_length, suit.symbol())
if max_parenthesis:
return "{}-{}({}){}|".format(usual_min_length, usual_max_length, strict_max_length, suit.symbol())
if usual_max_length == usual_min_length:
return "{}{}|".format(strict_max_length, suit.symbol())
return "{}-{}{}|".format(strict_min_length, strict_max_length, suit.symbol())


def print_suit_min_length(usual_min_length: int, strict_min_length: int, suit: Suit) -> str:
min_parenthesis = usual_min_length != strict_min_length
if min_parenthesis:
return "({}){}+{}|".format(strict_min_length, usual_min_length, suit.symbol())
return "{}-{}{}|".format(usual_min_length, usual_max_length, suit.symbol())


def print_suit_min_length(usual_min_length: int, suit: Suit) -> str:
return "{}+{}|".format(usual_min_length, suit.symbol())


Expand All @@ -148,12 +173,12 @@ def generate_suits_length_alert(bid_explanation: BidExplanations) -> str:
"usual_min_length"] >= 4 or suits_length_alert_as_dict[s]["strict_min_length"] >= 3
if print_max_length:
short_suits.append(s)
suits_text += print_suit_max_length(usual_max_length=suits_length_alert_as_dict[s]["usual_max_length"], strict_max_length=suits_length_alert_as_dict[s]["strict_max_length"],
usual_min_length=suits_length_alert_as_dict[s]["usual_min_length"], strict_min_length=suits_length_alert_as_dict[s]["strict_min_length"], suit=s)
suits_text += print_suit_max_length(
usual_max_length=suits_length_alert_as_dict[s]["usual_max_length"], usual_min_length=suits_length_alert_as_dict[s]["usual_min_length"], suit=s)
elif print_min_length:
long_suits.append(s)
suits_text += print_suit_min_length(
usual_min_length=suits_length_alert_as_dict[s]["usual_min_length"], strict_min_length=suits_length_alert_as_dict[s]["strict_min_length"], suit=s)
usual_min_length=suits_length_alert_as_dict[s]["usual_min_length"], suit=s)
suits_text = suits_text[:-1] if suits_text else suits_text

player_hands = [PlayerHand.from_pbn(pbn_hand)
Expand All @@ -163,38 +188,35 @@ def generate_suits_length_alert(bid_explanation: BidExplanations) -> str:
two_suiter_proba = two_suiter_mask.count(True)/len(two_suiter_mask)

if two_suiter_proba > 0.95:
is_sure = two_suiter_proba == 1
if len(long_suits) == 2:
return "{}two suiter, with {}".format("Usually " if not is_sure else "", suits_text)
return "{}".format(suits_text)
if len(long_suits) == 1 and len(short_suits) == 1 and suits_length_alert_as_dict[short_suits[0]]["usual_max_length"] <= 1:
return "{}splinter, with {}".format("Usually " if not (is_sure and suits_length_alert_as_dict[short_suits[0]]["strict_max_length"] <= 1) else "", suits_text)
return "Splinter : {}".format(suits_text)
if len(long_suits) == 1:
return "{}two suiter, with {} and another unkown suit".format("Usually " if not is_sure else "", suits_text)
return "Two suiter : {} and another".format(suits_text)
else:
return "{}unknown two suiter, {}".format("Usually " if not is_sure else "", suits_text)
return "Unknown two suiter".format()

six_card_mask = [player_hand.ordered_pattern(
)[0] >= 6 for player_hand in player_hands]
six_card_proba = six_card_mask.count(True)/len(six_card_mask)

if six_card_proba >= 0.95:
is_sure = six_card_proba == 1
if len(long_suits) == 1:
return "{}one suiter, with {}".format("Usually " if not is_sure else "", suits_text)
return "{}".format(suits_text)
else:
return "{}unknown one suiter {}".format("Usually " if not is_sure else "", suits_text)
return "Unknown one suiter {}".format(suits_text)

five_card_mask = [player_hand.ordered_pattern(
)[0] >= 5 for player_hand in player_hands]
five_card_proba = five_card_mask.count(True)/len(five_card_mask)
if five_card_proba == 0.95 and len(long_suits) == 0:
is_sure = five_card_proba == 1
return "{}an unkown five card + suit {}".format("Usually " if not is_sure else "", suits_text)
return "An unkown five card + suit {}".format("suits_text")

return "{}".format(suits_text)


def generate_alert_from_bid_explanation(bid_explanation: BidExplanations) -> str:
def generate_alert_from_bid_explanation(bid_explanation: BidExplanations) -> str|None:
if bid_explanation.n_samples >= 5:
# print("Number of samples : {}".format(bid_explanation.n_samples))
hcp_text = generete_hcp_alert(bid_explanation=bid_explanation)
Expand All @@ -203,8 +225,18 @@ def generate_alert_from_bid_explanation(bid_explanation: BidExplanations) -> str
hcp_text, "\n" if length_text else "", length_text)
return final_text

return ""
return None


def request_from_pickle_file(str_sequence: List[str]):
with open('C:/Users/lucbe/OneDrive/Documents/Bridge/alerts.pickle', 'rb') as f:
dict_of_alerts: Dict[BidPosition, BidExplanations] = pickle.load(f)

position = BidPosition(str_sequence, [False, False])
print(dict_of_alerts[position])


if __name__ == "__main__":
generate_alerts(100)
generate_alerts(1000)
# request_from_pickle_file(["2N","PASS","3S"])
# print(manual_alert(["PASS", "PASS", "1S"]))
72 changes: 43 additions & 29 deletions src/tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,11 @@
from Board import Board
import git

NEW_BIDDING_TIME : List[float] = [0,0]
OLD_BIDDING_TIME : List[float] = [0,0]
NEW_CARD_TIME : List[float] = [0,0]
OLD_CARD_TIME: List[float] = [0,0]
NEW_BIDDING_TIME: List[float] = [0, 0]
OLD_BIDDING_TIME: List[float] = [0, 0]
NEW_CARD_TIME: List[float] = [0, 0]
OLD_CARD_TIME: List[float] = [0, 0]


def from_lin_to_request(lin_str: str, card_to_remove_after: Card_ | None = None, bid_to_remove_after: str | None = None):
if card_to_remove_after is not None and bid_to_remove_after is not None:
Expand Down Expand Up @@ -105,7 +106,7 @@ def bidding_el_to_pbn(el: str):
turn_to_play = leader

return json.dumps({
"hand": diag.hands[turn_to_play].print_as_pbn(),
"hand": diag.hands[turn_to_play].print_as_pbn() if turn_to_play != dealer.offset(2) else diag.hands[declarer].print_as_pbn(),
"dummy_hand": diag.hands[declarer.offset(2)].print_as_pbn(),
"dealer": dealer.abbreviation(),
"vuln": vul_str,
Expand Down Expand Up @@ -147,28 +148,27 @@ def play_full_deal(deal: Deal):


def send_request(type_of_action: str, data: Dict, direction: Direction, open_room: bool):
new_ben_called = (open_room and direction in [Direction.NORTH, Direction.SOUTH]) or (not open_room and direction in [Direction.EAST,Direction.WEST])
new_ben_called = (open_room and direction in [Direction.NORTH, Direction.SOUTH]) or (
not open_room and direction in [Direction.EAST, Direction.WEST])
port = "http://localhost:{}".format("8081" if new_ben_called else "8082")
start = time.time()
res = requests.post('{}/{}'.format(port, type_of_action), json=data)
request_time = time.time()-start
if type_of_action=="play_card" :
if new_ben_called :
NEW_CARD_TIME[0]+=request_time
NEW_CARD_TIME[1]+=1
else :
OLD_CARD_TIME[0]+=request_time
OLD_CARD_TIME[1]+=1
elif type_of_action=="place_bid" :
if new_ben_called :
NEW_BIDDING_TIME[0]+=request_time
NEW_BIDDING_TIME[1]+=1
else :
OLD_BIDDING_TIME[0]+=request_time
OLD_BIDDING_TIME[1]+=1



if type_of_action == "play_card":
if new_ben_called:
NEW_CARD_TIME[0] += request_time
NEW_CARD_TIME[1] += 1
else:
OLD_CARD_TIME[0] += request_time
OLD_CARD_TIME[1] += 1
elif type_of_action == "place_bid":
if new_ben_called:
NEW_BIDDING_TIME[0] += request_time
NEW_BIDDING_TIME[1] += 1
else:
OLD_BIDDING_TIME[0] += request_time
OLD_BIDDING_TIME[1] += 1

print(res.json())
return res.json()

Expand Down Expand Up @@ -334,19 +334,33 @@ def count_average_hcp() :
print(len(boards))
print(average_hcp_per_dir)

def count_average_hcp():
with open("./test_data/10K_ranked_deals.pbn") as f:
boards = f.read().strip("\n").split("\n\n")
deals: List[Deal] = [Deal.from_pbn(board) for board in boards]
hcp_per_dir = {dir: 0 for dir in Direction}
for deal in deals:
for dir in Direction:
hcp_per_dir[dir] += deal.diag.hands[dir].hcp()
average_hcp_per_dir = {dir: total_hcp /
len(deals) for dir, total_hcp in hcp_per_dir.items()}
print(len(boards))
print(average_hcp_per_dir)


def run_tm_btwn_ben_versions(force_same_sequence: bool = False, force_same_lead: bool = False, force_same_card_play: bool = False):
with open("./test_data/test_data.pbn") as f:
boards = f.read().strip("\n").split("\n\n")
deals: List[Deal] = [Deal.from_pbn(board) for board in boards]



for deal in deals:
deal.diag = Diag.generate_random()
pbn = run_deal_on_both_rooms(
deal, force_same_sequence, force_same_lead, force_same_card_play)
print("New Ben times average : bidding : {},carding : {}".format(NEW_BIDDING_TIME[0]/NEW_BIDDING_TIME[1],NEW_CARD_TIME[0]/NEW_CARD_TIME[1]))
print("Old Ben times average : bidding : {},carding : {}".format(OLD_BIDDING_TIME[0]/OLD_BIDDING_TIME[1],OLD_CARD_TIME[0]/OLD_CARD_TIME[1]))
print("New Ben times average : bidding : {},carding : {}".format(
NEW_BIDDING_TIME[0]/NEW_BIDDING_TIME[1], NEW_CARD_TIME[0]/NEW_CARD_TIME[1]))
print("Old Ben times average : bidding : {},carding : {}".format(
OLD_BIDDING_TIME[0]/OLD_BIDDING_TIME[1], OLD_CARD_TIME[0]/OLD_CARD_TIME[1]))
with open("./test_data/{}.pbn".format("First test table"), "a") as f:
f.write("\n{}".format(pbn))

Expand Down Expand Up @@ -387,8 +401,8 @@ def compare_two_tests(set_of_boards_1: List[Board], set_of_boards_2: List[Board]
# compare_two_tests(load_test_pbn("avant.pbn"),
# load_test_pbn("après.pbn"))
# load_test_pbn("c4f380988fc67c0fe6e5f4bc5502d67a3b45d2c0.pbn")
link = r"https://play.intobridge.com/hand?lin=pn%7CBen,Ben,Ben,Etha%7Cmd%7C1SAQ643HAD532CKT64,SK2HJ4DQJ98CAQ853,SJT97HQ632DAK6C92,S85HKT9875DT74CJ7%7Cah%7CBoard%203%7Cmb%7C1S%7Cmb%7C2C%7Cmb%7C3C%7Cmb%7Cp%7Cmb%7C4S%7Cmb%7Cp%7Cmb%7Cp%7Cmb%7Cp%7Cpc%7CDQ%7Cpc%7CDK%7Cpc%7CD4%7Cpc%7CD3%7Cpc%7CH2%7Cpc%7CH5%7Cpc%7CHA%7Cpc%7CHJ%7Cpc%7CD2%7Cpc%7CD8%7Cpc%7CDA%7Cpc%7CD7%7Cpc%7CH3%7Cpc%7CH9%7Cpc%7CS6%7Cpc%7CH4%7Cpc%7CSA%7Cpc%7CSK%7Cpc%7CS7%7Cpc%7CS5%7Cpc%7CS3%7Cpc%7CS2%7Cpc%7CS9%7Cpc%7CS8%7Cpc%7CH6%7Cpc%7CH7%7Cpc%7CS4%7Cpc%7CC3%7Cpc%7CD5%7Cpc%7CD9%7Cpc%7CD6%7Cpc%7CDT%7Cpc%7CHK%7Cpc%7CSQ%7Cpc%7CC5%7Cpc%7CHQ%7Cpc%7CC6%7Cpc%7CCQ%7Cpc%7CC2%7Cpc%7CC7%7Cpc%7CCA%7Cpc%7CC9%7Cpc%7CCJ%7Cpc%7CC4%7Cpc%7CC8%7Cpc%7CST%7Cpc%7CH8%7Cpc%7CCT%7Cpc%7CSJ%7Cpc%7CHT%7Cpc%7CCK%7Cpc%7CDJ%7Cmc%7C10%7Csv%7Ce%7C"
print(from_lin_to_request(link, Card_.from_str("SA")))
link = r"https://play.intobridge.com/hand?lin=pn%7CBen,Etha,Ben,Ben%7Cmd%7C3SAJ986HAK5D9764C8,SQ543H97642D5CT76,SKT2HQJTD82CAKQJ9,S7H83DAKQJT3C5432%7Cah%7CBoard%201%7Cmb%7C1N%7Cmb%7C2C%7Cmb%7C2H%7Cmb%7Cp%7Cmb%7C2S%7Cmb%7Cp%7Cmb%7C3D%7Cmb%7Cp%7Cmb%7C3S%7Cmb%7Cp%7Cmb%7C3N%7Cmb%7Cp%7Cmb%7Cp%7Cmb%7Cp%7Cpc%7CC4%7Cpc%7CC8%7Cpc%7CCT%7Cpc%7CCQ%7Cpc%7CCA%7Cpc%7CC2%7Cpc%7CD4%7Cpc%7CC7%7Cpc%7CCK%7Cpc%7CC3%7Cpc%7CD6%7Cpc%7CC6%7Cpc%7CCJ%7Cpc%7CC5%7Cpc%7CD7%7Cpc%7CH2%7Cpc%7CC9%7Cpc%7CD3%7Cpc%7CD9%7Cpc%7CH4%7Cpc%7CHT%7Cpc%7CH3%7Cpc%7CH5%7Cpc%7CH6%7Cpc%7CHJ%7Cpc%7CH8%7Cpc%7CHK%7Cpc%7CH7%7Cpc%7CS9%7Cpc%7CS3%7Cpc%7CSK%7Cpc%7CS7%7Cpc%7CHQ%7Cpc%7CDT%7Cpc%7CHA%7Cpc%7CH9%7Cpc%7CSA%7Cpc%7CS4%7Cpc%7CS2%7Cpc%7CDJ%7Cpc%7CSJ%7Cpc%7CSQ%7Cpc%7CST%7Cpc%7CDK%7Cpc%7CD5%7Cpc%7CD2%7Cpc%7CDQ%7Cpc%7CS6%7Cpc%7CDA%7Cpc%7CS8%7Cpc%7CS5%7Cpc%7CD8%7Cmc%7C10%7C"
print(from_lin_to_request(link, Card_.from_str("CA")))

# print(from_lin_to_request(link, None))
# count_average_hcp()
Loading

0 comments on commit 2ba39b8

Please sign in to comment.