예제 #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);
        }
예제 #2
0
        //Generates a world with a specific count of regions and items
        //First generates many worlds to determine an average complexity then returns a world generated with a certain tolerance of that complexity
        //This takes a while to run, mainly because each complexity calculation takes ~2 seconds due to running the external python script
        static string GenerateWorld(int regioncount, int itemcount)
        {
            double goalcomplexity = AverageComplexity(regioncount, itemcount).Average(x => x.top50);
            //Now generate worlds until one is generated within a certain tolerance of the average
            double tolerance = .10; //10%

            while (true)
            {
                //Generate a world, check its complexity
                WorldGenerator generator  = new WorldGenerator(regioncount, itemcount);
                WorldGraph     generated  = generator.Generate();
                int            test       = generated.GetLocationCount();
                double         complexity = generator.GetComplexity().top50;
                if (goalcomplexity * (1 - tolerance) < complexity && complexity < goalcomplexity * (1 + tolerance))
                {
                    //Once complexity within x% of average has been generated, return json of the world so it can be saved
                    return(generated.ToJson());
                }
            }
        }
예제 #3
0
        //Generate a random worldgraph using the specified number of regions and item list
        public WorldGraph Generate()
        {
            HashSet <Region> regions = new HashSet <Region>(); //Set of all regions in the world

            for (int i = 0; i < Regions; i++)
            {
                Region r = new Region("Region-" + i.ToString()); //Each region is named Region_x. So Region-1, Region-2, etc.
                regions.Add(r);
            }
            //Not must loop through each region to add exits
            //Separate loop from the previous so that all regions are available to add as exits
            foreach (Region r in regions)
            {
                //The first region has some specific conditions:
                // 1. First exit is guaranteed to have no requirement and goes to hub region
                // 2. There is a guaranteed second exit, that will have a single item requirement
                // 3. There is a 50% chance to have a third exit, which has a 50% chance between a single item and no item
                if (r.Name == "Region-0")
                {
                    //Add exit to hub region with no requirement
                    List <string> currentexits = new List <string>();
                    Region        hub          = regions.First(x => x.Name == "Region-1");
                    AddExitsNoRequirement(regions, r, hub);
                    currentexits.Add("Region-1");
                    //Add exit to 2nd region with single requirement
                    Region second = GetRandomAvailableRegion(regions, r, currentexits);
                    AddExitsOneRequirement(regions, r, second);
                    currentexits.Add(second.Name);
                    //50% chance to add a third region
                    int random = rng.Next(1, 3); //Either 1 or 2
                    if (random == 2)
                    {
                        Region third = GetRandomAvailableRegion(regions, r, currentexits);
                        random = rng.Next(1, 3);
                        //50% chance to have 1 requirement, 50% chance to have none
                        if (random == 2)
                        {
                            AddExitsNoRequirement(regions, r, third);
                        }
                        else
                        {
                            AddExitsOneRequirement(regions, r, third);
                        }
                    }
                }
                //The second region is the hub region and also has some specific conditions:
                // 1. Will connect to 5 regions besides the start region
                // 2. Half of its exits will have no item requirement, the other half will have one
                else if (r.Name == "Region-1")
                {
                    List <string> currentexits = new List <string>();
                    currentexits.Add("Region-0");
                    for (int i = 0; i < 5; i++) //Run for 5 iterations
                    {
                        Region to = GetRandomAvailableRegion(regions, r, currentexits);
                        if (i < 2) //First 3 exits (including start region) have no requirement
                        {
                            AddExitsNoRequirement(regions, r, to);
                        }
                        else //Next 3 iterations will have 1 requirement
                        {
                            AddExitsOneRequirement(regions, r, to);
                        }
                        currentexits.Add(to.Name);
                    }
                }
                //Every other region will have a number of exits in [1, 4], however max of 2 chosen at generation, 2 more can be added by a later region
                else
                {
                    int ExitNum = rng.Next(1, 3); //Generate random number in [1, 2]
                    //In case r already has exits, create a list which contains all its current exits
                    List <string> currentexits = new List <string>();
                    foreach (Exit e in r.Exits)
                    {
                        currentexits.Add(e.ToRegionName);
                    }
                    while (r.Exits.Count < ExitNum) //Possible that location already has specified number of exits, no big deal if so
                    {
                        Region to = GetRandomAvailableRegion(regions, r, currentexits);
                        if (!string.IsNullOrEmpty(to.Name))
                        {
                            AddExits(regions, r, to);  //Add exit from r to the random region
                            currentexits.Add(to.Name); //Also add dest region to list so it does not get added twice
                        }
                        else //Don't want to do this if r has 0 exits, but that logic is handled in GetRandomAvailableRegion
                        {
                            break;
                        }
                    }
                }
            }
            //Must make sure all locations are reachable
            Generated = new WorldGraph("Region-0", "Goal", regions.ToHashSet(), MajorItemList);
            List <Region> unreachable = Generated.GetUnreachableRegions();

            while (unreachable.Count > 0) //At least one reachable location
            {
                //Create a connection from a random reachable location to a random unreachable location
                List <Region> regionscopy = regions.ToList();
                helper.Shuffle(regionscopy);
                Region from = regionscopy.First(x => !unreachable.Contains(x)); //Not in unreachable, so it is reachable
                helper.Shuffle(unreachable);
                Region to = unreachable.First();                                //Unreachable
                AddExits(regions, from, to);                                    //Add connection between two regions to join subgraphs
                Generated   = new WorldGraph("Region-0", "Goal", regions.ToHashSet(), MajorItemList);
                unreachable = Generated.GetUnreachableRegions();                //Recompute reachability
            }
            //Now before adding items, we will get the last region and place the goal there- No other items will be placed there
            Generated = new WorldGraph("Region-0", "Goal", regions.ToHashSet(), MajorItemList);
            Region   goalregion   = Generated.Regions.Last();
            Item     goalitem     = new Item("Goal", 3);                          //Create goal item
            Location goallocation = new Location("Final Boss", "None", goalitem); //Create location for goal item with no requirement since entrance to region will have full requirement
            //We want all exits to the goal region to require every item so that they will all be required to complete the game
            string fullrequirement = "";

            foreach (Item i in MajorItemList)
            {
                fullrequirement += i.Name + " and ";
            }
            fullrequirement = fullrequirement.Substring(0, fullrequirement.Length - 5); //Remove final " and "
            regions.First(x => x == goalregion).Locations.Add(goallocation);
            foreach (Exit e in regions.First(x => x == goalregion).Exits)
            {
                e.Requirements = fullrequirement;
            }
            //Must also write to requirements leading into final region
            foreach (Region r in regions)
            {
                foreach (Exit e in r.Exits)
                {
                    if (e.ToRegionName == goalregion.Name)
                    {
                        e.Requirements = fullrequirement;
                    }
                }
            }
            //Finally, generate item locations and place the location in the region
            foreach (Region r in regions)
            {
                //The starting region has some specific conditions:
                // 1. Three locations with no requirement
                // 2. 50% chance of a 4th location with one requirement
                if (r.Name == "Region-0")
                {
                    int random = rng.Next(3, 5);
                    for (int i = 0; i < random; i++)
                    {
                        if (i < random - 1) //Guaranteed 3 locations with no requirement
                        {
                            Location l = new Location("Region-0_Location-" + i.ToString(), "None", new Item());
                            regions.First(x => x == r).Locations.Add(l); //Add generated location to region
                        }
                        else //Possible 4th location, have 1 requirement
                        {
                            Location l = new Location("Region-0_Location-" + i.ToString(), GenerateOneRandomRequirement(), new Item());
                            regions.First(x => x == r).Locations.Add(l); //Add generated location to region
                        }
                    }
                }
                //The second region is the hub region and also has some specific conditions:
                // 1. Two locations with no requirement
                // 2. One location with one requirement
                // 3. One location with two requirements
                else if (r.Name == "Region-1")
                {
                    for (int i = 0; i < 4; i++)
                    {
                        if (i < 2)
                        {
                            Location l = new Location("Region-1_Location-" + i.ToString(), "None", new Item());
                            regions.First(x => x == r).Locations.Add(l); //Add generated location to region
                        }
                        else if (i == 2)
                        {
                            Location l = new Location("Region-1_Location-" + i.ToString(), GenerateOneRandomRequirement(), new Item());
                            regions.First(x => x == r).Locations.Add(l); //Add generated location to region
                        }
                        else if (i == 3)
                        {
                            Location l = new Location("Region-1_Location-" + i.ToString(), GenerateTwoRandomRequirements(), new Item());
                            regions.First(x => x == r).Locations.Add(l); //Add generated location to region
                        }
                    }
                }
                //Every other region will generate 2 to 4 locations, unless region contains goal, in which case we want that to be the only location in that region
                else if (r != goalregion)
                {
                    //Generate 2 to 4 locations per region
                    int random = rng.Next(2, 5);
                    for (int i = 0; i < random; i++)
                    {
                        //Generate a location with:
                        // Name: Region-x_Location-y, ex Region-5_Location-2
                        // Requirement: Randomly Generated
                        // Item: null item
                        Location l = new Location(r.Name + "_Location-" + i.ToString(), GenerateRandomRequirement(false), new Item());
                        regions.First(x => x == r).Locations.Add(l); //Add generated location to region
                    }
                }
            }
            //Now that we have a total number of regions and a count of major items, must generate junk items to fill out the item list
            List <Item> ItemList = MajorItemList; //Copy major item list and add goal item

            ItemList.Add(goalitem);
            Generated = new WorldGraph("Region-0", "Goal", regions.ToHashSet(), ItemList.OrderByDescending(x => x.Importance).ThenBy(x => x.Name).ToList()); //Remake generated now that items have been added
            int locationcount = Generated.GetLocationCount();                                                                                                //Get location count and find difference so we know how many junk items to generate
            int difference    = locationcount - MajorItemList.Count();

            for (int i = 0; i < difference; i++)
            {
                //For a junk item, importance will be either 0 or 1, so generate one of those numbers randomly
                int  importance = rng.Next(0, 2);
                Item newitem    = new Item("JunkItem" + importance.ToString(), importance); //Name will either be JunkItem0 or JunkItem1
                ItemList.Add(newitem);
            }
            Generated = new WorldGraph("Region-0", "Goal", regions.ToHashSet(), ItemList.OrderByDescending(x => x.Importance).ThenBy(x => x.Name).ToList()); //Remake generated now that items have been added
            return(Generated);
        }
