def _list_best_options(self): """ Returns a list of the highest probability asks you can make, according to self.info Ties are broken in this method by the following criteria: 1. Number of guarenteed cards in half suit If player A is guarenteed to have more cards in the half suit than player B, ask them This method does not account for the "danger" of asking certain players yet """ best_asks = [] highest_prob = 0 most_hs_cards = 0 for card in card_utils.gen_all_cards(): for ID in self._get_opponents(): if self._check_legal_ask(ID, card): if self.info[ID][card] != NO: best_asks = [(ID, card)] highest_prob = self.info[ID][card] most_hs_cards = self.hs_info[ID][ card_utils.find_half_suit(card)] elif self.info[ID][card] == highest_prob: if self.hs_info[ID][card_utils.find_half_suit( card)] > most_hs_cards: best_asks = [(ID, card)] most_hs_cards = self.hs_info[ID][ card_utils.find_half_suit(card)] elif self.hs_info[ID][card_utils.find_half_suit( card)] == most_hs_cards: best_asks.append((ID, card)) return best_asks
def _update_public_info(self, check_cards=tuple(card_utils.gen_all_cards())): self.public_info = self._update_recurse(copy.deepcopy( self.public_info), self.remaining_hs, self.public_hs_info, self.num_cards, check_cards=check_cards)
def generate_state_vector(info, hs_info, num_cards, public_info, ID_player): """ Generates a state vector from the player's information The structure of the state vector is as follows: Length: 708 (SIZE_STATES) The order of players in each section starts with the ID of the player and counts up mod 6 (example: 3, 4, 5, 0, 1, 2) The order of cards is defined in card_utils.gen_all_cards The first 54 * 6 = 324 entries are for the info dict, with the values being the same as in the info dict The next 54 * 6 = 324 entries are for the public_info dict, with the values being the same as in the public_info dict The next 9 * 6 = 54 entries are for the hs_info, with the values being the same as in the hs_info dict The last 6 entries are the number of cards each player has :param info: defined in Player class :param hs_info: defined in Player class :param num_cards: defined in Player class :param public_info: defined in Player class :param ID_player: ID of the player :return: a state vector """ player_order = list(range(6))[ID_player:] + list(range(6))[:ID_player] state = [] # Info dict for ID in player_order: for card in card_utils.gen_all_cards(): state.append(info[ID][card]) # Public Info dict for ID in player_order: for card in card_utils.gen_all_cards(): state.append(public_info[ID][card]) # Half suit info dict for ID in player_order: for hs in card_utils.gen_all_halfsuits(): state.append(hs_info[ID][hs]) # Num cards for ID in player_order: state.append(num_cards[ID]) return state
def _init_info_start_game(ID, own_cards): """ Returns the info dictionary assuming its the start of a new game Given your own cards and ID """ info = {} for player_id in range(NUM_PLAYERS): if player_id == ID: player_cards = { card: NO for card in card_utils.gen_all_cards() } for card in own_cards: player_cards[card] = YES else: player_cards = { card: UNSURE for card in card_utils.gen_all_cards() } info[player_id] = player_cards.copy() return info
def _init_public_info_start_game(): """ Returns an "empty" public info dictionary :return: a dictionary that represents the public information at the start of the game """ public_info_dict = {} for ID in range(NUM_PLAYERS): player_info = {} for card in card_utils.gen_all_cards(): player_info[card] = UNSURE public_info_dict[ID] = player_info return public_info_dict
def generate_action_number(ID_ask, ID_target, card): """ Generates an action vector from the player's action This vector is universal to all players, meaning it is independent of player ID For example, player 0 asking player 3 for a Jc will return the ] same vector as player 1 asking player 4 for a Jc This is to ensure that the model can be used universally among all players :param ID_ask: ID of player asking :param ID_target: ID of target :param card: Card being asked :return: Action number (0-161) """ player_num = (((ID_target - ID_ask) % 6) - 1)//2 card_num = list(card_utils.gen_all_cards()).index(card) return player_num * constants.DECK_SIZE + card_num
def make_optimal_ask(self): """ Returns the optimal person and card to ask Format: (target_player_id, card) """ ask_guarenteed = self._check_card_guarenteed() if ask_guarenteed: return ask_guarenteed # For now, just ask randomly if no obvious card target = self._get_opponents()[random.randint(0, 2)] valid = [] for c in card_utils.gen_all_cards(): if self._check_legal_ask(target, c): valid.append(c) return target, valid[random.randint(0, len(valid) - 1)]
def print_info(self): for ID in range(NUM_PLAYERS): if self.ID == ID: print("Player {} (You)".format(ID)) else: print("Player {}".format(ID)) known_yes = [] known_no = [] for card in card_utils.gen_all_cards(): if self.info[ID][card] == YES: known_yes.append(card) elif self.info[ID][card] == NO: known_no.append(card) print("Guarenteed to have: ") print(" ".join(known_yes)) print("Guarenteed to not have: ") print(" ".join(known_no)) print()
def test_is_consistent_all_nos_3(self): info = { ID: {card: constants.UNSURE for card in card_utils.gen_all_cards()} for ID in range(constants.NUM_PLAYERS) } hs_info = { ID: {hs: 0 for hs in card_utils.gen_all_halfsuits()} for ID in range(constants.NUM_PLAYERS) } for ID in range(constants.NUM_PLAYERS): info[ID]["2h"] = constants.NO self.assertTrue( Player._is_consistent( info, ["8J"], hs_info, {ID: 1 for ID in range(constants.NUM_PLAYERS)}), "Incorrectly claimed inconsistency")
def test_is_consistent_all_nos(self): info = { ID: {card: constants.UNSURE for card in card_utils.gen_all_cards()} for ID in range(constants.NUM_PLAYERS) } hs_info = { ID: {hs: 0 for hs in card_utils.gen_all_halfsuits()} for ID in range(constants.NUM_PLAYERS) } for ID in range(constants.NUM_PLAYERS): info[ID]["2h"] = constants.NO self.assertFalse( Player._is_consistent( info, ["Lh"], hs_info, {ID: 1 for ID in range(constants.NUM_PLAYERS)}), "Failed to catch duplicate cards in _is_consistent")
def test_is_consistent_duplicate_cards(self): info = { ID: {card: constants.UNSURE for card in card_utils.gen_all_cards()} for ID in range(constants.NUM_PLAYERS) } hs_info = { ID: {hs: 1 for hs in card_utils.gen_all_halfsuits()} for ID in range(constants.NUM_PLAYERS) } info[0]["2h"] = constants.YES info[1]["2h"] = constants.YES self.assertFalse( Player._is_consistent( info, list(card_utils.gen_all_halfsuits()), hs_info, {ID: 9 for ID in range(constants.NUM_PLAYERS)}), "Failed to catch duplicate cards in _is_consistent")
def test_is_consistent_hs(self): info = { ID: {card: constants.UNSURE for card in card_utils.gen_all_cards()} for ID in range(constants.NUM_PLAYERS) } hs_info = { ID: {hs: 0 for hs in card_utils.gen_all_halfsuits()} for ID in range(constants.NUM_PLAYERS) } hs_info[1]["Hc"] = 4 info[1]["9c"] = constants.NO info[1]["Tc"] = constants.NO self.assertTrue( Player._is_consistent( info, list(card_utils.gen_all_halfsuits()), hs_info, {ID: 9 for ID in range(constants.NUM_PLAYERS)}), "Incorrectly claimed inconsistency")
def _is_consistent(info_dict, remaining_hs, hs_info, num_cards, check_cards=tuple(card_utils.gen_all_cards())): """ Checks if an info dictionary is consistent. If the info dictionary has a contradiction, returns false. Otherwise returns true. Info dictionaries must follow these rules: 1. No two players can have YES for the same card 2. If the half suit has not been called yet, at least 1 player must have at least the possibility of having any card in that half suit 3. If a player has at least X cards in a half suit, they cannot have more than HS_SIZE-X cards being NO in that half suit 4. If a player has X cards in their hand, they cannot have more than DECK_SIZE-X cards being NO :param info_dict: either a public info dict or player info dict :param remaining_hs: the remaining half suits in the game :param hs_info: the half suit info :param num_cards: number of each players cards :param check_cards: cards to check for consistency :return: True if consistent, false otherwise """ # Calculate check_hs based on check_cards check_hs = set([]) for c in check_cards: hs = card_utils.find_half_suit(c) if hs not in check_hs and hs in remaining_hs: check_hs.add(hs) # Rule 1 for card in check_cards: players_yes = 0 for ID in range(NUM_PLAYERS): if info_dict[ID][card] == YES: players_yes += 1 if players_yes > 1: return False # Rule 2 for hs in check_hs: for card in card_utils.find_cards(hs): players_no = 0 for ID in range(NUM_PLAYERS): if info_dict[ID][card] == NO: players_no += 1 if players_no == NUM_PLAYERS: return False # Rule 3 for hs in check_hs: for ID in range(NUM_PLAYERS): hs_no_count = 0 for card in card_utils.find_cards(hs): if info_dict[ID][card] == NO: hs_no_count += 1 if hs_info[ID][hs] + hs_no_count > HS_SIZE: return False # Rule 4 for ID in range(NUM_PLAYERS): no_count = 0 for card in card_utils.gen_all_cards(): if info_dict[ID][card] == NO: no_count += 1 if num_cards[ID] + no_count > DECK_SIZE: return False return True