import math import torch import numpy as np class Node: def __init__(self, game, args, state, parent = None, action = None, prob = 0, visits = 0): self.game = game self.args = args self.state = state self.parent = parent self.action = action self.prob = prob self.children = [] self.visits = visits self.value = 0 def leaf_or_not(self): return len(self.children) > 0 def search(self): best_child = None best_ucb = -np.inf for child in self.children: ucb = self.get_ucb(child) if best_ucb < ucb: best_ucb = ucb best_child = child return best_child def get_ucb(self, child): if child.visits == 0: q_value = 0 else: q_value = 1 - ((child.value / child.visits) + 1) / 2 return q_value + self.args['EXPLORATION_CONSTANT'] * (math.sqrt(self.visits) / (child.visits + 1)) * child.prob def expand(self, policy): for move, prob in enumerate(policy): if prob > 0: child = self.state.copy() child = self.game.make_move(child, move, 1) if self.args["ADVERSARIAL"]: child = self.game.change_perspective(child, player = -1) child = Node(self.game, self.args, child, self, move, prob) self.children.append(child) def backpropagate(self,state_value): self.value += state_value self.visits += 1 if self.args["ADVERSARIAL"]: state_value = self.game.get_opponent_value(state_value) if self.parent is not None: self.parent.backpropagate(state_value) class Alpha_MCTS: def __init__(self, game, args, model): self.game = game self.args = args self.model = model @torch.no_grad() def search(self, states, spGames): policy, _ = self.model( torch.tensor(self.game.get_encoded_state(states), device = self.model.device )) policy = torch.softmax(policy, axis = 1).cpu().numpy() if self.args["ROOT_RANDOMNESS"]: policy = (1 - self.args["DIRICHLET_EPSILON"]) * policy + self.args["DIRICHLET_EPSILON"] * np.random.dirichlet([self.args["DIRICHLET_ALPHA"]] * self.game.possible_state, size = policy.shape[0]) for i, spg in enumerate(spGames): spg_policy = policy[i] valid_state = self.game.get_valid_moves(states[i]) spg_policy *= valid_state spg_policy /= np.sum(spg_policy) spg.root = Node(self.game, self.args, states[i], visits = 1) spg.root.expand(spg_policy) for _ in range(self.args["NO_OF_SEARCHES"]): for spg in spGames: spg.node = None node = spg.root while node.leaf_or_not(): node = node.search() is_terminal, value = self.game.know_terminal_value(node.state, node.action) if self.args["ADVERSARIAL"]: value = self.game.get_opponent_value(value) if is_terminal: node.backpropagate(value) else: spg.node = node expandabel_spgs = [mapping_index for mapping_index in range(len(spGames)) if spGames[mapping_index].node is not None] if len(expandabel_spgs) > 0: states = np.stack([spGames[mapping_index].node.state for mapping_index in expandabel_spgs]) policy, value = self.model( torch.tensor(self.game.get_encoded_state(states), device = self.model.device) ) policy = torch.softmax(policy, axis = 1).cpu().numpy().astype(np.float64) value = value.cpu().numpy() for i, mapping_index in enumerate(expandabel_spgs): node = spGames[mapping_index].node spg_policy, spg_value = policy[i], value[i] valid_state = self.game.get_valid_moves(node.state) spg_policy *= valid_state spg_policy /= np.sum(spg_policy) node.expand(spg_policy) node.backpropagate(spg_value)