예제 #4
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);
        }
예제 #5
0
        //Experiment space
        static void Main(string[] args)
        {
            Fill       filler   = new Fill();
            Search     searcher = new Search();
            Statistics stats    = new Statistics();

            //string testjsontext = File.ReadAllText("../../../WorldGraphs/World3.json");
            //WorldGraph testworld = JsonConvert.DeserializeObject<WorldGraph>(testjsontext);

            //double[] testaverages = new double[5];
            //for (int regioncount = 10; regioncount <= 50; regioncount += 5)
            //{
            //    for (int itemcount = 5; itemcount <= Math.Min(regioncount, 30); itemcount += 5)
            //    {
            //        List<TestComplexityOutput> complexity = AverageComplexity(regioncount, itemcount);
            //        Console.WriteLine("Regions: " + regioncount + ", Items: " + itemcount);
            //        Console.WriteLine("Sum: " + complexity.Average(x => x.sum));
            //        Console.WriteLine("Avg: " + complexity.Average(x => x.average));
            //        Console.WriteLine("Max: " + complexity.Average(x => x.max));
            //        Console.WriteLine("SOS: " + complexity.Average(x => x.sumofsquares));
            //        Console.WriteLine("Avg50: " + complexity.Average(x => x.top50));
            //        Console.WriteLine("Avg75: " + complexity.Average(x => x.top75));
            //        Console.Write(Environment.NewLine);
            //    }
            //}

            //string generatedjson = GenerateWorld(50, 30);

            //string jsontest = File.ReadAllText("../../../WorldGraphs/World5.json");
            //WorldGraph testworld = JsonConvert.DeserializeObject<WorldGraph>(jsontest);
            //int testlocationcount = testworld.GetLocationCount();

            //Search testsearcher = new Search();
            //testsearcher.PathsToRegion(world, world.Regions.First(x => x.Name == "Waterfall"));

            //Parser testparse = new Parser();
            //string result = testparse.Simplify("(Sword and Bow and Bow) or Has(Key,2)"); //Should be simplified to something like (Sword and Bow) or Has(Key,2)
            //string result2 = testparse.Simplify("Sword or Sword and Bow"); //Should be simplified to Sword
            ////majoritempool.RemoveAt(8);
            ////majoritempool.RemoveAt(0);
            //bool result = testparse.RequirementsMet("(Sword and Bow) or Has(Key,2)", majoritempool);

            //string testjsontext = File.ReadAllText("../../../WorldGraphs/TestWorldOriginal.json");
            //WorldGraph testworld = JsonConvert.DeserializeObject<WorldGraph>(testjsontext);
            //SphereSearchInfo testoutput = searcher.SphereSearch(testworld);
            //Print_Spheres(testoutput);

            string[] algos = { "Random", "Forward", "Assumed" };
            foreach (string worldname in testworlds)
            {
                DateTime   expstart = DateTime.Now;
                string     jsontext = File.ReadAllText("../../../WorldGraphs/" + worldname + ".json");
                WorldGraph world    = JsonConvert.DeserializeObject <WorldGraph>(jsontext);
                int        l        = world.GetLocationCount();
                //Loop to perform fill
                for (int i = 0; i < 3; i++) //0 = Random, 1 = Forward, 2 = assumed
                {
                    if (dotests[i])
                    {
                        //List<InterestingnessOutput> intstats = new List<InterestingnessOutput>();
                        //double totaltime = 0;
                        int savecounter = 0;
                        int countofexp  = db.Results.Count(x => x.Algorithm == algos[i] && x.World == worldname);
                        //int countofexp = 0;
                        while (countofexp < trials) //Go until there are trial number of records in db
                        {
                            InterestingnessOutput intstat = new InterestingnessOutput();
                            double difference             = -1;
                            while (true)                                    //If something goes wrong in playthrough search, may need to retry
                            {
                                WorldGraph  input           = world.Copy(); //Copy so that world is not passed by reference and overwritten
                                List <Item> majoritempool   = input.Items.Where(x => x.Importance == 2).ToList();
                                List <Item> minoritempool   = input.Items.Where(x => x.Importance < 2).ToList();
                                WorldGraph  randomizedgraph = new WorldGraph();
                                DateTime    start           = DateTime.Now; //Start timing right before algorithm
                                //Decide which algo to use based on i
                                switch (i)
                                {
                                case 0:
                                    randomizedgraph = filler.RandomFill(input, majoritempool);
                                    break;

                                case 1:
                                    randomizedgraph = filler.ForwardFill(input, majoritempool);
                                    break;

                                case 2:
                                    randomizedgraph = filler.AssumedFill(input, majoritempool);
                                    break;
                                }
                                randomizedgraph = filler.RandomFill(randomizedgraph, minoritempool); //Use random for minor items always since they don't matter
                                //Calculate metrics
                                DateTime end = DateTime.Now;
                                difference = (end - start).TotalMilliseconds;
                                //totaltime += difference;
                                //string randomizedjson = JsonConvert.SerializeObject(randomizedgraph);
                                //SphereSearchInfo output = searcher.SphereSearch(randomizedgraph);
                                //Print_Spheres(output);
                                try
                                {
                                    intstat = stats.CalcDistributionInterestingness(randomizedgraph);
                                    break; //Was successful, continue
                                }
                                catch { } //Something went wrong, retry fill from scratch
                            }
                            //intstats.Add(intstat);
                            //Store result in database
                            Result result = new Result();
                            result.Algorithm       = algos[i];
                            result.World           = worldname;
                            result.Completable     = intstat.completable;
                            result.ExecutionTime   = difference;
                            result.Bias            = intstat.bias.biasvalue;
                            result.BiasDirection   = intstat.bias.direction;
                            result.Interestingness = intstat.interestingness;
                            result.Fun             = intstat.fun;
                            result.Challenge       = intstat.challenge;
                            result.Satisfyingness  = intstat.satisfyingness;
                            result.Boredom         = intstat.boredom;
                            db.Entry(result).State = EntityState.Added;
                            savecounter++;
                            if (savecounter >= 1000) //Save every 1000 results processed
                            {
                                db.SaveChanges();
                                savecounter = 0;
                            }
                            countofexp++;
                        }
                        //double avgint = intstats.Where(x => x.completable).Average(x => x.interestingness);
                        //Console.WriteLine("Average interestingness for " + algos[i] + " Fill in world " + worldname + ": " + avgint);
                        //double avgbias = intstats.Where(x => x.completable).Average(x => x.bias.biasvalue);
                        //Console.WriteLine("Average bias for " + algos[i] + " Fill in world " + worldname + ": " + avgbias);
                        //double avgfun = intstats.Where(x => x.completable).Average(x => x.fun);
                        //Console.WriteLine("Average fun for " + algos[i] + " Fill in world " + worldname + ": " + avgfun);
                        //double avgchal = intstats.Where(x => x.completable).Average(x => x.challenge);
                        //Console.WriteLine("Average challenge for " + algos[i] + " Fill in world " + worldname + ": " + avgchal);
                        //double avgsat = intstats.Where(x => x.completable).Average(x => x.satisfyingness);
                        //Console.WriteLine("Average satisfyingness for " + algos[i] + " Fill in world " + worldname + ": " + avgsat);
                        //double avgbore = intstats.Where(x => x.completable).Average(x => x.boredom);
                        //Console.WriteLine("Average boredom for " + algos[i] + " Fill in world " + worldname + ": " + avgbore);
                        //double avgtime = totaltime / trials;
                        //Console.WriteLine("Average time to generate for " + algos[i] + " Fill in world " + worldname + ": " + avgtime + "ms");

                        db.SaveChanges(); //Save changes when combo of algo and world is done
                    }
                    //Console.Write(Environment.NewLine);
                }
                //Console.Write(Environment.NewLine);
                //Console.Write(Environment.NewLine);
                DateTime expend        = DateTime.Now;
                double   expdifference = (expend - expstart).TotalMinutes;
                Console.WriteLine("Time to perform " + trials + " iterations for world " + worldname + ": " + expdifference + " minutes");
            }
            Console.ReadLine();
        }