def play_move(self, game_state, statcache): """Play a move in game.""" first_moving = Game.player_with_priority(game_state) == 0 move = self.get_play(statcache) old_gs = game_state game_state = Game.apply_move(game_state, move) statcache.past_states.append(game_state) return move, game_state
def play_move(self, game_state, statcache): """Play a random move in game.""" move = choice(list(Game.legal_plays(game_state))) game_state = Game.apply_move(game_state, move) statcache.past_states.append(game_state) return move, game_state
def get_play(self, statcache): """ Return the best play, after simulating possible plays and updating plays and wins stats. """ game_state = statcache.past_states[-1] pwp = Game.player_with_priority(game_state) legal = Game.legal_plays(game_state) # Bail out early if there is no real choice to be made. if not legal: return [] if len(legal) == 1: return legal[0] games = 0 begin = datetime.datetime.utcnow() spinner = itertools.cycle(['-', '/', '|', '\\']) sys.stdout.write("Thinking ") while datetime.datetime.utcnow() - begin < self.calculation_time: self.run_simulation(statcache) sys.stdout.write(next(spinner)) sys.stdout.flush() sys.stdout.write('\b') games += 1 first_moving = Game.player_with_priority(game_state) == 0 if True or first_moving: print("SIMULATED {} playouts/s ({} playouts)".format( games * 1.0 / self.simulation_time, games)) CURSOR_UP_ONE = '\x1b[1A' ERASE_LINE = '\x1b[2K' if first_moving: print(ERASE_LINE + CURSOR_UP_ONE) moves_states = [] game_state = Game.set_print_moves(game_state, False) for p in legal: new_state = Game.apply_move(game_state, p) new_state = decarded_state(new_state) moves_states.append((p, tuple(new_state))) game_state = Game.set_print_moves(game_state, True) player = Game.acting_player(game_state) # Pick the move with the highest percentage of wins. percent_wins, move = max( (statcache.bot_stats(pwp).wins.get((player, S), 0) * 1.0 / statcache.bot_stats(pwp).plays.get((player, S), 1), p) for p, S in moves_states) ''' if self.show_simulation_results: # Display the stats for each possible play. for x in sorted( ((100 * statcache.bot_stats(pwp).wins.get((player, S), 0) * 1.0 / statcache.bot_stats(pwp).plays.get((player, S), 1), statcache.bot_stats(pwp).wins.get((player, S), 0), statcache.bot_stats(pwp).plays.get((player, S), 0), p) for p, S in moves_states), reverse=True ): print("{3}: {0:.2f}% ({1} / {2})".format(*x)) ''' return move
def run_simulation(self, statcache): state = statcache.past_states[-1] state = Game.set_print_moves(state, False) pwp = Game.player_with_priority(state) first_moving = Game.player_with_priority(state) == 0 # A bit of an optimization here, so we have a local # variable lookup instead of an attribute access each loop. plays, wins, legal_moves_cache = \ statcache.bot_stats(pwp).plays, \ statcache.bot_stats(pwp).wins, \ statcache.bot_stats(pwp).legal_moves_cache visited_states = set() player = Game.acting_player(state) expand = True for t in range(1, self.max_moves + 1): if state not in legal_moves_cache: legal_moves_cache[state] = Game.legal_plays(state) legal = legal_moves_cache[state] moves_states = [] play_randomly = False for p in legal: if (p[1], state) in plays: new_state = Game.apply_move(state, p) moves_states.append((p, new_state, new_state)) else: play_randomly = True break if play_randomly: move = choice(legal) state = Game.apply_move(state, move) elif all(plays.get((player, S)) for p, S in moves_states): # If we have stats on all of the legal moves here, use them. log_total = log( sum(plays[(player, S)] for p, S in moves_states)) value, move, state = max( ((wins[(player, S)] / plays[(player, S)]) + self.C * sqrt(log_total / plays[(player, S)]), p, S) for p, S, ended_game in moves_states) else: # Otherwise, just make an arbitrary decision. move, state, ended_game = choice(moves_states) # `player` here and below refers to the player # who moved into that particular state. state_clone = decarded_state(state) # print("moving {}".format(move)) # print("moving {} to state {}".format(move, state_clone)) if expand and (player, state_clone) not in plays: expand = False plays[(player, state_clone)] = 0 wins[(player, state_clone)] = 0 visited_states.add((player, state_clone)) player = Game.acting_player(state) winner = Game.winner(state) if winner >= 0: break for player, state in visited_states: if (player, state) not in plays: continue plays[(player, state)] += 1 if player == winner: wins[(player, state)] += 1
def play_move(self, game_state, statcache): """Plays the move in game that wins the most over the test iterations.""" legal_plays = Game.legal_plays(game_state) pwp = Game.player_with_priority(game_state) sorted_plays = [] for p in legal_plays: if p[0] == "resolve_combat" : sorted_plays.append(p) for card_type in ["land", "creature", "enchantment"]: for p in legal_plays: card_index = p[1] if p[0].startswith("card"): card = Game.get_hand(game_state, pwp)[card_index] if Card.card_type(card) == card_type: sorted_plays.append(p) attacks = [] for p in legal_plays: if p[0] == "announce_attackers": attacks.append(p) attacks.sort(key=lambda t: len(t[1]), reverse=True) for p in attacks: sorted_plays.append(p) for p in legal_plays: card_index = p[1] if p[0].startswith("card"): card = Game.get_hand(game_state, pwp)[card_index] if Card.card_type(card) not in ["creature", "land", "enchantment"]: sorted_plays.append(p) for p in legal_plays: if p not in sorted_plays and p[0] != "pass_the_turn" : sorted_plays.append(p) for p in legal_plays: if p[0] == "pass_the_turn": sorted_plays.append(p) if len(sorted_plays) > 1: for counter, play in enumerate(sorted_plays): if counter == len(sorted_plays) - 1 and play[0] == "pass_the_turn": print(" return: {}".format(Game.move_display_string(game_state, play))) else: print(" {}: {}".format(counter + 1, Game.move_display_string(game_state, play))) print(" p: Print your hand and the game board.") answered = False while not answered: choice = raw_input("Type the number of the action you want to play: ") if choice == 'p' or choice == 'P': Game.print_board(game_state, show_opponent_hand=False); return self.play_move(game_state, statcache) elif (not choice) and sorted_plays[-1][0] == "pass_the_turn": choice = len(sorted_plays) answered = True elif choice in [str(x) for x in range(0,len(sorted_plays)+1)]: choice = int(choice) if choice >= 1 and choice < len(sorted_plays) + 1: answered = True else: return self.play_move(game_state, statcache) else: choice = 1 game_state = Game.apply_move(game_state, sorted_plays[choice - 1]) statcache.past_states.append(game_state) return sorted_plays[choice - 1], game_state