Esempio n. 1
0
        public TankAction[] GetTankActionsFromTankToAttackTankAtPointAlongDirectionOfMovement(
            int playerIndex, int tankNumber, Point targetPoint, Direction finalMovementDir,
            EdgeOffset[] edgeOffsets, bool keepMovingCloserOnFiringLastBullet)
        {
            Tank                           tank                      = Game.Current.Players[playerIndex].Tanks[tankNumber];
            MobileState                    tankState                 = GameState.GetMobileState(tank.Index);
            TurnCalculationCache           turnCalcCache             = Game.Current.Turns[GameState.Tick].CalculationCache;
            Cell                           targetCell                = turnCalcCache.CellMatrix[targetPoint];
            FiringLineMatrix               firingLinesForTanksMatrix = GameState.CalculationCache.FiringLinesForTanksMatrix;
            AttackTargetDistanceCalculator attackCalculator          = new AttackTargetDistanceCalculator(
                ElementType.TANK, firingLinesForTanksMatrix, GameState.CalculationCache, turnCalcCache);

            attackCalculator.MovementDirections = new Direction[] { finalMovementDir };
            attackCalculator.EdgeOffsets        = edgeOffsets;
            CombinedMovementAndFiringDistanceCalculation combinedDistCalc
                = attackCalculator.GetShortestAttackDistanceFromCurrentTankPosition(tank.Index,
                                                                                    targetCell);

            return(PathCalculator.GetTanksActionsOnOutgoingShortestAttackPathFromCurrentTankPosition(
                       tank.Index, combinedDistCalc, GameState.CalculationCache, keepMovingCloserOnFiringLastBullet));

            /* was:
             * DirectionalMatrix<DistanceCalculation> incomingDistanceMatrix
             *  = attackCalculator.CalculateMatrixOfShortestDistancesToTargetCell(targetCell);
             * DistanceCalculation distanceCalc = incomingDistanceMatrix[tankState];
             * return PathCalculator.GetTankActionsOnIncomingShortestPath(incomingDistanceMatrix, tankState.Dir, tankState.Pos.X, tankState.Pos.Y,
             *  targetPoint.X, targetPoint.Y, firingLinesForTanksMatrix, keepMovingCloserOnFiringLastBullet);
             */
        }
Esempio n. 2
0
        public int GetAttackDistanceFromTankToTankAtPointAlongDirectionOfMovement(
            int playerIndex, int tankNumber, Point targetPoint, Direction finalMovementDir,
            EdgeOffset[] edgeOffsets)
        {
            Tank                           tank                      = Game.Current.Players[playerIndex].Tanks[tankNumber];
            MobileState                    tankState                 = GameState.GetMobileState(tank.Index);
            TurnCalculationCache           turnCalcCache             = Game.Current.Turns[GameState.Tick].CalculationCache;
            Cell                           targetCell                = turnCalcCache.CellMatrix[targetPoint];
            FiringLineMatrix               firingLinesForTanksMatrix = GameState.CalculationCache.FiringLinesForTanksMatrix;
            AttackTargetDistanceCalculator attackCalculator          = new AttackTargetDistanceCalculator(
                ElementType.TANK, firingLinesForTanksMatrix, GameState.CalculationCache, turnCalcCache);

            attackCalculator.MovementDirections = new Direction[] { finalMovementDir };
            attackCalculator.EdgeOffsets        = edgeOffsets;
            CombinedMovementAndFiringDistanceCalculation combinedDistCalc
                = attackCalculator.GetShortestAttackDistanceFromCurrentTankPosition(tank.Index,
                                                                                    targetCell);

            if (combinedDistCalc == null)
            {
                return(Constants.UNREACHABLE_DISTANCE);
            }
            else
            {
                return(combinedDistCalc.TicksTillTargetShot);
            }

            /* was:
             * DirectionalMatrix<DistanceCalculation> incomingDistanceMatrix
             *  = attackCalculator.CalculateMatrixOfShortestDistancesToTargetCell(targetCell);
             * DistanceCalculation distanceCalc = incomingDistanceMatrix[tankState];
             * return distanceCalc.Distance;
             */
        }
 public static TankAction[] GetTankActionsOnIncomingShortestPath(
     DirectionalMatrix <DistanceCalculation> distances,
     Direction dir, int fromX, int fromY, int targetX, int targetY,
     FiringLineMatrix firingLineMatrix,
     bool keepMovingCloserOnFiringLastBullet = false)
 {
     Node[] nodes = GetIncomingNodesOnShortestPath(
         distances, dir, fromX, fromY, targetX, targetY, firingLineMatrix, keepMovingCloserOnFiringLastBullet);
     return(ConvertNodesToTankActions(nodes));
 }
 public static TankAction[] GetTankActionsOnIncomingShortestPath(
     DirectionalMatrix <DistanceCalculation> distances,
     MobileState attackingTankState, Point targetPos,
     FiringLineMatrix firingLineMatrix,
     bool keepMovingCloserOnFiringLastBullet = false)
 {
     return(GetTankActionsOnIncomingShortestPath(distances, attackingTankState.Dir,
                                                 attackingTankState.Pos.X, attackingTankState.Pos.Y, targetPos.X, targetPos.Y,
                                                 firingLineMatrix, keepMovingCloserOnFiringLastBullet));
 }
