public CombinedMovementAndFiringDistanceCalculation( DistanceCalculation movementDistanceToFiringLine, FiringDistance firingDistance, Direction finalMovementDirectionTowardsTarget) { MovementDistanceToFiringLine = movementDistanceToFiringLine; FiringDistance = firingDistance; FinalMovementDirectionTowardsTarget = finalMovementDirectionTowardsTarget; // Calculate ticks until the target is shot: int ticksTillTargetShot = MovementDistanceToFiringLine.Distance + FiringDistance.TicksTillTargetShot; if (ticksTillTargetShot > Constants.UNREACHABLE_DISTANCE) { ticksTillTargetShot = Constants.UNREACHABLE_DISTANCE; } TicksTillTargetShot = ticksTillTargetShot; // Calculate ticks until the last shot is fired: int ticksTillLastShotFired = MovementDistanceToFiringLine.Distance + FiringDistance.TicksTillLastShotFired; if (ticksTillLastShotFired > Constants.UNREACHABLE_DISTANCE) { ticksTillLastShotFired = Constants.UNREACHABLE_DISTANCE; } TicksTillLastShotFired = ticksTillLastShotFired; }
public static TankAction[] GetTankActionsOnShortestPath( DirectionalMatrix <DistanceCalculation> distances, Direction dir, int x, int y) { DistanceCalculation distanceCalc = distances[dir, x, y]; if (distanceCalc.Distance == Constants.UNREACHABLE_DISTANCE) { return(new TankAction[0]); } TankAction[] tankActions = new TankAction[distanceCalc.Distance]; int index = distanceCalc.Distance; Node node = new Node(ActionType.Moving, dir, x, y); while (index != 0) { index--; tankActions[index] = node.Dir.ToTankAction(); node = distanceCalc.AdjacentNode; if (node.ActionType == ActionType.Firing) { index--; tankActions[index] = TankAction.FIRE; node = new Node(ActionType.Moving, node.Dir, node.X, node.Y); } distanceCalc = distances[node.Dir, node.X, node.Y]; } return(tankActions); }
public DirectionalMatrix <DistanceCalculation> CalculateShortestDistancesFromTank(ref MobileState tankState) { DirectionalMatrix <DistanceCalculation> distanceMatrix = new DirectionalMatrix <DistanceCalculation>(Walls.Width, Walls.Height); distanceMatrix[tankState.Dir, tankState.Pos] = new DistanceCalculation(0, new Node()); // Mark taboo areas: if (TabooAreas != null) { for (int r = 0; r < TabooAreas.Length; r++) { Rectangle tabooRect = TabooAreas[r]; foreach (Point tabooPoint in tabooRect.GetPoints()) { foreach (Direction dir in BoardHelper.AllRealDirections) { if (CellMatrix[tabooPoint].IsValid) { distanceMatrix[dir, tabooPoint] = new DistanceCalculation( DistanceCalculationConstants.TABOO_DISTANCE, new Node()); } } } } } // Restrict the area of the board within which to do shortest path calculations (i.e. for efficiency reasons): if (RestrictedMovementArea != Rectangle.Unrestricted) { foreach (Direction restrictedAreaDir in BoardHelper.AllRealDirections) { Rectangle outerEdge = RestrictedMovementArea.GetOuterEdgeInDirection(restrictedAreaDir); if (outerEdge.IntersectsWith(Walls.BoardBoundary)) { foreach (Point restrictedPoint in outerEdge.GetPoints()) { if (Walls.BoardBoundary.ContainsPoint(restrictedPoint) && CellMatrix[restrictedPoint].IsValid) { foreach (Direction dir in BoardHelper.AllRealDirections) { distanceMatrix[dir, restrictedPoint] = new DistanceCalculation( DistanceCalculationConstants.TABOO_DISTANCE, new Node()); } } } } } } if (TicksWithoutFiring > 0) { FindShortestPathsWithRestrictionsOnFiring(tankState, distanceMatrix); } else { FindShortestPaths(tankState, distanceMatrix); } return(distanceMatrix); }
private void AddNodesToQueueForMovingOverTarget( DirectionalMatrix <DistanceCalculation> attackMatrix, TwoValuedCircularBuffer <Node> bfsQueue, TankLocation tankLocationAtTargetPoint) { // Give a distance of zero for internal points, but don't add them to the queue: DistanceCalculation zeroDistCalc = new DistanceCalculation(0, new Node()); foreach (Point interiorPoint in tankLocationAtTargetPoint.TankBody.GetPoints()) { foreach (Direction dir in BoardHelper.AllRealDirections) { attackMatrix[dir, interiorPoint] = zeroDistCalc; } } // Calculate the tank positions that will destroy the base or bullet via movement from various directions: foreach (Direction movementDir in MovementDirections) { Direction oppositeDir = movementDir.GetOpposite(); // Get the inside edge (segment) of the tank centred at the base in the opposite direction: Segment tankPositionsInDirection = tankLocationAtTargetPoint.InsideEdgesByDirection[(int)oppositeDir]; // For each point on the segment add it to the bfs queue with a distance of zero: foreach (Cell tankCellInDir in tankPositionsInDirection.Cells) { if (tankCellInDir.IsValid) { Node node = new Node(ActionType.Moving, movementDir, tankCellInDir.Position); bfsQueue.Add(node, 0); } } } }
public static Node[] GetOutgoingNodesOnShortestPath( DirectionalMatrix <DistanceCalculation> distances, Direction dir, int x, int y) { DistanceCalculation distanceCalc = distances[dir, x, y]; if (distanceCalc.Distance == Constants.UNREACHABLE_DISTANCE) { return(new Node[0]); } Node[] nodes = new Node[distanceCalc.Distance]; int index = distanceCalc.Distance; Node node = new Node(ActionType.Moving, dir, x, y); while (index != 0) { index--; nodes[index] = node; node = distanceCalc.AdjacentNode; if (node.ActionType == ActionType.Firing) { index--; nodes[index] = node; node = new Node(ActionType.Moving, node.Dir, node.X, node.Y); } distanceCalc = distances[node.Dir, node.X, node.Y]; } return(nodes); }
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); }
public CombinedMovementAndFiringDistanceCalculation GetShortestAttackDistanceGivenTheSourcePointsMovementMatrix( DirectionalMatrix <DistanceCalculation> movementDistanceMatrix, Cell target) { int bestCombinedTicksUntilTargetShot = Constants.UNREACHABLE_DISTANCE; CombinedMovementAndFiringDistanceCalculation bestCombinedDistance = null; foreach (Direction attackDir in MovementDirections) { foreach (EdgeOffset edgeOffset in EdgeOffsets) { Direction outwardDir = attackDir.GetOpposite(); Line <FiringDistance> firingLine = FiringLineMatrix[target.Position, outwardDir, edgeOffset]; for (int i = 0; i < firingLine.Length; i++) { FiringDistance firingDist = firingLine[i]; // Ignore firing line points that start off with a normal movement, // as that path can be found by moving to the closer point in a non-firing line way: if (firingDist.CanMoveOrFire) { continue; } // Ignore invalid starting points on the firing line: if (!(GameStateCalculationCache.GameState.Walls.BoardBoundary.ContainsPoint(firingDist.StartingTankPosition) && TurnCalculationCache.TankLocationMatrix[firingDist.StartingTankPosition].IsValid)) { break; } DistanceCalculation movementDist = movementDistanceMatrix[attackDir, firingDist.StartingTankPosition]; int combinedTicks = movementDist.Distance + firingDist.TicksTillTargetShot; if (combinedTicks < bestCombinedTicksUntilTargetShot) { bestCombinedTicksUntilTargetShot = combinedTicks; bestCombinedDistance = new CombinedMovementAndFiringDistanceCalculation(movementDist, firingDist, attackDir); } } } } return(bestCombinedDistance); }
public static Node[] GetOutgoingNodesOnShortestAttackPath( CombinedMovementAndFiringDistanceCalculation combinedCalculation, DirectionalMatrix <DistanceCalculation> distances, bool keepMovingCloserOnFiringLastBullet = false) { int length = combinedCalculation.TicksTillTargetShot; if (length == Constants.UNREACHABLE_DISTANCE) { return(new Node[0]); } Point startingTankPositionOnFiringLine = combinedCalculation.FiringDistance.StartingTankPosition; Direction finalAttackDir = combinedCalculation.FinalMovementDirectionTowardsTarget; DistanceCalculation distanceCalc = combinedCalculation.MovementDistanceToFiringLine; // Add nodes to move onto firing line (in reverse order): Node[] nodes = new Node[combinedCalculation.TicksTillTargetShot]; int nodeIndex = distanceCalc.Distance; Node node = new Node(ActionType.Moving, finalAttackDir, startingTankPositionOnFiringLine.X, startingTankPositionOnFiringLine.Y); while (nodeIndex != 0) { nodeIndex--; nodes[nodeIndex] = node; node = distanceCalc.AdjacentNode; if (node.ActionType == ActionType.Firing) { nodeIndex--; nodes[nodeIndex] = node; node = new Node(ActionType.Moving, node.Dir, node.X, node.Y); } distanceCalc = distances[node.Dir, node.X, node.Y]; } // Add firing line nodes: FiringDistanceCalculator.AddFiringLineNodesToRoute(combinedCalculation.FiringDistance, finalAttackDir, nodes, ref nodeIndex, keepMovingCloserOnFiringLastBullet); if (nodeIndex < length) { Array.Resize(ref nodes, nodeIndex); } return(nodes); }