[gbrainy] New scoring algorithm and fixes to the data result display



commit 4d167b5a5cca89b861d82c0ec4201d47def58b52
Author: Jordi Mas <jmas softcatala org>
Date:   Sat Nov 28 18:37:06 2009 +0100

    New scoring algorithm and fixes to the data result display

 .../Classical/Dialogs/PlayerHistoryDialog.cs       |   41 +++++-
 src/Core/Main/GameSession.cs                       |  166 +++++++++++++++-----
 src/Core/Views/FinishView.cs                       |   20 ++-
 3 files changed, 179 insertions(+), 48 deletions(-)
---
diff --git a/src/Clients/Classical/Dialogs/PlayerHistoryDialog.cs b/src/Clients/Classical/Dialogs/PlayerHistoryDialog.cs
index 5c12ffb..fa6e5e3 100644
--- a/src/Clients/Classical/Dialogs/PlayerHistoryDialog.cs
+++ b/src/Clients/Classical/Dialogs/PlayerHistoryDialog.cs
@@ -174,6 +174,7 @@ namespace gbrainy.Clients.Classical
 				double px, py;
 				PlayerHistory history = dlg.PlayerHistory;
 				double ratio;
+				int pos;
 
 				if (history.Games.Count == 0)
 					return;
@@ -183,11 +184,17 @@ namespace gbrainy.Clients.Classical
 				if (dlg.checkbutton_logic.Active) { // Logic
 					cr.Color = logic_color;
 					cr.MoveTo (x, area_h - (area_h * history.Games[0].logic_score / 100));
+
+					pos = 1;
 					for (int i = 1; i < history.Games.Count; i++)
 					{
-						px = x + (ratio * i);
+						if (history.Games[i].logic_score < 0)
+							continue;
+
+						px = x + (ratio * pos);
 						py = y + area_h - (area_h * history.Games[i].logic_score / 100);
 						cr.LineTo (px, py);
+						pos++;
 					}
 					cr.Stroke ();
 				}
@@ -195,11 +202,17 @@ namespace gbrainy.Clients.Classical
 				if (dlg.checkbutton_calculation.Active) { // Math
 					cr.Color = math_color;
 					cr.MoveTo (x, area_h - (area_h * history.Games[0].math_score / 100));
+
+					pos = 1;
 					for (int i = 1; i < history.Games.Count; i++)
 					{
-						px = x + (ratio * i);
+						if (history.Games[i].math_score < 0)
+							continue;
+
+						px = x + (ratio * pos);
 						py = y + area_h - (area_h * history.Games[i].math_score / 100);
 						cr.LineTo (px, py);
+						pos++;
 					}
 					cr.Stroke ();
 				}
@@ -207,11 +220,17 @@ namespace gbrainy.Clients.Classical
 				if (dlg.checkbutton_memory.Active) { // Memory
 					cr.Color = memory_color;
 					cr.MoveTo (x, area_h - (area_h * history.Games[0].memory_score / 100));
+
+					pos = 1;
 					for (int i = 1; i < history.Games.Count; i++)
 					{
-						px = x + (ratio * i);
+						if (history.Games[i].memory_score < 0)
+							continue;
+
+						px = x + (ratio * pos);
 						py = y + area_h - (area_h * history.Games[i].memory_score / 100);
 						cr.LineTo (px, py);
+						pos++;
 					}
 					cr.Stroke ();
 				}
@@ -219,11 +238,17 @@ namespace gbrainy.Clients.Classical
 				if (dlg.checkbutton_verbal.Active) { // Verbal
 					cr.Color = verbal_color;
 					cr.MoveTo (x, area_h - (area_h * history.Games[0].verbal_score / 100));
+
+					pos = 1;
 					for (int i = 1; i < history.Games.Count; i++)
 					{
-						px = x + (ratio * i);
+						if (history.Games[i].verbal_score < 0)
+							continue;
+
+						px = x + (ratio * pos);
 						py = y + area_h - (area_h * history.Games[i].verbal_score / 100);
 						cr.LineTo (px, py);
+						pos++;
 					}
 					cr.Stroke ();
 				}
