From a5679b4270590e661bce99c9d6cd10292bf07243 Mon Sep 17 00:00:00 2001 From: Mark Joshwel Date: Tue, 19 Nov 2024 03:59:29 +0800 Subject: [PATCH] game: working score + stats calcs --- ColourMeOKGame/Assets/Scripts/AccountUI.cs | 2 +- ColourMeOKGame/Assets/Scripts/Backend.cs | 15 ++-- ColourMeOKGame/Assets/Scripts/GameManager.cs | 75 +++++++++++-------- ColourMeOKGame/Assets/Scripts/Gameplay.cs | 14 ++-- .../Assets/Scripts/LocalPlayerData.cs | 54 +++++++------ ColourMeOKGame/Assets/Scripts/SideViewUI.cs | 26 +++---- 6 files changed, 98 insertions(+), 88 deletions(-) diff --git a/ColourMeOKGame/Assets/Scripts/AccountUI.cs b/ColourMeOKGame/Assets/Scripts/AccountUI.cs index 1c68236..a46cabc 100644 --- a/ColourMeOKGame/Assets/Scripts/AccountUI.cs +++ b/ColourMeOKGame/Assets/Scripts/AccountUI.cs @@ -661,7 +661,7 @@ private void OnLocalPlayerDataChangeCallback(LocalPlayerData data) $"updating AccountView ui with lkUsername={data.LastKnownUsername} and lkEmail={data.LastKnownEmail}"); _usernameField.value = data.LastKnownUsername; _emailField.value = data.LastKnownEmail; - _header.text = $"Signed in as {data.LastKnownUsername}"; + if (state == State.SignedIn) _header.text = $"Signed in as {data.LastKnownUsername}"; } /// diff --git a/ColourMeOKGame/Assets/Scripts/Backend.cs b/ColourMeOKGame/Assets/Scripts/Backend.cs index a1443a4..55fd00a 100644 --- a/ColourMeOKGame/Assets/Scripts/Backend.cs +++ b/ColourMeOKGame/Assets/Scripts/Backend.cs @@ -571,7 +571,7 @@ public void GetRecentScores(Action { @@ -618,7 +618,7 @@ private void GetBestScores(Action { @@ -702,21 +702,23 @@ private void GetBestScores(Action LocalPlayerData.MaxRecentLocalScores) recentScoreQueue.Dequeue(); + while (recentScoreQueue.Count > LocalPlayerData.MaxRecentScores) recentScoreQueue.Dequeue(); GetBestScores((bestRes, bestScores) => { if (bestRes != DatabaseTransactionResult.Ok) { Debug.Log("failed to get recent scores"); + GameManager.Instance.FireLocalPlayerDataChangeCallbacks(GameManager.Instance.Data); callback(recentRes, 0f); return; } var bestScoreQueue = GameManager.Instance.Data.BestOnlineScores; foreach (var score in bestScores) bestScoreQueue.Enqueue(score); - while (bestScoreQueue.Count > LocalPlayerData.MaxBestOnlineScores) bestScoreQueue.Dequeue(); + while (bestScoreQueue.Count > LocalPlayerData.MaxBestScores) bestScoreQueue.Dequeue(); + GameManager.Instance.FireLocalPlayerDataChangeCallbacks(GameManager.Instance.Data); callback(DatabaseTransactionResult.Ok, GameManager.Instance.Data.CalculateUserRating()); }); }); @@ -737,14 +739,17 @@ private void GetBestScores(Action { if (task.IsCompletedSuccessfully) { + Debug.Log($"updated online user rating to {userRating}"); callback(DatabaseTransactionResult.Ok); } else diff --git a/ColourMeOKGame/Assets/Scripts/GameManager.cs b/ColourMeOKGame/Assets/Scripts/GameManager.cs index 8ce1299..7937eef 100644 --- a/ColourMeOKGame/Assets/Scripts/GameManager.cs +++ b/ColourMeOKGame/Assets/Scripts/GameManager.cs @@ -175,42 +175,47 @@ public void SignalGameEnd(List playedRounds) var historicalLightnessAcc = 0f; var historicalChromaAcc = 0f; var historicalHueAcc = 0f; - var historicalRounds = 0; + var historicalPerceivedAcc = 0f; + var historicalGames = 0; - foreach (var localScore in _data.RecentLocalScores) + foreach (var localScore in _data.RecentLocalScores.Take(LocalPlayerData.MaxRecentScores)) { historicalLightnessAcc += localScore.AvgLightnessAccuracy; historicalChromaAcc += localScore.AvgChromaAccuracy; historicalHueAcc += localScore.AvgHueAccuracy; - historicalRounds += localScore.NoOfRounds; + historicalPerceivedAcc += localScore.AvgPerceivedAccuracy; + historicalGames++; } - foreach (var onlineScore in _data.RecentOnlineScores) + foreach (var onlineScore in _data.RecentOnlineScores.Take(LocalPlayerData.MaxRecentScores)) { historicalLightnessAcc += onlineScore.AvgLightnessAccuracy; historicalChromaAcc += onlineScore.AvgChromaAccuracy; historicalHueAcc += onlineScore.AvgHueAccuracy; - historicalRounds += onlineScore.NoOfRounds; + historicalPerceivedAcc += onlineScore.AvgPerceivedAccuracy; + historicalGames++; } - foreach (var onlineScore in _data.BestOnlineScores) + foreach (var onlineScore in _data.BestOnlineScores.Take(LocalPlayerData.MaxBestScores)) { historicalLightnessAcc += onlineScore.AvgLightnessAccuracy; historicalChromaAcc += onlineScore.AvgChromaAccuracy; historicalHueAcc += onlineScore.AvgHueAccuracy; - historicalRounds += onlineScore.NoOfRounds; + historicalPerceivedAcc += onlineScore.AvgPerceivedAccuracy; + historicalGames++; } - historicalLightnessAcc /= historicalRounds; - historicalChromaAcc /= historicalRounds; - historicalHueAcc /= historicalRounds; + historicalGames = Math.Max(1, historicalGames); + historicalLightnessAcc /= historicalGames; + historicalChromaAcc /= historicalGames; + historicalHueAcc /= historicalGames; + historicalPerceivedAcc /= historicalGames; // calculate round averages var gameLightnessAcc = 0d; var gameChromaAcc = 0d; var gameHueAcc = 0d; var gamePerceivedAcc = 0d; - var templateColour = Color.clear; var responseColour = Color.clear; var roundNumber = 1; @@ -226,7 +231,7 @@ public void SignalGameEnd(List playedRounds) var roundLightnessAcc = Math.Clamp(dLCh.L * 100d, 0d, 100d); var roundChromaAcc = Math.Clamp(dLCh.C * 100d, 0d, 100d); var roundHueAcc = Math.Clamp(dLCh.h * 100d, 0d, 100d); - var roundPerceivedAcc = Math.Clamp((100d - distance.dE) * 100d, 0d, 100d); + var roundPerceivedAcc = Math.Clamp((1d - distance.dE) * 100d, 0d, 100d); gameLightnessAcc += roundLightnessAcc; gameChromaAcc += roundChromaAcc; @@ -242,7 +247,7 @@ public void SignalGameEnd(List playedRounds) showcaseTemplate.style.backgroundColor = templateColour; showcaseResponse.style.backgroundColor = responseColour; showcaseInfo.text = - $"{roundLightnessAcc:N0}% {roundChromaAcc:N0}% {roundHueAcc:N0}% ({roundPerceivedAcc:N0}%)"; + $"{roundLightnessAcc * (roundPerceivedAcc / 100d):N0}% {roundChromaAcc * (roundPerceivedAcc / 100d):N0}% {roundHueAcc * (roundPerceivedAcc / 100d):N0}% ({roundPerceivedAcc:N0}%)"; } else { @@ -256,26 +261,31 @@ public void SignalGameEnd(List playedRounds) gameChromaAcc /= Gameplay.RoundsPerGame; gameHueAcc /= Gameplay.RoundsPerGame; gamePerceivedAcc /= Gameplay.RoundsPerGame; - - // NOTE: this is NOT equiv to user rating, this is just a per-game accuracy score - // all that math is done in LocalPlayerData.CalculateUserRating - var gameAccuracy = (gameLightnessAcc + gameChromaAcc + gameHueAcc + gamePerceivedAcc) / 4; + + var adjustedHistoricalLightnessAcc = historicalLightnessAcc * (historicalPerceivedAcc / 100d); + var adjustedHistoricalChromaAcc = historicalChromaAcc * (historicalPerceivedAcc / 100d); + var adjustedHistoricalHueAcc = historicalHueAcc * (historicalPerceivedAcc / 100d); + var adjustedGameLightnessAcc = gameLightnessAcc * (gamePerceivedAcc / 100d); + var adjustedGameChromaAcc = gameChromaAcc * (gamePerceivedAcc / 100d); + var adjustedGameHueAcc = gameHueAcc * (gamePerceivedAcc / 100d); + var gameAccuracy = (gameLightnessAcc + gameChromaAcc + gameHueAcc + gamePerceivedAcc) / 4d; // make comparison texts - var lAccDeltaText = (gameLightnessAcc > historicalLightnessAcc ? "+" : "-") + - Math.Abs(gameLightnessAcc - historicalLightnessAcc).ToString("F2"); - var cAccDeltaText = (gameChromaAcc > historicalChromaAcc ? "+" : "-") + - Math.Abs(gameChromaAcc - historicalChromaAcc).ToString("F2"); - var hAccDeltaText = (gameHueAcc > historicalHueAcc ? "+" : "-") + - Math.Abs(gameHueAcc - historicalHueAcc).ToString("F2"); + var lAccDeltaText = (adjustedGameLightnessAcc > adjustedHistoricalLightnessAcc ? "+" : "-") + + $"{Math.Abs(adjustedGameLightnessAcc - adjustedHistoricalLightnessAcc):F1}%"; + var cAccDeltaText = (adjustedGameChromaAcc > adjustedHistoricalChromaAcc ? "+" : "-") + + $"{Math.Abs(adjustedGameChromaAcc - adjustedHistoricalChromaAcc):F1}%"; + var hAccDeltaText = (adjustedGameHueAcc > adjustedHistoricalHueAcc ? "+" : "-") + + $"{Math.Abs(adjustedGameHueAcc - adjustedHistoricalHueAcc):F1}%"; var score = new LocalPlayerData.Score(DateTime.Now, playedRounds.Count, - (float)gameLightnessAcc, - (float)gameChromaAcc, - (float)gameHueAcc, + (float)adjustedGameLightnessAcc, + (float)adjustedGameChromaAcc, + (float)adjustedGameHueAcc, (float)gamePerceivedAcc); + // breakpoint here: why is gamePerceivedAcc always "100"? var oldRating = _data.CalculateUserRating(); _data.RegisterLocalScore(score); FireLocalPlayerDataChangeCallbacks(Instance.Data); @@ -298,7 +308,6 @@ public void SignalGameEnd(List playedRounds) { Debug.Log("couldn't calculate user rating"); TransitionToResultsView(_data.CalculateUserRating()); - FireLocalPlayerDataChangeCallbacks(Instance.Data); return; } @@ -322,17 +331,17 @@ void TransitionToResultsView(float rating) { Debug.Log("signal GameManager-UIManager transition to results view"); - var ratingDifferenceDescriptor = oldRating > rating ? "decreased" : "increased"; + var ratingDifferenceDescriptor = rating > oldRating ? "increased" : "decreased"; var ratingText = rating >= 0 ? $"\nYour rating has {ratingDifferenceDescriptor} by {Math.Abs(rating - oldRating):F2}." : "\nYour rating could not be calculated."; // build the result text and show the results view - ui.UI.Q // ReSharper disable once MemberCanBePrivate.Global - public const double SecondsPerRound = 15d; + public const double SecondsPerRound = 12d; /// /// countdown text label for showing the countdown @@ -118,7 +118,7 @@ private void StoreRoundInfo() TemplateColour = _templateColour.style.backgroundColor.value, ResponseColour = _responseColour.style.backgroundColor.value }); - Debug.Log("stored round info"); + Debug.Log($"stored round info (Template is {_templateColour.style.backgroundColor.value}, Response is {_responseColour.style.backgroundColor.value})"); } /// @@ -127,13 +127,13 @@ private void StoreRoundInfo() /// private void GenerateNewTemplateColour() { - // - lightness: 40-80 - // - chroma: 0.05-0.20 + // - lightness: 30-90 + // - chroma: 0.03-0.20 // - hue: all (0-360) var r = new Random(); - var l = Math.Clamp(r.NextDouble() * 40d + 40d, 40d, 100d); - var c = Math.Clamp(r.NextDouble() * 0.15d + 0.05d, 0.05d, 0.20d); + var l = Math.Clamp(r.NextDouble() * 60d + 30d, 30d, 90d); + var c = Math.Clamp(r.NextDouble() * 0.17d + 0.03d, 0.03d, 0.20d); var h = Math.Clamp(r.NextDouble() * 360d, 0d, 360d); var colour = Colorimetry.RawLchToColor(l, c, h); Debug.Log($"generated new template colour LCh({l:F}, {c:F}, {h:F}) -> {colour}"); @@ -147,7 +147,7 @@ private void Render() { var remaining = (_countdownDatetime - DateTime.Now).TotalSeconds; _roundText.text = $"{Round}/{RoundsPerGame}"; - _countdownText.text = $"{remaining:F}"; + _countdownText.text = $"{remaining:F2}"; } /// diff --git a/ColourMeOKGame/Assets/Scripts/LocalPlayerData.cs b/ColourMeOKGame/Assets/Scripts/LocalPlayerData.cs index 783bd60..ace8d33 100644 --- a/ColourMeOKGame/Assets/Scripts/LocalPlayerData.cs +++ b/ColourMeOKGame/Assets/Scripts/LocalPlayerData.cs @@ -9,17 +9,17 @@ public class LocalPlayerData /// /// maximum number of the best online scores to keep track of /// - public const int MaxBestOnlineScores = 10; + public const int MaxBestScores = 10; /// /// maximum number of recent local scores to keep track of /// - public const int MaxRecentLocalScores = 10; + public const int MaxRecentScores = 10; /// /// the gamma value used in the exponential user rating calculation /// - private const float ExponentialUserRatingGamma = 1.75f; + private const float ExponentialUserRatingGamma = 2f; /// /// last known email used @@ -149,10 +149,23 @@ public void SaveToTheWorld() /// the score to register public void RegisterLocalScore(Score score) { - while (RecentLocalScores.Count >= MaxRecentLocalScores) RecentLocalScores.Dequeue(); + while (RecentLocalScores.Count >= MaxRecentScores) RecentLocalScores.Dequeue(); RecentLocalScores.Enqueue(score); } + /// + /// registers a score to the player's online data + /// + /// + /// the + /// + public static double UserRatingScalingF(double rating) + { + var rawUserRating = Math.Clamp(rating, 0d, 100d); + var exponentialRating = 100d * Math.Pow(rawUserRating / 100d, ExponentialUserRatingGamma); + return Math.Clamp(exponentialRating, 0d, 100d); + } + /// /// calculates the user rating based on whatever local data is available /// @@ -164,9 +177,9 @@ public float CalculateUserRating() // in this case 20 best scores, and 10 recent scores are used // ensure the scores don't exceed their arbitrary limits - while (RecentOnlineScores.Count > MaxRecentLocalScores) RecentOnlineScores.Dequeue(); - while (RecentLocalScores.Count > MaxRecentLocalScores) RecentLocalScores.Dequeue(); - while (BestOnlineScores.Count > MaxBestOnlineScores) BestOnlineScores.Dequeue(); + while (RecentOnlineScores.Count > MaxRecentScores) RecentOnlineScores.Dequeue(); + while (RecentLocalScores.Count > MaxRecentScores) RecentLocalScores.Dequeue(); + while (BestOnlineScores.Count > MaxBestScores) BestOnlineScores.Dequeue(); // if online scores are available, use them var recentScores = RecentOnlineScores.Count > 0 ? RecentOnlineScores : RecentLocalScores; @@ -177,37 +190,20 @@ public float CalculateUserRating() foreach (var score in recentScores.Take(10)) { + totalRating += (score.AvgLightnessAccuracy + score.AvgChromaAccuracy + score.AvgHueAccuracy + score.AvgPerceivedAccuracy) / 4d; scores++; - var dL = score.AvgLightnessAccuracy; - var dC = score.AvgChromaAccuracy; - var dH = score.AvgHueAccuracy; - var dE = Math.Sqrt(score.AvgLightnessAccuracy * score.AvgLightnessAccuracy - + score.AvgChromaAccuracy * score.AvgChromaAccuracy - + score.AvgHueAccuracy * score.AvgHueAccuracy); - totalRating += (dL + dC + dH + dE + dE) / 5d; } foreach (var score in bestScores.Take(20)) { + totalRating += (score.AvgLightnessAccuracy + score.AvgChromaAccuracy + score.AvgHueAccuracy + score.AvgPerceivedAccuracy) / 4d; scores++; - var dL = score.AvgLightnessAccuracy; - var dC = score.AvgChromaAccuracy; - var dH = score.AvgHueAccuracy; - var dE = Math.Sqrt(score.AvgLightnessAccuracy * score.AvgLightnessAccuracy - + score.AvgChromaAccuracy * score.AvgChromaAccuracy - + score.AvgHueAccuracy * score.AvgHueAccuracy); - totalRating += (dL + dC + dH + dE + dE) / 5d; } - scores = Math.Max(1, scores); - totalRating /= scores; + var rating = UserRatingScalingF(totalRating /= Math.Max(1, scores)); + Debug.Log($"locally calculated user rating: lin: {totalRating} -> exp: {rating}"); - var rawUserRating = Math.Clamp(totalRating, 0d, 100d); - var exponentialRating = 100d * Math.Pow(rawUserRating / 100d, ExponentialUserRatingGamma); - - Debug.Log($"locally calculated user rating: lin: {rawUserRating} -> exp: {exponentialRating}"); - - return (float)exponentialRating; + return (float)rating; } public struct Score diff --git a/ColourMeOKGame/Assets/Scripts/SideViewUI.cs b/ColourMeOKGame/Assets/Scripts/SideViewUI.cs index 5375a03..55611bb 100644 --- a/ColourMeOKGame/Assets/Scripts/SideViewUI.cs +++ b/ColourMeOKGame/Assets/Scripts/SideViewUI.cs @@ -111,7 +111,7 @@ private void RenderFromPlayerData(LocalPlayerData data) var totalLightnessAcc = 0f; var totalChromaAcc = 0f; var totalHueAcc = 0f; - var totalRounds = 0; + var totalGames = 0; // average out all the scores we have to get a stable-ish average @@ -120,7 +120,7 @@ private void RenderFromPlayerData(LocalPlayerData data) totalLightnessAcc += localScore.AvgLightnessAccuracy; totalChromaAcc += localScore.AvgChromaAccuracy; totalHueAcc += localScore.AvgHueAccuracy; - totalRounds += localScore.NoOfRounds; + totalGames++; } foreach (var onlineScore in data.RecentOnlineScores) @@ -128,7 +128,7 @@ private void RenderFromPlayerData(LocalPlayerData data) totalLightnessAcc += onlineScore.AvgLightnessAccuracy; totalChromaAcc += onlineScore.AvgChromaAccuracy; totalHueAcc += onlineScore.AvgHueAccuracy; - totalRounds += onlineScore.NoOfRounds; + totalGames++; } foreach (var onlineScore in data.BestOnlineScores) @@ -136,14 +136,14 @@ private void RenderFromPlayerData(LocalPlayerData data) totalLightnessAcc += onlineScore.AvgLightnessAccuracy; totalChromaAcc += onlineScore.AvgChromaAccuracy; totalHueAcc += onlineScore.AvgHueAccuracy; - totalRounds += onlineScore.NoOfRounds; + totalGames++; } - Debug.Log($"tL={totalLightnessAcc} tC={totalChromaAcc} tH={totalHueAcc} tR={totalRounds}"); - if (totalRounds == 0) totalRounds = 1; - var lightnessAcc = totalLightnessAcc / totalRounds; - var chromaAcc = totalChromaAcc / totalRounds; - var hueAcc = totalHueAcc / totalRounds; + Debug.Log($"tL={totalLightnessAcc} tC={totalChromaAcc} tH={totalHueAcc} tG={totalGames}"); + if (totalGames == 0) totalGames = 1; + var lightnessAcc = totalLightnessAcc / totalGames; + var chromaAcc = totalChromaAcc / totalGames; + var hueAcc = totalHueAcc / totalGames; var playerText = GameManager.Instance.Backend.IsSignedIn ? data.LastKnownUsername : $"{data.LastKnownUsername} (Not Signed In)"; @@ -151,9 +151,9 @@ private void RenderFromPlayerData(LocalPlayerData data) // finally, set the labels _playerText.text = playerText; - _ratingText.text = $"{rating:F}"; - _lightnessAccuracyText.text = $"{lightnessAcc:F}"; - _chromaAccuracyText.text = $"{chromaAcc:F}"; - _hueAccuracyText.text = $"{hueAcc:F}"; + _ratingText.text = $"{rating:F3}"; + _lightnessAccuracyText.text = $"{lightnessAcc:F2}%"; + _chromaAccuracyText.text = $"{chromaAcc:F2}%"; + _hueAccuracyText.text = $"{hueAcc:F2}%"; } } \ No newline at end of file