Example #1
0
        //Calculates the bias for a given permutation of items in the world graph
        public BiasOutput CalcDistributionBias(WorldGraph world)
        {
            //Get total counts for majors and items so a percent can be calculated
            int totalmajorcount    = world.Items.Where(x => x.Importance >= 2).Count();
            int totallocationcount = world.GetLocationCount();
            //Find spheres in randomized world
            Search            searcher = new Search();
            List <WorldGraph> spheres  = searcher.SphereSearch(world).Spheres;

            //Initialize variables to use in the loop
            double[] spherebias           = new double[spheres.Count];
            int      rollingmajorcount    = 0;
            int      rollinglocationcount = 0;

            for (int i = 0; i < spheres.Count; i++)
            {
                WorldGraph sphere = spheres[i];
                //Check the number of major items in the sphere, not counting those in previous spheres
                int majoritemcount = sphere.CollectMajorItems().Count - rollingmajorcount;
                rollingmajorcount += majoritemcount;
                //Check the number of locations in the sphere, not counting those in previous spheres
                int locationcount = sphere.GetLocationCount() - rollinglocationcount;
                rollinglocationcount += locationcount;
                //Find the percentage of major items and locations in this sphere
                double majorpercent    = majoritemcount / (double)totalmajorcount;
                double locationpercent = locationcount / (double)totallocationcount;
                //Now find the difference between the two percentages
                double difference = majorpercent - locationpercent;
                spherebias[i] = difference;
            }
            //Now that we have a list of biases find the sum of their absolute values to determine absolute bias
            //Also use the positivity of bias before and after the median to determine bias direction
            double overallsum = 0;
            double beforesum  = 0;                          //Sums bias before median so a bias direction can be computed
            double aftersum   = 0;                          //Sums bias after median so a bias direction can be computed
            bool   even       = spherebias.Length % 2 == 0; //Want to check if even so can determine when after the median is
            int    median     = spherebias.Length / 2;      //Use median to determine bias direction

            for (int i = 0; i < spherebias.Length; i++)
            {
                overallsum += Math.Abs(spherebias[i]);
                if (i < median) //Before median, add to that sum
                {
                    beforesum += spherebias[i];
                }
                else if ((i >= median && even) || (i > median && !even)) //After median, add to that sum. If it's even then >= makes sense so every index is checked, if odd then skip middle
                {
                    aftersum = spherebias[i];
                }
            }
            //Package output and return
            BiasOutput output = new BiasOutput();

            output.biasvalue = overallsum / spherebias.Length; //Get average of absolute value to determine overall bias
            output.direction = beforesum < aftersum;           //If bias is more positive before the median, the direction is toward the beginning, otherwise toward end
            return(output);
        }
Example #2
0
        //Calculate info about human-like playthrough and then return the score
        public InterestingnessOutput CalcDistributionInterestingness(WorldGraph world)
        {
            PlaythroughInfo info = new PlaythroughInfo();

            try
            {
                info = searcher.PlaythroughSearch(world.Copy());
            }
            catch
            {
                throw new Exception(); //Something went wrong, have calling code retry
            }
            BiasOutput biasinfo = CalcDistributionBias(world);

            return(ScorePlaythrough(world, info, biasinfo));
        }
