from bridgebots import Suit, Direction, Card, Rank from bridgebots.pbn import from_pbn_deal from pbn_util import pbn_deal_string from collections import Counter DEALER_LIST = ['N', 'E', 'S', 'W'] VULNERABILITY_LIST = ["None","NS","EW","All","NS","EW","All","None","EW","All","None","NS","All","None","NS","EW"] SUITS = [Suit.SPADES, Suit.HEARTS, Suit.DIAMONDS, Suit.CLUBS] RANKS = [r.abbreviation() for r in Rank] def get_vulnerabilty_from_board_number(board_no: int) -> str: # Get vulnerability from board number return VULNERABILITY_LIST[(board_no-1) % 16] def get_dealer_from_board_number(board_no: int) -> str: return DEALER_LIST[(board_no-1) % 4] def validate_dataframe(df) -> None: """ validate submitted dataframe before saving pbn """ # Check column names to be NESW missing_direction = [direction.abbreviation() for direction in Direction if not direction.abbreviation() in df.columns] assert len(missing_direction) == 0, f"Expect directions in table to be {','.join([d.abbreviation() for d in Direction])}" + \ f" missing {','.join(missing_direction)}" err_msg = '' # Check each direction to have 13 cards invalid_num_cards_direction = [direction.name for direction in Direction if len((''.join(df[direction.abbreviation()].tolist()))) != 13] if invalid_num_cards_direction: err_msg += f'Expect 13 cards in each hand, received invalid number of cards in {",".join(invalid_num_cards_direction)}. ' # Check card values in RANKS cards = [] invalid_values = set() for direction in Direction: for ranks, suit in zip(df[direction.abbreviation()], SUITS): for r in ranks: try: Rank.from_str(r) except: invalid_values.add(r) assert not invalid_values, f'Expect card values in {",".join(RANKS)}, received {invalid_values}' for direction in Direction: for ranks, suit in zip(df[direction.abbreviation()], SUITS): cards.extend([Card(rank=Rank.from_str(v), suit=suit) for v in ranks]) # Check duplicated and missing cards duplicated, missing = get_invalid_cards(cards) if len(duplicated) > 0: err_msg += f'Duplicated cards: {duplicated}. ' if len(missing) > 0: err_msg += f'Missing cards: {missing}. ' assert not err_msg, err_msg def df_info(df): # missing_direction = [direction.abbreviation() for direction in Direction if not direction.abbreviation() in df.columns] num_cards_direction = {direction.name: len((''.join(df[direction.abbreviation()].tolist()))) for direction in Direction} invalid_values = set() for direction in Direction: for ranks, suit in zip(df[direction.abbreviation()], SUITS): for r in ranks: try: Rank.from_str(r) except: invalid_values.add(r) if invalid_values: return f'Expect card values in {",".join(RANKS)}, received {invalid_values}' cards = [] for direction in Direction: for ranks, suit in zip(df[direction.abbreviation()], SUITS): cards.extend([Card(rank=Rank.from_str(v), suit=suit) for v in ranks]) # Check duplicated and missing cards duplicated, missing = get_invalid_cards(cards) if (not duplicated) and (not missing) and not [True for n in num_cards_direction.values() if n!=13]: return f'It looks good!' print_info = f'Number of cards: {num_cards_direction} \n' print_info += f'Duplicated cards: {",".join(sorted(map(Card.__repr__,duplicated)))} \n' print_info += f'Missing cards: {",".join(sorted(map(Card.__repr__,missing)))}' return print_info def get_invalid_cards(cards: list[Card]) -> tuple[set, set]: """Get duplicated and missing cards""" counter_cards = Counter(cards) duplicated = {c for c, cnt in counter_cards.items() if cnt > 1} missing = {Card(s,r) for r in Rank for s in Suit if not Card(s,r) in counter_cards.keys()} return duplicated, missing def roll_direction(df, idx): new_df = df.copy(deep=True) for i, direction in enumerate(DEALER_LIST): new_direction = DEALER_LIST[(idx+i)%4] new_df[direction] = df[new_direction] return new_df