Exemplo n.º 1
0
    /// <summary>
    /// Scores a location's fairness in terms of its distance to other spawns
    /// (being farther away and equidistant gives a higher score)
    /// and the number of times it has been used already (fewer uses means a higher score).
    /// </summary>
    /// <returns>A "score" for the location that increases as the location is:
    /// 1) Farther away on average from the other locations,
    /// 2) Closer to perfectly equidistant from each other location, and
    /// 3) Used fewer times in the past to spawn something.</returns>
    public float ScoreLocation(Location l, FilledRegion region, List <Location> scoreAgainst)
    {
        if (scoreAgainst.Count == 0)
        {
            return(100.0f);
        }

        //Get the distance to each base. There are multiple distances to a base because the level might wrap around.
        List <float>[] dists = new List <float> [scoreAgainst.Count];
        for (int i = 0; i < dists.Length; ++i)
        {
            dists[i] = new List <float>();
        }
        float[] minDists = new float[scoreAgainst.Count];

        //Get the distances to each base (including all possible wrap-around distances).
        Location size = new Location(LevelGen.Map.GetLength(0), LevelGen.Map.GetLength(1));

        for (int i = 0; i < scoreAgainst.Count; ++i)
        {
            dists[i].Add(Location.DistanceSquared(l, scoreAgainst[i]));

            if (LevelGen.GenSettings.WrapX)
            {
                dists[i].Add(Location.DistanceSquared(l, new Location(scoreAgainst[i].X - size.X, scoreAgainst[i].Y)));
                dists[i].Add(Location.DistanceSquared(l, new Location(scoreAgainst[i].X + size.X, scoreAgainst[i].Y)));
            }

            if (LevelGen.GenSettings.WrapY)
            {
                dists[i].Add(Location.DistanceSquared(l, new Location(scoreAgainst[i].X, scoreAgainst[i].Y - size.Y)));
                dists[i].Add(Location.DistanceSquared(l, new Location(scoreAgainst[i].X, scoreAgainst[i].Y + size.Y)));
            }

            //Extra diagonal distances.
            if (LevelGen.GenSettings.WrapX && LevelGen.GenSettings.WrapY)
            {
                dists[i].Add(Location.DistanceSquared(l, new Location(scoreAgainst[i].X - size.X, scoreAgainst[i].Y - size.Y)));
                dists[i].Add(Location.DistanceSquared(l, new Location(scoreAgainst[i].X + size.X, scoreAgainst[i].Y - size.Y)));
                dists[i].Add(Location.DistanceSquared(l, new Location(scoreAgainst[i].X - size.X, scoreAgainst[i].Y + size.Y)));
                dists[i].Add(Location.DistanceSquared(l, new Location(scoreAgainst[i].X + size.X, scoreAgainst[i].Y + size.Y)));
            }
        }

        //Get the average of the distances.
        float average = 0.0f, min;

        for (int i = 0; i < dists.Length; ++i)
        {
            //Get the smallest distance to this base.
            min = Single.MaxValue;
            foreach (float f in dists[i])
            {
                min = Math.Min(f, min);
            }

            //Use the logarithm of the distance, because the difference
            //   between two bases' distances from given "l" is more
            //   problematic if the bases are both closer to "l".
            minDists[i] = (float)Math.Log(Math.Sqrt(min) + 1.0f, DistLogScale);
            average    += minDists[i];
        }
        average /= (float)dists.Length;

        //Get the standard deviation of the distances.
        float std = 0.0f, temp;

        float[] tempA = new float[dists.Length];
        for (int i = 0; i < dists.Length; ++i)
        {
            temp = minDists[i] - average;
            std += temp * temp;
        }
        std /= (float)dists.Length;
        std  = (float)Math.Sqrt(std);

        //Combine the average, the standard deviation, and the number of times this location has been used before.
        //Raise the standard deviation to a power, and use a multiplier for the number of uses.
        try
        {
            return((average - (float)Math.Pow(std, StandardDeviationPower)) * (float)Math.Pow(UsedScale, Uses[l]));
        }
        catch (Exception e)
        {
            throw new ArgumentOutOfRangeException("The spawn for a " + region.ToString() + " region is outside the map!");
        }
    }