Esempio n. 5
0
 /// <summary>
 /// AttackTargetDistanceCalculator constructor
 /// </summary>
 /// <param name="targetElementType">The type of element (base, tank or bullet) which is being attacked</param>
 /// <param name="firingLineMatrix">The firing line matrix must be applicable to the target element type's extent</param>
 /// <param name="gameStateCalculationCache"></param>
 /// <param name="turnCalculationCache"></param>
 public AttackTargetDistanceCalculator(
     ElementType targetElementType,
     FiringLineMatrix firingLineMatrix,
     GameStateCalculationCache gameStateCalculationCache,
     TurnCalculationCache turnCalculationCache) : this()
 {
     FiringLineMatrix          = firingLineMatrix;
     GameStateCalculationCache = gameStateCalculationCache;
     TurnCalculationCache      = turnCalculationCache;
     TargetElementType         = targetElementType;
 }
        public static Node[] GetIncomingNodesOnShortestPath(
            DirectionalMatrix <DistanceCalculation> distances,
            Direction dir, int fromX, int fromY, int toX, int toY,
            FiringLineMatrix firingLineMatrix       = null,
            bool keepMovingCloserOnFiringLastBullet = false)
        {
            DistanceCalculation distanceCalc = distances[dir, fromX, fromY];

            if (distanceCalc.Distance == Constants.UNREACHABLE_DISTANCE)
            {
                return(new Node[0]);
            }

            int length = distanceCalc.Distance;

            Node[] nodes = new Node[length];
            int    nodeIndex;
            Node   node;

            nodeIndex = 0;
            node      = distanceCalc.AdjacentNode;
            while (nodeIndex != length)
            {
                if (node.ActionType == ActionType.FiringLine)
                {
                    Line <FiringDistance> firingLine     = firingLineMatrix[toX, toY, node.Dir.GetOpposite(), node.EdgeOffset];
                    FiringDistance        firingDistance = firingLine[node.FiringLineIndex];
                    FiringDistanceCalculator.AddFiringLineNodesToRoute(firingDistance,
                                                                       firingLine.DirectionOfLine.GetOpposite(), nodes, ref nodeIndex, keepMovingCloserOnFiringLastBullet);
                    break;
                }

                // Add the node:
                nodes[nodeIndex] = node;
                nodeIndex++;
                if (node.ActionType == ActionType.Firing)
                {
                    // Add a moving node - the only reason to fire is to move...
                    node             = new Node(ActionType.Moving, node.Dir, node.X + node.Dir.GetXOffset(), node.Y + node.Dir.GetYOffset());
                    nodes[nodeIndex] = node;
                    nodeIndex++;
                }
                distanceCalc = distances[node.Dir, node.X, node.Y];
                node         = distanceCalc.AdjacentNode;
            }
            if (nodeIndex < length)
            {
                Array.Resize(ref nodes, nodeIndex);
            }
            return(nodes);
        }
Esempio n. 7
0
        /* TODO: Allow easy comparison across actions...
         * public int[] GetAttackDistanceOfTankToEnemyBasePerTankAction(int playerIndex, int tankNumber)
         * {
         *  MobileState tankState = GetTankState(playerIndex, tankNumber);
         *  int[] attackDistancesByTankAction = new int[Constants.TANK_ACTION_COUNT];
         *  foreach (TankAction tankAction in TankHelper.TankActions)
         *  {
         *      if (tankState.IsActive)
         *      {
         *
         *
         *          DirectionalMatrix<DistanceCalculation> attackDistanceMatrix
         *              = GameState.CalculationCache.GetIncomingDistanceMatrixForBase(1 - playerIndex);
         *          return attackDistanceMatrix[tankState].Distance;
         *      }
         *      return Constants.UNREACHABLE_DISTANCE;
         *  }
         *  return attackDistancesByTankAction;
         * }
         */

        public TankAction[] GetActionsToAttackEnemyBase(int playerIndex, int tankNumber)
        {
            MobileState tankState = GetTankState(playerIndex, tankNumber);

            if (tankState.IsActive)
            {
                Base             enemyBase        = GetEnemyBase(playerIndex);
                FiringLineMatrix firingLineMatrix = GameState.CalculationCache.FiringLinesForPointsMatrix;
                DirectionalMatrix <DistanceCalculation> attackDistanceMatrix
                    = GameState.CalculationCache.GetIncomingDistanceMatrixForBase(1 - playerIndex);
                return(PathCalculator.GetTankActionsOnIncomingShortestPath(attackDistanceMatrix, tankState,
                                                                           enemyBase.Pos, firingLineMatrix, keepMovingCloserOnFiringLastBullet: true));
            }
            return(NoTankActions);
        }
