public override MctsNode Simulate(POGame game) { POGame gameCopy = game.getCopy(); // initials root node var initLeafs = new List <MctsNode>(); var root = new MctsNode(_playerId, new List <MctsNode.ScoreExt> { new MctsNode.ScoreExt(1.0, _scoring) }, gameCopy, null, null); // simulate MctsNode bestNode = Simulate(_deltaTime, root, ref initLeafs); return(bestNode); }
public override PlayerTask GetMove(POGame poGame) { var player = poGame.CurrentPlayer; // Implement a simple Mulligan Rule if (player.MulliganState == Mulligan.INPUT) { List <int> mulligan = new CustomScore().MulliganRule().Invoke(player.Choice.Choices.Select(p => poGame.getGame().IdEntityDic[p]).ToList()); return(ChooseTask.Mulligan(player, mulligan)); } if (poGame.CurrentPlayer.Options().Count == 1) { return(poGame.CurrentPlayer.Options()[0]); } POGame initialState = poGame.getCopy(); Node root = new Node(); Node selectedNode; Node nodeToSimulate; float scoreOfSimulation; int iterations = 0; InitializeRoot(root, initialState); Stopwatch stopwatch = new Stopwatch(); stopwatch.Start(); while (stopwatch.ElapsedMilliseconds <= MAX_TIME) { poGame = initialState; selectedNode = Selection(root, iterations, ref poGame); nodeToSimulate = Expansion(selectedNode, ref poGame); for (int i = 0; i < NUM_SIMULATIONS; i++) { scoreOfSimulation = Simulation(nodeToSimulate, poGame); Backpropagation(nodeToSimulate, scoreOfSimulation); iterations++; } } stopwatch.Stop(); return(SelectAction.selectTask(SELECTION_ACTION_METHOD, root, iterations, EXPLORE_CONSTANT)); }
private Node Selection(Node root, int iterations, ref POGame poGame) { Node bestNode = new Node(); double bestScore = double.MinValue; double childScore = 0; POGame pOGameIfSimulationFail = poGame.getCopy(); foreach (Node node in root.children) { childScore = TreePolicies.selectTreePolicy(TREE_POLICY, node, iterations, EXPLORE_CONSTANT, ref poGame, SCORE_IMPORTANCE, greedyAgent); if (childScore > bestScore) { bestScore = childScore; bestNode = node; } } List <PlayerTask> taskToSimulate = new List <PlayerTask>(); taskToSimulate.Add(bestNode.task); if (bestNode.task.PlayerTaskType != PlayerTaskType.END_TURN) { poGame = poGame.Simulate(taskToSimulate)[bestNode.task]; } if (poGame == null) { root.children.Remove(bestNode); if (root.children.Count == 0) { root = root.parent; } poGame = pOGameIfSimulationFail; return(Selection(root, iterations, ref poGame)); } if (bestNode.children.Count != 0) { bestNode = Selection(bestNode, iterations, ref poGame); } return(bestNode); }
private Node Expansion(Node leaf, ref POGame poGame) { Node nodeToSimulate; POGame pOGameIfSimulationFail = poGame.getCopy(); if (leaf.timesVisited == 0 || leaf.depth >= TREE_MAXIMUM_DEPTH || leaf.task.PlayerTaskType == PlayerTaskType.END_TURN) { nodeToSimulate = leaf; } else { foreach (PlayerTask task in poGame.CurrentPlayer.Options()) { leaf.children.Add(new Node(task, leaf, leaf.depth + 1)); } nodeToSimulate = leaf.children[0]; List <PlayerTask> taskToSimulate = new List <PlayerTask>(); taskToSimulate.Add(nodeToSimulate.task); if (nodeToSimulate.task.PlayerTaskType != PlayerTaskType.END_TURN) { poGame = poGame.Simulate(taskToSimulate)[nodeToSimulate.task]; } while (poGame == null) { if (leaf.children.Count <= 1) { return(leaf); } poGame = pOGameIfSimulationFail; taskToSimulate.Clear(); leaf.children.Remove(leaf.children[0]); nodeToSimulate = leaf.children[0]; taskToSimulate.Add(nodeToSimulate.task); if (nodeToSimulate.task.PlayerTaskType != PlayerTaskType.END_TURN) { poGame = poGame.Simulate(taskToSimulate)[nodeToSimulate.task]; } } } return(nodeToSimulate); }
private Node Expand(Node node, POGame state) { Node child; do { child = node.Children[Rand.Next(node.Children.Count)]; }while (child.Children.Count > 0); POGame childState = state.getCopy(); childState = childState.Simulate(new List <PlayerTask> { child.Action })[child.Action]; InitializeNode(child, childState); return(child); }
public override PlayerTask GetMove(POGame poGame) { Controller player = poGame.CurrentPlayer; IEnumerable <KeyValuePair <PlayerTask, POGame> > validOpts = poGame.Simulate(player.Options()).Where(x => x.Value != null); int optcount = validOpts.Count(); int maxMilliseconds = 25000; // Math.Min(Math.Max(optcount * 2000, 8000), 20000); // Implement a simple Mulligan Rule if (player.MulliganState == Mulligan.INPUT) { List <int> mulligan = new MyScore().MulliganRule().Invoke(player.Choice.Choices.Select(p => poGame.getGame().IdEntityDic[p]).ToList()); return(ChooseTask.Mulligan(player, mulligan)); } PlayerTask returnTask = validOpts.Any() ? GetMoveMCTS(poGame.getCopy(), maxMilliseconds) : player.Options().First(x => x.PlayerTaskType == PlayerTaskType.END_TURN); if (returnTask == null) { returnTask = player.Options().First(x => x.PlayerTaskType == PlayerTaskType.END_TURN); } return(returnTask); }
public override PlayerTask GetMove(POGame poGame) { var player = poGame.CurrentPlayer; // Implement a simple Mulligan Rule if (player.MulliganState == Mulligan.INPUT) { List <int> mulligan = new ControlScore().MulliganRule().Invoke(player.Choice.Choices.Select(p => poGame.getGame().IdEntityDic[p]).ToList()); return(ChooseTask.Mulligan(player, mulligan)); } // Apply MCTS and do the best move PlayerTask action = null; try { if (mcts) { action = MCTS(poGame.getCopy()); } else { var legalMoves = poGame.Simulate(player.Options()).Where(x => x.Value != null); return(legalMoves.Any() ? legalMoves.OrderBy(x => Score(x.Value, player.PlayerId)).Last().Key : player.Options().First(x => x.PlayerTaskType == PlayerTaskType.END_TURN)); } } catch (NullReferenceException) { action = player.Options().First(x => x.PlayerTaskType == PlayerTaskType.END_TURN); } if (myDebug) { Console.WriteLine(); Console.WriteLine(poGame.FullPrint()); Console.WriteLine("Chose action: " + action); } return(action); }
public MctsNode(int playerId, List <ScoreExt> scorings, POGame game, PlayerTask task, MctsNode parent) { _parent = parent; _scorings = scorings; _playerId = playerId; _game = game.getCopy(); _task = task; VisitCount = 1; if (Task != null) { Dictionary <PlayerTask, POGame> dir = Game.Simulate(new List <PlayerTask> { Task }); POGame newGame = dir[Task]; Game = newGame; // simulation has failed, maybe reduce score? if (Game == null) { _endTurn = 1; } else { _gameState = Game.State == SabberStoneCore.Enums.State.RUNNING ? 0 : (PlayerController.PlayState == PlayState.WON ? 1 : -1); _endTurn = Game.CurrentPlayer.Id != _playerId ? 1 : 0; foreach (ScoreExt scoring in Scorings) { scoring.Controller = PlayerController; _score += scoring.Value * scoring.Rate(); } _score /= Scorings.Count; TotalScore += _score; } } }
public PlayerTask Search() { List <PlayerTask> options = player.Options(); if (options.Count == 1 && options[0].PlayerTaskType == PlayerTaskType.END_TURN) { return(options.First()); } StopWatch.Start(); var selectionStrategy = SelectionStrategies.GetSelectionStrategy(Selection); var stateRateStrategy = StateRateStrategies.GetStateRateStrategy(StateRate); while (StopWatch.ElapsedMilliseconds < COMPUTATIONAL_BUDGET) { POGame state = InitialState.getCopy(); Node lastNode = TreePolicy(Root); float delta = DefaultPolicyHeuristic(lastNode); Backup(lastNode, delta); } StopWatch.Stop(); return(ChildSelection.SelectBestChild(InitialState, Root, EXPLORATION_CONSTANT, player, selectionStrategy, stateRateStrategy).Action); }
public MinMaxAgentState(POGame game) { this.currentGameState = game.getCopy(); this.actionsToReachCurrentGameState = new List <PlayerTask>(); this.entireActionList = new List <PlayerTask>(currentGameState.CurrentPlayer.Options()); }
//POGame poGame2; public override PlayerTask GetMove(POGame poGame) { var player = poGame.CurrentPlayer; // Implement a simple Mulligan Rule if (player.MulliganState == Mulligan.INPUT) { List <int> mulligan = new CustomScore().MulliganRule().Invoke(player.Choice.Choices.Select(p => poGame.getGame().IdEntityDic[p]).ToList()); return(ChooseTask.Mulligan(player, mulligan)); } #if DEBUG Console.WriteLine($"root:{GetGameHashCode(poGame)}"); #endif PlayerTask bestAction = null; if (poGame.CurrentPlayer.Options().Count == 1) { bestAction = poGame.CurrentPlayer.Options()[0]; } else { stopwatchForThisTurn.Start(); long bestActionCode = 0; Trajectory trajectory = new Trajectory(); List <PlayerTask> taskToSimulate = new List <PlayerTask>(1); taskToSimulate.Add(null); POGame poGameRoot = poGame; Node root; long gameHashCodeRoot = GetGameHashCode(poGameRoot); if (!nodeHashMap.TryGetValue(gameHashCodeRoot, out root)) { root = new Node(); nodeHashMap.Add(gameHashCodeRoot, root); } Expand(root, poGameRoot); /*foreach (var child in root.edges) * { * Console.WriteLine(child.actionHashCode); * }*/ long think_time = (30 * 1000 - stopwatchForThisTurn.ElapsedMilliseconds) / Math.Max(3, 5 - movesInThisTurn); Stopwatch stopwatch = new Stopwatch(); stopwatch.Start(); //for (int itr = 0; itr < 100; ++itr) while (stopwatch.ElapsedMilliseconds <= think_time) { Node node = root; poGame = poGameRoot.getCopy(); long gameHashCode = gameHashCodeRoot; long actionHashCodeNext = 0; bool simulateResult = true; int index = 0; trajectory.Clear(); // traverse do { index = Select(node); /*if (index >= node.edges.Count) * { * Console.WriteLine($"{index}, {node.edges.Count}, {node == root}"); * Debugger.Break(); * }*/ actionHashCodeNext = node.edges[index].actionHashCode; // Until the end of my own turn if (actionHashCodeNext == 0) { trajectory.Add((node, index)); break; } taskToSimulate[0] = null; foreach (PlayerTask task in poGame.CurrentPlayer.Options()) { if (GetActionHashCode(task) == actionHashCodeNext) { taskToSimulate[0] = task; break; } } if (taskToSimulate[0] == null) { // Hash key conflict return(poGame.CurrentPlayer.Options().First()); /*foreach (PlayerTask task in poGame.CurrentPlayer.Options()) * { * Console.WriteLine($"{task}, {GetActionHashCode(task)}"); * } * Console.WriteLine("---"); * foreach (var edge in node.edges) * { * Console.WriteLine($"{edge.task}, {edge.actionHashCode}"); * } * poGame2 = node.poGame; * Console.WriteLine(poGame2.Turn); * Console.WriteLine(gameHashCode); * Console.WriteLine("---"); * foreach (var minion in poGame.CurrentPlayer.BoardZone) * { * Console.WriteLine(minion.CantAttackHeroes); * } * var tasks = poGame.CurrentPlayer.Options(); * foreach (PlayerTask task in tasks) * { * Console.WriteLine($"{task}, {GetActionHashCode(task)}"); * } * Debugger.Break();*/ } poGame = poGame.Simulate(taskToSimulate)[taskToSimulate[0]]; long gameHashCodeNext = GetGameHashCode(poGame); if (gameHashCode == gameHashCodeNext) { // loop node.edges.RemoveAt(index); continue; } gameHashCode = gameHashCodeNext; trajectory.Add((node, index)); if (!nodeHashMap.TryGetValue(gameHashCode, out node)) { node = new Node(); //node.poGame = poGame; nodeHashMap.Add(gameHashCode, node); } } while (node.edges != null); if (simulateResult == false) { continue; } if (actionHashCodeNext != 0) { #if DEBUG Console.WriteLine($"expand:{gameHashCode}"); #endif Expand(node, poGame); float value = Simulate(node, poGame); Backup(trajectory, value); } else { float value; if (node.edges[index].visitCount == 0) { value = ScoreToValue(Score(poGame)); } else { value = node.edges[index].totalValue / node.edges[index].visitCount; } Backup(trajectory, value); } } stopwatch.Stop(); //Console.WriteLine($"{think_time}, {root.visitCount}, {root.visitCount * 1000 / stopwatch.ElapsedMilliseconds} nps"); // Choose the most visited node float best = Single.MinValue; foreach (Edge child in root.edges) { if (child.visitCount >= best) { best = child.visitCount; bestActionCode = child.actionHashCode; } } // Choose an action with a matching hash code foreach (PlayerTask task in poGameRoot.CurrentPlayer.Options()) { if (GetActionHashCode(task) == bestActionCode) { bestAction = task; break; } } } stopwatchForThisTurn.Stop(); ++movesInThisTurn; if (bestAction.PlayerTaskType == PlayerTaskType.END_TURN) { //Console.WriteLine(movesInThisTurn); stopwatchForThisTurn.Reset(); movesInThisTurn = 0; nodeHashMap.Clear(); } return(bestAction); }
public override PlayerTask GetMove(POGame game) { var player = game.CurrentPlayer; // Implement a simple Mulligan Rule if (player.MulliganState == Mulligan.INPUT) { List <int> mulligan = new CustomScore().MulliganRule().Invoke(player.Choice.Choices.Select(p => game.getGame().IdEntityDic[p]).ToList()); return(ChooseTask.Mulligan(player, mulligan)); } var opponent = game.CurrentOpponent; var options = player.Options(); var validOpts = game.Simulate(options).Where(x => x.Value != null); var optcount = validOpts.Count(); #if DEBUG /* * var score_res = validOpts.Select(x => score(x, player.PlayerId, (optcount >= 5) ? ((optcount >= 25) ? 1 : 2) : 3)).OrderBy(x => x.Value); * * Console.WriteLine("Round Nr:" + Convert.ToString(game.Turn)); * Console.WriteLine("HeroHP: " + Convert.ToString(player.Hero.Health) + "\tOppHP: " + Convert.ToString(game.CurrentOpponent.Hero.Health)); * Console.WriteLine("HeroMinionHP: " + Convert.ToString(player.BoardZone.Sum(p => p.Health)) + "\tOppMinionHP: " + Convert.ToString(game.CurrentOpponent.BoardZone.Sum(p => p.Health))); * Console.WriteLine("HeroMinionAtk: " + Convert.ToString(player.BoardZone.Sum(p => p.AttackDamage)) + "\tOppMinionAtk: " + Convert.ToString(game.CurrentOpponent.BoardZone.Sum(p => p.AttackDamage))); * Console.WriteLine("HeroNbMinions: " + Convert.ToString(player.BoardZone.Count) + "\tOppMinionNB: " + Convert.ToString(game.CurrentOpponent.BoardZone.Count)); * * * foreach (var tmp_score in score_res) * { * * Console.WriteLine(Convert.ToString(tmp_score.Key) + Convert.ToString(tmp_score.Value)); * } * Console.WriteLine("-------------------------------------------------------"); * //PrintLog(game, score_res); */ #endif /* * if (player.Hero.Health < DEFENSE_HEALTH_THRESHOLD * player.Hero.BaseHealth) * RuntimeScaling[0] += 0.1; * * if (opponent.Hero.Health < DEFENSE_HEALTH_THRESHOLD * opponent.Hero.Health) * RuntimeScaling[1] += 0.1; */ var opt1 = options.Where(x => x.HasSource && x.Source.Card.Name == "Reno Jackson"); //if (opt1.Count() > 0 && (player.Hero.Health - opponent.Hero.AttackDamage - opponent.BoardZone.Sum(p => p.AttackDamage) <= 3 || player.Hero.Health<10)) //if (opt1.Count() > 0 && (player.Hero.Health - opponent.Hero.AttackDamage - opponent.BoardZone.Sum(p => p.AttackDamage) <= 6 )) if (opt1.Count() > 0 && (player.Hero.Health - opponent.Hero.AttackDamage - opponent.BoardZone.Sum(p => p.AttackDamage) <= 3 || player.Hero.Health < 10)) { var tmp_game = game.getCopy(); //Reno Jackson has 6 mana int mana = (tmp_game.CurrentPlayer.RemainingMana - 6) > 0 ? (tmp_game.CurrentPlayer.RemainingMana - 6) : 0; //Console.WriteLine("mana " + Convert.ToString(tmp_game.CurrentPlayer.BaseMana)); var validOptsLoc = tmp_game.Simulate(options).Where(x => x.Value != null); var optcountLoc = validOptsLoc.Count(); var score_resLoc = validOptsLoc.Select(x => score(x, player.PlayerId, (optcountLoc >= 5) ? ((optcountLoc >= 25) ? 1 : 2) : 3)).Where(x => (x.Key.Source == null || x.Key.Source.Cost <= mana || x.Value == Int32.MaxValue)).Where(x => Convert.ToString(x.Key).Contains("Fireblast") != true || mana >= 1).OrderBy(x => x.Value); /*Console.WriteLine("OPTS"); * * foreach (var tmp_score in score_resLoc) * { * * Console.WriteLine(Convert.ToString(tmp_score.Key) + Convert.ToString(tmp_score.Value)); * } * Console.WriteLine("-------------------------------------------------------"); * * Console.WriteLine("Round Nr:" + Convert.ToString(game.Turn)); * Console.WriteLine("HeroHP: " + Convert.ToString(player.Hero.Health) + "\tOppHP: " + Convert.ToString(game.CurrentOpponent.Hero.Health)); * Console.WriteLine("HeroMinionHP: " + Convert.ToString(player.BoardZone.Sum(p => p.Health)) + "\tOppMinionHP: " + Convert.ToString(game.CurrentOpponent.BoardZone.Sum(p => p.Health))); * Console.WriteLine("HeroMinionAtk: " + Convert.ToString(player.BoardZone.Sum(p => p.AttackDamage)) + "\tOppMinionAtk: " + Convert.ToString(game.CurrentOpponent.BoardZone.Sum(p => p.AttackDamage))); * Console.WriteLine("HeroNbMinions: " + Convert.ToString(player.BoardZone.Count) + "\tOppMinionNB: " + Convert.ToString(game.CurrentOpponent.BoardZone.Count)); * * //Console.WriteLine("OPTS:" + Convert.ToString(score_res.First())); * //Console.WriteLine("OPTS:" + Convert.ToString(score_res.Last())); * //Console.WriteLine("OPTS:" + Convert.ToString(score_res.Count())); */ if (score_resLoc.Count() > 1) { //Console.WriteLine("OPTION TAKEN" + Convert.ToString(score_resLoc.Last().Key)); return(score_resLoc.Last().Key); } //Console.WriteLine("REEEEEEEEEEENNNNNNNNNNNOOOOOOOOOOOOOOO:" + Convert.ToString(player.Hero.Health)); //Console.WriteLine("OPTS:" + Convert.ToString(opt1.First())); return(opt1.First()); } var returnValue = validOpts.Any() ? validOpts.Select(x => score(x, player.PlayerId, (optcount >= 5) ? ((optcount >= 25) ? 1 : 2) : 3)).OrderBy(x => x.Value).Where(x => Convert.ToString(x.Key).Contains("Reno Jackson") != true).Last().Key : player.Options().First(x => x.PlayerTaskType == PlayerTaskType.END_TURN); return(returnValue); KeyValuePair <PlayerTask, int> score(KeyValuePair <PlayerTask, POGame> state, int player_id, int max_depth = 3) { int max_score = int.MinValue; if (max_depth > 0 && state.Value.CurrentPlayer.PlayerId == player_id) { var subactions = state.Value.Simulate(state.Value.CurrentPlayer.Options()).Where(x => x.Value != null); foreach (var subaction in subactions) { max_score = Math.Max(max_score, score(subaction, player_id, max_depth - 1).Value); } } max_score = Math.Max(max_score, Score(state.Value, player_id)); return(new KeyValuePair <PlayerTask, int>(state.Key, max_score)); } }
public MinMaxAgentState(POGame game) { State = game.getCopy(); ActionsToReachState = new List <PlayerTask>(); EntireActionList = new List <PlayerTask>(State.CurrentPlayer.Options()); }
public override MctsNode Simulate(POGame game) { Console.WriteLine("Current win rate is " + 0); POGame gameCopy = game.getCopy(); // initials root node var children = new List <MctsNode>(); var parent = new MctsNode(_playerId, new List <MctsNode.ScoreExt> { new MctsNode.ScoreExt(1, _scoring) }, gameCopy, null, null); // simulate MctsNode bestNode = Simulate(_deltaTime, parent, ref children); // initials opponent's history Initialize(gameCopy); var simulationQueue = new Queue <KeyValuePair <POGame, List <MctsNode> > >(); simulationQueue.Enqueue(new KeyValuePair <POGame, List <MctsNode> >(gameCopy, children)); int i = 0; while (i < _predictionParameters.SimulationDepth && simulationQueue.Count > 0) { // calculate the lower and upper time bound of the current depth double lowerSimulationTimeBound = _deltaTime + i * (2 * _deltaTime); KeyValuePair <POGame, List <MctsNode> > simulation = simulationQueue.Dequeue(); List <MctsNode> leafs = simulation.Value; leafs = leafs.Where(l => l.Game != null) .OrderByDescending(l => l.Score) .Take(leafs.Count > _predictionParameters.LeafCount ? _predictionParameters.LeafCount : leafs.Count) .ToList(); if (leafs.Count < 0) { return(bestNode); } Controller opponent = GetOpponent(simulation.Key); List <Prediction> predicitionMap = GetPredictionMap(simulation.Key, opponent); var oldSimulations = new Dictionary <POGame, List <MctsNode> >(); // the simulation time for one leaf double timePerLeaf = (2 * _deltaTime) / leafs.Count; // get all games from all leaf nodes for (int j = 0; j < leafs.Count; j++) { // calculate the lower time bound of the current leaf double lowerLeafTimeBound = lowerSimulationTimeBound + j * timePerLeaf; MctsNode leafNode = leafs[j]; POGame oppGame = leafNode.Game; double leafScore; // XXX: game can be null leafScore = SimulateOpponentWithPrediction(lowerLeafTimeBound, timePerLeaf, oppGame, opponent, predicitionMap, ref oldSimulations); // back-propagate score backpropagate(leafNode, leafScore); } // add new simulations foreach (KeyValuePair <POGame, List <MctsNode> > sim in oldSimulations) { simulationQueue.Enqueue(sim); } i++; } MctsNode resultnode = parent.Children .OrderByDescending(c => c.TotalScore) .First(); Console.WriteLine("Current win rate is " + resultnode.TotalScore); return(resultnode); }
private double SimulateOpponentWithPrediction(double lowerTimeBound, double timePerLeaf, POGame oppGame, Controller opponent, IReadOnlyList <Prediction> predicitionMap, ref Dictionary <POGame, List <MctsNode> > newSimulations) { double predictionScore = 0; if (!(predicitionMap?.Any() ?? false)) { return(predictionScore); } int denominator = predicitionMap.Count; var scorings = predicitionMap.GroupBy(p => p.Deck.Scoring) .Select(c => new MctsNode.ScoreExt(((double)c.Count() / denominator), c.Key)) .OrderByDescending(s => s.Value).ToList(); // the simulation time for one prediction double timePerPrediction = timePerLeaf / predicitionMap.Count; // use prediction for each game for (int i = 0; i < predicitionMap.Count; i++) { Prediction prediction = predicitionMap[i]; SetasideZone setasideZone; setasideZone = new SetasideZone(opponent); // create deck zone List <Card> deckCards = prediction.Deck.Cards; DeckZone deckZone; deckZone = new DeckZone(opponent); createZone(opponent, deckCards, deckZone, ref setasideZone); deckZone.Shuffle(); // create hand zone List <Card> handCards = prediction.Hand.Cards; HandZone handZone; handZone = new HandZone(opponent); createZone(opponent, handCards, handZone, ref setasideZone); var oppLeafNodes = new List <MctsNode>(); // forward game POGame forwardGame = oppGame.getCopy(); // upper time bound for simulation the opponent using the current prediction double oppSimulationTime = lowerTimeBound + (i + 1) * timePerPrediction / 2; // simulate opponent's moves while (forwardGame != null && forwardGame.State == SabberStoneCore.Enums.State.RUNNING && forwardGame.CurrentPlayer.Id == opponent.Id) { // simulate var oppRoot = new MctsNode(opponent.Id, scorings, forwardGame, null, null); MctsNode bestOppNode = Simulate(oppSimulationTime, oppRoot, ref oppLeafNodes); // get solution List <PlayerTask> solutions = bestOppNode.GetSolution(); for (int j = 0; j < solutions.Count && (forwardGame != null); j++) { PlayerTask oppTask = solutions[j]; Dictionary <PlayerTask, POGame> dir = forwardGame.Simulate(new List <PlayerTask> { oppTask }); forwardGame = dir[oppTask]; if (forwardGame != null && forwardGame.CurrentPlayer.Choice != null) { break; } } } // upper time bound for simulation the player using the forwarded game double simulationTime = oppSimulationTime + timePerPrediction / 2; double score = 0; var leafs = new List <MctsNode>(); // simulate player using forwarded opponent game while (forwardGame != null && forwardGame.State == SabberStoneCore.Enums.State.RUNNING && forwardGame.CurrentPlayer.Id == _playerId) { // simulate var root = new MctsNode(_playerId, new List <MctsNode.ScoreExt> { new MctsNode.ScoreExt(1.0, _scoring) }, forwardGame, null, null); MctsNode bestNode = Simulate(simulationTime, root, ref leafs); // get solution List <PlayerTask> solutions = bestNode.GetSolution(); for (int j = 0; j < solutions.Count && (forwardGame != null); j++) { PlayerTask task = solutions[j]; Dictionary <PlayerTask, POGame> dir = forwardGame.Simulate(new List <PlayerTask> { task }); forwardGame = dir[task]; if (forwardGame != null && forwardGame.CurrentPlayer.Choice != null) { break; } } // TODO: maybe penalty forwardGame == null score = bestNode.TotalScore; } predictionScore += score; if (forwardGame != null) { newSimulations.Add(forwardGame, leafs); } } return(predictionScore); }