def select_final_move(self, rootnode, rootstate): node = max(rootnode.childNodes, key=lambda c: c.visits) final_move = max(rootnode.childNodes, key=lambda c: c.visits ).move # return the move that was most visited opponent, guess = None, None # play against human player if we can if rootstate.real_players and not rootstate.real_players[ 0].lost and not rootstate.real_players[0].defence: opponent = rootstate.real_players[0] if final_move.name == "Guard": # if final move is Guard, then guess card opponent, guess = get_guess_card(rootstate.user_ctl.users, rootstate.playerToMove, rootstate.seen_cards) # control treshold for playing the Baron elif len(rootnode.childNodes) > 1 and final_move.name == "Baron" and node.wins / node.visits < 0.6 and \ rootnode.childNodes[0].move.name != "Princess" and rootnode.childNodes[1].move.name != "Princess": if rootnode.childNodes[0].move.name != "Baron": return rootnode.childNodes[0].move, opponent, None else: return rootnode.childNodes[1].move, opponent, None return final_move, opponent, None
def get_move(state): # print(state.playerHands[state.playerToMove]) move, victim, guess = get_move(state, ismcts=True) if move: return move, victim, guess move_counter = defaultdict(int) moves = state.get_moves() for _ in range(201): rootstate = state.clone_and_randomize() value, move = Minimax._minimax(rootstate, True, -Minimax.INF, Minimax.INF, 1) move_counter[move] += 1 # from pprint import pprint # pprint(move_counter) result_move = max(moves, key=lambda item: move_counter[item]) victim, guess = None, None if result_move.name == "Guard": victim, guess = get_guess_card(state.user_ctl.users, state.playerToMove, state.seen_cards) return result_move, victim, guess
def select(self, state, node, **kwargs): """ Selection step :param state: current state of the game :return: """ assert len(state.playerHands[state.playerToMove]) == 2 while not state.round_over and not node.get_untried_moves( state.get_moves()): victim, guess = None, None # node is fully expanded and non-terminal available_moves = state.get_moves() node = node.ucb_select_child(available_moves) if node.move.name == "Guard" and kwargs.get('extra', False): victim, guess = get_guess_card(state.user_ctl.users, state.playerToMove, state.seen_cards) state.do_move(node.move, victim=victim, guess=guess, verbose=kwargs.get('verbose', False)) return node
def _minimax(state, is_maximizing_player, alpha, beta, depth): if state.round_over: try: assert len([1 for user in state.user_ctl.users if user.lost]) == 1 or not state.deck except AssertionError: import pdb pdb.set_trace() if not state.deck: def played_card_sum(player): result = 0 for moved_player, card in state.currentTrick: if moved_player == player: result += card.value return result cards = [(player, state.playerHands[player], played_card_sum(player)) for player in state.user_ctl.users if not player.lost] cards.sort(key=lambda item: (item[1], item[2]), reverse=True) winner = cards[0][0] if winner == state.playerToMove: if is_maximizing_player: return depth - 100, None else: return 100 - depth, None else: if is_maximizing_player: return 100 - depth, None else: return depth - 100, None if is_maximizing_player: return depth - 100, None else: return 100 - depth, None if is_maximizing_player: best_value = -Minimax.INF else: best_value = Minimax.INF previous_move = None best_move = None for move in state.get_moves(): if move == previous_move: continue previous_move = move victim, guess = None, None if move.name == "Guard": victim, guess = get_guess_card(state.user_ctl.users, state.playerToMove, state.seen_cards) state_copy = deepcopy(state) state_copy.do_move(move, victim=victim, guess=guess) value, _ = Minimax._minimax(state_copy, not is_maximizing_player, alpha, beta,depth + 1) if is_maximizing_player: if best_value < value: best_value = value best_move = move alpha = max(alpha, best_value) else: if best_value > value: best_value = value best_move = move beta = min(beta, best_value) if beta <= alpha: break return best_value, best_move
def get_move(self, rootstate, itermax, verbose=True, **kwargs): """ Conduct an ISMCTS search for itermax iterations starting from rootstate. Return the best move from the rootstate. """ print(rootstate.playerHands[rootstate.playerToMove]) move, victim, guess = get_move(rootstate, ismcts=True) if move: return move, victim, guess moves = rootstate.get_moves() trees_number = kwargs.get('trees_number', 50) decision_counter = defaultdict(int) for j in range(trees_number): state_copy = rootstate.clone_and_randomize() rootnode = Node() counter = 0 for i in range(itermax // trees_number): state = deepcopy(state_copy) node = rootnode # determinize node = self.select(state, node) if counter == 5: print(rootnode.tree_to_string(indent=0)) node = self.expand(state, node) if counter == 5: print(rootnode.tree_to_string(indent=0)) self.simulate(state) if counter == 5: print(rootnode.tree_to_string(indent=0)) self.backpropagate(state, node) if counter == 5: print(rootnode.tree_to_string(indent=0)) import pdb pdb.set_trace() counter += 1 decision_counter[self.select_final_move(rootnode, rootstate)[0]] += 1 # Output some information about the tree - can be omitted print("{} -> {}".format(moves[0], decision_counter[moves[0]])) print("{} -> {}".format(moves[1], decision_counter[moves[1]])) if decision_counter[moves[0]] >= decision_counter[moves[1]]: if moves[0].name == "Guard": # if final move is Guard, then guess card victim, guess = get_guess_card(rootstate.user_ctl.users, rootstate.playerToMove, rootstate.seen_cards) if guess: return moves[0], victim, guess return moves[0], None, None else: if moves[1].name == "Guard": # if final move is Guard, then guess card victim, guess = get_guess_card(rootstate.user_ctl.users, rootstate.playerToMove, rootstate.seen_cards) if guess: return moves[1], victim, guess return moves[1], None, None
def get_move(state, ismcts=False): # TODO: fix multiple opponents case (selecting opponents) cards = state.playerHands[state.playerToMove] victim, guess = None, None maid = Maid() guard = Guard() princess = Princess() priest = Priest() baron = Baron() prince = Prince() king = King() countess = Countess() opponent = [ player for player in state.user_ctl.users if player != state.playerToMove ][0] twin_cards = [priest, baron, maid, priest] victim, guess = get_guess_card(state.user_ctl.users, state.playerToMove, state.seen_cards) card_count = { Princess(): 1, Countess(): 1, King(): 1, Prince(): 2, Maid(): 2, Baron(): 2, Priest(): 2, Guard(): 5, } # substract used cards for card, counter in state.used_cards.items(): card_count[card] -= counter assert card_count[card] >= 0 # substract cards in hand for card in state.playerHands[state.playerToMove]: card_count[card] -= 1 assert card_count[card] >= 0 def play_guard(): """ Selects probable guess when playing with Guard :return: probable guess card """ available_cards = sorted( [(counter, card) for card, counter in card_count.items() if card != guard], reverse=True) most_probable_cards = [ card for counter, card in available_cards if counter == available_cards[0][0] ] return random.choice(most_probable_cards) def get_probability(player_card): c = 0 if card_count[player_card] > 0: for card in twin_cards: if card_count[card] == 2 or (card_count[card] == 1 and card in cards): c += 1 return 1 / c for card, counter in card_count.items(): if counter > 0 or card in cards: c += 1 return 1 / c def lose_with_baron(card): """ Will the current player lose the game if he makes move with Baron :param card: :return: """ if card != baron: return False if cards[0] == baron: if guess and cards[1] <= guess: return True if cards[1] <= baron: return True if cards[1] == baron: if guess and cards[0] <= guess: return True if cards[0] <= baron: return True return False # follow the rules about Countess if countess in cards: if king in cards: return king, None, None if prince in cards: return prince, None, None # if player has guard and knows an opponent's card if guard in cards and victim: return guard, victim, guess # if player knows an opponent's card and has a greater card if baron in cards and victim: if cards[0] == baron and cards[1] > guess: return baron, victim, None if cards[1] == baron and cards[0] > guess: return baron, victim, None # play with prince if player holds the princess if guess == princess and prince in cards: return prince, victim, None # make last move with minimal card if sum(card_count.values()) <= 2: card = min(cards) if card == guard: guess = play_guard() return min(cards), victim, guess # do not play with princess if cards[0] == princess: if cards[1] == guard: guess = play_guard() return cards[1], victim, guess # do not play with princess try: if cards[1] == princess: if cards[0] == guard: guess = play_guard() return cards[0], victim, guess except: import pdb pdb.set_trace() # if both cards are same, play any of them for card in card_dict.values(): if cards.count(card) == 2: if card == guard: guess = play_guard() assert guess != guard return card, victim, guess # return seen card by an opponent if any for card in cards: # TODO: когда мы не видели карту, но нашего барона видели, а вторая карта слишком маленькая if not ismcts and card != guard and card in state.seen_cards[opponent][ state.playerToMove] and card_count[ guard] > 0 and not lose_with_baron(card): return card, victim, guess # Guard and Priest if not ismcts and guard in cards and priest in cards: return priest, victim, guess # check if twin card has high prob to be guessed for card in twin_cards: # TODO: когда мы не видели карту, но нашего барона видели, а вторая карта слишком маленькая if not ismcts and card in cards and get_probability( card) >= 0.5 and card_count[guard] > 0 and not lose_with_baron( card): return card, victim, guess # return nothing for ismcts algorithm if ismcts: return [None] * 3 card = min(cards) if card == guard: guess = play_guard() if lose_with_baron(card): card = max(cards) return card, victim, guess