def hint_first_free_card(self, round_info, player_number): original_player_number = player_number player_number = utils.next_player_number(round_info, original_player_number) while player_number is not original_player_number: if player_number is round_info.player_turn: player_hand = round_info.true_hand_info() else: player_hand = utils.get_player_hand_by_number( round_info, player_number) for card in player_hand: if card.revealed_rank is None: return ChoiceDetails( Choice.HINT, HintDetails(player_number, card.real_rank)) elif card.revealed_suit is None: return ChoiceDetails( Choice.HINT, HintDetails(player_number, card.real_suit)) player_number = utils.next_player_number(round_info, player_number) player_number = utils.next_player_number(round_info, original_player_number) player_hand = utils.get_player_hand_by_number(round_info, player_number) for card in player_hand: return ChoiceDetails(Choice.HINT, HintDetails(player_number, card.real_rank))
def check_for_forced_discard(self, round_info, player_number): if player_number is round_info.player_turn: player_hand = round_info.true_hand_info() else: player_hand = utils.get_player_hand_by_number( round_info, player_number) known = self.list_all_known_cards(round_info, player_number) remaining = utils.list_remaining_playable_cards(round_info) for card in player_hand: if known[card.real_suit][card.real_rank] > 1: return ChoiceDetails(Choice.DISCARD, card.hand_position) for card in player_hand: if remaining[card.real_suit][card.real_rank] > 1: return ChoiceDetails(Choice.DISCARD, card.hand_position) best_card_rank = -1 best_card = None for card in player_hand: if card.real_rank.value > best_card_rank: best_card = card.hand_position best_card_rank = card.real_rank.value return ChoiceDetails(Choice.DISCARD, best_card)
def play(self, round_info): possible_plays = round_info.true_hand_info().playable_cards(round_info) if len(possible_plays) == 0: return ChoiceDetails(Choice.DISCARD, choice(round_info.player_hand).hand_position) else: return ChoiceDetails(Choice.PLAY, choice(possible_plays).hand_position) """
def check_for_play(self, round_info, player_number): if player_number is round_info.player_turn: player_hand = round_info.player_hand else: player_hand = utils.get_player_hand_by_number( round_info, player_number) for card in player_hand: if card.revealed_rank is not None and card.revealed_suit is not None and \ round_info.board_state[card.revealed_suit] + 1 is card.revealed_rank.value: return ChoiceDetails(Choice.PLAY, card.hand_position) return ChoiceDetails(Choice.PLAY, random.choice(player_hand).hand_position)
def check_for_discard(self, round_info, player_number): if round_info.hints is utils.MAX_HINTS: return False if player_number is round_info.player_turn: player_hand = round_info.player_hand else: player_hand = utils.get_player_hand_by_number( round_info, player_number) unmarked = [] for card in player_hand: if card.revealed_rank is None and card.revealed_suit is None: unmarked.append(card) if len(unmarked) is 0: for card in player_hand: if card.revealed_rank is None or card.revealed_suit is None: unmarked.append(card) if len(unmarked) is 0: unmarked = player_hand return ChoiceDetails(Choice.DISCARD, random.choice(unmarked).hand_position)
def check_for_obvious_play(self, round_info, player_number): if player_number is round_info.player_turn: player_hand = round_info.player_hand else: player_hand = utils.get_player_hand_by_number(round_info, player_number) board_state_stars_align = round_info.board_state[utils.Suit.BLUE] + 1 prev = False for suit in round_info.board_state: if prev is not False and round_info.board_state[suit] is not prev: board_state_stars_align = -1 prev = round_info.board_state[suit] best_card = -1 best_card_rank = 6 for card in player_hand: if (card.is_playable(round_info) or (card.revealed_rank is not None and card.revealed_rank.value is board_state_stars_align)) and \ card.revealed_rank.value < best_card_rank: best_card = card.hand_position best_card_rank = card.revealed_rank.value if best_card >= 0: return ChoiceDetails( Choice.PLAY, best_card ) return False
def check_for_discard_tip(self, round_info, player_number, hint_pass_score=2.5, distance_to_player_multiplier=0.99): original_player_number = player_number player_number = utils.next_player_number(round_info, original_player_number) potential_discardable_ranks = {} potential_discardable_suits = {} while player_number is not original_player_number: potential_discardable_ranks[player_number] = {} potential_discardable_suits[player_number] = {} for rank in utils.Rank: potential_discardable_ranks[player_number][rank] = [] for suit in utils.Suit: potential_discardable_suits[player_number][suit] = [] player_hand = utils.get_player_hand_by_number(round_info, player_number) for card in player_hand: if round_info.board_state[card.real_suit] >= card.real_rank.value: if card.revealed_suit is None: potential_discardable_suits[player_number][card.real_suit].append(card) if card.revealed_rank is None: potential_discardable_ranks[player_number][card.real_rank].append(card) player_number = utils.next_player_number(round_info, player_number) max_player_number = 0 max_potential = 0 max_hint = 0 def check_card_potential(card, player): player_distance = player - original_player_number - 1 if player_distance < 0: player_distance += round_info.number_of_players card_potential = pow(distance_to_player_multiplier, player_distance) return card_potential for player in potential_discardable_ranks: for rank in potential_discardable_ranks[player]: potential = 0 for card in potential_discardable_ranks[player][rank]: potential += check_card_potential(card, player) if potential > max_potential: max_player_number = player max_potential = potential max_hint = rank for suit in potential_discardable_suits[player]: potential = 0 for card in potential_discardable_suits[player][suit]: potential += check_card_potential(card, player) if potential > max_potential: max_player_number = player max_potential = potential max_hint = suit if max_potential >= hint_pass_score: return ChoiceDetails( Choice.HINT, HintDetails(max_player_number, max_hint) ) return False
def check_for_play(self, round_info, player_number): if player_number is round_info.player_turn: player_hand = round_info.true_hand_info() else: player_hand = utils.get_player_hand_by_number( round_info, player_number) known = self.list_all_known_cards(round_info, player_number) best_card = -1 chain_bonus = False best_card_rank = 6 for card in player_hand: if round_info.board_state[ card.real_suit] + 1 is card.real_rank.value: new_chain = False if card.real_rank.value <= 4 and known[card.real_suit][ utils.Rank(card.real_rank.value + 1)] > 0: new_chain = True if (chain_bonus and new_chain and card.real_rank.value < best_card_rank) or \ (not chain_bonus and (new_chain or card.real_rank.value < best_card_rank)): best_card = card.hand_position best_card_rank = card.real_rank.value chain_bonus = new_chain if best_card >= 0: return ChoiceDetails(Choice.PLAY, best_card) return False
def check_card_usefulness(self, round_info, card): point_of_uselessness = self.point_of_uselessness useless = False if card.revealed_rank is None and card.revealed_suit is not None: if round_info.board_state[card.revealed_suit] == 5 or \ (point_of_uselessness[card.revealed_suit] is not None and round_info.board_state[card.revealed_suit] + 1 is point_of_uselessness[card.revealed_suit].value): useless = True if card.revealed_rank is not None and card.revealed_suit is None: useless = True for suit in utils.Suit: if round_info.board_state[suit] < card.revealed_rank.value and \ (point_of_uselessness[suit] is None or point_of_uselessness[suit].value > card.revealed_rank.value): useless = False if card.revealed_suit is not None and card.revealed_rank is not None: if round_info.board_state[card.revealed_suit] < card.revealed_rank.value and \ (point_of_uselessness[card.revealed_suit] is None or point_of_uselessness[card.revealed_suit].value > card.revealed_rank.value): useless = False else: useless = True if useless: return ChoiceDetails( Choice.DISCARD, card.hand_position ) return False
def check_for_hint(self, round_info, player_number): if round_info.hints <= 1: return False original_player_number = player_number player_number = utils.next_player_number(round_info, original_player_number) hinted_plays = {} while player_number is not original_player_number: hinted_plays[player_number] = {} for suit in utils.Suit: hinted_plays[player_number][suit] = {} for rank in utils.Rank: hinted_plays[player_number][suit][rank] = 0 player_hand = utils.get_player_hand_by_number( round_info, player_number) for card in player_hand: if round_info.board_state[card.real_suit] < card.real_rank.value and \ (card.revealed_rank is not None or card.revealed_suit is not None): hinted_plays[player_number][card.real_suit][ card.real_rank] += 1 player_number = utils.next_player_number(round_info, player_number) player_number = utils.next_player_number(round_info, original_player_number) best_hint_player = None best_hint_rank = 6 best_hint_type = None while player_number is not original_player_number: player_hand = utils.get_player_hand_by_number( round_info, player_number) for card in player_hand: already_hinted = False for player in hinted_plays: if player is not player_number and hinted_plays[player][ card.real_suit][card.real_rank] is not 0: already_hinted = True if round_info.board_state[card.real_suit] < card.real_rank.value < best_hint_rank and \ not already_hinted: if card.revealed_rank is None: best_hint_player = player_number best_hint_rank = card.real_rank.value best_hint_type = card.real_rank elif card.revealed_suit is None: best_hint_player = player_number best_hint_rank = card.real_rank.value best_hint_type = card.real_suit player_number = utils.next_player_number(round_info, player_number) if best_hint_player is not None: return ChoiceDetails(Choice.HINT, HintDetails(best_hint_player, best_hint_type)) return False
def check_for_discard(self, round_info, player_number): if player_number is round_info.player_turn: player_hand = round_info.player_hand else: player_hand = utils.get_player_hand_by_number( round_info, player_number) return ChoiceDetails(Choice.DISCARD, random.choice(player_hand).hand_position)
def check_for_hint(self, round_info, player_number): if round_info.hints is 0: return False players = list(range(0, round_info.number_of_players)) random.shuffle(players) for player in players: if player is not player_number: hand = deepcopy( utils.get_player_hand_by_number(round_info, player)) hand.shuffle() for card in hand: if random.randint(0, 1) is 0: if card.revealed_rank is None: return ChoiceDetails( Choice.HINT, HintDetails(player, card.real_rank)) elif card.revealed_suit is None: return ChoiceDetails( Choice.HINT, HintDetails(player, card.real_suit)) else: if card.revealed_suit is None: return ChoiceDetails( Choice.HINT, HintDetails(player, card.real_suit)) elif card.revealed_rank is None: return ChoiceDetails( Choice.HINT, HintDetails(player, card.real_rank)) for player in players: if player is not player_number: hand = utils.get_player_hand_by_number(round_info, player) for card in hand: return ChoiceDetails(Choice.HINT, HintDetails(player, card.real_suit))
def check_for_guess_discard(self, round_info, player_number): if player_number is round_info.player_turn: player_hand = round_info.player_hand else: player_hand = utils.get_player_hand_by_number(round_info, player_number) unmarked = [] for card in player_hand: if card.revealed_rank is None and card.revealed_suit is None: unmarked.append(card) if len(unmarked) == 0: known = utils.list_all_known_cards(round_info, player_number)[0] remaining = utils.list_remaining_playable_cards(round_info) discarded = utils.list_discarded_cards(round_info) for card in player_hand: if card.revealed_rank is None: add = True for rank in utils.Rank: if round_info.board_state[card.revealed_suit] < rank.value and \ remaining[card.revealed_suit][rank] == 1 and \ known[card.revealed_suit][rank] - discarded[card.revealed_suit][rank] == 0: add = False if add: unmarked.append(card) elif card.revealed_suit is None: add = True for suit in remaining: if round_info.board_state[suit] < card.revealed_rank.value and \ remaining[suit][card.revealed_rank] == 1 and \ known[suit][card.revealed_rank] - discarded[suit][card.revealed_rank] == 0: add = False if add: unmarked.append(card) if len(unmarked) == 0: unmarked = player_hand oldest = unmarked[0] for card in unmarked: if card.drawn_on_turn < oldest.drawn_on_turn: oldest = card return ChoiceDetails( Choice.DISCARD, oldest.hand_position )
def check_for_necessary_tip(self, round_info, player_number): if round_info.hints == 0: return False remaining = utils.list_remaining_playable_cards(round_info) next_player_hand = utils.next_player_hand(round_info, player_number) next_player_number = utils.next_player_number(round_info, player_number) discarded_position = self.check_for_guess_discard(round_info, next_player_number)[1] discarded = next_player_hand[discarded_position] if round_info.board_state[discarded.real_suit] < discarded.real_rank.value and \ remaining[discarded.real_suit][discarded.real_rank] == 1 and \ discarded.real_rank.value - round_info.board_state[discarded.real_suit] <= 1: if discarded.revealed_rank is None: return ChoiceDetails( Choice.HINT, HintDetails(utils.next_player_number(round_info, player_number), discarded.real_rank) ) else: return ChoiceDetails( Choice.HINT, HintDetails(utils.next_player_number(round_info, player_number), discarded.real_suit) ) return False
def check_for_obvious_discard(self, round_info, player_number): if player_number is round_info.player_turn: player_hand = round_info.player_hand else: player_hand = utils.get_player_hand_by_number(round_info, player_number) known = utils.list_all_known_cards(round_info)[0] discarded = utils.list_discarded_cards(round_info) remaining = utils.list_remaining_playable_cards(round_info) for card in player_hand: useless = False if card.revealed_suit is not None: useless = True future_ranks_useless = False for rank in utils.Rank: if round_info.board_state[card.revealed_suit] < rank.value: if not future_ranks_useless and remaining[card.revealed_suit][rank] - \ known[card.revealed_suit][rank] + discarded[card.revealed_suit][rank] > 0: useless = False else: future_ranks_useless = True if card.revealed_rank is not None: useless = True for suit in utils.Suit: if round_info.board_state[suit] < card.revealed_rank.value: if remaining[suit][card.revealed_rank] - known[suit][card.revealed_rank] + \ discarded[suit][card.revealed_rank] > 0: useless = False if card.revealed_suit is not None and card.revealed_rank is not None: if round_info.board_state[card.revealed_suit] >= card.revealed_rank.value: useless = True if useless: return ChoiceDetails( Choice.DISCARD, card.hand_position ) return False
def check_card_usefulness(self, round_info, card): remaining = utils.list_remaining_playable_cards(round_info) useless = False point_of_uselessness = {} for suit in utils.Suit: point_of_uselessness[suit] = None for rank in utils.Rank: if round_info.board_state[suit] < rank.value: if point_of_uselessness[suit] is None and remaining[suit][rank] == 0: point_of_uselessness[suit] = rank if card.revealed_suit is not None: if round_info.board_state[card.revealed_suit] == 5 or \ (point_of_uselessness[card.revealed_suit] is not None and round_info.board_state[card.revealed_suit] + 1 is point_of_uselessness[card.revealed_suit].value): useless = True if card.revealed_rank is not None: useless = True for suit in utils.Suit: if round_info.board_state[suit] < card.revealed_rank.value and \ (point_of_uselessness[suit] is None or point_of_uselessness[suit].value > card.revealed_rank.value): useless = False if card.revealed_suit is not None and card.revealed_rank is not None: if round_info.board_state[card.revealed_suit] < card.revealed_rank.value and \ (point_of_uselessness[card.revealed_suit] is None or point_of_uselessness[card.revealed_suit].value > card.revealed_rank.value): useless = False else: useless = True if useless: return ChoiceDetails( Choice.DISCARD, card.hand_position ) return False
def check_for_discard_tip(self, round_info, player_number, hint_pass_score=1.8, false_tip_penalty=-10.0, distance_to_player_multiplier=1.01, only_next_player=False): original_player_number = player_number player_number = utils.next_player_number(round_info, original_player_number) potential_discardable_ranks = {} potential_discardable_suits = {} while player_number is not original_player_number: potential_discardable_ranks[player_number] = {} potential_discardable_suits[player_number] = {} for rank in utils.Rank: potential_discardable_ranks[player_number][rank] = [] for suit in utils.Suit: potential_discardable_suits[player_number][suit] = [] player_hand = utils.get_player_hand_by_number(round_info, player_number) for card in player_hand: if card.revealed_suit is None: potential_discardable_suits[player_number][card.real_suit].append(card) if card.revealed_rank is None: potential_discardable_ranks[player_number][card.real_rank].append(card) player_number = utils.next_player_number(round_info, player_number) max_player_number = 0 max_potential = 0 max_hint = 0 def check_card_potential(card, player, hint_type, hint): player_distance = player - original_player_number - 1 if player_distance < 0: player_distance += round_info.number_of_players card_potential = pow(distance_to_player_multiplier, player_distance) card_with_hint = deepcopy(card) if hint_type == 'rank': card_with_hint.revealed_rank = hint else: card_with_hint.revealed_suit = hint if self.check_card_usefulness(round_info, card_with_hint) is False: card_potential += false_tip_penalty return card_potential for player in potential_discardable_ranks: player_distance = player - original_player_number - 1 if player_distance < 0: player_distance += round_info.number_of_players if player_distance == 0 or not only_next_player: for rank in potential_discardable_ranks[player]: potential = 0 for card in potential_discardable_ranks[player][rank]: potential += check_card_potential(card, player, 'rank', rank) if potential > max_potential: max_player_number = player max_potential = potential max_hint = rank for suit in potential_discardable_suits[player]: potential = 0 for card in potential_discardable_suits[player][suit]: potential += check_card_potential(card, player, 'suit', suit) if potential > max_potential: max_player_number = player max_potential = potential max_hint = suit if max_potential >= hint_pass_score: return ChoiceDetails( Choice.HINT, HintDetails(max_player_number, max_hint) ) return False
def check_for_hinted_play(self, round_info, player_number): if player_number is round_info.player_turn: player_hand = round_info.player_hand else: player_hand = utils.get_player_hand_by_number(round_info, player_number) alignment_delta = 2 max_hint_size = 10 if round_info.lives == 1: alignment_delta = 0 max_hint_size = 1 hinted_ranks = {} hinted_suits = {} for suit in utils.Suit: hinted_suits[suit] = 0 for rank in utils.Rank: hinted_ranks[rank] = 0 for x in range(0, len(player_hand)): if player_hand[x].revealed_suit is not None and player_hand[x].revealed_rank is None and \ self.card_hint_type[player_number][x] == "Play": hinted_suits[player_hand[x].revealed_suit] += 1 if player_hand[x].revealed_rank is not None and player_hand[x].revealed_suit is None and \ self.card_hint_type[player_number][x] == "Play": hinted_ranks[player_hand[x].revealed_rank] += 1 known = utils.list_all_known_cards(round_info, player_number)[0] remaining = utils.list_remaining_playable_cards(round_info) discarded = utils.list_discarded_cards(round_info) best_hint = -1 best_hint_size = max_hint_size best_alignment = 0 hint_type = None for suit in hinted_suits: if 0 < hinted_suits[suit] <= best_hint_size: rank = round_info.board_state[suit] + 1 if rank <= 5: rank_rank = utils.Rank(rank) if remaining[suit][rank_rank] - known[suit][rank_rank] + discarded[suit][rank_rank] > 0: best_hint = suit best_hint_size = hinted_suits[suit] best_alignment = 1 hint_type = 'suit' board_alignment = {} for rank in utils.Rank: board_alignment[rank] = 0 for suit in round_info.board_state: rank = round_info.board_state[suit] + 1 if rank <= 5: rank_rank = utils.Rank(rank) if remaining[suit][rank_rank] - known[suit][rank_rank] + discarded[suit][rank_rank] > 0: board_alignment[rank_rank] += 1 for rank in hinted_ranks: if 0 < board_alignment[rank] and ((0 < hinted_ranks[rank] < best_hint_size) or (0 < hinted_ranks[rank] <= best_hint_size and best_alignment < board_alignment[rank])): best_hint = rank best_hint_size = hinted_ranks[rank] best_alignment = board_alignment[rank] hint_type = 'rank' if best_hint != -1 and best_hint_size <= best_alignment + alignment_delta: for x in range(0, len(player_hand)): if hint_type == 'rank': if player_hand[x].revealed_rank is not None and player_hand[x].revealed_suit is None and \ player_hand[x].revealed_rank is best_hint and \ self.card_hint_type[player_number][x] == "Play": return ChoiceDetails( Choice.PLAY, x ) else: if player_hand[x].revealed_suit is not None and player_hand[x].revealed_rank is None and \ player_hand[x].revealed_suit is best_hint and \ self.card_hint_type[player_number][x] == "Play": return ChoiceDetails( Choice.PLAY, x ) return False
def check_for_play_tip(self, round_info, player_number, hint_pass_score=2, double_hint_multiplier=2.3, distance_to_playable_multiplier=0.5, distance_to_player_multiplier=0.99, lower_rank_multiplier=1.1): if round_info.hints <= 1: return False original_player_number = player_number player_number = utils.next_player_number(round_info, original_player_number) hinted_plays = {} while player_number is not original_player_number: hinted_plays[player_number] = {} for suit in utils.Suit: hinted_plays[player_number][suit] = {} for rank in utils.Rank: hinted_plays[player_number][suit][rank] = 0 player_hand = utils.get_player_hand_by_number(round_info, player_number) for card in player_hand: if round_info.board_state[card.real_suit] < card.real_rank.value and \ (card.revealed_rank is not None or card.revealed_suit is not None): hinted_plays[player_number][card.real_suit][card.real_rank] += 1 player_number = utils.next_player_number(round_info, player_number) player_number = utils.next_player_number(round_info, original_player_number) potential_playable_ranks = {} potential_playable_suits = {} while player_number is not original_player_number: potential_playable_ranks[player_number] = {} potential_playable_suits[player_number] = {} for rank in utils.Rank: potential_playable_ranks[player_number][rank] = [] for suit in utils.Suit: potential_playable_suits[player_number][suit] = [] player_hand = utils.get_player_hand_by_number(round_info, player_number) for card in player_hand: already_hinted = False for player in hinted_plays: if player is not player_number and hinted_plays[player][card.real_suit][card.real_rank] != 0: already_hinted = True if round_info.board_state[card.real_suit] < card.real_rank.value and not already_hinted: if card.revealed_suit is None: potential_playable_suits[player_number][card.real_suit].append(card) if card.revealed_rank is None: potential_playable_ranks[player_number][card.real_rank].append(card) player_number = utils.next_player_number(round_info, player_number) max_player_number = 0 max_potential = 0 max_hint = 0 def check_card_potential(card, player): player_distance = player - original_player_number - 1 if player_distance < 0: player_distance += round_info.number_of_players card_potential = \ pow(distance_to_playable_multiplier, card.real_rank.value - round_info.board_state[card.real_suit] - 1) * \ pow(distance_to_player_multiplier, player_distance) * \ pow(lower_rank_multiplier, 5-card.real_rank.value) if (card.revealed_suit is not None or card.revealed_rank is not None) and \ card.real_rank.value - round_info.board_state[card.real_suit] == 1: card_potential *= double_hint_multiplier return card_potential for player in potential_playable_ranks: if debug: self.info('{0}'.format(player)) for rank in potential_playable_ranks[player]: potential = 0 for card in potential_playable_ranks[player][rank]: potential += check_card_potential(card, player) if debug: self.info('{0} {1}'.format(rank, potential)) if potential > max_potential: max_player_number = player max_potential = potential max_hint = rank for suit in potential_playable_suits[player]: potential = 0 for card in potential_playable_suits[player][suit]: potential += check_card_potential(card, player) if debug: self.info('{0} {1}'.format(suit, potential)) if potential > max_potential: max_player_number = player max_potential = potential max_hint = suit if max_potential >= hint_pass_score: return ChoiceDetails( Choice.HINT, HintDetails(max_player_number, max_hint) ) return False
def check_for_necessary_tip(self, round_info, player_number): if round_info.hints == 0 or round_info.hints == utils.MAX_HINTS: return False if player_number is round_info.player_turn: player_hand = round_info.player_hand else: player_hand = utils.get_player_hand_by_number(round_info, player_number) remaining = utils.list_remaining_playable_cards(round_info) next_player_hand = utils.next_player_hand(round_info, player_number) next_player_number = utils.next_player_number(round_info, player_number) if self.check_for_obvious_play(round_info, next_player_number) is not False: return False distrust = False play = self.check_for_hinted_play(round_info, next_player_number) if play is not False: play_position = play[1] played_card = next_player_hand[play_position] if round_info.board_state[played_card.real_suit] is not played_card.real_rank.value - 1: distrust = True own_play = None if self.check_for_obvious_play(round_info, player_number) is not False: own_play = self.check_for_obvious_play(round_info, player_number)[1] if own_play is None and self.check_for_hinted_play(round_info, player_number) is not False: own_play = self.check_for_hinted_play(round_info, player_number)[1] if own_play is not None and \ round_info.board_state[played_card.real_suit] is played_card.real_rank.value - 2: own_card = player_hand[own_play] if (own_card.revealed_rank is not None and own_card.revealed_rank.value is played_card.real_rank.value - 1) or \ (own_card.revealed_suit is not None and own_card.revealed_suit is played_card.real_suit): distrust = False else: if self.check_for_obvious_discard(round_info, next_player_number) is False: played_position = self.check_for_guess_discard(round_info, next_player_number)[1] played_card = next_player_hand[played_position] if round_info.board_state[played_card.real_suit] < played_card.real_rank.value and \ remaining[played_card.real_suit][played_card.real_rank] == 1 and \ self.check_card_usefulness(round_info, played_card) is False: distrust = True if distrust: if debug and round_info.log: self.info("good_tip:") answer = self.check_for_good_tip(round_info, player_number, only_next_player=True) if answer is False: if debug and round_info.log: self.info("risky_tip:") answer = self.check_for_risky_tip(round_info, player_number, only_next_player=True) if answer is not False: return answer if played_card.revealed_rank is None: return ChoiceDetails( Choice.HINT, HintDetails(utils.next_player_number(round_info, player_number), played_card.real_rank) ) else: return ChoiceDetails( Choice.HINT, HintDetails(utils.next_player_number(round_info, player_number), played_card.real_suit) ) return False
def check_for_play_tip(self, round_info, player_number, hint_pass_score=0.9, double_hint_multiplier=0.3, false_tip_penalty=-2.5, distance_to_player_multiplier=1.01, lower_rank_multiplier=1.07, information_tip_value=0.2, already_has_play_multiplier=0.5, chain_bonus_multiplier=1.3, only_next_player=False): if round_info.hints <= 1: return False original_player_number = player_number if player_number is round_info.player_turn: original_player_hand = round_info.player_hand else: original_player_hand = utils.get_player_hand_by_number(round_info, player_number) player_number = utils.next_player_number(round_info, original_player_number) worth_hinting = {} predicted_board_state = {original_player_number: deepcopy(round_info.board_state)} while player_number is not original_player_number: predicted_board_state[player_number] = deepcopy( predicted_board_state[utils.prev_player_number(round_info, player_number)]) worth_hinting[player_number] = False play = self.check_for_obvious_play(round_info, player_number) if play is False: play = self.check_for_hinted_play(round_info, player_number) if play is False: worth_hinting[player_number] = True if play is not False: player_hand = utils.get_player_hand_by_number(round_info, player_number) suit = player_hand[play[1]].real_suit rank = player_hand[play[1]].real_rank if predicted_board_state[player_number][suit] is rank.value - 1: predicted_board_state[player_number][suit] += 1 player_number = utils.next_player_number(round_info, player_number) player_number = original_player_number hinted_plays = {} first_time = True while player_number is not original_player_number or first_time: first_time = False hinted_plays[player_number] = {} for suit in utils.Suit: hinted_plays[player_number][suit] = {} for rank in utils.Rank: hinted_plays[player_number][suit][rank] = 0 player_number = utils.next_player_number(round_info, player_number) player_number = utils.next_player_number(round_info, original_player_number) for card in original_player_hand: if card.revealed_rank is not None and card.revealed_suit is not None and \ round_info.board_state[card.real_suit] < card.real_rank.value: hinted_plays[original_player_number][card.real_suit][card.real_rank] += 1 while player_number is not original_player_number: player_hand = utils.get_player_hand_by_number(round_info, player_number) for card in player_hand: if round_info.board_state[card.real_suit] < card.real_rank.value and \ (card.revealed_rank is not None or card.revealed_suit is not None): hinted_plays[player_number][card.real_suit][card.real_rank] += 1 player_number = utils.next_player_number(round_info, player_number) player_number = utils.next_player_number(round_info, original_player_number) potential_playable_ranks = {} potential_playable_suits = {} while player_number is not original_player_number: potential_playable_ranks[player_number] = {} potential_playable_suits[player_number] = {} for rank in utils.Rank: potential_playable_ranks[player_number][rank] = [] for suit in utils.Suit: potential_playable_suits[player_number][suit] = [] player_hand = utils.get_player_hand_by_number(round_info, player_number) for card in player_hand: if card.revealed_suit is None: potential_playable_suits[player_number][card.real_suit].append(card) if card.revealed_rank is None: potential_playable_ranks[player_number][card.real_rank].append(card) player_number = utils.next_player_number(round_info, player_number) max_player_number = 0 max_potential = -5 max_hint = 0 known = {} for suit in utils.Suit: known[suit] = {} for rank in utils.Rank: known[suit][rank] = 0 for card in original_player_hand: if card.revealed_rank is not None and card.revealed_suit is not None: known[card.revealed_suit][card.revealed_rank] += 1 for hand in round_info.other_players_hands: if original_player_number is not hand.player_number: for card in hand: known[card.real_suit][card.real_rank] += 1 def check_card_potential(card, player, board_state, current_rank=None, pure_info=False): player_distance = player - original_player_number - 1 if player_distance < 0: player_distance += round_info.number_of_players already_hinted = False if card.revealed_suit is None and card.revealed_rank is None: for players in hinted_plays: if hinted_plays[players][card.real_suit][card.real_rank] != 0: already_hinted = True card_potential = pow(distance_to_player_multiplier, player_distance) *\ pow(lower_rank_multiplier, 5 - card.real_rank.value) if (card.revealed_suit is not None or card.revealed_rank is not None) and \ card.real_rank.value - board_state[card.real_suit] <= 1 and \ self.card_hint_type[player][card.hand_position] == "Play": card_potential *= double_hint_multiplier if card.real_rank.value <= 4 and card.real_rank.value - board_state[card.real_suit] == 1 and \ known[card.real_suit][utils.Rank(card.real_rank.value + 1)] > 0: card_potential *= chain_bonus_multiplier if already_hinted: card_potential += false_tip_penalty if pure_info \ or (max(round_info.board_state.values()) < card.real_rank.value - 1 and current_rank is None) \ or (self.card_hint_type[player][card.hand_position] == "Information"): card_potential = information_tip_value * pow(distance_to_player_multiplier, player_distance) * \ pow(lower_rank_multiplier, card.real_rank.value - 1) if not already_hinted and \ card.real_rank.value - board_state[card.real_suit] == 1 and \ self.card_hint_type[player][card.hand_position] == "Information": card_potential = pow(distance_to_player_multiplier, player_distance) * \ pow(lower_rank_multiplier, 5 - card.real_rank.value) if card.real_rank.value <= 4 and card.real_rank.value - board_state[card.real_suit] == 1 and \ known[card.real_suit][utils.Rank(card.real_rank.value + 1)] > 0: card_potential *= chain_bonus_multiplier elif (card.revealed_suit is None and card.revealed_rank is None) and \ ((current_rank is None and board_state[card.real_suit] is not card.real_rank.value - 1) or (current_rank is not None and card.real_rank.value is not current_rank)): card_potential += false_tip_penalty if current_rank is not None and card.real_rank.value is current_rank: current_rank += 1 return card_potential, current_rank for player in potential_playable_ranks: if debug and round_info.log: self.info('{0}'.format(player)) info_rank = None info_suit = None player_distance = player - original_player_number - 1 if player_distance < 0: player_distance += round_info.number_of_players if player_distance == 0 or not only_next_player: if player_distance == 0: play = self.check_for_hinted_play(round_info, player) target_hand = utils.get_player_hand_by_number(round_info, player) if play is not False: if target_hand[play[1]].revealed_rank is None: info_rank = target_hand[play[1]].real_rank if target_hand[play[1]].revealed_suit is None: info_suit = target_hand[play[1]].real_suit else: play = self.check_for_guess_discard(round_info, player) if target_hand[play[1]].revealed_rank is None: info_rank = target_hand[play[1]].real_rank if target_hand[play[1]].revealed_suit is None: info_suit = target_hand[play[1]].real_suit for rank in potential_playable_ranks[player]: board_state = deepcopy(predicted_board_state[utils.prev_player_number(round_info, player)]) potential = 0 for card in potential_playable_ranks[player][rank]: if rank is not info_rank: answer = check_card_potential(card, player, board_state)[0] else: answer = check_card_potential(card, player, board_state, pure_info=True)[0] if answer >= pow(distance_to_player_multiplier, player_distance) * \ pow(lower_rank_multiplier, 5 - card.real_rank.value): board_state[card.real_suit] += 1 potential += answer if not worth_hinting[player] and rank is not info_rank: potential *= already_has_play_multiplier if debug and round_info.log: self.info('{0} {1}'.format(rank, potential)) if potential > max_potential: max_player_number = player max_potential = potential max_hint = rank board_state = deepcopy(predicted_board_state[utils.prev_player_number(round_info, player)]) for suit in potential_playable_suits[player]: potential = 0 current_rank = board_state[suit] + 1 for card in potential_playable_suits[player][suit]: if suit is not info_suit: answer = check_card_potential(card, player, board_state, current_rank) else: answer = check_card_potential(card, player, board_state, current_rank, pure_info=True) potential += answer[0] current_rank = answer[1] if not worth_hinting[player] and suit is not info_suit: potential *= already_has_play_multiplier if debug and round_info.log: self.info('{0} {1}'.format(suit, potential)) if potential > max_potential: max_player_number = player max_potential = potential max_hint = suit if max_potential >= hint_pass_score: return ChoiceDetails( Choice.HINT, HintDetails(max_player_number, max_hint) ) return False
def read_board(self, round_info, player_number): self.known = utils.list_others_cards(round_info, player_number) if player_number is round_info.player_turn: player_hand = round_info.player_hand else: player_hand = utils.get_player_hand_by_number(round_info, player_number) play_actions = [] for card in player_hand: play_actions.append(self.read_own_card(round_info, card)) hint_actions = [] if round_info.hints > 0: hint_actions = self.read_others_hands(round_info, player_number) used_actions = [] used_hints = [] max_of_weights = -1 used_play = None for play in play_actions: if round_info.log and debug: self.info("{0} {1} {2} {3}".format(play[0], play[1][0][0], self.get_chance(play[1][0]), play[2])) sum_of_weights = self.get_chance(play[1][0]) if sum_of_weights > max_of_weights: max_of_weights = sum_of_weights used_play = play used_actions.append(used_play) max_of_weights = -1 used_play = None for play in play_actions: if round_info.log and debug: self.info("{0} {1} {2} {3}".format(play[0], play[1][1][0], self.get_chance(play[1][1]), play[2])) sum_of_weights = self.get_chance(play[1][1]) if sum_of_weights >= max_of_weights: max_of_weights = sum_of_weights used_play = play used_actions.append(used_play) if round_info.hints > 0: max_of_weights = -1 used_hint = None for hint in hint_actions: if round_info.log and debug: self.info("{0} {1} {2} {3} {4}".format(hint[0], hint[1][0][0], self.get_chance(hint[1][0]), hint[2], hint[3])) sum_of_weights = self.get_chance(hint[1][0]) if sum_of_weights > max_of_weights: max_of_weights = sum_of_weights used_hint = hint used_hints.append(used_hint) if len(used_hints) > 0 and used_hints[0] is None: used_hints = [] macro_weights = self.decide_macro_action(round_info, used_actions, used_hints) macro_max = 0 macro_action = "Discard" sum_of_weights = self.get_chance(macro_weights[1][0]) if macro_max < sum_of_weights: macro_max = sum_of_weights macro_action = "Play" sum_of_weights = self.get_chance(macro_weights[1][1]) if macro_max < sum_of_weights: macro_max = sum_of_weights macro_action = "Discard" sum_of_weights = self.get_chance(macro_weights[1][2]) if macro_max < sum_of_weights and round_info.hints > 0 and len(used_hints) > 0: macro_action = "Hint" used_state = None action = None if macro_action == "Play": used_state = used_actions[0] action = ChoiceDetails( Choice.PLAY, used_actions[0][2] ) if macro_action == "Discard": used_state = used_actions[1] action = ChoiceDetails( Choice.DISCARD, used_actions[1][2] ) if macro_action == "Hint": used_state = used_hints[0] action = ChoiceDetails( Choice.HINT, HintDetails(used_hints[0][2], used_hints[0][3]) ) if round_info.log and debug: self.info("{0}".format(macro_action)) self.info("{0} {1} {2}".format(used_state[0], used_state[1][0][0], self.get_chance(used_state[1][0]))) self.info("{0} {1} {2}".format(macro_weights[0], macro_weights[1][0][0], self.get_chance(macro_weights[1][0]))) return action
def check_for_save_tip(self, round_info, player_number): best_player_number = -1 best_play_priority = False best_hint_type = None best_hint_rank = 6 remaining = utils.list_remaining_playable_cards(round_info) original_player_number = player_number player_number = utils.next_player_number(round_info, utils.next_player_number(round_info, player_number)) while player_number is not original_player_number and not best_play_priority: player_hand = utils.get_player_hand_by_number(round_info, player_number) play_priority = False prev_player = utils.prev_player_number(round_info, player_number) play = self.check_for_obvious_play(round_info, prev_player) if play is False: play = self.check_for_hinted_play(round_info, prev_player) if play is not False: play_priority = True discarded_position = self.check_for_guess_discard(round_info, player_number)[1] discarded_card = player_hand[discarded_position] if remaining[discarded_card.real_suit][discarded_card.real_rank] == 1 and \ max(round_info.board_state.values()) < discarded_card.real_rank.value - 1 and \ discarded_card.revealed_rank is None and discarded_card.revealed_suit is None: best_player_number = player_number best_play_priority = play_priority best_hint_type = discarded_card.real_rank player_number = utils.next_player_number(round_info, player_number) if best_hint_type is None: player_number = utils.next_player_number(round_info, utils.next_player_number(round_info, player_number)) while player_number is not original_player_number and not best_play_priority: player_hand = utils.get_player_hand_by_number(round_info, player_number) play_priority = False prev_player = utils.prev_player_number(round_info, player_number) if prev_player is not original_player_number: play = self.check_for_obvious_play(round_info, prev_player) if play is False: play = self.check_for_hinted_play(round_info, prev_player) if play is not False: play_priority = True for card in player_hand: if remaining[card.real_suit][card.real_rank] == 1 and \ max(round_info.board_state.values()) < card.real_rank.value - 1 and \ card.revealed_rank is None and card.revealed_suit is None and \ best_hint_rank > card.real_rank.value: best_player_number = player_number best_play_priority = play_priority best_hint_type = card.real_rank best_hint_rank = card.real_rank.value player_number = utils.next_player_number(round_info, player_number) if best_hint_type is not None: return ChoiceDetails( Choice.HINT, HintDetails(best_player_number, best_hint_type) ) return False
def read_board(self, round_info, player_number): self.known = utils.list_others_cards(round_info, player_number) if player_number is round_info.player_turn: player_hand = round_info.player_hand else: player_hand = utils.get_player_hand_by_number( round_info, player_number) play_actions = [] for card in player_hand: play_actions.append(self.read_own_card(round_info, card)) hint_actions = [] if round_info.hints > 0: hint_actions = self.read_others_hands(round_info, player_number) use_random = False if random.random() <= random_action: use_random = True used_actions = [] used_hints = [] if use_random: micro_decision = random.random() sum_of_weights = 0 for play in play_actions: sum_of_weights += 1 / len(play_actions) if sum_of_weights - 1 / len( play_actions) <= micro_decision <= sum_of_weights: used_actions.append(play) else: max_of_weights = 0 used_play = None total_count = 1 for play in play_actions: total_count += play[1][0][0] total_count = math.log(total_count) for play in play_actions: if round_info.log and debug: self.info("{0} {1} {2} {3}".format( play[0], play[1][0][0], self.learning_state.get_chance(play[1][0]), play[2])) sum_of_weights = self.learning_state.get_chance(play[1][0]) \ + exploration_param * math.sqrt(total_count / play[1][0][0]) if sum_of_weights > max_of_weights: max_of_weights = sum_of_weights used_play = play used_actions.append(used_play) if use_random: micro_decision = random.random() sum_of_weights = 0 for play in play_actions: sum_of_weights += 1 / len(play_actions) if sum_of_weights - 1 / len( play_actions) <= micro_decision <= sum_of_weights: used_actions.append(play) else: max_of_weights = 0 used_play = None total_count = 1 for play in play_actions: total_count += play[1][1][0] total_count = math.log(total_count) for play in play_actions: if round_info.log and debug: self.info("{0} {1} {2} {3}".format( play[0], play[1][1][0], self.learning_state.get_chance(play[1][1]), play[2])) sum_of_weights = self.learning_state.get_chance(play[1][1]) \ + exploration_param * math.sqrt(total_count / play[1][1][0]) if sum_of_weights > max_of_weights: max_of_weights = sum_of_weights used_play = play used_actions.append(used_play) if round_info.hints > 0: if use_random: micro_decision = random.random() sum_of_weights = 0 for hint in hint_actions: sum_of_weights += 1 / len(hint_actions) if sum_of_weights - 1 / len( hint_actions) <= micro_decision <= sum_of_weights: used_hints.append(hint) else: max_of_weights = 0 used_hint = None total_count = 1 for hint in hint_actions: total_count += hint[1][0][0] total_count = math.log(total_count) for hint in hint_actions: if round_info.log and debug: self.info("{0} {1} {2} {3} {4}".format( hint[0], hint[1][0][0], self.learning_state.get_chance(hint[1][0]), hint[2], hint[3])) sum_of_weights = self.learning_state.get_chance(hint[1][0]) \ + exploration_param * math.sqrt(total_count / hint[1][0][0]) if sum_of_weights > max_of_weights: max_of_weights = sum_of_weights used_hint = hint used_hints.append(used_hint) if len(used_hints) > 0 and used_hints[0] is None: used_hints = [] macro_weights = self.decide_macro_action(round_info, used_actions, used_hints) macro_max = 0 macro_action = "Discard" total_count = 0 for weight in macro_weights[1]: total_count += len(weight) - 1 total_count = math.log(total_count) sum_of_weights = self.learning_state.get_chance(macro_weights[1][0]) \ + exploration_param * math.sqrt(total_count / (len(macro_weights[1][0]) - 1)) if macro_max < sum_of_weights: macro_max = sum_of_weights macro_action = "Play" sum_of_weights = self.learning_state.get_chance(macro_weights[1][1]) \ + exploration_param * math.sqrt(total_count / (len(macro_weights[1][1]) - 1)) if macro_max < sum_of_weights: macro_max = sum_of_weights macro_action = "Discard" sum_of_weights = self.learning_state.get_chance(macro_weights[1][2]) \ + exploration_param * math.sqrt(total_count / (len(macro_weights[1][2]) - 1)) if macro_max < sum_of_weights and round_info.hints > 0 and len( used_hints) > 0: macro_action = "Hint" if use_random: stop = 1 if round_info.hints == 0: stop -= 0.33 macro_decision = random.uniform(0, stop) if macro_decision <= 0.34: macro_action = "Play" elif macro_decision <= 0.67: macro_action = "Discard" else: macro_action = "Hint" used_state = None action = None if macro_action == "Play": used_state = used_actions[0] action = ChoiceDetails(Choice.PLAY, used_actions[0][2]) if macro_action == "Discard": used_state = used_actions[1] action = ChoiceDetails(Choice.DISCARD, used_actions[1][2]) if macro_action == "Hint": used_state = used_hints[0] action = ChoiceDetails( Choice.HINT, HintDetails(used_hints[0][2], used_hints[0][3])) if round_info.log and debug: self.info("{0}".format(macro_action)) self.info("{0} {1} {2}".format( used_state[0], used_state[1][0][0], self.learning_state.get_chance(used_state[1][0]))) self.info("{0} {1} {2}".format( macro_weights[0], macro_weights[1][0][0], self.learning_state.get_chance(macro_weights[1][0]))) self.learning_state.append_to_history( (round_info.player_turn, action, used_state, play_actions, hint_actions, macro_weights)) return action