File size: 4,281 Bytes
743c074
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
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