text: class dotdict(dict):
      def __getattr__(self, name):
          if name.startswith('__'):
              raise AttributeError
          return self[name]
  
      def __setattr__(self, key, value):
          self[key] = value
  
      def copy(self):
          data = super().copy()
          return self.__class__(data)
  
  
  def get_iter_file(iteration: int):
      return f'iteration-{iteration:04d}.pkl'
  
  
  def scale_temp(scale_factor: float, min_temp: float, cur_temp: float, turns: int, const_max_turns: int) -> float:
      if (turns + 1) % int(scale_factor * const_max_turns) == 0:
          return max(min_temp, cur_temp / 2)
      else:
          return cur_temp
  
  
  def default_temp_scaling(*args, **kwargs) -> float:
      return scale_temp(0.15, 0.2, *args, **kwargs)
  
  
  def get_game_results(result_queue, game_cls, _get_index=None):
      player_to_index = {p: i for i, p in enumerate(range(game_cls.num_players()))}
  
      num_games = result_queue.qsize()
      wins = [0] * game_cls.num_players()
      draws = 0
      game_len_sum = 0
  
      for _ in range(num_games):
          state, winstate, agent_id = result_queue.get()
          game_len_sum += state.turns
  
          for player, is_win in enumerate(winstate):
              if is_win:
                  if player == len(wins):
                      draws += 1
                  else:
                      index = _get_index(player, agent_id) if _get_index else player_to_index[player]
                      wins[index] += 1
  
      return wins, draws, game_len_sum / num_games if num_games else 0
  
  
  def plot_mcts_tree(mcts, max_depth=2):
      import networkx as nx
      import matplotlib.pyplot as plt
      G = nx.Graph()
  
      global node_idx
      node_idx = 0
  
      def find_nodes(cur_node, _past_node=None, _past_i=None, _depth=0):
          if _depth > max_depth: return
          global node_idx
          cur_idx = node_idx
  
          G.add_node(cur_idx, a=cur_node.a, q=round(cur_node.q, 2), n=cur_node.n, v=round(cur_node.v, 2))
          if _past_node:
              G.add_edge(cur_idx, _past_i)
          node_idx += 1
  
          for node in cur_node._children:
              find_nodes(node, cur_node, cur_idx, _depth+1)
  
      find_nodes(mcts._root)
      labels = {node: '\n'.join(['{}: {}'.format(k, v) for k, v in G.nodes[node].items()]) for node in G.nodes}
      #pos = nx.spring_layout(G, k=0.15, iterations=50)
      pos = nx.nx_agraph.graphviz_layout(G, prog='dot', args='-Gnodesep=1.0 -Goverlap=false')
      nx.draw(G, pos, labels=labels)
      plt.show()
  