Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

chore: Develop #6

Merged
merged 26 commits into from
Apr 20, 2023
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
0fab7b6
count points
lucbellicaud Mar 31, 2023
759ae4d
alert symbol instead of abbrevation
lucbellicaud Apr 3, 2023
2ba39b8
Squashed commit of the following:
lucbellicaud Apr 6, 2023
ca9b168
DDS lead
lucbellicaud Apr 12, 2023
54ff53b
feat: Add otel into development docker-compose
zdraganov Apr 13, 2023
10399da
Fix small cards dds 2
lucbellicaud Apr 13, 2023
4b6006d
Merge branch 'develop' of https://github.com/Seven-of-Di/ben into dev…
lucbellicaud Apr 13, 2023
9a1e5f0
test lead
lucbellicaud Apr 13, 2023
2660d36
Merge branch 'staging' into develop
lucbellicaud Apr 13, 2023
80bd6eb
feat: Intrument Ben with Tracing for PlayCard flow
zdraganov Apr 13, 2023
481b0db
feat: Add integrnal loadtest package for simplify running loadtests
zdraganov Apr 14, 2023
6c2fc07
feat: Update the README.md for the loadtest package
zdraganov Apr 14, 2023
421ac69
fixed big bug : not taking pb into account
lucbellicaud Apr 17, 2023
103ae6b
Merge branch 'develop' of https://github.com/Seven-of-Di/ben into dev…
lucbellicaud Apr 17, 2023
421e035
Update transform_play_card.py
lucbellicaud Apr 17, 2023
760dd47
feat: Reconstructure the code and add more samples
zdraganov Apr 18, 2023
52269b4
optimize sample ordering
lucbellicaud Apr 18, 2023
97aa583
Clean up variables names + time tricking
lucbellicaud Apr 18, 2023
1f007e0
feat: Change to use hypercorn
zdraganov Apr 18, 2023
f8c6873
Add some tracers
lucbellicaud Apr 18, 2023
0814048
Merge branch 'develop' of https://github.com/Seven-of-Di/ben into dev…
lucbellicaud Apr 18, 2023
3297b54
feat: Make the number of leading samples configurable
zdraganov Apr 19, 2023
d1073b6
Less redoubles + fix tracer of dds
lucbellicaud Apr 19, 2023
e111c6e
Merge branch 'develop' of https://github.com/Seven-of-Di/ben into dev…
lucbellicaud Apr 19, 2023
4ce4bbe
back to 100 samples
lucbellicaud Apr 19, 2023
4cbd2cb
feat: Include game information in the traces attributes
zdraganov Apr 19, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
DDS lead
  • Loading branch information
lucbellicaud committed Apr 12, 2023
commit ca9b1688b51e3789c0dd499c6551f37b7d19511b
88 changes: 41 additions & 47 deletions src/bots.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
from utils import Card_, multiple_list_comparaison, Direction, PlayerHand, BiddingSuit, Rank, Suit, TOTAL_DECK, Diag
from human_carding import play_real_card
from PlayRecord import PlayRecord
from Sequence import Sequence

DDS = ddsolver.DDSolver()

Expand Down Expand Up @@ -297,49 +298,54 @@ def __init__(self, vuln, hand_str, models):
self.sd_model = models.sd_model

def lead(self, auction):
contract = bidding.get_contract(auction)
if contract is None :
raise Exception("Contract should not be None if asking for a lead")

level = int(contract[0])
tricks_to_defeat_contract = 13-(6+level)+1
strain = bidding.get_strain_i(contract)