Esempio n. 8
0
        public int GetAttackDistanceFromTankToHypotheticalTankAtPoint(
            int playerIndex, int tankNumber, Point targetTankPoint, EdgeOffset[] edgeOffsets)
        {
            Tank                           tank                      = Game.Current.Players[playerIndex].Tanks[tankNumber];
            MobileState                    tankState                 = GameState.GetMobileState(tank.Index);
            TurnCalculationCache           turnCalcCache             = Game.Current.Turns[GameState.Tick].CalculationCache;
            Cell                           targetCell                = turnCalcCache.CellMatrix[targetTankPoint];
            FiringLineMatrix               firingLinesForTanksMatrix = GameState.CalculationCache.FiringLinesForTanksMatrix;
            AttackTargetDistanceCalculator attackCalculator          = new AttackTargetDistanceCalculator(
                ElementType.TANK, firingLinesForTanksMatrix, GameState.CalculationCache, turnCalcCache);

            attackCalculator.MovementDirections = BoardHelper.AllRealDirections;
            attackCalculator.EdgeOffsets        = edgeOffsets;
            CombinedMovementAndFiringDistanceCalculation combinedDistCalc
                = attackCalculator.GetShortestAttackDistanceFromCurrentTankPosition(tank.Index,
                                                                                    targetCell);

            return(combinedDistCalc.TicksTillTargetShot);
        }
        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);
        }
        private static void CalculateBulletThreats(BulletCalculation bulletCalc, GameState gameState, Player you)
        {
            TurnCalculationCache turnCalcCache = Game.Current.Turns[gameState.Tick].CalculationCache;

            Tank[] tanks;
            if (you != null)
            {
                tanks = you.Tanks;
            }
            else
            {
                tanks = Game.Current.Tanks;
            }
            foreach (Tank tank in tanks)
            {
                MobileState tankState = gameState.GetMobileState(tank.Index);
                foreach (BulletPathCalculation bulletPathCalc in bulletCalc.BulletPaths)
                {
                    List <BulletThreat> bulletThreats = new List <BulletThreat>();
                    foreach (BulletPathPoint bulletPathPoint in bulletPathCalc.BulletPathPoints)
                    {
                        if (bulletPathPoint.DangerArea.ContainsPoint(tankState.Pos))
                        {
                            BulletThreat bulletThreat = new BulletThreat(bulletPathCalc)
                            {
                                FiringTank     = bulletPathCalc.Bullet.Tank,
                                TankThreatened = tank
                            };
                            bulletThreats.Add(bulletThreat);

                            // Take the bullet on:
                            FiringLineMatrix firingLineMatrix = gameState.CalculationCache.FiringLinesForPointsMatrix;
                            AttackTargetDistanceCalculator attackCalculator = new AttackTargetDistanceCalculator(
                                ElementType.BULLET, firingLineMatrix, gameState.CalculationCache, turnCalcCache);
                            attackCalculator.MovementDirections = new Direction[]
                            {
                                bulletPathCalc.BulletState.Dir.GetOpposite()
                            };
                            Cell bulletCell = turnCalcCache.CellMatrix[bulletPathCalc.BulletState.Pos];
                            DirectionalMatrix <DistanceCalculation> distanceCalcs
                                = attackCalculator.CalculateMatrixOfShortestDistancesToTargetCell(bulletCell);
                            DistanceCalculation distanceCalc = distanceCalcs[tankState];
                            if (distanceCalc.Distance <= bulletPathPoint.TicksToEscape)
                            {
                                Node[] nodes = PathCalculator.GetIncomingNodesOnShortestPath(
                                    distanceCalcs, tankState.Dir, tankState.Pos.X, tankState.Pos.Y,
                                    bulletPathCalc.BulletState.Pos.X, bulletPathCalc.BulletState.Pos.Y,
                                    firingLineMatrix, keepMovingCloserOnFiringLastBullet: true);
                                bulletThreat.NodePathToTakeOnBullet = nodes;
                                TankAction[] tankActions
                                    = PathCalculator.GetTankActionsOnIncomingShortestPath(distanceCalcs,
                                                                                          tankState.Dir, tankState.Pos.X, tankState.Pos.Y,
                                                                                          bulletPathCalc.BulletState.Pos.X, bulletPathCalc.BulletState.Pos.Y,
                                                                                          firingLineMatrix, keepMovingCloserOnFiringLastBullet: true);
                                bulletThreat.TankActionsToTakeOnBullet = tankActions;
                            }

                            // Move off his path in one direction:
                            Point tankOffset           = tankState.Pos - bulletPathCalc.BulletState.Pos;
                            Point bulletMovementOffset = bulletPathCalc.BulletState.Dir.GetOffset();

                            Point     newTankPoint1;
                            Point     newTankPoint2;
                            Direction newTankDir1;
                            Direction newTankDir2;

                            if (bulletPathCalc.BulletState.Dir.ToAxis() == Axis.Horizontal)
                            {
                                newTankPoint1 = new Point(
                                    (short)tankState.Pos.X,
                                    (short)(bulletPathCalc.BulletState.Pos.Y - Constants.TANK_OUTER_EDGE_OFFSET));
                                newTankDir1   = Direction.UP;
                                newTankPoint2 = new Point(
                                    (short)tankState.Pos.X,
                                    (short)(bulletPathCalc.BulletState.Pos.Y + Constants.TANK_OUTER_EDGE_OFFSET));
                                newTankDir2 = Direction.DOWN;
                            }
                            else
                            {
                                newTankPoint1 = new Point(
                                    (short)(bulletPathCalc.BulletState.Pos.X - Constants.TANK_OUTER_EDGE_OFFSET),
                                    (short)tankState.Pos.Y);
                                newTankDir1   = Direction.LEFT;
                                newTankPoint2 = new Point(
                                    (short)(bulletPathCalc.BulletState.Pos.X + Constants.TANK_OUTER_EDGE_OFFSET),
                                    (short)tankState.Pos.Y);
                                newTankDir2 = Direction.RIGHT;
                            }

                            DirectionalMatrix <DistanceCalculation> tankDistanceCalcs
                                = gameState.CalculationCache.GetDistanceMatrixFromTankByTankIndex(tank.Index);

                            distanceCalc = tankDistanceCalcs[newTankDir1, newTankPoint1.X, newTankPoint1.Y];
                            if (distanceCalc.Distance <= bulletPathPoint.TicksToEscape)
                            {
                                Node[] shortestPathsIn1Direction
                                    = PathCalculator.GetOutgoingNodesOnShortestPath(tankDistanceCalcs,
                                                                                    newTankDir1, newTankPoint1.X, newTankPoint1.Y);
                                bulletThreat.LateralMoveInOneDirection = shortestPathsIn1Direction;
                                TankAction[] tankActionsIn1Direction
                                    = PathCalculator.GetTankActionsOnOutgoingShortestPath(tankDistanceCalcs,
                                                                                          newTankDir1, newTankPoint1.X, newTankPoint1.Y);
                                bulletThreat.TankActionsForLateralMoveInOneDirection = tankActionsIn1Direction;
                            }

                            distanceCalc = tankDistanceCalcs[newTankDir2, newTankPoint2.X, newTankPoint2.Y];
                            if (distanceCalc.Distance <= bulletPathPoint.TicksToEscape)
                            {
                                Node[] shortestPathsInOtherDirection = PathCalculator.GetOutgoingNodesOnShortestPath(tankDistanceCalcs,
                                                                                                                     newTankDir2, newTankPoint2.X, newTankPoint2.Y);
                                bulletThreat.LateralMoveInOtherDirection = shortestPathsInOtherDirection;
                                TankAction[] tankActionsInOtherDirection
                                    = PathCalculator.GetTankActionsOnOutgoingShortestPath(tankDistanceCalcs,
                                                                                          newTankDir2, newTankPoint2.X, newTankPoint2.Y);
                                bulletThreat.TankActionsForLateralMoveInOtherDirection = tankActionsInOtherDirection;
                            }
                        }
                    }
                    bulletPathCalc.BulletThreats = bulletThreats.ToArray();
                }
            }
        }
        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);
                }
            }
        }