Example #3
0
        /*
         * Score info about human-like playthrough
         * Several considerations:
         * 1. Number of locations collected for each region traversed
         * 2. Number of regions traversed between finding major or helpful items
         * 3. Number of regions traversed between finding major items
         */
        public InterestingnessOutput ScorePlaythrough(WorldGraph world, PlaythroughInfo input, BiasOutput biasinfo)
        {
            //First, calculate fun metric, which desires a consistently high rate of checking item locations
            Queue <int>   RollingAvg = new Queue <int>();
            List <double> avgs       = new List <double>();
            List <bool>   highavg    = new List <bool>();

            foreach (int num in input.LocationsPerTraversal)
            {
                if (RollingAvg.Count == 5) //Rolling average of last 5 values
                {
                    RollingAvg.Dequeue();
                }
                RollingAvg.Enqueue(num);
                double avg = RollingAvg.Average();
                highavg.Add(avg >= 1); //If average is above 1, considered high enough to be fun, so add true to list, else add false
                avgs.Add(avg);
            }
            double fun = (double)highavg.Count(x => x) / highavg.Count(); //Our "Fun" score is the percentage of high values in the list
            //Next calculate challenge metric, which desires rate at which items are found to be within some optimal range so that it is not too often or too rare
            double LocationToItemRatio = (double)world.GetLocationCount() / world.Items.Where(x => x.Importance == 2).Count();
            int    low  = (int)Math.Floor(LocationToItemRatio * .5);
            int    high = (int)Math.Ceiling(LocationToItemRatio * 1.5);

            RollingAvg = new Queue <int>();
            avgs       = new List <double>();
            List <bool> avginrange = new List <bool>();

            foreach (int num in input.BetweenMajorList)
            {
                if (RollingAvg.Count == 3) //Tighter rolling average of last 3 values
                {
                    RollingAvg.Dequeue();
                }
                RollingAvg.Enqueue(num);
                double avg = RollingAvg.Average();
                avginrange.Add(low <= avg && avg <= high); //If value is within range rather than too high or too low, add true to list to indicate it is within a good range
                avgs.Add(avg);
            }
            double challenge = (double)avginrange.Count(x => x) / avginrange.Count(); //Our "Challenge" score is the percentage of values in the list within desirable range
            //Next calculate satisfyingness metric based on how many locations are unlocked when an item is found
            double      LocationToItemRatioWithoutInitial = (double)(world.GetLocationCount() - input.InitialReachableCount) / world.Items.Where(x => x.Importance == 2).Count();
            int         satthreshold = (int)Math.Floor(LocationToItemRatioWithoutInitial); //Set threshold as number of not-immediately-accessible locations divided by number of major items
            List <bool> SatisfyingReachesThreshold = new List <bool>();

            foreach (int num in input.LocationsUnlockedPerMajorFound)
            {
                SatisfyingReachesThreshold.Add(num >= satthreshold);
            }
            double satisfyingness = (double)SatisfyingReachesThreshold.Count(x => x) / SatisfyingReachesThreshold.Count(); //Our "Satisfyingness" score is the percentage of values above the desired threshold
            //Finally calculate boredom by observing regions which were visited more often than is expected
            //First get a count of how many times each region was visited
            List <int> visitcounts = new List <int>();

            foreach (Region r in world.Regions)
            {
                visitcounts.Add(input.Traversed.Count(x => x.Name == r.Name));
            }
            //Calculate threshold with max number of times region should be visited being the number of traversals divided by number of regions
            double      TraversedToRegionRatio = (double)input.Traversed.Count() / world.Regions.Count();
            int         borethreshold          = (int)Math.Ceiling(TraversedToRegionRatio);
            List <bool> VisitsAboveThreshold   = new List <bool>();

            foreach (int num in visitcounts)
            {
                VisitsAboveThreshold.Add(num > borethreshold);                                          //Again as before, add list of bool when value is above threshold
            }
            double boredom = (double)VisitsAboveThreshold.Count(x => x) / VisitsAboveThreshold.Count(); //Our "Boredom" score is the percentage of values above the desired threshold
            //Add calculated stats to output. If a result is NaN (possible when not completable) save as -1
            InterestingnessOutput output = new InterestingnessOutput();

            output.bias           = biasinfo;
            output.fun            = double.IsNaN(fun) ? -1 : fun;
            output.challenge      = double.IsNaN(challenge) ? -1 : challenge;
            output.satisfyingness = double.IsNaN(satisfyingness) ? -1 : satisfyingness;
            output.boredom        = double.IsNaN(boredom) ? -1 : boredom;
            //Use stats to calculate final interestingness score
            //Each score is a double in the range [0, 1]
            //Multiply each score (or its 1 - score if low score is desirable) by its percentage share of the total
            double biasscore      = (1 - output.bias.biasvalue) * .2;
            double funscore       = output.fun * .2;
            double challengescore = output.challenge * .2;
            double satscore       = output.satisfyingness * .2;
            double borescore      = (1 - output.boredom) * .2;
            double intscore       = biasscore + funscore + challengescore + satscore + borescore;

            //If any components are NaN, consider interestingness as NaN as well
            if (double.IsNaN(fun) || double.IsNaN(challenge) || double.IsNaN(satisfyingness) || double.IsNaN(boredom))
            {
                output.interestingness = -1;
            }
            else
            {
                output.interestingness = intscore;
            }
            output.completable = input.Completable;
            return(output);
        }