lead_card_indexes, lead_softmax = self.get_lead_candidates(auction)
accepted_samples, tricks = self.simulate_outcomes(
accepted_samples = self.get_accepted_samples(
4096, auction, lead_card_indexes)

candidate_cards = []
samples = []

base_diag = Diag({d:PlayerHand({s:[] for s in Suit}) for d in Direction})
base_diag.hands[Direction.WEST] = PlayerHand.from_pbn(self.hand_str)
samples = [deepcopy(base_diag) for i in range(min(100, accepted_samples.shape[0]))]
pips = {s:[] for s in Suit}
for card in TOTAL_DECK :
if card not in base_diag.hands[Direction.WEST].cards and card.rank<=Rank.SEVEN :
pips[card.suit].append(card.rank)
for i in range(min(100, accepted_samples.shape[0])):
temp_pips = deepcopy(pips)
samples[i].hands[Direction.NORTH] = PlayerHand.from_pbn(hand_to_str(accepted_samples[i, 0, :]),temp_pips)
samples[i].hands[Direction.EAST] = PlayerHand.from_pbn(hand_to_str(accepted_samples[i, 1, :]),temp_pips)
samples[i].hands[Direction.SOUTH] = PlayerHand.from_pbn(hand_to_str(accepted_samples[i, 2, :]),temp_pips)

dd_solved = DDS.solve(strain, 0, [], [diag.print_as_pbn(first_direction=Direction.WEST) for diag in samples])
dd_solved = {Card_.get_from_52(k):v for k,v in dd_solved.items()}

candidate_cards : List[CandidateCard] = []
for i, card_i in enumerate(lead_card_indexes):
x_card = str(Card.from_code(card_i, xcards=True))
card = Card_.from_str(x_card) if x_card[1]!="x" else Card_(Suit.from_str(x_card[0]),min(base_diag.hands[Direction.WEST].suits[Suit.from_str(x_card[0])]))
candidate_cards.append(CandidateCard(
card=Card.from_code(card_i, xcards=True),
insta_score=lead_softmax[0, card_i],
expected_tricks=np.mean(tricks[:, i, 0]),
p_make_contract=np.mean(tricks[:, i, 1])
p_make_contract=1-sum([1 if v>=tricks_to_defeat_contract else 0 for v in dd_solved[card]])/len(dd_solved[card]),
expected_tricks=sum((dd_solved[card]))/len(dd_solved[card])
))
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)

opening_lead = candidate_cards[0].card.code()
candidate_cards, key=lambda c: c.p_make_contract if c.p_make_contract!=None else 1)

