//Experiment space static void Main(string[] args) { Fill filler = new Fill(); Search searcher = new Search(); Statistics stats = new Statistics(); ////Uncomment to test different complexity measures and generate many worlds with different parameters. //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); // } //} //Loop through each algorithm set to be used and each world in the list, performing specified algorithm on specified world and recording information about the result. 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); //Loop to perform fill algorithms for (int i = 0; i < 3; i++) //0 = Random, 1 = Forward, 2 = Assumed { if (dotests[i]) { int savecounter = 0; int countofexp = db.Results.Count(x => x.Algorithm == algos[i] && x.World == worldname); 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; try { intstat = stats.CalcDistributionInterestingness(randomizedgraph); break; //Was successful, continue } catch { } //Something went wrong, retry fill from scratch ////Uncomment to print the spheres of the result. //SphereSearchInfo output = searcher.SphereSearch(randomizedgraph); //Print_Spheres(output); } //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++; } db.SaveChanges(); //Save changes when combo of algo and world is done } } DateTime expend = DateTime.Now; double expdifference = (expend - expstart).TotalMinutes; Console.WriteLine("Time to perform " + trials + " iterations for world " + worldname + ": " + expdifference + " minutes"); //Print how long this world took to do } Console.ReadLine(); }
/* * 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); }
//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(); }