runSelfPlaySession method

Future<void> runSelfPlaySession(
  1. int numGames, {
  2. int simulations = 30,
})

Implementation

Future<void> runSelfPlaySession(int numGames, {int simulations = 30}) async {
  final search = MuZeroSearch(model);
  final random = math.Random();

  for (int g = 0; g < numGames; g++) {
    final game = Game(variant: Variant.standard());
    List<int> history = [0];
    List<GameStep> gameSteps = [];

    while (!game.gameOver && history.length < 100) {
      final legalMoves = game.generateLegalMoves();
      if (legalMoves.isEmpty) break;

      final List<int> legalActions = legalMoves
          .map((m) => encodeMove(m, game))
          .toList();

      // 1. MCTS SEARCH
      final rootState = model.represent(history);
      MCTSNode root = MCTSNode(rootState);

      final prediction = model.predict(rootState);
      root.priors = search.filterPriors(
        prediction['policy']!.data,
        legalActions,
      );

      for (int i = 0; i < simulations; i++) {
        search.runSimulation(root);
      }

      // 2. CREATE TARGET POLICY
      List<double> targetPi = List.filled(4098, 0.0);
      int totalVisits = root.visitCounts.values.fold(0, (a, b) => a + b);

      // Temperature handling
      double temp = (history.length < 30) ? 1.0 : 0.5;
      double sumVisitExp = 0;
      for (int action in root.priors.keys) {
        double v = (root.visitCounts[action] ?? 0).toDouble();
        double p = math.pow(v + 1e-8, 1 / temp).toDouble();
        targetPi[action] = p;
        sumVisitExp += p;
      }

      for (int i = 0; i < targetPi.length; i++) {
        if (targetPi[i] > 0) targetPi[i] /= (sumVisitExp + 1e-10);
      }

      // 3. SELECT ACTION
      int chosenAction = _sampleAction(targetPi, random);
      gameSteps.add(GameStep(List.from(history), List.from(targetPi)));

      Move chosenMove = legalMoves.firstWhere(
        (m) => encodeMove(m, game) == chosenAction,
      );
      game.makeMove(chosenMove);

      history.add(chosenAction);
      if (history.length > 16) history.removeAt(0);
    }

    // 4. BACKFILL OUTCOME (Corrected Result Logic)
    double outcome = 0.0;
    // final result = game.result;

    if (game.won) {
      // game.winner returns the player index (0 for White, 1 for Black)
      outcome = (game.winner == Bishop.white) ? 1.0 : -1.0;
    } else if (game.drawn) {
      outcome = 0.0; // Standard for draws
    }

    for (var step in gameSteps) {
      step.targetValue = outcome;
      if (replayBuffer.length >= maxBufferSize) replayBuffer.removeAt(0);
      replayBuffer.add(step);
    }
  }
}