dissertation / mainCode / Game / DifficultyManager.cs
DifficultyManager.cs
Raw
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;

public class DifficultyManager
{
    private Dictionary<String, double> means = new Dictionary<string, double>();
    private Dictionary<String, double> stdDevs = new Dictionary<string, double>();

    public double gamerScore;
    
    private List<String> keys =
        new List<string>{"accuracy", "killsPerSecond", "headshotPercentage", "timeToDamage", "averageHealthLoss", "timeToKill"};
    public void LogGame(GameResults results)
    {
        // add game to log of previous 10 games
        GameLog gameLog = JsonUtility.FromJson<GameLog>(PlayerPrefs.GetString("gameLog"));

        if (gameLog.gameResultsList.Count >= 10) gameLog.gameResultsList.RemoveAt(0);
        if (gameLog.accuracies.Count >= 10) gameLog.accuracies.RemoveAt(0);
        if (gameLog.headshotPercentage.Count >= 10) gameLog.headshotPercentage.RemoveAt(0);
        if (gameLog.averageHealthLoss.Count >= 10) gameLog.averageHealthLoss.RemoveAt(0);
        if (gameLog.killsPerSecond.Count >= 10) gameLog.killsPerSecond.RemoveAt(0);
        if (gameLog.timeToDamage.Count >= 10) gameLog.timeToDamage.RemoveAt(0);
        if (gameLog.timeToKill.Count >= 10) gameLog.timeToKill.RemoveAt(0);
        

        gameLog.gameResultsList.Add(results);
        gameLog.accuracies.Add(results.accuracy);
        gameLog.headshotPercentage.Add(results.headshotPercentage);
        gameLog.averageHealthLoss.Add(results.averageHealthLoss > 0 ? results.averageHealthLoss : 100f);
        gameLog.killsPerSecond.Add(results.killsPerSecond);
        gameLog.timeToDamage.Add(results.timeToDamage > 0 ? results.timeToDamage : 100f);
        gameLog.timeToKill.Add(results.timeToKill > 0 ? results.timeToKill : 100f);
        String newLog = JsonUtility.ToJson(gameLog);
        PlayerPrefs.SetString("gameLog", newLog);
    }
    
    public int DecideLevel(GameResults results)
    {
        // return an int representing how much to shift the level (0 means stay, -1/-2 lower, +1/+2 higher)
        // creates a score based on a normal distribution of all statistics
        GameLog gameLog = JsonUtility.FromJson<GameLog>(PlayerPrefs.GetString("gameLog"));
        if (gameLog.gameResultsList.Count < 5)
        {
            return 0;
        }
        
        GetStats(gameLog);

        var accuracyScore = GetScore("accuracy", results.accuracy);
        var headshotScore = GetScore("headshotPercentage", results.headshotPercentage);
        var killsScore = GetScore("killsPerSecond", results.killsPerSecond);
        var ttdScore = 1 - GetScore("timeToDamage", results.timeToDamage);
        var healthScore = 1 - GetScore("averageHealthLoss", results.averageHealthLoss);
        var ttkScore = 1 - GetScore("timeToKill", results.timeToKill);
        double winScore;
        if (results.won)
        {
            winScore = 1f;
        }
        else
        {
            winScore = 0f;
        }

        gamerScore = winScore * 40 + accuracyScore * 10 + killsScore * 10 + headshotScore * 10 + ttdScore * 10 +
                           healthScore * 10 + ttkScore * 10;
        //gamerScore *= 1.25f; // sets it to be a num between 0 - 100
        // weighted winning more as if you were able to win but played poorly it wouldnt change the difficulty at all - if you are consistently winning it should make it harder until you start losing sometimes

        Debug.Log(gamerScore);
        
        if (gamerScore >= 80)
        {
            return 2;
        } else if (gamerScore >= 60)
        {
            return 1;
        } else if (gamerScore >= 40)
        {
            return 0;
        } else if (gamerScore >= 20)
        {
            return -1;
        }
        else
        {
            return -2;
        }

    }

    private void GetStats(GameLog log)
    {
            means.Add("accuracy", log.accuracies.Average());
            means.Add("killsPerSecond", log.killsPerSecond.Average());
            means.Add("headshotPercentage", log.headshotPercentage.Average());
            means.Add("timeToDamage", log.timeToDamage.Average());
            means.Add("averageHealthLoss", log.averageHealthLoss.Average());
            means.Add("timeToKill", log.timeToKill.Average());
            
           GetStdDev("accuracy", log.accuracies);
           GetStdDev("killsPerSecond", log.killsPerSecond);
           GetStdDev("headshotPercentage", log.headshotPercentage);
           GetStdDev("timeToDamage", log.timeToDamage);
           GetStdDev("averageHealthLoss", log.averageHealthLoss);
           GetStdDev("timeToKill", log.timeToKill);

    }

    private void GetStdDev(String key, List<float> values)
    {
        var avg = means[key];
        var sum = values.Sum(f => Math.Pow(f - avg, 2f));
        stdDevs.Add(key, Math.Sqrt((sum) / values.Count)); 
    }

    private double GetScore(String key, float value)
    {
        return Phi((value - means[key]) / stdDevs[key]);
    }

    private double Phi(double zScore)
    {
        float a1 = 0.254829592f;
        float a2 = -0.284496736f;
        float a3 = 1.421413741f;
        float a4 = -1.453152027f;
        float a5 = 1.061405429f;
        float p = 0.3275911f;

        int sign = 1;
        if (zScore < 0) sign = -1;

        zScore = Math.Abs(zScore) / (float) Math.Sqrt(2);

        double t = 1f / (1 + p * zScore);
        double y = 1f - ((((a5 * t + a4) * t + a3) * t + a2) * t + a1) * t * (float) Math.Exp(-zScore * zScore);

        return 0.5f * (1 + sign * y);
    }
    
}