def initialize_variables(self, round_info): self.remaining = utils.list_remaining_playable_cards(round_info) self.point_of_uselessness = {} for suit in utils.Suit: self.point_of_uselessness[suit] = None for rank in utils.Rank: if round_info.board_state[suit] < rank.value: if self.point_of_uselessness[suit] is None and self.remaining[suit][rank] == 0: self.point_of_uselessness[suit] = rank original_player_number = round_info.player_turn player_number = utils.next_player_number(round_info, original_player_number) oldest_age = -1 for card in round_info.player_hand: card_age = round_info.current_turn - card.drawn_on_turn if card_age > oldest_age: oldest_age = card_age self.oldest_card[original_player_number] = card.hand_position while player_number is not original_player_number: player_hand = utils.get_player_hand_by_number(round_info, player_number) oldest_age = -1 for card in player_hand: card_age = round_info.current_turn - card.drawn_on_turn if card_age > oldest_age: oldest_age = card_age self.oldest_card[player_number] = card.hand_position player_number = utils.next_player_number(round_info, player_number)
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_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_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_soft_pass_turn(self, round_info, player_number): original_player_number = player_number player_number = utils.next_player_number(round_info, player_number) needed_passes = 1 while player_number is not original_player_number: if not self.check_for_obvious_discard(round_info, player_number): needed_passes += 1 player_number = utils.next_player_number(round_info, player_number) if round_info.hints <= needed_passes: return False else: return self.hint_first_free_card(round_info, player_number)
def extrapolate_board_state(self, round_info, target_player): player_number = utils.next_player_number(round_info, round_info.player_turn) predicted_board_state = deepcopy(round_info.board_state) while player_number is not target_player: 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 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 self.info("{0} {1}".format(suit, rank)) if suit is None or rank is None or predicted_board_state[suit] is rank.value - 1: predicted_board_state[suit] += 1 player_number = utils.next_player_number(round_info, player_number) return predicted_board_state
def initialize_card_hint_history(self, round_info): original_player_number = round_info.player_turn player_number = original_player_number just_began = True self.hand_size = len(round_info.player_hand) while just_began or player_number is not original_player_number: just_began = False self.card_hint_type[player_number] = [] for x in range(len(round_info.player_hand)): self.card_hint_type[player_number].append(None) player_number = utils.next_player_number(round_info, player_number)
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 initialize_card_hint_history(self, round_info): original_player_number = round_info.player_turn player_number = original_player_number just_began = True self.hand_size = len(round_info.player_hand) while just_began or player_number is not original_player_number: just_began = False self.card_hint_type[player_number] = [] for x in range(len(round_info.player_hand)): self.card_hint_type[player_number].append(None) player_number = utils.next_player_number(round_info, player_number) self.discard_tip = self.learning_state.discard_tip_parameters self.good_tip = self.learning_state.good_tip_parameters self.risky_tip = self.learning_state.risky_tip_parameters self.information_tip = self.learning_state.information_tip_parameters
def read_others_hands(self, round_info, player_number): original_player_number = player_number hints = round_info.hints if 2 <= hints <= 7: hints = 2 actions = [] hinted_plays = {} player_hand = round_info.player_hand 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) 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.real_suit] < card.real_rank.value: hinted_plays[original_player_number][card.real_suit][card.real_rank] += 1 player_number = utils.next_player_number(round_info, original_player_number) 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) while player_number is not original_player_number: if player_number is not round_info.player_turn: player_distance = player_number - original_player_number - 1 if player_distance < 0: player_distance += round_info.number_of_players if player_distance > 2: player_distance = 2 targets = {} for rank in utils.Rank: targets[rank] = [] for suit in utils.Suit: targets[suit] = [] player_hand = utils.get_player_hand_by_number(round_info, player_number) for card in player_hand: if card.revealed_rank is None: targets[card.real_rank].append(card) if card.revealed_suit is None: targets[card.real_suit].append(card) def basic_check(targets, hint, rank=False): current_alignment = 0 future_alignment = 0 corrected = 0 obviously_useless = 0 falsely_hinted = 0 chain_bonus = 0 card_age = 0 oldest_card = False last_remaining = 0 for card in targets: already_hinted = False if card.revealed_suit is None and card.revealed_rank is None: for players in hinted_plays: if players is not player_number and \ hinted_plays[players][card.real_suit][card.real_rank] != 0: already_hinted = True card_with_hint = deepcopy(card) if 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 not False \ and self.check_card_usefulness(round_info, card) is False: obviously_useless += 1 if rank: card_with_hint.revealed_suit = card.real_suit else: card_with_hint.revealed_rank = card.real_rank if round_info.board_state[card.real_suit] is card.real_rank.value - 1: if self.remaining[card.real_suit][card.real_rank] == 1 \ and ((rank and card.revealed_suit is None) or (not rank and card.revealed_rank is None)): last_remaining += 1 if card.revealed_rank is not None or card.revealed_suit is not None and \ round_info.current_turn - card.drawn_on_turn > 2: corrected += 1 if already_hinted: falsely_hinted += 1 else: current_alignment += 1 chain = 1 if card.real_rank.value + chain <= 5 and \ self.known[card.real_suit][utils.Rank(card.real_rank.value + chain)] > 0: chain += 1 chain_bonus += 1 elif self.check_card_usefulness(round_info, card_with_hint) is False: if self.remaining[card.real_suit][card.real_rank] == 1 \ and ((rank and card.revealed_suit is None) or (not rank and card.revealed_rank is None)): last_remaining += 1 if card.revealed_rank is not None or card.revealed_suit is not None: corrected += 1 future_alignment += 1 if round_info.board_state[card.real_suit] is not card.real_rank.value - 1 \ and ((rank and card.revealed_suit is None) or (not rank and card.revealed_rank is None)): falsely_hinted += 1 if round_info.current_turn - card.drawn_on_turn > card_age: card_age = round_info.current_turn - card.drawn_on_turn if card.hand_position is self.oldest_card[card.player_number]: oldest_card = True if card_age > 3: card_age = 3 if current_alignment > 2: current_alignment = 2 if future_alignment > 2: future_alignment = 2 if obviously_useless > 2: obviously_useless = 2 if corrected > 2: corrected = 2 if falsely_hinted > 2: falsely_hinted = 2 chain_bonus = math.ceil(chain_bonus / 2) if chain_bonus > 2: chain_bonus = 2 if last_remaining > 1: last_remaining = 1 state = (rank, current_alignment, future_alignment, obviously_useless, corrected, falsely_hinted, chain_bonus, last_remaining, card_age, oldest_card, player_distance) weights = self.get_hint_weights(state) return state, weights, player_number, hint for rank in utils.Rank: if len(targets[rank]) > 0: actions.append(basic_check(targets[rank], rank, True)) for suit in utils.Suit: if len(targets[suit]) > 0: actions.append(basic_check(targets[suit], suit, False)) player_number = utils.next_player_number(round_info, player_number) return actions
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 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_play_history(self, round_info): original_player_number = round_info.player_turn player_number = original_player_number amount_of_players = len(self.card_hint_type) current_board_state = deepcopy(round_info.board_state) current_lives = round_info.lives current_hints = round_info.hints current_discarded = round_info.discarded current_played = round_info.played for i in range(-1, -amount_of_players - 1, -1): if len(round_info.history) + i >= 0: move = round_info.history[i] if move[0] is Choice.PLAY: if move[2].real_rank.value is current_board_state[move[2].real_suit]: if current_board_state[move[2].real_suit] == 5: current_hints = max(0, current_hints - 1) current_board_state[move[2].real_suit] -= 1 current_played.pop() else: current_discarded.pop() current_lives = min(utils.LIVES, current_lives + 1) if move[0] is Choice.DISCARD: current_discarded.pop() current_hints = max(0, current_hints - 1) if move[0] is Choice.HINT: current_hints = min(current_hints + 1, utils.MAX_HINTS) for i in range(-amount_of_players, 0): if len(round_info.history) + i >= 0: move = round_info.history[i] if debug and round_info.log: self.info("{0}, {1}, {2}, {3}, {4}".format(player_number, move[0], move[1], move[2], move[3])) if move[0] is Choice.PLAY or move[0] is Choice.DISCARD: if debug and round_info.log: self.info("{0}".format(self.card_hint_type[player_number][move[1]])) if move[0] is Choice.PLAY: if move[2].real_rank.value is current_board_state[move[2].real_suit] + 1: current_board_state[move[2].real_suit] += 1 if current_board_state[move[2].real_suit] == 5: current_hints = min(current_hints + 1, utils.MAX_HINTS) current_played.append(move[2]) else: current_discarded.append(move[2]) current_lives = max(1, current_lives - 1) if move[0] is Choice.DISCARD: current_hints = min(utils.MAX_HINTS, current_hints + 1) current_discarded.append(move[2]) self.card_hint_type[player_number][move[1]] = None if move[3] == 0: for x in range(move[1], self.hand_size - 1): self.card_hint_type[player_number][x] = self.card_hint_type[player_number][x + 1] if move[0] is Choice.HINT: target_player = move[1] hint = move[2] hint_type = "Play" current_round_info = deepcopy(round_info) current_round_info.player_turn = player_number current_round_info.board_state = deepcopy(current_board_state) current_round_info.lives = current_lives current_round_info.hints = current_hints current_round_info.played = current_played current_round_info.discarded = current_discarded current_round_info.other_players_hands = [] hands = deepcopy(round_info.hands_history[i - 1]) for hand in hands: if hand.player_number is player_number: current_round_info.player_hand = hand else: current_round_info.other_players_hands.append(hand) player_distance = target_player - player_number - 1 if player_distance < 0: player_distance += round_info.number_of_players if debug and round_info.log: self.info("{0}".format(round_info.player_hand)) self.info("{0}, C: {1}, N: {2}".format(player_distance, current_round_info.player_hand, current_round_info.other_players_hands[ 0].current_knowledge())) if target_player is original_player_number: player_hand = round_info.player_hand else: player_hand = round_info.hands_history[i][target_player] if player_distance == 0: answer = self.check_for_obvious_play(current_round_info, target_player) if answer is False: answer = self.check_for_hinted_play(current_round_info, target_player) if answer is not False: if debug and round_info.log: self.info("{0}".format(answer)) position = answer[1] self.card_hint_type[target_player][position] = "Information" if player_hand[position].revealed_rank is not None and \ player_hand[position].revealed_suit is not None: hint_type = "Information" else: answer = self.check_for_guess_discard(current_round_info, target_player) if debug and round_info.log: self.info("{0}".format(answer)) position = answer[1] if isinstance(hint, utils.Rank): if player_hand[position].revealed_rank is not None and \ player_hand[position].revealed_rank is hint: hint_type = "Information" else: if player_hand[position].revealed_suit is not None and \ player_hand[position].revealed_suit is hint: hint_type = "Information" if isinstance(hint, utils.Rank) and max(current_board_state.values()) < hint.value - 1: hint_type = "Information" for x in range(0, len(player_hand)): if isinstance(hint, utils.Rank): if player_hand[x].revealed_rank is not None and player_hand[x].revealed_rank is hint: if self.card_hint_type[target_player][x] is None: self.card_hint_type[target_player][x] = hint_type elif player_hand[x].revealed_suit is not None: self.card_hint_type[target_player][x] = None else: if player_hand[x].revealed_suit is not None and player_hand[x].revealed_suit is hint: if self.card_hint_type[target_player][x] is None: self.card_hint_type[target_player][x] = hint_type elif player_hand[x].revealed_rank is not None: self.card_hint_type[target_player][x] = None current_hints = max(0, current_hints - 1) if debug and round_info.log: self.info("{0}".format(hint_type)) player_number = utils.next_player_number(round_info, player_number)
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=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_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 analyze_game(self, round_info, score): scores = self.learning_state.score_history #prog = 16 prog = statistics.median_low(scores) - 2 amount = abs(score - prog) amount = math.ceil(amount) original_player_number = round_info.player_turn player_number = original_player_number first = True while player_number != original_player_number or first: first = False if player_number == original_player_number: player_hand = round_info.player_hand else: player_hand = utils.get_player_hand_by_number( round_info, player_number) for card in player_hand: for state in card.hint_states: self.penalize_hint(state[0], round_info, 0.05078125) player_number = utils.next_player_number(round_info, player_number) if score > prog: for i in range(-1, -len(self.learning_state.states_history) - 1, -1): action = self.learning_state.states_history[i][1][0] state = self.learning_state.states_history[i][2][0] macro_state = self.learning_state.states_history[i][5][0] if round_info.log and debug: self.info("{0} {1}".format(action, state)) if action is Choice.PLAY: self.reward_own_play(state, round_info, amount / 16) self.reward_macro(macro_state, 0, round_info, amount) if action is Choice.DISCARD: self.reward_own_discard(state, round_info, amount / 8) self.reward_macro(macro_state, 1, round_info, amount) if action is Choice.HINT: self.reward_hint(state, round_info, amount / 8) self.reward_macro(macro_state, 2, round_info, amount) elif score < prog: for i in range(-1, -len(self.learning_state.states_history) - 1, -1): action = self.learning_state.states_history[i][1][0] state = self.learning_state.states_history[i][2][0] macro_state = self.learning_state.states_history[i][5][0] if round_info.log and debug: self.info("{0} {1}".format(action, state)) if action is Choice.PLAY: self.penalize_own_play(state, round_info, amount / 16) self.penalize_macro(macro_state, 0, round_info, amount) if action is Choice.DISCARD: self.penalize_own_discard(state, round_info, amount / 8) self.penalize_macro(macro_state, 1, round_info, amount) if action is Choice.HINT: self.penalize_hint(state, round_info, amount / 8) self.penalize_macro(macro_state, 2, round_info, amount) scores[0] = ((len(scores) - 1) * scores[0] + score) / len(scores) scores.append(score) while len(scores) > 1001: scores[0] = ( (len(scores) - 1) * scores[0] - scores[1]) / (len(scores) - 2) scores.pop(1) self.learning_state.score_history = scores self.learning_state.states_history = []