@@ -231,11 +256,17 @@ namespace gbrainy.Clients.Classical
 				if (dlg.checkbutton_total.Active) { // Total			
 					cr.Color = total_color;
 					cr.MoveTo (x, area_h - (area_h * history.Games[0].total_score / 100));
+
+					pos = 1;
 					for (int i = 1; i < history.Games.Count; i++)
 					{
-						px = x + (ratio * i);
+						if (history.Games[pos].total_score < 0)
+							continue;
+
+						px = x + (ratio * pos);
 						py = y + area_h - (area_h * history.Games[i].total_score / 100);
 						cr.LineTo (px, py);
+						pos++;
 					}
 					cr.Stroke ();
 				}
diff --git a/src/Core/Main/GameSession.cs b/src/Core/Main/GameSession.cs
index 0ffcde1..1d0f361 100644
--- a/src/Core/Main/GameSession.cs
+++ b/src/Core/Main/GameSession.cs
@@ -60,6 +60,14 @@ namespace gbrainy.Core.Main
 			Finished,
 		}
 
+		// Data kept for every game type in raw data (processed with a formula after)
+		public class Statistics
+		{
+			public int Played { get; set; }
+			public int Won { get; set; }
+			public int Scored { get; set; }
+		}
+
 		private TimeSpan game_time;
 		private int games_played;
 		private int games_won;
@@ -69,14 +77,13 @@ namespace gbrainy.Core.Main
 		private bool paused;
 		private string current_time;
 		private TimeSpan one_sec = TimeSpan.FromSeconds (1);
-		private int [] scores;
-		private int [] games;
 		private int total_score;
 		private bool scored_game;
 		private SessionStatus status;
 		private ViewsControler controler;
 		private ISynchronizeInvoke synchronize;
 		private PlayerHistory history;
+		private Statistics [] statistics;
 
 		public event EventHandler DrawRequest;
 		public event EventHandler <UpdateUIStateEventArgs> UpdateUIElement;
@@ -89,9 +96,12 @@ namespace gbrainy.Core.Main
 			timer = new System.Timers.Timer ();
 			timer.Elapsed += TimerUpdater;
 			timer.Interval = (1 * 1000); // 1 second
-		
-			scores = new int [(int) ScoresType.Last];
-			games = new int [(int) ScoresType.Last];
+
+			statistics = new Statistics [(int) ScoresType.Last];
+
+			for (int i = 0; i < (int) ScoresType.Last; i++)
+				statistics [i] = new Statistics ();
+
 			controler = new ViewsControler (this);
 			Status = SessionStatus.NotPlaying;
 			history = new PlayerHistory ();
