code: def on_train_result(self, *, algorithm, result, **kwargs):
      # Get the win rate for the train batch.
      # Note that normally, one should set up a proper evaluation config,
      # such that evaluation always happens on the already updated policy,
      # instead of on the already used train_batch.
      for policy_id, rew in result["hist_stats"].items():
          mo = re.match("^policy_(.+)_reward$", policy_id)
          if mo is None:
              continue
          policy_id = mo.group(1)
  
          # Calculate this policy's win rate.
          won = 0
          for r in rew:
              if r > 0.0:  # win = 1.0; loss = -1.0
                  won += 1
          win_rate = won / len(rew)
          self.win_rates[policy_id] = win_rate
  
          # Policy is frozen; ignore.
          if policy_id in self.non_trainable_policies:
              continue
  
          print(
              f"Iter={algorithm.iteration} {policy_id}'s "
              f"win-rate={win_rate} -> ",
              end="",
          )
  
          # If win rate is good -> Snapshot current policy and decide,
          # whether to freeze the copy or not.
          if win_rate > args.win_rate_threshold:
              is_main = re.match("^main(_\\d+)?$", policy_id)
              initializing_exploiters = False
  
              # First time, main manages a decent win-rate against random:
              # Add league_exploiter_0 and main_exploiter_0 to the mix.
              if is_main and len(self.trainable_policies) == 3:
                  initializing_exploiters = True
                  self.trainable_policies.add("league_exploiter_1")
                  self.trainable_policies.add("main_exploiter_1")
              else:
                  keep_training = (
                      False
                      if is_main
                      else np.random.choice([True, False], p=[0.3, 0.7])
                  )
                  if policy_id in self.main_policies:
                      new_pol_id = re.sub(
                          "_\\d+$",
                          f"_{len(self.main_policies) - 1}",
                          policy_id,
                      )
                      self.main_policies.add(new_pol_id)
                  elif policy_id in self.main_exploiters:
                      new_pol_id = re.sub(
                          "_\\d+$",
                          f"_{len(self.main_exploiters)}",
                          policy_id,
                      )
                      self.main_exploiters.add(new_pol_id)
                  else:
                      new_pol_id = re.sub(
                          "_\\d+$",
                          f"_{len(self.league_exploiters)}",
                          policy_id,
                      )
                      self.league_exploiters.add(new_pol_id)
  
                  if keep_training:
                      self.trainable_policies.add(new_pol_id)
                  else:
                      self.non_trainable_policies.add(new_pol_id)
  
                  print(
                      f"adding new opponents to the mix ({new_pol_id})."
                  )
  
              # Update our mapping function accordingly.
              def policy_mapping_fn(
                  agent_id, episode, worker, **kwargs
              ):
                  # Pick, whether this is ...
                  type_ = np.random.choice([1, 2])
  
                  # 1) League exploiter vs any other.
                  if type_ == 1:
                      league_exploiter = "league_exploiter_" + str(
                          np.random.choice(
                              list(range(len(self.league_exploiters)))
                          )
                      )
                      # This league exploiter is frozen: Play against a
                      # trainable policy.
                      if (
                          league_exploiter
                          not in self.trainable_policies
                      ):
                          opponent = np.random.choice(
                              list(self.trainable_policies)
                          )
                      # League exploiter is trainable: Play against any other
                      # non-trainable policy.
                      else:
                          opponent = np.random.choice(
                              list(self.non_trainable_policies)
                          )
                      print(f"{league_exploiter} vs {opponent}")
                      return (
                          league_exploiter
                          if episode.episode_id % 2 == agent_id
                          else opponent
                      )
  
                  # 2) Main exploiter vs main.
                  else:
                      main_exploiter = "main_exploiter_" + str(
                          np.random.choice(
                              list(range(len(self.main_exploiters)))
                          )
                      )
                      # Main exploiter is frozen: Play against the main
                      # policy.
                      if main_exploiter not in self.trainable_policies:
                          main = "main"
                      # Main exploiter is trainable: Play against any
                      # frozen main.
                      else:
                          main = np.random.choice(
                              list(self.main_policies - {"main"})
                          )
                      # print(f"{main_exploiter} vs {main}")
                      return (
                          main_exploiter
                          if episode.episode_id % 2 == agent_id
                          else main
                      )
  
              # Set the weights of the new polic(y/ies).
              if initializing_exploiters:
                  main_state = algorithm.get_policy("main").get_state()
                  pol_map = algorithm.workers.local_worker().policy_map
                  pol_map["main_0"].set_state(main_state)
                  pol_map["league_exploiter_1"].set_state(main_state)
                  pol_map["main_exploiter_1"].set_state(main_state)
                  # We need to sync the just copied local weights to all the
                  # remote workers as well.
                  algorithm.workers.sync_weights(
                      policies=[
                          "main_0",
                          "league_exploiter_1",
                          "main_exploiter_1",
                      ]
                  )
  
                  def _set(worker):
                      worker.set_policy_mapping_fn(policy_mapping_fn)
                      worker.set_is_policy_to_train(
                          self.trainable_policies
                      )
  
                  algorithm.workers.foreach_worker(_set)
              else:
                  new_policy = algorithm.add_policy(
                      policy_id=new_pol_id,
                      policy_cls=type(algorithm.get_policy(policy_id)),
                      policy_mapping_fn=policy_mapping_fn,
                      policies_to_train=self.trainable_policies,
                  )
                  main_state = algorithm.get_policy(
                      policy_id
                  ).get_state()
                  new_policy.set_state(main_state)
                  # We need to sync the just copied local weights to all the
                  # remote workers as well.
                  algorithm.workers.sync_weights(policies=[new_pol_id])
  
              self._print_league()
  
          else:
              print("not good enough; will keep learning ...")
  
imports: import argparse
  import os
  import re
  import numpy
  import ray
  from ray import air, tune
  from ray.rllib.algorithms.callbacks import DefaultCallbacks
  from ray.rllib.algorithms.ppo import PPOConfig
  from ray.rllib.env.utils import try_import_pyspiel, try_import_open_spiel
  from ray.rllib.env.wrappers.open_spiel import OpenSpielEnv
  from ray.rllib.examples.policy.random_policy import RandomPolicy
  from ray.rllib.examples.self_play_with_open_spiel import ask_user_for_action
  from ray.rllib.policy.policy import PolicySpec
  from ray.tune import register_env
  from open_spiel.python.rl_environment import Environment
label_desc: Use image node_id 2 for calling the LeagueBasedSelfPlayCallback obj's underlying member method code with example usage: obj.on_train_result() and returns: unknown, unknown