Beispiel #1
0
        /// <summary>
        /// Checks if the given position is a choke point by looking in 8 directions.
        /// </summary>
        /// <param name="pos"> The position to check. </param>
        /// <param name="width"> The width of a choke point. </param>
        /// <returns> True if the position is a choke point; false otherwise. </returns>
        private bool IsPositionChokePoint(Position pos, int width)
        {
            // The directions represented as a position, along with how far there is to the nearest cliff.
            var directions = new List <Tuple <Position, int> >
            {
                new Tuple <Position, int>(new Position(-1, 0), -1),                 // West
                new Tuple <Position, int>(new Position(1, 0), -1),                  // East
                new Tuple <Position, int>(new Position(0, -1), -1),                 // South
                new Tuple <Position, int>(new Position(0, 1), -1),                  // North
                new Tuple <Position, int>(new Position(1, -1), -1),                 // South-east
                new Tuple <Position, int>(new Position(-1, 1), -1),                 // North-west
                new Tuple <Position, int>(new Position(-1, -1), -1),                // South-west
                new Tuple <Position, int>(new Position(1, 1), -1)                   // North-east
            };

            // The opposite directions.
            var directionCombinations = new Dictionary <int, int>
            {
                { 0, 1 },                 // West + East
                { 2, 3 },                 // South + north
                { 4, 5 },                 // South-east + north-west
                { 6, 7 }                  // South-west + north-east
            };

            // Find the nearest cliff in each of the 8 directions.
            for (var i = 0; i < width + 1; i++)
            {
                foreach (var dc in directionCombinations)
                {
                    var dir1 = directions[dc.Key].Item1;
                    var dir2 = directions[dc.Value].Item1;

                    var newPos1 = new Position(pos.Item1 + (dir1.Item1 * i), pos.Item2 + (dir1.Item2 * i));
                    var newPos2 = new Position(pos.Item1 + (dir2.Item1 * i), pos.Item2 + (dir2.Item2 * i));

                    if (MapHelper.WithinMapBounds(newPos1, this.map.XSize, this.map.YSize))
                    {
                        if (directions[dc.Key].Item2 < 0 &&
                            this.map.HeightLevels[newPos1.Item1, newPos1.Item2] == Enums.HeightLevel.Cliff)
                        {
                            directions[dc.Key] = new Tuple <Position, int>(dir1, i);
                        }
                    }

                    if (MapHelper.WithinMapBounds(newPos2, this.map.XSize, this.map.YSize))
                    {
                        if (directions[dc.Value].Item2 < 0 &&
                            this.map.HeightLevels[newPos2.Item1, newPos2.Item2] == Enums.HeightLevel.Cliff)
                        {
                            directions[dc.Value] = new Tuple <Position, int>(dir2, i);
                        }
                    }
                }
            }

            // The directions to check the ranges between to get a rather clear view
            var finalCombinations = new List <Position>
            {
                new Position(0, 1),
                new Position(0, 4),
                new Position(0, 7),
                new Position(1, 6),
                new Position(1, 5),
                new Position(2, 3),
                new Position(2, 5),
                new Position(2, 7),
                new Position(3, 6),
                new Position(3, 4)
            };

            // Check the range to cliffs for every interesting direction combination
            foreach (var fc in finalCombinations)
            {
                var dir1Value = directions[fc.Item1].Item2;
                var dir2Value = directions[fc.Item2].Item2;

                if (dir1Value < 0)
                {
                    continue;
                }
                if (dir2Value < 0)
                {
                    continue;
                }

                // -1 because otherwise we count the start position twice.
                if ((dir1Value + dir2Value) - 1 <= width)
                {
                    return(true);
                }
            }

            return(false);
        }