@@ -168,37 +178,37 @@ namespace gbrainy.Core.Main
 
 		public int LogicScore {
 			get {
-				if (games [(int) ScoresType.LogicPuzzles] == 0)
-					return 0;
-			
-				return scores [(int) ScoresType.LogicPuzzles] * 10 / games [(int) ScoresType.LogicPuzzles];
+				if (statistics [(int) ScoresType.LogicPuzzles].Played == 0)
+					return -1;
+
+				return ScoreFormula (statistics [(int) ScoresType.LogicPuzzles]);
 			}
 		}
 
 		public int MemoryScore {
 			get {
-				if (games [(int) ScoresType.MemoryTrainers] == 0)
-					return 0;
-			
-				return scores [(int) ScoresType.MemoryTrainers] * 10 / games [(int) ScoresType.MemoryTrainers];
+				if (statistics [(int) ScoresType.MemoryTrainers].Played == 0)
+					return -1;
+	
+				return ScoreFormula (statistics [(int) ScoresType.MemoryTrainers]);
 			}
 		}
 
 		public int MathScore {
 			get {
-				if (games [(int) ScoresType.CalculationTrainers] == 0)
-					return 0;
-			
-				return scores [(int) ScoresType.CalculationTrainers] * 10 / games [(int) ScoresType.CalculationTrainers];
+				if (statistics [(int) ScoresType.CalculationTrainers].Played == 0)
+					return -1;
+
+				return ScoreFormula (statistics [(int) ScoresType.CalculationTrainers]);
 			}
 		}
 
 		public int VerbalScore {
-			get {
-				if (games [(int) ScoresType.VerbalAnalogies] == 0)
-					return 0;
-			
-				return scores [(int) ScoresType.VerbalAnalogies] * 10 / games [(int) ScoresType.VerbalAnalogies];
+			get {	
+				if (statistics [(int) ScoresType.VerbalAnalogies].Played == 0)
+					return -1;
+	
+				return ScoreFormula (statistics [(int) ScoresType.VerbalAnalogies]);
 			}
 		}
 
@@ -239,8 +249,10 @@ namespace gbrainy.Core.Main
 				EndSession ();
 
 			current_time = TimeSpanToStr (game_time);
-			scores = new int [(int) ScoresType.Last];
-			games = new int [(int) ScoresType.Last];
+
+			for (int i = 0; i < (int) ScoresType.Last; i++)
+				statistics [i] = new Statistics ();
+
 			total_score = 0;
 			games_played = 0;
 			games_won = 0;
@@ -295,47 +307,123 @@ namespace gbrainy.Core.Main
 			paused = false;
 		}
 
