public static BulletCalculation GetBulletCalculation(GameState gameState, Player you = null)
        {
            TurnCalculationCache         turnCalcCache          = Game.Current.Turns[gameState.Tick].CalculationCache;
            BulletCalculation            bulletCalc             = new BulletCalculation();
            List <BulletPathCalculation> bulletPathCalculations = new List <BulletPathCalculation>();

            for (int b = Constants.MIN_BULLET_INDEX; b <= Constants.MAX_BULLET_INDEX; b++)
            {
                MobileState bulletState = gameState.GetMobileState(b);
                if (bulletState.IsActive)
                {
                    DoBulletPathCalculations(gameState, turnCalcCache, bulletPathCalculations, b, bulletState);
                }
            }
            bulletCalc.BulletPaths = bulletPathCalculations.ToArray();

            CalculateBulletThreats(bulletCalc, gameState, you);

            return(bulletCalc);
        }
        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();
                }
            }
        }