Beispiel #2
0
        /// <summary>
        /// Checks if the position is a ramp with a choke point of the given width or not.
        /// </summary>
        /// <param name="pos"> The position to check for a choke point. </param>
        /// <param name="width"> The width a place should have to be considered a choke point. </param>
        /// <returns> True if the place is a choke point; false otherwise. </returns>
        private bool IsRampChokePoint(Position pos, int width)
        {
            var originalRampType         = this.map.HeightLevels[pos.Item1, pos.Item2];
            var horizontalFirstEncounter = this.map.HeightLevels[pos.Item1, pos.Item2];
            var verticalFirstEncounter   = this.map.HeightLevels[pos.Item1, pos.Item2];

            // Figure out what is hit first when going either right or up
            for (var i = 0; i < 10; i++)
            {
                // Check to the right
                if (MapHelper.WithinMapBounds(pos.Item1, pos.Item2 + i, this.xSize, this.ySize))
                {
                    var right = this.map.HeightLevels[pos.Item1, pos.Item2 + i];
                    if (horizontalFirstEncounter == originalRampType &&
                        right != originalRampType)
                    {
                        horizontalFirstEncounter = this.map.HeightLevels[pos.Item1, pos.Item2 + i];
                    }
                }

                // Check up
                if (MapHelper.WithinMapBounds(pos.Item1 + i, pos.Item2, this.xSize, this.ySize))
                {
                    var up = this.map.HeightLevels[pos.Item1 + i, pos.Item2];
                    if (verticalFirstEncounter == originalRampType &&
                        up != originalRampType)
                    {
                        verticalFirstEncounter = this.map.HeightLevels[pos.Item1 + i, pos.Item2];
                    }
                }
            }

            // Decide which way the the edges of the ramp are.
            var directionChange = (horizontalFirstEncounter == Enums.HeightLevel.Cliff &&
                                   verticalFirstEncounter != Enums.HeightLevel.Cliff)
                                      ? new Position(0, 1)
                                      : new Position(1, 0);

            var firstPos  = new Position(pos.Item1, pos.Item2);
            var secondPos = new Position(pos.Item1, pos.Item2);

            // Find the two sides of the ramp.
            for (var i = 0; i < 10; i++)
            {
                // If not within the map, don't bother.
                if (!MapHelper.WithinMapBounds(
                        pos.Item1 + (i * directionChange.Item1),
                        pos.Item2 + (i * directionChange.Item2),
                        this.xSize,
                        this.ySize))
                {
                    continue;
                }

                if (!MapHelper.WithinMapBounds(
                        pos.Item1 - (i * directionChange.Item1),
                        pos.Item2 - (i * directionChange.Item2),
                        this.xSize,
                        this.ySize))
                {
                    continue;
                }

                if (firstPos.Equals(pos) &&
                    this.map.HeightLevels[pos.Item1 + (i * directionChange.Item1), pos.Item2 + (i * directionChange.Item2)] == Enums.HeightLevel.Cliff)
                {
                    firstPos = new Position(
                        pos.Item1 + (i * directionChange.Item1),
                        pos.Item2 + (i * directionChange.Item2));
                }

                if (secondPos.Equals(pos) &&
                    this.map.HeightLevels[pos.Item1 - (i * directionChange.Item1), pos.Item2 - (i * directionChange.Item2)] == Enums.HeightLevel.Cliff)
                {
                    secondPos = new Position(
                        pos.Item1 - (i * directionChange.Item1),
                        pos.Item2 - (i * directionChange.Item2));
                }
            }

            // Check the actual width.
            var actualWidth = Math.Abs(firstPos.Item1 - secondPos.Item1) + Math.Abs(firstPos.Item2 - secondPos.Item2) - 1;

            return(actualWidth <= width);
        }
Beispiel #3
0
        /// <summary>
        /// Figures out how many steps of the path between the start bases the two Xel'Naga towers cover.
        /// If the towers cover more than one base, their worth is halved. If they cover a start base, their worth is cut in four (these two stack).
        /// </summary>
        /// <returns> A value between 0.0 and 1.0 multiplied by significance based on how many steps that are covered. </returns>
        private double XelNagaPlacement()
        {
            // If no Xel'Naga tower are found, return 0.
            if (this.xelNagaPosition2 == null)
            {
                return(0d);
            }

            double stepsCovered2 =
                this.pathBetweenStartBases.Count(
                    step => MapHelper.CloseTo(step, this.xelNagaPosition2, this.mfo.DistanceToXelNaga));

            double max2    = this.mfo.StepsInXelNagaRangeMax;
            double min2    = this.mfo.StepsInXelNagaRangeMin;
            var    actual2 = (stepsCovered2 > max2)
                            ? max2 - (stepsCovered2 - max2)
                            : stepsCovered2;

            if (actual2 < min2)
            {
                actual2 = min2;
            }

            var normalized2        = (actual2 - min2) / (max2 - min2);
            var basesInVision2     = this.bases.Count(@base => MapHelper.CloseTo(this.xelNagaPosition2, @base));
            var startBaseInVision2 = MapHelper.CloseTo(this.xelNagaPosition2, this.startBasePosition1) ||
                                     MapHelper.CloseTo(this.xelNagaPosition2, this.startBasePosition2);

            if (basesInVision2 >= 2)
            {
                normalized2 /= 2;
            }
            if (startBaseInVision2)
            {
                normalized2 /= 4;
            }

            // If only one Xel'Naga tower is found, return the significance for just that one, but halved.
            if (this.xelNagaPosition1 == null)
            {
                return((normalized2 * this.mfo.XelNagaPlacementSignificance) / 2d);
            }

            double stepsCovered1 =
                this.pathBetweenStartBases.Count(
                    step => MapHelper.CloseTo(step, this.xelNagaPosition1, this.mfo.DistanceToXelNaga));

            double max1    = this.mfo.StepsInXelNagaRangeMax;
            double min1    = this.mfo.StepsInXelNagaRangeMin;
            var    actual1 = (stepsCovered1 > max1)
                            ? max1 - (stepsCovered1 - max1)
                            : stepsCovered1;

            if (actual1 < min1)
            {
                actual1 = min1;
            }

            var normalized1        = (actual1 - min1) / (max1 - min1);
            var basesInVision1     = this.bases.Count(@base => MapHelper.CloseTo(this.xelNagaPosition1, @base));
            var startBaseInVision1 = MapHelper.CloseTo(this.xelNagaPosition1, this.startBasePosition1) ||
                                     MapHelper.CloseTo(this.xelNagaPosition1, this.startBasePosition2);

            if (basesInVision1 >= 2)
            {
                normalized1 /= 2;
            }
            if (startBaseInVision1)
            {
                normalized1 /= 4;
            }

            return(((normalized1 + normalized2) / 2d) * this.mfo.XelNagaPlacementSignificance);
        }