+		/*
+			How the game session is scored
+
+			* Every game has a scoring algorithm that scores the player performance within the game.
+		   	  This takes into account time used and tips (result is from 0 to 10)
+			* The results are added to the games and scores arrays where we store the results for
+			  the different game types (verbal, logic, etc)
+			* We apply a ScoreFormula function that balances the total result with the number of
+	  		  games played (is not the same 100% games won playing 2 than 10 games) and the difficulty
+			
+			The final result is a number from 0 to 100
+		*/
+
 		public bool ScoreGame (string answer)
 		{
 			int score;
+			bool won;
+			int components = 0;
 
 			if (CurrentGame == null || scored_game == true)
 				return false;
 
 			score = CurrentGame.Score (answer);
-			if (score > 0)
+			if (score > 0) {
 				GamesWon++;
+				won = true;
+			} else
+				won = false;
 
 			switch (CurrentGame.Type) {
 			case Game.Types.LogicPuzzle:
-				scores [(int) ScoresType.LogicPuzzles] += score;
-				games [(int) ScoresType.LogicPuzzles]++;
+				statistics [(int) ScoresType.LogicPuzzles].Scored += score;
+				statistics [(int) ScoresType.LogicPuzzles].Played++;
+				if (won) statistics [(int) ScoresType.LogicPuzzles].Won++;
 				break;
 			case Game.Types.MemoryTrainer:
-				scores [(int) ScoresType.MemoryTrainers] += score;
-				games [(int) ScoresType.MemoryTrainers]++;
+				statistics [(int) ScoresType.MemoryTrainers].Scored += score;
+				statistics [(int) ScoresType.MemoryTrainers].Played++;
+				if (won) statistics [(int) ScoresType.MemoryTrainers].Won++;
 				break;
 			case Game.Types.MathTrainer:
-				scores [(int) ScoresType.CalculationTrainers] += score;
-				games [(int) ScoresType.CalculationTrainers]++;
+				statistics [(int) ScoresType.CalculationTrainers].Scored += score;
+				statistics [(int) ScoresType.CalculationTrainers].Played++;
+				if (won) statistics [(int) ScoresType.CalculationTrainers].Won++;
 				break;
 			case Game.Types.VerbalAnalogy:
-				scores [(int) ScoresType.VerbalAnalogies] += score;
-				games [(int) ScoresType.VerbalAnalogies]++;
+				statistics [(int) ScoresType.VerbalAnalogies].Scored += score;
+				statistics [(int) ScoresType.VerbalAnalogies].Played++;
+				if (won) statistics [(int) ScoresType.VerbalAnalogies].Won++;
 				break;
 			default:
 				break;
 			}
-		
+
 			total_score = 0;
-			for (int i = 0; i < (int) ScoresType.Last; i++) {
-				total_score += scores [i];
+
+			// Updates total score taking only into account played game types
+			if (LogicScore >= 0) {
+				total_score += LogicScore;
+				components++;
+			}
+
+			if (MemoryScore >= 0) {
+				total_score += MemoryScore;
+				components++;
+			}
+
+			if (MathScore >= 0) {
+				total_score += MathScore;
+				components++;
+			}
+
+			if (VerbalScore >= 0) {
+				total_score += VerbalScore;
+				components++;
 			}
 
-			total_score = total_score * 10 / games_played;
+			total_score = total_score / components;
+
 			scored_game = true;
-			return (score > 0) ? true: false;
-		}	
+			return won;
+		}
+
+		//
+		// Applies scoring formula to the session
+		//
+		int ScoreFormula (Statistics stats)
+		{
+			int logbase;
+			double score, factor;
+
+			switch (Difficulty) {
+			case Game.Difficulty.Easy:
+				logbase = 10;
+				break;
+			case Game.Difficulty.Medium:
+				logbase = 20;
+				break;
+			case Game.Difficulty.Master:
+				logbase = 30;
+				break;
+			default:
+				throw new InvalidOperationException ("Invalid switch value");
+			}
+
+			// Simple percentage of games won vs played
+			score = stats.Scored > 0 ? stats.Scored / stats.Played * 10 : 0;
+
+			// Puts score of the game in prespective for the whole game
+			factor = Math.Log (stats.Won + 2, logbase); // +2 to avoid log 0
+
+			score = score * factor;
+
+			if (score > 100) score = 100;
+
+			return (int) score;
+		}
 
 		private void TimerUpdater (object source, ElapsedEventArgs e)
 		{
diff --git a/src/Core/Views/FinishView.cs b/src/Core/Views/FinishView.cs
index c2ca1c7..46e61ac 100644
--- a/src/Core/Views/FinishView.cs
+++ b/src/Core/Views/FinishView.cs
@@ -85,19 +85,31 @@ namespace gbrainy.Core.Views
 			gr.DrawTextCentered (x + bar_w / 2, y + area_h + 0.03, Catalog.GetString ("Total"));
 
 			x = x + space_x * 2;
-			DrawBar (gr, x, y + area_h, bar_w, bar_h, session.LogicScore);
+
+			if (session.LogicScore >= 0)
+				DrawBar (gr, x, y + area_h, bar_w, bar_h, session.LogicScore);
+
 			gr.DrawTextCentered (x + bar_w / 2, y + area_h + 0.03, 	Catalog.GetString ("Logic")); 
 
 			x = x + space_x * 2;
-			DrawBar (gr, x, y + area_h, bar_w, bar_h, session.MathScore);
+
+			if (session.MathScore >= 0)
+				DrawBar (gr, x, y + area_h, bar_w, bar_h, session.MathScore);
+
 			gr.DrawTextCentered (x + bar_w / 2, y + area_h + 0.03, Catalog.GetString ("Calculation"));
 
 			x = x + space_x * 2;
-			DrawBar (gr, x, y + area_h, bar_w, bar_h, session.MemoryScore);
+
+			if (session.MemoryScore >= 0)
+				DrawBar (gr, x, y + area_h, bar_w, bar_h, session.MemoryScore);
+
 			gr.DrawTextCentered (x + bar_w / 2, y + area_h + 0.03, Catalog.GetString ("Memory"));
 
 			x = x + space_x * 2;
-			DrawBar (gr, x, y + area_h, bar_w, bar_h, session.VerbalScore);
+
+			if (session.VerbalScore >= 0)
+				DrawBar (gr, x, y + area_h, bar_w, bar_h, session.VerbalScore);
+
 			gr.DrawTextCentered (x + bar_w / 2, y + area_h + 0.03, Catalog.GetString ("Verbal"));
 		}
 



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]