/// <summary> /// Calculates a range for a target beta. /// </summary> /// /// <param name="theta"> The theta. </param> /// /// <returns> /// A two-element array with min and max values of ratings. /// </returns> private double[] calcTargetBetas(double theta) { double[] randNums = new double[2]; for (int index = 0; index < randNums.Length; index++) { double rndNum; do { SimpleRNG.SetSeedFromRandom(); rndNum = SimpleRNG.GetNormal(TARGET_DISTR_MEAN, TARGET_DISTR_SD); } while (rndNum <= TARGET_LOWER_LIMIT || rndNum >= TARGET_UPPER_LIMIT || rndNum == 1 || rndNum == 0); // [SC][2016.03.14] || randomNum == 1 || randomNum == 0 randNums[index] = rndNum; } Array.Sort(randNums); // [SC][2016.10.07] this is the old equation to calculate target beta // theta + Math.Log(randomNum / (1 - randomNum)); double minBeta = theta + Math.Log((1 - randNums[1]) / randNums[1]); // [SC][2016.10.07] a modified version of the equation from the original data; better suits the data double maxBeta = theta + Math.Log((1 - randNums[0]) / randNums[0]); return(new double[] { minBeta, maxBeta }); }
////////////////////////////////////////////////////////////////////////////////////// ////// START: functions for calculating matching scenario #region functions for calculating matching scenario /// <summary> /// Calculates expected beta for target scenario. Returns ID of a scenario with beta closest to the target beta. /// If two more scenarios match then scenario that was least played is chosen. /// </summary> /// /// <exception cref="ArgumentException"> Thrown when one or more arguments /// have unsupported or illegal values. </exception> /// /// <param name="gameID"> Identifier for the game. </param> /// <param name="playerID"> Identifier for the player. </param> /// /// <returns> /// A string. /// </returns> internal string TargetScenarioID(string gameID, string playerID) // [SC][2016.03.14] CalculateTargetScenarioID => TargetScenarioID // [SC] get player rating. // [2016.11.14] modified { double playerRating = asset.PlayerRating(ADAPTER_TYPE, gameID, playerID); // [ASSET] // [SC] get IDs of available scenarios List <string> scenarioIDList = asset.AllScenariosIDs(ADAPTER_TYPE, gameID); // [ASSET] if (scenarioIDList.Count == 0) { throw new System.ArgumentException("No scenarios found for adaptation " + ADAPTER_TYPE + " in game " + gameID); } // [SC] calculate min and max possible ratings for candidate scenarios double[] minMaxRatings = calcTargetBetas(playerRating); // [SC][2016.12.07] modified // [SC] info for the scenarios within the min/max rating range and with the lowest play count List <string> inScenarioID = new List <string>(); double inMinPlayCount = 0; // [SC] info for the closest scenarios outside of the min/max rating range and the lowest play count List <string> outScenarioID = new List <string>(); double outMinPlayCount = 0; double outMinDistance = 0; // [SC] iterate through the list of all scenarios foreach (string scenarioID in scenarioIDList) { if (String.IsNullOrEmpty(scenarioID)) { throw new System.ArgumentException("Null scenario ID found for adaptation " + ADAPTER_TYPE + " in game " + gameID); } // [2016.11.14] modified double scenarioRating = asset.ScenarioRating(ADAPTER_TYPE, gameID, scenarioID); // [ASSET] double scenarioPlayCount = asset.ScenarioPlayCount(ADAPTER_TYPE, gameID, scenarioID); // [ASSET] // [SC] the scenario rating is within min/max rating range if (scenarioRating >= minMaxRatings[0] && scenarioRating <= minMaxRatings[1]) { if (inScenarioID.Count == 0 || scenarioPlayCount < inMinPlayCount) { inScenarioID.Clear(); inScenarioID.Add(scenarioID); inMinPlayCount = scenarioPlayCount; } else if (scenarioPlayCount == inMinPlayCount) { inScenarioID.Add(scenarioID); } } else // [SC] the scenario rating is outside of the min/max rating range { double distance = Math.Min(Math.Abs(scenarioRating - minMaxRatings[0]), Math.Abs(scenarioRating - minMaxRatings[1])); if (outScenarioID.Count == 0 || distance < outMinDistance) { outScenarioID.Clear(); outScenarioID.Add(scenarioID); outMinDistance = distance; outMinPlayCount = scenarioPlayCount; } else if (distance == outMinDistance && scenarioPlayCount < outMinPlayCount) { outScenarioID.Clear(); outScenarioID.Add(scenarioID); outMinPlayCount = scenarioPlayCount; } else if (distance == outMinDistance && scenarioPlayCount == outMinPlayCount) { outScenarioID.Add(scenarioID); } } } if (inScenarioID.Count() > 0) { return(inScenarioID[SimpleRNG.Next(inScenarioID.Count())]); } return(outScenarioID[SimpleRNG.Next(outScenarioID.Count())]); }