Beispiel #4
0
        /// <summary>
        /// Calculates the fitness for the map.
        /// </summary>
        /// <returns> A double representing the fitness of the map. The higher, the better. </returns>
        public double CalculateFitness()
        {
            this.bases = new List <Position>();

            // Find a startbase. No need to find all start bases, as the other base should be a complete mirror of this base.
            for (var tempY = this.ySize - 1; tempY >= 0; tempY--)
            {
                for (var tempX = 0; tempX < this.xSize; tempX++)
                {
                    // Find start base 1
                    if (this.map.MapItems[tempX, tempY] == Enums.Item.StartBase &&
                        this.startBasePosition2 == null)
                    {
                        this.startBasePosition2 = new Tuple <int, int>(tempX + 2, tempY - 2);
                    }

                    // Find start base 2
                    if (this.map.MapItems[tempX, tempY] == Enums.Item.StartBase &&
                        this.startBasePosition1 == null &&
                        this.startBasePosition2 != null)
                    {
                        if (Math.Abs(tempX - this.startBasePosition2.Item1) > 3 ||
                            Math.Abs(tempY - this.startBasePosition2.Item2) > 3)
                        {
                            this.startBasePosition1 = new Position(tempX + 2, tempY - 2);
                        }
                    }

                    // Check for highest level
                    if ((this.map.HeightLevels[tempX, tempY] == Enums.HeightLevel.Height1 ||
                         this.map.HeightLevels[tempX, tempY] == Enums.HeightLevel.Height2) &&
                        this.map.HeightLevels[tempX, tempY] > this.heighestLevel)
                    {
                        this.heighestLevel = this.map.HeightLevels[tempX, tempY];
                    }

                    var tempPos = new Position(tempX, tempY);

                    // Check if the area is a base
                    if (this.map.MapItems[tempX, tempY] == Enums.Item.Base &&
                        !MapHelper.CloseToAny(tempPos, this.bases, 5))
                    {
                        this.bases.Add(new Position(tempX + 2, tempY - 2));
                    }

                    // Save the first XelNaga tower found
                    if (this.map.MapItems[tempX, tempY] == Enums.Item.XelNagaTower &&
                        this.xelNagaPosition2 == null)
                    {
                        this.xelNagaPosition2 = tempPos;
                    }

                    // Save the second XelNaga tower found
                    if (this.map.MapItems[tempX, tempY] == Enums.Item.XelNagaTower &&
                        this.xelNagaPosition2 != null &&
                        this.xelNagaPosition1 == null &&
                        !MapHelper.CloseTo(tempPos, this.xelNagaPosition2, 4))
                    {
                        this.xelNagaPosition1 = tempPos;
                    }
                }
            }

            // If no start bases are found, the map is not feasible and running fitness calculations is not worth it.
            if (this.startBasePosition1 == null || this.startBasePosition2 == null)
            {
                this.FitnessValues = new MapFitnessValues(
                    -this.mfo.MaxTotalFitness,
                    -this.mfo.MaxTotalFitness,
                    -this.mfo.MaxTotalFitness,
                    -this.mfo.MaxTotalFitness,
                    -this.mfo.MaxTotalFitness,
                    -this.mfo.MaxTotalFitness,
                    -this.mfo.MaxTotalFitness,
                    -this.mfo.MaxTotalFitness,
                    -this.mfo.MaxTotalFitness,
                    -this.mfo.MaxTotalFitness,
                    -this.mfo.MaxTotalFitness);
                return(this.FitnessValues.TotalFitness);
            }

            this.pathBetweenStartBases = this.mapPathfinding.FindPathFromTo(
                this.startBasePosition1,
                this.startBasePosition2,
                this.mfo.PathfindingIgnoreDestructibleRocks);

            var baseSpace  = this.BaseSpace();
            var baseHeight = this.BaseHeightLevel();
            var pathBetweenStartBasesFitness   = this.PathBetweenStartBases();
            var newHeightReached               = this.NewHeightReached();
            var distanceToNaturalExpansion     = this.DistanceToNaturalExpansion();
            var distanceToNonNaturalExpansions = this.DistanceToNonNaturalExpansions();
            var expansionsAvailable            = this.ExpansionsAvailable();
            var chokePoints      = this.ChokePoints();
            var xelNagaPlacement = this.XelNagaPlacement();
            var startBaseOpeness = this.StartBaseOpeness();
            var baseOpeness      = this.BaseOpeness();

            this.FitnessValues = new MapFitnessValues(
                baseSpace,
                baseHeight,
                pathBetweenStartBasesFitness,
                newHeightReached,
                distanceToNaturalExpansion,
                distanceToNonNaturalExpansions,
                expansionsAvailable,
                chokePoints,
                xelNagaPlacement,
                startBaseOpeness,
                baseOpeness);

            return(this.FitnessValues.TotalFitness);
        }