if opening_lead % 8 == 7:
# it's a pip ~> choose a random one
pips_mask = np.array([0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1])
lefty_led_pips = self.hand52.reshape(
(4, 13))[opening_lead // 8] * pips_mask
opening_lead52 = (opening_lead // 8) * 13 + \
random.choice(np.nonzero(lefty_led_pips)[0])
else:
opening_lead52 = deck52.card32to52(opening_lead)

samples = []
for i in range(min(100, accepted_samples.shape[0])):
samples.append('%s %s %s' % (
hand_to_str(accepted_samples[i, 0, :]),
hand_to_str(accepted_samples[i, 1, :]),
hand_to_str(accepted_samples[i, 2, :]),
))
# for c in candidate_cards :
# print(c.card,c.p_make_contract)

return CardResp(
card=Card.from_code(opening_lead52),
card=candidate_cards[0].card,
candidates=candidate_cards,
samples=samples
)
Expand All @@ -363,7 +369,7 @@ def get_lead_candidates(self, auction):

return candidates, lead_softmax

def simulate_outcomes(self, n_samples, auction, lead_card_indexes):
def get_accepted_samples(self, n_samples, auction, lead_card_indexes):
contract = bidding.get_contract(auction)

decl_i = bidding.get_decl_i(contract)
Expand Down Expand Up @@ -393,19 +399,7 @@ def simulate_outcomes(self, n_samples, auction, lead_card_indexes):
X_sd[:, (32 + 5 + 3*32):] = accepted_samples[:,
2, :].reshape((n_accepted, 32))

tricks = np.zeros((n_accepted, len(lead_card_indexes), 2))

for j, lead_card_i in enumerate(lead_card_indexes):
X_sd[:, :32] = 0
X_sd[:, lead_card_i] = 1

tricks_softmax = self.sd_model.model(X_sd)

tricks[:, j, 0:1] = expected_tricks(tricks_softmax.copy())
tricks[:, j, 1:2] = p_make_contract(
contract, tricks_softmax.copy())

return accepted_samples, tricks
return accepted_samples


class CardPlayer:
Expand Down Expand Up @@ -485,7 +479,7 @@ def play_card(self, trick_i, leader_i, current_trick52, players_states, probabil

def get_cards_dd_evaluation(self, trick_i, leader_i, current_trick52, players_states, probabilities_list):

def create_diag_from_32(array_of_array_32: List[np.ndarray], pips: List[Card_]):
def create_diag_from_32(base_diag : Diag,array_of_array_32: List[np.ndarray], pips: List[Card_]):
diag = deepcopy(base_diag)
pips_as_dict = {
s: [card.rank for card in pips if card.suit == s] for s in Suit}
Expand Down Expand Up @@ -528,7 +522,7 @@ def get_base_diag() -> Diag:
low_hidden_cards = [
c for c in self.hidden_cards if c.rank <= Rank.SEVEN]
n_samples = players_states[0].shape[0]
samples_as_diag = [create_diag_from_32([players_states[j][i, trick_i, :32] for j in range(
samples_as_diag = [create_diag_from_32(base_diag,[players_states[j][i, trick_i, :32] for j in range(
4)], low_hidden_cards) for i in range(n_samples)]


Expand Down
2 changes: 1 addition & 1 deletion src/examples/OpeningLead.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -292,7 +292,7 @@
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.10.1"
"version": "3.10.2"
},
"vscode": {
"interpreter": {
Expand Down
11 changes: 6 additions & 5 deletions src/generate_alerts.py
Original file line number Diff line number Diff line change
Expand Up @@ -125,12 +125,13 @@ def generate_usual_alert_from_dict(dic: Dict, ascending: bool) -> int:


def generete_hcp_alert(bid_explanation: BidExplanations) -> str:
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(usual_minimum_hcp)
maximum_text = str(usual_maximum_hcp)
maximum_text = str(strict_maximum_hcp)
return "{}-{}hcp".format(minimum_text, maximum_text)


Expand Down Expand Up @@ -216,7 +217,7 @@ def generate_suits_length_alert(bid_explanation: BidExplanations) -> str:
return "{}".format(suits_text)


def generate_alert_from_bid_explanation(bid_explanation: BidExplanations) -> str|None:
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 @@ -228,15 +229,15 @@ def generate_alert_from_bid_explanation(bid_explanation: BidExplanations) -> str
return None


def request_from_pickle_file(str_sequence: List[str]):
def request_from_pickle_file(str_sequence: List[str]) -> BidExplanations:
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])
return (dict_of_alerts[position])


if __name__ == "__main__":
generate_alerts(1000)
# request_from_pickle_file(["2N","PASS","3S"])
# print(request_from_pickle_file(["2N"]).hcp_distribution)
# print(manual_alert(["PASS", "PASS", "1S"]))
31 changes: 12 additions & 19 deletions src/tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
OLD_BIDDING_TIME: List[float] = [0, 0]
NEW_CARD_TIME: List[float] = [0, 0]
OLD_CARD_TIME: List[float] = [0, 0]
boards_with_different_leads = []


def from_lin_to_request(lin_str: str, card_to_remove_after: Card_ | None = None, bid_to_remove_after: str | None = None):
Expand Down Expand Up @@ -50,7 +51,6 @@ def bidding_el_to_pbn(el: str):

bidding_str = lin_str.split("mb|")[1:-1]
bidding_str = [bidding_el_to_pbn(el.strip("|")) for el in bidding_str]
print(bidding_str)

play_str = lin_str.split("pc")[1:-1]
play = [s.strip("|") for s in play_str]
Expand Down Expand Up @@ -106,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() if turn_to_play != dealer.offset(2) else diag.hands[declarer].print_as_pbn(),
"hand": diag.hands[turn_to_play].print_as_pbn() if turn_to_play != declarer.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 @@ -299,9 +299,11 @@ def play_full_deal(deal: Deal, force_same_sequence: bool, force_same_lead: bool,
raise Exception("Leader shouldn't be None")

if force_same_card_play and contract == other_play_record.sequence.calculate_final_contract(dealer=deal.dealer) and other_play_record is not None:
play_record = deepcopy(other_play_record)
play_record.sequence = sequence
return play_record
if lead == other_play_record.play_record.as_unordered_one_dimension_list()[0].suit_first_str():
play_record = deepcopy(other_play_record)
play_record.sequence = sequence
return play_record
boards_with_different_leads.append(deal.board_number)

return full_card_play(deal, sequence, lead, open_room)

Expand All @@ -322,17 +324,6 @@ def run_deal_on_both_rooms(deal: Deal, force_same_sequence: bool = False, force_

return "{}\n{}".format(open_room_board.print_as_pbn(open_room=True), closed_room_board.print_as_pbn(open_room=False))

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 count_average_hcp():
with open("./test_data/10K_ranked_deals.pbn") as f:
Expand Down Expand Up @@ -361,6 +352,8 @@ def run_tm_btwn_ben_versions(force_same_sequence: bool = False, force_same_lead:
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("Boards with differents leads : {}".format(
boards_with_different_leads))
with open("./test_data/{}.pbn".format("First test table"), "a") as f:
f.write("\n{}".format(pbn))

Expand Down Expand Up @@ -396,13 +389,13 @@ def compare_two_tests(set_of_boards_1: List[Board], set_of_boards_2: List[Board]


if __name__ == "__main__":
# run_tm_btwn_ben_versions(force_same_card_play=True,force_same_lead=True)
# run_tm_btwn_ben_versions(force_same_card_play=True,force_same_sequence=True)
# tests = run_tests()
# 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,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")))
link = r"https://play.intobridge.com/hand?lin=pn|Ben,Ben,Ben,Ben|md|4SQ64HK73DKQJTCAT6,SK98753HQ8D54CK32,SJHA962D862CQJ984,SAT2HJT54DA973C75|ah|Board%2014|mb|p|mb|1N|mb|p|mb|2C|mb|p|mb|2D|mb|p|mb|2N|mb|p|mb|p|mb|p|pc|S7|pc|SJ|pc|SA|pc|S4|pc|ST|pc|S6|pc|S3|pc|C4|pc|S2|pc|SQ|pc|SK|pc|H2|pc|S9|pc|D2|pc|H4|pc|C6|pc|S8|pc|D6|pc|D3|pc|H7|pc|S5|pc|H6|pc|H5|pc|DT|pc|HQ|pc|H9|pc|HT|pc|HK|pc|DJ|pc|D5|pc|D8|pc|DA|pc|D9|pc|DQ|pc|D4|pc|C8|pc|DK|pc|C2|pc|C9|pc|D7|pc|H3|pc|H8|pc|HA|pc|HJ|pc|CQ|pc|C7|pc|CT|pc|CK|pc|C3|pc|CJ|pc|C5|pc|CA|mc|5|"
print(from_lin_to_request(link, Card_.from_str("H2")))

# print(from_lin_to_request(link, None))
# count_average_hcp()
10 changes: 7 additions & 3 deletions src/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -365,13 +365,17 @@ def from_lin(string: str) -> PlayerHand:
return PlayerHand.from_cards(cards)

@staticmethod
def from_pbn(string: str) -> PlayerHand:
def from_pbn(string: str, pips : Dict[Suit,List[Rank]]={}) -> PlayerHand:
"""Create a hand from a string with the following syntax '752.Q864.84.AT62'"""
tab_of_suit = string.split('.')
cards = []
for index, suit in enumerate(tab_of_suit):
temp = suit.replace("10", "T").replace("X", "T")
while temp.find('x') != -1:
if pips!= {} :
temp = temp.replace('x', pips[Suit(index)].pop().abbreviation(), 1)
continue

for rank in Rank:
if rank.abbreviation() in temp:
continue
Expand Down Expand Up @@ -419,8 +423,8 @@ def __repr__(self) -> str:
return f"PlayerHand({repr_str})"

def __str__(self) -> str:
suit_arrays = [[SPADES_SYMBOL], [HEARTS_SYMBOL],
[DIAMONDS_SYMBOL], [CLUBS_SYMBOL]]
suit_arrays = [["♠"], ["♥"],
["♦"], ["♣"]]
for card in sorted(self.cards, reverse=True):
suit_arrays[card.suit.value].append(repr(card))
repr_str = " ".join("".join(suit) for suit in suit_arrays)
Expand Down