public void AttackClosestEnemyTankToOwnBase(GameState currGameState, Tank killerTank, Tank targetTank, TankActionSet actionSet, bool[] moveChosenByTankNumber) { MobileState killerTankState = currGameState.GetMobileState(killerTank.Index); if (killerTankState.IsActive) { MobileState enemyTankState = currGameState.GetMobileState(targetTank.Index); if (enemyTankState.IsActive) { DirectionalMatrix <DistanceCalculation> attackMatrix = currGameState.CalculationCache.GetIncomingAttackMatrixForTankByTankIndex(targetTank.Index); if (attackMatrix != null) { DistanceCalculation attackCalculation = attackMatrix[killerTankState.Dir, killerTankState.Pos]; TankAction[] tankActions = PathCalculator.GetTankActionsOnIncomingShortestPath(attackMatrix, killerTankState, enemyTankState.Pos, currGameState.CalculationCache.FiringLinesForTanksMatrix, keepMovingCloserOnFiringLastBullet: true); if (tankActions.Length > 0) { actionSet.Actions[killerTank.Number] = tankActions[0]; moveChosenByTankNumber[killerTank.Number] = true; } } } } }
private bool TrySetBestMoveSoFar(GameState currGameState, GameSituation gameSituation) { TankActionSet actionSet = gameSituation.GenerateTankActions(YourPlayerIndex, currGameState.Tick); Coordinator.SetBestMoveSoFar(actionSet); return(true); }
public bool TrySetTankActions(TankActionSet actionSet, int timeoutInMilliseconds) { if (actionSet == null) { return(true); } if (actionSet.Tick != Game.Current.CurrentTurn.Tick) { LogDebugMessage("Tank actions not submitted as tick {0} is in the past", actionSet.Tick); return(false); } int playerIndex = actionSet.PlayerIndex; GameState currentGameState = Game.Current.CurrentTurn.GameState; int numberAlive = 0; int tankId = -1; TankAction tankAction = TankAction.NONE; int tankNumber = -1; for (int t = 0; t < Constants.TANKS_PER_PLAYER; t++) { Tank tank = Game.Current.Players[actionSet.PlayerIndex].Tanks[t]; MobileState tankState = currentGameState.GetMobileState(tank.Index); if (tankState.IsActive) { numberAlive++; tankId = tank.Id; tankAction = actionSet.Actions[t]; tankNumber = tank.Number; } } LogDebugMessage("Sending tank actions for player {0}", Solver.YourPlayerIndex); if (numberAlive == 1) { LogDebugMessage(" Tank {0} action {1} (tank id: {2})", tankNumber, tankAction, tankId); return(Communicator.TrySetAction(playerIndex, tankId, tankAction, CommunicatorCallback, timeoutInMilliseconds)); } else if (numberAlive == 2) { TankAction tankAction1 = actionSet.Actions[0]; TankAction tankAction2 = actionSet.Actions[1]; LogDebugMessage(" Tank 0 action {0}", tankAction1); LogDebugMessage(" Tank 1 action {0}", tankAction2); return(Communicator.TrySetActions(playerIndex, tankAction1, tankAction2, CommunicatorCallback, timeoutInMilliseconds)); } else { return(true); } }
protected override void ChooseMoves() { GameState currGameState = Game.Current.CurrentTurn.GameState; TankActionSet actionSet = new TankActionSet(YourPlayerIndex, currGameState.Tick); for (int tankNumber = 0; tankNumber < Constants.TANKS_PER_PLAYER; tankNumber++) { Tank tank = You.Tanks[tankNumber]; MobileState tankState = currGameState.GetMobileState(tank.Index); if (!tankState.IsActive) { continue; } int midX = currGameState.Walls.Width / 2; int midY = currGameState.Walls.Height / 2; int targetX; int targetY; Direction direction; if (tankState.Pos.X > midX) { targetX = currGameState.Walls.Width - Constants.SEGMENT_SIZE; } else { targetX = Constants.SEGMENT_SIZE; } if (tankState.Pos.Y > midY) { targetY = currGameState.Walls.Height - Constants.SEGMENT_SIZE; direction = Direction.UP; } else { targetY = Constants.SEGMENT_SIZE; direction = Direction.DOWN; } DirectionalMatrix <DistanceCalculation> distancesFromTank = currGameState.CalculationCache.GetDistanceMatrixFromTankByTankIndex(tank.Index); TankAction[] tankActions = PathCalculator.GetTankActionsOnOutgoingShortestPath( distancesFromTank, direction, targetX, targetY); if (tankActions.Length > 0) { actionSet.Actions[tank.Number] = tankActions[0]; } } Coordinator.SetBestMoveSoFar(actionSet); }
private void RespondToBullets(GameState currGameState, TankActionSet actionSet, bool[] moveChosen) { BulletCalculation bulletCalc = BulletCalculator.GetBulletCalculation(currGameState, You); foreach (BulletPathCalculation bulletPathCalc in bulletCalc.BulletPaths) { BulletThreat[] bulletThreats = bulletPathCalc.BulletThreats; for (int i = 0; i < bulletThreats.Length; i++) { BulletThreat bulletThreat = bulletThreats[i]; if (bulletThreat.TankThreatened.Player == You && !moveChosen[bulletThreat.TankThreatened.Number]) { if ((bulletPathCalc.BaseThreatened == You.Base) && (bulletThreat.NodePathToTakeOnBullet != null) && (bulletThreat.NodePathToTakeOnBullet.Length > 0)) { actionSet.Actions[bulletThreat.TankThreatened.Number] = bulletThreat.TankActionsToTakeOnBullet[0]; moveChosen[bulletThreat.TankThreatened.Number] = true; continue; } if (bulletThreat.LateralMoveInOneDirection != null && bulletThreat.LateralMoveInOneDirection.Length > 0) { actionSet.Actions[bulletThreat.TankThreatened.Number] = bulletThreat.TankActionsForLateralMoveInOneDirection[0]; moveChosen[bulletThreat.TankThreatened.Number] = true; continue; } if (bulletThreat.LateralMoveInOtherDirection != null && bulletThreat.LateralMoveInOtherDirection.Length > 0) { actionSet.Actions[bulletThreat.TankThreatened.Number] = bulletThreat.TankActionsForLateralMoveInOtherDirection[0]; moveChosen[bulletThreat.TankThreatened.Number] = true; continue; } if ((bulletThreat.NodePathToTakeOnBullet != null) && (bulletThreat.NodePathToTakeOnBullet.Length > 0)) { actionSet.Actions[bulletThreat.TankThreatened.Number] = bulletThreat.TankActionsToTakeOnBullet[0]; moveChosen[bulletThreat.TankThreatened.Number] = true; continue; } } } } }
protected override void ChooseMoves() { GameState currGameState = Game.Current.CurrentTurn.GameState; TankActionSet actionSet = new TankActionSet(YourPlayerIndex, currGameState.Tick); for (int t = 0; t < Constants.TANKS_PER_PLAYER; t++) { int tankAction = rnd.Next(6); actionSet.Actions[t] = (TankAction)tankAction; } Coordinator.SetBestMoveSoFar(actionSet); }
public TankActionSet GenerateTankActions(int yourPlayerIndex, int tick) { TankActionSet tankActionSet = new TankActionSet(yourPlayerIndex, tick); foreach (Tank tank in Game.Current.Players[yourPlayerIndex].Tanks) { TankSituation tankSituation = TankSituationsByTankIndex[tank.Index]; TankAction bestAction = tankSituation.GetBestTankAction(); tankActionSet.Actions[tank.Number] = bestAction; } return(tankActionSet); }
public void SetBestMoveSoFar(TankActionSet bestMove) { PerformActionInALock <TankActionSet>(bestMove, BestMoveLock, "Lock timeout with best move lock", delegate(TankActionSet bm) { if (CanMovesBeChosen) { /* Get a static copy of the CurrentGameState in case of concurrency issues * (i.e. the tick advances, and a new CurrentGameState is set): */ if (bestMove.Tick >= Game.Current.CurrentTurn.Tick) { BestMoveSoFar = bm; } } }); }
protected override void ChooseMoves() { try { GameState currGameState = Game.Current.CurrentTurn.GameState; TankActionSet actionSet = new TankActionSet(YourPlayerIndex, currGameState.Tick); bool[] moveChosenByTankNumber = new bool[Constants.TANKS_PER_PLAYER]; RespondToBullets(currGameState, actionSet, moveChosenByTankNumber); ChooseActions(currGameState, actionSet, moveChosenByTankNumber); Coordinator.SetBestMoveSoFar(actionSet); return; } catch (Exception exc) { // Swallow errors } // Shortest Path Bot code: ChooseShortestPathBotMoves(); }
private void ChooseShortestPathBotMoves() { GameState currGameState = Game.Current.CurrentTurn.GameState; TankActionSet actionSet = new TankActionSet(YourPlayerIndex, currGameState.Tick); bool[] moveChosen = new bool[Constants.TANKS_PER_PLAYER]; RespondToBullets(currGameState, actionSet, moveChosen); Tuple <int, int, TankAction>[] tankNumberDistanceAndActionArray = new Tuple <int, int, TankAction> [Constants.TANKS_PER_PLAYER]; // The closest bot can attack the base: for (int tankNumber = 0; tankNumber < Constants.TANKS_PER_PLAYER; tankNumber++) { Tank tank = You.Tanks[tankNumber]; MobileState tankState = currGameState.GetMobileState(tank.Index); if (!tankState.IsActive) { tankNumberDistanceAndActionArray[tankNumber] = new Tuple <int, int, TankAction>(tankNumber, Constants.UNREACHABLE_DISTANCE, TankAction.NONE); continue; } Base enemyBase = Opponent.Base; DirectionalMatrix <DistanceCalculation> distancesToEnemyBase = currGameState.CalculationCache.GetIncomingDistanceMatrixForBase(Opponent.Index); DistanceCalculation distanceCalc = distancesToEnemyBase[tankState.Dir, tankState.Pos]; FiringLineMatrix firingLineMatrix = currGameState.CalculationCache.FiringLinesForPointsMatrix; TankAction[] tankActions = PathCalculator.GetTankActionsOnIncomingShortestPath(distancesToEnemyBase, tankState.Dir, tankState.Pos.X, tankState.Pos.Y, enemyBase.Pos.X, enemyBase.Pos.Y, firingLineMatrix, keepMovingCloserOnFiringLastBullet: false); if (tankActions.Length == 0) { tankNumberDistanceAndActionArray[tankNumber] = new Tuple <int, int, TankAction>(tankNumber, Constants.UNREACHABLE_DISTANCE, TankAction.NONE); } else { tankNumberDistanceAndActionArray[tankNumber] = new Tuple <int, int, TankAction>(tankNumber, distanceCalc.Distance, tankActions[0]); } } int chosenTank; if (tankNumberDistanceAndActionArray[1].Item2 < tankNumberDistanceAndActionArray[0].Item2) { chosenTank = 1; } else { chosenTank = 0; } actionSet.Actions[chosenTank] = tankNumberDistanceAndActionArray[chosenTank].Item3; // The other bot can attack the closest enemy tank: Tank killerTank = You.Tanks[1 - chosenTank]; MobileState killerTankState = currGameState.GetMobileState(killerTank.Index); if (killerTankState.IsActive) { Tank enemyTank1 = Opponent.Tanks[0]; MobileState enemyTankState1 = currGameState.GetMobileState(enemyTank1.Index); int killerDistance1 = Constants.UNREACHABLE_DISTANCE; TankAction killerAction1 = TankAction.NONE; if (enemyTankState1.IsActive) { DirectionalMatrix <DistanceCalculation> attackMatrix1 = currGameState.CalculationCache.GetIncomingAttackMatrixForTankByTankIndex(enemyTank1.Index); if (attackMatrix1 != null) { DistanceCalculation attackCalculation1 = attackMatrix1[killerTankState.Dir, killerTankState.Pos]; killerDistance1 = attackCalculation1.Distance; TankAction[] tankActions = PathCalculator.GetTankActionsOnIncomingShortestPath(attackMatrix1, killerTankState, enemyTankState1.Pos, currGameState.CalculationCache.FiringLinesForTanksMatrix, keepMovingCloserOnFiringLastBullet: true); if (tankActions.Length > 0) { killerAction1 = tankActions[0]; } } } Tank enemyTank2 = Opponent.Tanks[1]; MobileState enemyTankState2 = currGameState.GetMobileState(enemyTank2.Index); int killerDistance2 = Constants.UNREACHABLE_DISTANCE; TankAction killerAction2 = TankAction.NONE; if (enemyTankState2.IsActive) { DirectionalMatrix <DistanceCalculation> attackMatrix2 = currGameState.CalculationCache.GetIncomingAttackMatrixForTankByTankIndex(enemyTank2.Index); if (attackMatrix2 != null) { DistanceCalculation attackCalculation2 = attackMatrix2[killerTankState.Dir, killerTankState.Pos]; killerDistance2 = attackCalculation2.Distance; TankAction[] tankActions = PathCalculator.GetTankActionsOnIncomingShortestPath(attackMatrix2, killerTankState, enemyTankState2.Pos, currGameState.CalculationCache.FiringLinesForTanksMatrix, keepMovingCloserOnFiringLastBullet: true); if (tankActions.Length > 0) { killerAction2 = tankActions[0]; } } } actionSet.Actions[1 - chosenTank] = (killerDistance1 <= killerDistance2) ? killerAction1 : killerAction2; } Coordinator.SetBestMoveSoFar(actionSet); }
public virtual void Start() { if (Coordinator == null) { throw new ApplicationException("The solver has no coordinator"); } Initialize(); while (SolverState != SolverState.Stopping) { // Wait for the solver state to change: while ((SolverState != SolverState.CanChooseMoves) && (SolverState != SolverState.Stopping)) { Thread.Sleep(10); } bool replayingMoves = false; int currentTick = Game.Current.CurrentTurn.Tick; SolverState = SolverState.ChoosingMoves; try { if (GameToReplay != null && currentTick <= TickToReplayTo && GameToReplay.Turns[currentTick].TankActionsTakenAfterPreviousTurn != null) { replayingMoves = true; LogDebugMessage("Replaying saved moves on turn {0}", currentTick); TankActionSet tankActionSet = new TankActionSet(YourPlayerIndex, currentTick); TankAction[] tankActionsTaken = GameToReplay.Turns[currentTick].TankActionsTakenAfterPreviousTurn; for (int tNum = 0; tNum < Constants.TANKS_PER_PLAYER; tNum++) { Tank tank = You.Tanks[tNum]; TankAction actionToTake = tankActionsTaken[tank.Index]; tankActionSet.Actions[tNum] = actionToTake; } Coordinator.SetBestMoveSoFar(tankActionSet); } else { LogDebugMessage("Choosing moves on turn {0}", currentTick); #if DEBUG Stopwatch swatch = Stopwatch.StartNew(); #endif ChooseMoves(); #if DEBUG swatch.Stop(); LogDebugMessage("Finished choosing moves. Duration: {0}", swatch.Elapsed); #endif } } catch (Exception exc) { LogDebugError(exc, exc.Message); System.Diagnostics.Debug.WriteLine( "Error while choosing moves: {0}", exc); System.Diagnostics.Debug.WriteLine("Stack trace:"); System.Diagnostics.Debug.WriteLine(exc.StackTrace); } if (SolverState == SolverState.Stopping) { break; } SolverState = SolverState.Thinking; try { if (!replayingMoves) { LogDebugMessage("Thinking on turn {0}", currentTick); #if DEBUG Stopwatch swatch = Stopwatch.StartNew(); #endif Think(); // If this method returns due to finishing its work, then the next state can be set to WaitingToChooseMoves. // Otherwise it must return when the state is changed to CanChooseMoves or Stopping. #if DEBUG swatch.Stop(); LogDebugMessage("Finished thinking. Duration: {0}", swatch.Elapsed); #endif } } catch (Exception exc) { System.Diagnostics.Debug.WriteLine( "Error while thinking: {0}", exc); System.Diagnostics.Debug.WriteLine("Stack trace:"); System.Diagnostics.Debug.WriteLine(exc.StackTrace); } if (SolverState == SolverState.Stopping) { break; } if (SolverState != SolverState.CanChooseMoves) { SolverState = SolverState.WaitingToChooseMoves; // The SolverState setter will ignore this if in a CanChooseMoves state, // thus preventing a nasty race condition that would lead to an endless loop. } } SolverState = SolverState.Stopped; }
protected Turn() { BulletIds = new int[Constants.TANK_COUNT]; TankActionsTakenAfterPreviousTurn = new TankAction[Constants.TANK_COUNT]; TankActionSetsSent = new TankActionSet[Constants.PLAYERS_PER_GAME]; }
public void ChooseActions(GameState currGameState, TankActionSet actionSet, bool[] moveChosenByTankNumber) { int[] minDistanceToAttackEnemyBaseByPlayerIndex = new int[Constants.PLAYERS_PER_GAME]; int[] minDistanceToDefendOwnBaseByPlayerIndex = new int[Constants.PLAYERS_PER_GAME]; bool[] isUnmarked = new bool[Constants.PLAYERS_PER_GAME]; Direction[] attackDirOnEnemyBaseByPlayerIndex = new Direction[Constants.PLAYERS_PER_GAME]; for (int p = 0; p < 2; p++) { attackDirOnEnemyBaseByPlayerIndex[p] = Direction.NONE; } Tank closestFriendlyTankToAttackEnemyBase = null; Tank closestFriendlyTankToDefendOwnBase = null; Tank closestEnemyTankToAttackYourBase = null; TankAction[] tankActionsForClosestFriendlyTankToAttackEnemyBase = null; TankAction[] tankActionsForClosestFriendlyTankToDefendOwnBase = null; foreach (Player player in Game.Current.Players) { int minDistanceToEnemyBase = Constants.UNREACHABLE_DISTANCE; foreach (Tank tank in player.Tanks) { MobileState tankState = currGameState.GetMobileState(tank.Index); if (!tankState.IsActive) // TODO: Check if locked in a firefight also { continue; } DirectionalMatrix <DistanceCalculation> distanceCalcMatrix = currGameState.CalculationCache.GetIncomingDistanceMatrixForBase(1 - player.Index); DistanceCalculation baseAttackCalc = distanceCalcMatrix[tankState]; int distanceToEnemyBase = baseAttackCalc.Distance; if (distanceToEnemyBase < minDistanceToEnemyBase) { minDistanceToEnemyBase = distanceToEnemyBase; attackDirOnEnemyBaseByPlayerIndex[player.Index] = baseAttackCalc.AdjacentNode.Dir; if (player == You) { closestFriendlyTankToAttackEnemyBase = tank; Base enemyBase = Game.Current.Players[1 - You.Index].Base; FiringLineMatrix firingLineMatrix = currGameState.CalculationCache.FiringLinesForPointsMatrix; tankActionsForClosestFriendlyTankToAttackEnemyBase = PathCalculator.GetTankActionsOnIncomingShortestPath( distanceCalcMatrix, tankState, enemyBase.Pos, firingLineMatrix, true); } else { closestEnemyTankToAttackYourBase = tank; } } } minDistanceToAttackEnemyBaseByPlayerIndex[player.Index] = minDistanceToEnemyBase; } foreach (Player player in Game.Current.Players) { int minDistanceToDefendOwnBase = Constants.UNREACHABLE_DISTANCE; foreach (Tank tank in player.Tanks) { MobileState tankState = currGameState.GetMobileState(tank.Index); if (tankState.IsActive) // TODO: Check if locked in a firefight also { DirectionalMatrix <DistanceCalculation> distanceMatrix = currGameState.CalculationCache.GetDistanceMatrixFromTankByTankIndex(tank.Index); Direction attackDir = attackDirOnEnemyBaseByPlayerIndex[1 - player.Index]; Direction defenceDir = attackDir.GetOpposite(); if (defenceDir == Direction.NONE) { defenceDir = Direction.RIGHT; } Point defencePos = player.Base.Pos + (Constants.SEGMENT_SIZE + 1) * defenceDir.GetOffset(); DistanceCalculation tankDefenceCalc = distanceMatrix[ defenceDir, defencePos]; int distance = tankDefenceCalc.Distance; if (distance < minDistanceToDefendOwnBase) { minDistanceToDefendOwnBase = distance; if (player == You) { closestFriendlyTankToDefendOwnBase = tank; tankActionsForClosestFriendlyTankToDefendOwnBase = PathCalculator.GetTankActionsOnOutgoingShortestPath( distanceMatrix, defenceDir, defencePos.X, defencePos.Y); } } } } minDistanceToDefendOwnBaseByPlayerIndex[player.Index] = minDistanceToDefendOwnBase; if (minDistanceToAttackEnemyBaseByPlayerIndex[1 - player.Index] < minDistanceToDefendOwnBase) { isUnmarked[1 - player.Index] = true; } } /* * int minAttackDistance = minDistanceToAttackEnemyBaseByPlayerIndex[You.Index]; * if (Game.Current.CurrentTurn.Tick + minAttackDistance > Game.Current.FinalTickInGame) * { * // Can't attack in time, rather defend... * return; * } */ // You're unmarked... Tank attackTank = closestFriendlyTankToAttackEnemyBase; if (isUnmarked[You.Index] && attackTank != null) { bool shouldAttack = true; // Enemy is marked... if (!isUnmarked[1 - You.Index]) { int minAttackDistance = minDistanceToAttackEnemyBaseByPlayerIndex[You.Index]; int minEnemyAttackDistance = minDistanceToAttackEnemyBaseByPlayerIndex[1 - You.Index]; // But if you stop marking them, by going for their base, they can get there first... if ((minAttackDistance > minEnemyAttackDistance) && (closestFriendlyTankToDefendOwnBase == attackTank)) { shouldAttack = false; // TODO: See if your other tank can do defence duty instead... } } if (shouldAttack && !moveChosenByTankNumber[attackTank.Number]) { actionSet.Actions[attackTank.Number] = tankActionsForClosestFriendlyTankToAttackEnemyBase[0]; moveChosenByTankNumber[attackTank.Number] = true; } } // If there is a slack of less than 5 ticks to defend your base, get back to defend it: Tank defenceTank = closestFriendlyTankToDefendOwnBase; if ((defenceTank != null) && (minDistanceToAttackEnemyBaseByPlayerIndex[1 - You.Index] < minDistanceToDefendOwnBaseByPlayerIndex[You.Index] + 5)) { if (!moveChosenByTankNumber[defenceTank.Number]) { actionSet.Actions[defenceTank.Number] = tankActionsForClosestFriendlyTankToAttackEnemyBase[0]; moveChosenByTankNumber[defenceTank.Number] = true; } } // Otherwise choose an enemy tank to attack: for (int t = 0; t < Constants.TANKS_PER_PLAYER; t++) { if ((closestEnemyTankToAttackYourBase != null) && (!moveChosenByTankNumber[t])) { // Rather attack an enemy tank in a firefight with own tank.. Tank killerTank = You.Tanks[t]; AttackClosestEnemyTankToOwnBase(currGameState, killerTank, closestEnemyTankToAttackYourBase, actionSet, moveChosenByTankNumber); } } }
protected override void ChooseMoves() { GameState currGameState = Game.Current.CurrentTurn.GameState; TankAction[] tankActionsForLiveTank = new TankAction[] { TankAction.UP, TankAction.DOWN, TankAction.FIRE, TankAction.LEFT, TankAction.RIGHT, TankAction.NONE }; TankAction[] tankActionsForDeadTank = new TankAction[] { TankAction.NONE }; TankAction[][] tankActionsPerTank = new TankAction[Constants.TANK_COUNT][]; foreach (Tank tank in You.Tanks) { MobileState mobileState = currGameState.GetMobileState(tank.Index); if (mobileState.IsActive) { // Check if needing to avoid a bullet or shoot a bullet on the path to the base or the tank itself // If so, limit the actions to only those that allow avoiding the bullet tankActionsPerTank[tank.Number] = tankActionsForLiveTank; } else { tankActionsPerTank[tank.Number] = tankActionsForDeadTank; } } double[,] valueEstimates = new double[tankActionsPerTank[0].Length, tankActionsPerTank[1].Length]; GameStateValueEstimator valueEstimator = new GameStateValueEstimator(); valueEstimator.Player = You; Tank tank0 = You.Tanks[0]; Tank tank1 = You.Tanks[1]; double bestValue = double.NegativeInfinity; TankAction[] bestTankActions = null; for (int ta0 = 0; ta0 < tankActionsPerTank[0].Length; ta0++) { TankAction tankAction0 = tankActionsPerTank[0][ta0]; for (int ta1 = 0; ta1 < Constants.TANK_COUNT; ta1++) { TankAction tankAction1 = tankActionsPerTank[1][ta1]; GameState gameStateClone = currGameState.Clone(); gameStateClone.Tick++; TankAction[] tankActions = new TankAction[Constants.TANK_COUNT]; tankActions[tank0.Index] = tankAction0; tankActions[tank1.Index] = tankAction1; MutableGameStateEngine.ApplyAllActions(gameStateClone as MutableGameState, tankActions); valueEstimator.GameState = gameStateClone; double value = valueEstimator.Evaluate(); if (value > bestValue) { bestValue = value; bestTankActions = tankActions; } } } TankActionSet actionSet = new TankActionSet(YourPlayerIndex, currGameState.Tick); if (bestTankActions != null) { actionSet.Actions[tank0.Number] = bestTankActions[tank0.Number]; actionSet.Actions[tank1.Number] = bestTankActions[tank1.Number]; } Coordinator.SetBestMoveSoFar(actionSet); }