/** * Get gameplay parameter value for desired target difficulty * uses PMDeltaLastTheta for PMDelta algorithm */ public DiffParams computeNewDiffParams(double targetDifficulty, bool doNotUpdateLRAccuracy = false) { DiffParams diffParams = new DiffParams(); diffParams.LogRegReady = true; diffParams.AlgorithmWanted = Algorithm; diffParams.Betas = null; diffParams.LogRegError = DDALogRegError.OK; //Loading data List <DDADataManager.Attempt> attempts = DataManager.getAttempts(PlayerId, ChallengeId, LRNbLastAttemptsToConsider); //Data translation for LR LogisticRegression.DataLR data = new LogisticRegression.DataLR(); List <double[]> indepVars = new List <double[]>(); List <double> depVars = new List <double>(); foreach (DDADataManager.Attempt attempt in attempts) { indepVars.Add(attempt.Thetas); depVars.Add(attempt.Result); } data.LoadDataFromList(indepVars, depVars); //On met a jour le dernier theta en fonction des datas si on ne l'a pas deja set if (indepVars.Count > 0 && !PMInitialized) { PMLastTheta = indepVars[indepVars.Count - 1][0]; PMWonLastTime = depVars[depVars.Count - 1] > 0 ? true : false; PMInitialized = true; } //Check if enough data to update LogReg if (attempts.Count < 10) { Debug.Log("Less than 10 attempts, can not use LogReg prediciton"); diffParams.LogRegReady = false; diffParams.LogRegError = DDALogRegError.NOT_ENOUGH_SAMPLES; } else { //Chekcing wins and fails double nbFail = 0; double nbWin = 0; foreach (DDADataManager.Attempt attempt in attempts) { if (attempt.Result == 0) { nbFail++; } else { nbWin++; } } //If only three fails or three wins if (nbFail <= 3 || nbWin <= 3) { Debug.Log("Less than 4 wins or 4 fails, will not use LogReg"); diffParams.LogRegReady = false; if (nbWin <= 3) { diffParams.LogRegError = DDALogRegError.NOT_ENOUGH_WINS; } if (nbFail <= 3) { diffParams.LogRegError = DDALogRegError.NOT_ENOUGH_FAILS; } } } if (diffParams.LogRegReady) { //Debug.Log("Using " + data.DepVar.Length + " lines to update model"); if (!doNotUpdateLRAccuracy && !LRAccuracyUpToDate) { //Ten fold cross val LRAccuracy = 0; for (int i = 0; i < 10; i++) { double AccuracyNow = 0; data = data.shuffle(); int nk = 10; for (int k = 0; k < nk; k++) { LogisticRegression.DataLR dataTrain; LogisticRegression.DataLR dataTest; data.split(k * (100 / nk), (k + 1) * (100 / nk), out dataTrain, out dataTest); LogReg = LogisticRegression.ComputeModel(dataTrain); AccuracyNow += LogisticRegression.TestModel(LogReg, dataTest); } AccuracyNow /= nk; LRAccuracy += AccuracyNow; } LRAccuracy /= 10; LRAccuracyUpToDate = true; //Using all data to update model LogReg = LogisticRegression.ComputeModel(data); diffParams.NbAttemptsUsedToCompute = data.DepVar.Length; } else { data = data.shuffle(); LogReg = LogisticRegression.ComputeModel(data); diffParams.NbAttemptsUsedToCompute = data.DepVar.Length; } if (LRAccuracy < LRMinimalAccuracy) { Debug.Log("LogReg accuracy is under " + LRMinimalAccuracy + ", not using LogReg"); diffParams.LogRegReady = false; diffParams.LogRegError = DDALogRegError.ACCURACY_TOO_LOW; } if (!LogReg.isUsable()) { LRAccuracy = 0; diffParams.LogRegError = DDALogRegError.NEWTON_RAPHSON_ERROR; } else if (diffParams.LogRegReady) { //Verifying if LogReg is ok : must be able to work in both ways double errorSum = 0; double diffTest = 0.1; double[] pars = new double[1]; double[] parsForAllDiff = new double[10]; string res = ""; for (int i = 0; i < 8; i++) { pars[0] = LogReg.InvPredict(diffTest, pars, 0); //on regarde que la première variable. parsForAllDiff[i] = pars[0]; res = "D = " + diffTest + " par = " + pars[0]; errorSum += System.Math.Abs(diffTest - LogReg.Predict(pars)); //On passe dans les deux sens on doit avoir pareil res += " res = " + LogReg.Predict(pars) + "\n"; diffTest += 0.1; //Debug.Log(res); } if (errorSum > 1 || double.IsNaN(errorSum)) { Debug.Log("Model is not solid, error = " + errorSum); LRAccuracy = 0; if (errorSum > 1) { diffParams.LogRegError = DDALogRegError.SUM_ERROR_TOO_HIGH; } if (double.IsNaN(errorSum)) { diffParams.LogRegError = DDALogRegError.SUM_ERROR_IS_NAN; } } //Verifying if LogReg is ok : sd of diff predictions in all theta range must not be 0 double mean = 0; for (int i = 0; i < 8; i++) { mean += parsForAllDiff[i]; } mean /= 8; double sd = 0; for (int i = 0; i < 8; i++) { sd += (parsForAllDiff[i] - mean) * (parsForAllDiff[i] - mean); } sd = System.Math.Sqrt(sd); //Debug.Log("Model parameter estimation sd = " + sd); if (sd < 0.05 || double.IsNaN(sd)) { Debug.Log("Model parameter estimation is always the same : sd=" + sd); LRAccuracy = 0; if (sd < 0.05) { diffParams.LogRegError = DDALogRegError.SD_PRED_TOO_LOW; } if (double.IsNaN(sd)) { diffParams.LogRegError = DDALogRegError.SD_PRED_IS_NAN; } } } } //Saving params diffParams.TargetDiff = targetDifficulty; diffParams.LRAccuracy = LRAccuracy; //Determining theta //If we want pmdelta or we want log reg but it's not available if ((Algorithm == DDAAlgorithm.DDA_LOGREG && !diffParams.LogRegReady) || Algorithm == DDAAlgorithm.DDA_PMDELTA) { double delta = PMWonLastTime ? PMDeltaValue : -PMDeltaValue; delta *= Random.Range(PMDeltaExploMin, PMDeltaExploMax); diffParams.Theta = PMLastTheta + delta; diffParams.AlgorithmActuallyUsed = DDAAlgorithm.DDA_PMDELTA; //If regression is okay, we can tell the difficulty for this theta if (diffParams.LogRegReady) { double[] pars = new double[1]; pars[0] = diffParams.Theta; diffParams.TargetDiff = 1.0 - LogReg.Predict(pars); diffParams.TargetDiffWithExplo = diffParams.TargetDiff; } else //Otherwise we just can tell we aim for 0.5 { diffParams.TargetDiffWithExplo = 0.5; diffParams.TargetDiff = 0.5; } } //if we want log reg and it's available if (Algorithm == DDAAlgorithm.DDA_LOGREG && diffParams.LogRegReady) { diffParams.TargetDiffWithExplo = targetDifficulty + Random.Range(-LRExplo, LRExplo); diffParams.TargetDiffWithExplo = System.Math.Min(1.0, System.Math.Max(0, diffParams.TargetDiffWithExplo)); diffParams.Theta = LogReg.InvPredict(1.0 - diffParams.TargetDiffWithExplo); diffParams.AlgorithmActuallyUsed = DDAAlgorithm.DDA_LOGREG; } //if we want random log reg and it's available if (Algorithm == DDAAlgorithm.DDA_RANDOM_LOGREG && diffParams.LogRegReady) { diffParams.TargetDiff = Random.Range(0.0f, 1.0f); diffParams.TargetDiffWithExplo = diffParams.TargetDiff; //Pas d'explo on est en random diffParams.Theta = LogReg.InvPredict(1.0 - diffParams.TargetDiffWithExplo); diffParams.AlgorithmActuallyUsed = DDAAlgorithm.DDA_RANDOM_LOGREG; } //If we want random if (Algorithm == DDAAlgorithm.DDA_RANDOM_THETA || (Algorithm == DDAAlgorithm.DDA_RANDOM_LOGREG && !diffParams.LogRegReady)) { diffParams.Theta = Random.Range(0.0f, 1.0f); diffParams.AlgorithmActuallyUsed = DDAAlgorithm.DDA_RANDOM_THETA; //If regression is okay, we can tell the difficulty for this theta if (diffParams.LogRegReady) { double[] pars = new double[1]; pars[0] = diffParams.Theta; diffParams.TargetDiff = 1.0 - LogReg.Predict(pars); diffParams.TargetDiffWithExplo = diffParams.TargetDiff; } else //Otherwise, we don't know, let's put a negative value { diffParams.TargetDiffWithExplo = -1; diffParams.TargetDiff = -1; } } //Save betas if we have some if (LogReg != null && LogReg.Betas != null && LogReg.Betas.Length > 0) { diffParams.Betas = new double[LogReg.Betas.Length]; for (int i = 0; i < LogReg.Betas.Length; i++) { diffParams.Betas[i] = LogReg.Betas[i]; } } //Clamp 01 double. Super inportant pour éviter les infinis diffParams.Theta = diffParams.Theta > 1.0 ? 1.0 : diffParams.Theta; diffParams.Theta = diffParams.Theta < 0.0 ? 0.0 : diffParams.Theta; return(diffParams); }