[gbrainy/gbrainy_15x] Unit testing and some refactorings



commit f1f1210b4f528c1f9027e6bf35ecf686bf04414a
Author: Jordi Mas <jmas softcatala org>
Date:   Sat May 8 11:50:26 2010 +0200

    Unit testing and some refactorings

 Makefile.am                              |    1 +
 configure.ac                             |   34 +++++-
 src/Core/Main/GameSession.cs             |  231 ++++--------------------------
 src/Core/Main/GameSessionHistory.cs      |   78 ++++++++++
 src/Core/Main/PlayerHistory.cs           |  118 ++++------------
 src/Core/Main/PlayerPersonalRecord.cs    |   87 +++++++++++
 src/Core/Main/Preferences.cs             |    3 +-
 src/Core/Main/Score.cs                   |  171 ++++++++++++++++++++++
 src/Core/Main/Verbal/AnalogiesFactory.cs |   16 ++-
 src/Core/Makefile.am                     |    6 +-
 src/Core/Views/FinishView.cs             |   24 ++--
 src/Core/Views/PlayerHistoryView.cs      |   30 ++--
 tests/Core/AnalogiesFactoryTest.cs       |   55 +++++++
 tests/Core/PlayerHistoryTest.cs          |   75 ++++++++++
 tests/Core/PlayerPersonalRecordTest.cs   |   76 ++++++++++
 tests/Makefile.am                        |   36 +++++
 tests/README                             |    7 +
 tests/test_analogies.xml                 |   66 +++++++++
 18 files changed, 783 insertions(+), 331 deletions(-)
---
diff --git a/Makefile.am b/Makefile.am
index 3ace8e3..151a302 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -1,5 +1,6 @@
 SUBDIRS = 		\
 	src		\
+	tests		\
 	data		\
 	po		\
 	help
diff --git a/configure.ac b/configure.ac
index c757f62..9359faa 100644
--- a/configure.ac
+++ b/configure.ac
@@ -99,6 +99,36 @@ AC_SUBST(GETTEXT_PACKAGE)
 AC_DEFINE_UNQUOTED(GETTEXT_PACKAGE,"$GETTEXT_PACKAGE", [Gettext package])
 AM_GLIB_GNU_GETTEXT
 
+
+dnl -- NUnit required for (optional) unit tests
+NUNIT_REQUIRED=2.4.7
+
+AC_ARG_ENABLE(tests, AC_HELP_STRING([--enable-tests], [Enable NUnit tests]),
+	enable_tests=$enableval, enable_tests="no")
+
+if test "x$enable_tests" = "xno"; then
+	do_tests=no
+	AM_CONDITIONAL(ENABLE_TESTS, false)
+else
+	PKG_CHECK_MODULES(NUNIT, nunit >= $NUNIT_REQUIRED,
+		do_tests="yes", do_tests="no")
+
+	AC_SUBST(NUNIT_LIBS)
+	AC_PATH_PROG(NUNIT, nunit-console)
+	AM_CONDITIONAL(ENABLE_TESTS, test "x$do_tests" = "xyes")
+
+	if test "x$do_tests" = "xno"; then
+		PKG_CHECK_MODULES(NUNIT, mono-nunit >= 2.4,
+			do_tests="yes", do_tests="no")
+
+		AC_SUBST(NUNIT_LIBS)
+		AM_CONDITIONAL(ENABLE_TESTS, test "x$do_tests" = "xyes")
+
+		if test "x$do_tests" = "xno"; then
+			AC_MSG_WARN([Could not find nunit: tests will not be available])			fi
+	fi
+fi
+
 dnl --- Prologue
 
 AC_SUBST(CFLAGS)
@@ -133,10 +163,12 @@ src/Makefile
 src/Core/Makefile
 src/Games/Makefile
 src/Clients/Classical/Makefile
+tests/Makefile
 data/Makefile
 data/gbrainy.pc
 help/Makefile
 ])
 
-echo "Mono-addins:            ${enable_addins_sharp}"
+echo "Mono-addins:		${enable_addins_sharp}"
+echo "NUnit:			${enable_tests}"
 
diff --git a/src/Core/Main/GameSession.cs b/src/Core/Main/GameSession.cs
index 0b52f35..61fa3da 100644
--- a/src/Core/Main/GameSession.cs
+++ b/src/Core/Main/GameSession.cs
@@ -41,16 +41,6 @@ namespace gbrainy.Core.Main
 			AllGames		= MemoryTrainers | CalculationTrainers | LogicPuzzles | VerbalAnalogies
 		}
 
-		private enum ScoresType
-		{
-			None = 0,
-			LogicPuzzles,
-			MemoryTrainers,
-			CalculationTrainers,
-			VerbalAnalogies,
-			Last			
-		}
-
 		public enum SessionStatus
 		{
 			NotPlaying,
@@ -59,30 +49,19 @@ 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;
 		private Game current_game;
 		private GameManager game_manager;
 		private System.Timers.Timer timer;
 		private bool paused;
 		private string current_time;
 		private TimeSpan one_sec = TimeSpan.FromSeconds (1);
-		private int total_score;
 		private SessionStatus status;
 		private ViewsControler controler;
 		private ISynchronizeInvoke synchronize;
-		private PlayerHistory history;
-		private Statistics [] statistics;
+		private PlayerHistory player_history;
 		private int id;
+		private GameSessionHistoryExtended history;
 
 		public event EventHandler DrawRequest;
 		public event EventHandler <UpdateUIStateEventArgs> UpdateUIElement;
@@ -97,27 +76,27 @@ namespace gbrainy.Core.Main
 			timer.Elapsed += TimerUpdater;
 			timer.Interval = (1 * 1000); // 1 second
 
-			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 ();
+			player_history = new PlayerHistory ();
+			history = new GameSessionHistoryExtended ();
 		}
 
 		public int ID {
 			get {return id;}
 		}
 
+		public GameSessionHistoryExtended History {
+			get {return history;}
+		}
+
 		public Game.Types AvailableGames {
 			get { return game_manager.AvailableGames; }
 		}
 
 		public PlayerHistory PlayerHistory { 
-			set { history = value; }
-			get { return history; }
+			set { player_history = value; }
+			get { return player_history; }
 		}
 
 		public ISynchronizeInvoke SynchronizingObject { 
@@ -140,16 +119,6 @@ namespace gbrainy.Core.Main
 			set {game_time = value; }
 		}
 
-		public int GamesPlayed {
-			get {return games_played; }
-			set { games_played = value;}
-		}
-		
-		public int GamesWon {
-			get {return games_won; }
-			set {games_won = value; }
-		}
-
 		public bool Paused {
 			get {return paused; }
 			set {paused = value; }
@@ -183,46 +152,6 @@ namespace gbrainy.Core.Main
 			get {return  game_manager;}
 		}
 
-		public int TotalScore {
-			get {return total_score;}
-		}
-
-		public int LogicScore {
-			get {
-				if (statistics [(int) ScoresType.LogicPuzzles].Played == 0)
-					return -1;
-
-				return ScoreFormula (statistics [(int) ScoresType.LogicPuzzles]);
-			}
-		}
-
-		public int MemoryScore {
-			get {
-				if (statistics [(int) ScoresType.MemoryTrainers].Played == 0)
-					return -1;
-	
-				return ScoreFormula (statistics [(int) ScoresType.MemoryTrainers]);
-			}
-		}
-
-		public int MathScore {
-			get {
-				if (statistics [(int) ScoresType.CalculationTrainers].Played == 0)
-					return -1;
-
-				return ScoreFormula (statistics [(int) ScoresType.CalculationTrainers]);
-			}
-		}
-
-		public int VerbalScore {
-			get {	
-				if (statistics [(int) ScoresType.VerbalAnalogies].Played == 0)
-					return -1;
-	
-				return ScoreFormula (statistics [(int) ScoresType.VerbalAnalogies]);
-			}
-		}
-
 		public string TimePlayed {
 			get {
 				return (current_time == null) ? TimeSpanToStr (TimeSpan.FromSeconds (0)) : current_time;
@@ -233,7 +162,7 @@ namespace gbrainy.Core.Main
 			get {
 				TimeSpan average;
 
-				average = (games_played > 0) ? TimeSpan.FromSeconds (game_time.TotalSeconds / games_played) : game_time;
+				average = (history.GamesPlayed > 0) ? TimeSpan.FromSeconds (game_time.TotalSeconds / history.GamesPlayed) : game_time;
 				return TimeSpanToStr (average);
 			}
 		}
@@ -244,7 +173,7 @@ namespace gbrainy.Core.Main
 					return string.Empty;
 
 				String text;
-				text = String.Format (Catalog.GetString ("Games played: {0} ({1}% score)"),games_played, total_score);
+				text = String.Format (Catalog.GetString ("Games played: {0} ({1}% score)"), history.GamesPlayed, history.TotalScore);
 				text += String.Format (Catalog.GetString (" - Time: {0}"), current_time);
 
 				if (CurrentGame != null)
@@ -259,14 +188,14 @@ namespace gbrainy.Core.Main
 			get {
 				string s;
 
-				if (GamesPlayed >= 10) {
-					if (TotalScore >= 70)
+				if (history.GamesPlayed >= 10) {
+					if (history.TotalScore >= 70)
 						s = String.Format (Catalog.GetString ("Outstanding results"));
-					else if (TotalScore >= 50)
+					else if (history.TotalScore >= 50)
 						s = String.Format (Catalog.GetString ("Excellent results"));
-					else if (TotalScore >= 30)
+					else if (history.TotalScore >= 30)
 						s = String.Format (Catalog.GetString ("Good results"));
-					else if (TotalScore >= 20)
+					else if (history.TotalScore >= 20)
 						s = String.Format (Catalog.GetString ("Poor results"));
 					else s = String.Format (Catalog.GetString ("Disappointing results"));
 				} else
@@ -284,12 +213,7 @@ namespace gbrainy.Core.Main
 
 			current_time = TimeSpanToStr (game_time);
 
-			for (int i = 0; i < (int) ScoresType.Last; i++)
-				statistics [i] = new Statistics ();
-
-			total_score = 0;
-			games_played = 0;
-			games_won = 0;
+			history.Clear ();
 			game_time = TimeSpan.Zero;
 			timer.SynchronizingObject = SynchronizingObject;
 			EnableTimer = true;
@@ -297,7 +221,8 @@ namespace gbrainy.Core.Main
 
 		public void EndSession ()
 		{
-			history.SaveGameSession (this);
+			// Making a deep copy of GameSessionHistory type (base class) for serialization
+			player_history.SaveGameSession (history.Copy ());
 
 			if (CurrentGame != null)
 				CurrentGame.Finish ();
@@ -315,7 +240,7 @@ namespace gbrainy.Core.Main
 			if (CurrentGame != null)
 				CurrentGame.Finish ();
 
-			games_played++;
+			history.GamesPlayed++;
 			CurrentGame = game_manager.GetPuzzle ();
 			CurrentGame.SynchronizingObject = SynchronizingObject;
 			CurrentGame.DrawRequest += GameDrawRequest;
@@ -340,122 +265,18 @@ 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;
+			int game_score;
 
-			if (CurrentGame == null ||Status == SessionStatus.Answered)
+			if (CurrentGame == null || Status == SessionStatus.Answered)
 				return false;
 
-			score = CurrentGame.Score (answer);
-			if (score > 0) {
-				GamesWon++;
-				won = true;
-			} else
-				won = false;
-
-			switch (CurrentGame.Type) {
-			case Game.Types.LogicPuzzle:
-				statistics [(int) ScoresType.LogicPuzzles].Scored += score;
-				statistics [(int) ScoresType.LogicPuzzles].Played++;
-				if (won) statistics [(int) ScoresType.LogicPuzzles].Won++;
-				break;
-			case Game.Types.MemoryTrainer:
-				statistics [(int) ScoresType.MemoryTrainers].Scored += score;
-				statistics [(int) ScoresType.MemoryTrainers].Played++;
-				if (won) statistics [(int) ScoresType.MemoryTrainers].Won++;
-				break;
-			case Game.Types.MathTrainer:
-				statistics [(int) ScoresType.CalculationTrainers].Scored += score;
-				statistics [(int) ScoresType.CalculationTrainers].Played++;
-				if (won) statistics [(int) ScoresType.CalculationTrainers].Won++;
-				break;
-			case Game.Types.VerbalAnalogy:
-				statistics [(int) ScoresType.VerbalAnalogies].Scored += score;
-				statistics [(int) ScoresType.VerbalAnalogies].Played++;
-				if (won) statistics [(int) ScoresType.VerbalAnalogies].Won++;
-				break;
-			default:
-				break;
-			}
-
-			total_score = 0;
-
-			// 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 / components;
+			game_score = CurrentGame.Score (answer);
+			history.UpdateScore (CurrentGame.Type, Difficulty, game_score);
 
 			Status = SessionStatus.Answered;
-			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;
+			return (game_score > 0 ? true : false);
 		}
 
 		private void TimerUpdater (object source, ElapsedEventArgs e)
diff --git a/src/Core/Main/GameSessionHistory.cs b/src/Core/Main/GameSessionHistory.cs
new file mode 100644
index 0000000..07dd5cb
--- /dev/null
+++ b/src/Core/Main/GameSessionHistory.cs
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2007 Jordi Mas i Hernàndez <jmas softcatala org>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this program; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+using System;
+using Mono.Unix;
+using System.Timers;
+using System.ComponentModel;
+using System.Xml.Serialization;
+
+using gbrainy.Core.Views;
+using gbrainy.Core.Libraries;
+
+namespace gbrainy.Core.Main
+{
+	[Serializable]
+	// Old class name, to keep compatibility when serializing with previous PlayerHistory files
+	[XmlType("GameHistory")]
+	public class GameSessionHistory
+	{
+		[XmlElementAttribute ("games_played")]
+		public int GamesPlayed { get; set; }	
+
+		[XmlElementAttribute ("games_won")]
+		public int GamesWon { get; set; }
+
+		[XmlElementAttribute ("total_score")]
+		public int TotalScore { get; set; }
+
+		[XmlElementAttribute ("math_score")]
+		public int MathScore { get; set; }
+
+		[XmlElementAttribute ("logic_score")]
+		public int LogicScore { get; set; }
+
+		[XmlElementAttribute ("memory_score")]
+		public int MemoryScore { get; set; }
+
+		[XmlElementAttribute ("verbal_score")]
+		public int VerbalScore { get; set; }
+
+		public virtual void Clear ()
+		{	
+			GamesPlayed = GamesWon = TotalScore = MathScore = LogicScore = MemoryScore = VerbalScore = 0;
+		}
+
+		// Deep copy
+		public GameSessionHistory Copy ()
+		{
+			GameSessionHistory history = new GameSessionHistory ();
+
+			history.GamesPlayed = GamesPlayed;
+			history.GamesWon = GamesWon;
+			history.TotalScore = TotalScore;
+			history.MathScore = MathScore;
+			history.LogicScore = LogicScore;
+			history.MemoryScore = MemoryScore;
+			history.VerbalScore = VerbalScore;
+			return history;
+		}
+	}
+}
+
diff --git a/src/Core/Main/PlayerHistory.cs b/src/Core/Main/PlayerHistory.cs
index 903be5c..a88bcd5 100644
--- a/src/Core/Main/PlayerHistory.cs
+++ b/src/Core/Main/PlayerHistory.cs
@@ -28,50 +28,31 @@ namespace gbrainy.Core.Main
 	public class PlayerHistory
 	{
 		string file, config_path;
-		List <GameHistory> games;
+		List <GameSessionHistory> games;
 		int last_game;
+	
 
-		[Serializable]
-		public class GameHistory
+		public PlayerHistory ()
 		{
-			public int games_played;
-			public int games_won;
-			public int total_score;
-			public int math_score;
-			public int logic_score;
-			public int memory_score;
-			public int verbal_score;
+			ConfigPath = Path.Combine (Environment.GetFolderPath (Environment.SpecialFolder.ApplicationData),
+				 Defines.CONFIG_DIR);
+			last_game = -1;
 		}
 
-		public class PersonalRecord
-		{
-			public Game.Types GameType { get; set; }
-			public int PreviousScore { get; set; }
-			public int NewScore { get; set; }
-
-			public PersonalRecord (Game.Types type, int previous_score, int new_score)
-			{
-				GameType = type;
-				PreviousScore = previous_score;
-				NewScore = new_score;
+		public string ConfigPath {
+			set { 
+				config_path = value;
+				file = Path.Combine (config_path, "PlayerHistory.xml");
 			}
 		}
 
-		public PlayerHistory ()
-		{
-			config_path = Environment.GetFolderPath (Environment.SpecialFolder.ApplicationData);
-			config_path = Path.Combine (config_path, Defines.CONFIG_DIR);
-			file = Path.Combine (config_path, "PlayerHistory.xml");
-			last_game = -1;
-		}
-
-		public List <GameHistory> Games {
+		public List <GameSessionHistory> Games {
 			get {
 				if (games == null)
 				{
 					Load ();
 					if (games == null) 
-						games = new List <GameHistory> ();
+						games = new List <GameSessionHistory> ();
 				}
 				return games; 
 			}
@@ -83,76 +64,29 @@ namespace gbrainy.Core.Main
 			Save ();
 		}
 
-		public void SaveGameSession (GameSession session)
+		public void SaveGameSession (GameSessionHistory score)
 		{
-			if (session.GamesPlayed < Preferences.GetIntValue (Preferences.MinPlayedGamesKey)) {
+			if (score.GamesPlayed < Preferences.GetIntValue (Preferences.MinPlayedGamesKey)) {
 				last_game = -1;
 				return;
 			}
 
-			GameHistory history = new GameHistory ();
-	
-			history.games_played = session.GamesPlayed;
-			history.games_won = session.GamesWon;
-			history.math_score = session.MathScore;
-			history.logic_score = session.LogicScore;
-			history.memory_score = session.MemoryScore;
-			history.total_score = session.TotalScore;
-			history.verbal_score = session.VerbalScore;
-
 			if (Games.Count >= Preferences.GetIntValue (Preferences.MaxStoredGamesKey))
 				Games.RemoveAt (0);
 
-			Games.Add (history);
+			// Storing a copy to allow the input object to be modified
+			Games.Add (score.Copy ());
 			last_game = Games.Count - 1;
 			Save ();
 		}
 
 		// Check if the last recorded game has been a personal record
-		public List <PersonalRecord> GetLastGameRecords ()
+		public List <PlayerPersonalRecord> GetLastGameRecords ()
 		{
-			List <PersonalRecord> records = new List <PersonalRecord> ();
-			GameHistory higher;
-
-			// We can start to talk about personal records after 5 plays
-			if (last_game == -1 || Games.Count < 5)
-				return records;
-
-			higher = new GameHistory ();
-
-			// Find the higher record for every type of game
-			for (int i = 0; i < last_game; i++)
-			{
-				if (Games[i].logic_score > higher.logic_score) 
-					higher.logic_score = Games[i].logic_score;
-
-				if (Games[i].math_score > higher.math_score) 
-					higher.math_score = Games[i].math_score;
-
-				if (Games[i].memory_score > higher.memory_score) 
-					higher.memory_score = Games[i].memory_score;
-
-				if (Games[i].verbal_score > higher.verbal_score) 
-					higher.verbal_score = Games[i].verbal_score;				
-			}
-			
-			// It is a record?
-			if (Games[last_game].logic_score > higher.logic_score)
-				records.Add (new PersonalRecord (Game.Types.LogicPuzzle, higher.logic_score, Games[last_game].logic_score));
-
-			if (Games[last_game].math_score > higher.math_score)
-				records.Add (new PersonalRecord (Game.Types.MathTrainer, higher.math_score, Games[last_game].math_score));
-
-			if (Games[last_game].memory_score > higher.memory_score)
-				records.Add (new PersonalRecord (Game.Types.MemoryTrainer, higher.memory_score, Games[last_game].memory_score));
-
-			if (Games[last_game].verbal_score > higher.verbal_score)
-				records.Add (new PersonalRecord (Game.Types.VerbalAnalogy, higher.verbal_score, Games[last_game].verbal_score));
-
-			return records;
+			return PlayerPersonalRecord.GetLastGameRecords (games, last_game);
 		}
 
-		private void Save ()
+		public void Save ()
 		{
 			try {
 				if (!Directory.Exists (config_path))
@@ -160,29 +94,29 @@ namespace gbrainy.Core.Main
 
 				using (FileStream str = File.Create (file))
 				{
-					XmlSerializer bf = new XmlSerializer (typeof (List <GameHistory>));
+					XmlSerializer bf = new XmlSerializer (typeof (List <GameSessionHistory>));
 					bf.Serialize (str, Games);
 				}
 			}
 		
-			catch (Exception)
+			catch (Exception e)
 			{
+				Console.WriteLine ("PlayerHistory. Cannot save {0}", e);
 			}
 		}
 
-		private void Load ()
+		public void Load ()
 		{
 			try {
 				using (FileStream str = File.OpenRead (file))
 				{
-					XmlSerializer bf = new XmlSerializer (typeof (List <GameHistory>));
-				    	games = (List <GameHistory>) bf.Deserialize(str);
+					XmlSerializer bf = new XmlSerializer (typeof (List <GameSessionHistory>));
+				    	games = (List <GameSessionHistory>) bf.Deserialize(str);
 				}
 			}
 			catch (Exception)
 			{
 			}
-		}
-	
+		}	
 	}
 }
diff --git a/src/Core/Main/PlayerPersonalRecord.cs b/src/Core/Main/PlayerPersonalRecord.cs
new file mode 100644
index 0000000..c14d00c
--- /dev/null
+++ b/src/Core/Main/PlayerPersonalRecord.cs
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2008-2009 Jordi Mas i Hernàndez <jmas softcatala org>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this program; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Xml;
+using System.Xml.Serialization;
+
+namespace gbrainy.Core.Main
+{
+	public class PlayerPersonalRecord
+	{
+		public const int MIN_GAMES_RECORD = 5;
+
+		public Game.Types GameType { get; set; }
+		public int PreviousScore { get; set; }
+		public int NewScore { get; set; }
+
+		public PlayerPersonalRecord (Game.Types type, int previous_score, int new_score)
+		{
+			GameType = type;
+			PreviousScore = previous_score;
+			NewScore = new_score;
+		}
+
+		// Check if the last recorded game has been a personal record
+		static public List <PlayerPersonalRecord> GetLastGameRecords (List <GameSessionHistory> games, int last_game)
+		{
+			List <PlayerPersonalRecord> records = new List <PlayerPersonalRecord> ();
+			GameSessionHistory higher;
+
+			// We can start to talk about personal records after 5 plays
+			if (last_game == -1 || games.Count < MIN_GAMES_RECORD)
+				return records;
+
+			higher = new GameSessionHistory ();
+
+			// Find the higher record for every type of game
+			for (int i = 0; i < last_game; i++)
+			{
+				if (games[i].LogicScore > higher.LogicScore) 
+					higher.LogicScore = games[i].LogicScore;
+
+				if (games[i].MathScore > higher.MathScore) 
+					higher.MathScore = games[i].MathScore;
+
+				if (games[i].MemoryScore > higher.MemoryScore) 
+					higher.MemoryScore = games[i].MemoryScore;
+
+				if (games[i].VerbalScore > higher.VerbalScore) 
+					higher.VerbalScore = games[i].VerbalScore;				
+			}
+			
+			// It is a record?
+			if (games[last_game].LogicScore > higher.LogicScore)
+				records.Add (new PlayerPersonalRecord (Game.Types.LogicPuzzle, higher.LogicScore, games[last_game].LogicScore));
+
+			if (games[last_game].MathScore > higher.MathScore)
+				records.Add (new PlayerPersonalRecord (Game.Types.MathTrainer, higher.MathScore, games[last_game].MathScore));
+
+			if (games[last_game].MemoryScore > higher.MemoryScore)
+				records.Add (new PlayerPersonalRecord (Game.Types.MemoryTrainer, higher.MemoryScore, games[last_game].MemoryScore));
+
+			if (games[last_game].VerbalScore > higher.VerbalScore)
+				records.Add (new PlayerPersonalRecord (Game.Types.VerbalAnalogy, higher.VerbalScore, games[last_game].VerbalScore));
+
+			return records;
+		}
+	}
+}
diff --git a/src/Core/Main/Preferences.cs b/src/Core/Main/Preferences.cs
index 9972827..db5e5e9 100644
--- a/src/Core/Main/Preferences.cs
+++ b/src/Core/Main/Preferences.cs
@@ -154,8 +154,9 @@ namespace gbrainy.Core.Main
 			properties [key] = value.ToString ();
 		}
 
-		static void LoadDefaultValues ()
+		public static void LoadDefaultValues ()
 		{
+			properties.Clear ();
 			properties.Add (MemQuestionWarnKey, true.ToString ());
 			properties.Add (MemQuestionTimeKey, "4");
 			properties.Add (DifficultyKey, ((int)(Game.Difficulty.Medium)).ToString ());
diff --git a/src/Core/Main/Score.cs b/src/Core/Main/Score.cs
new file mode 100644
index 0000000..556abc9
--- /dev/null
+++ b/src/Core/Main/Score.cs
@@ -0,0 +1,171 @@
+/*
+ * Copyright (C) 2008-2010 Jordi Mas i Hernàndez <jmas softcatala org>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this program; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+using System;
+using Mono.Unix;
+using System.Timers;
+using System.ComponentModel;
+using System.Xml.Serialization;
+
+using gbrainy.Core.Views;
+using gbrainy.Core.Libraries;
+
+namespace gbrainy.Core.Main
+{
+	static public class Score
+	{
+		/*
+			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
+		*/
+		static public void UpdateSessionHistorycore (ref GameSessionHistoryExtended history, Game.Types type, Game.Difficulty difficulty, int game_score)
+		{
+			bool won;
+			int components = 0;
+
+			won = (game_score > 0 ? true : false);
+
+			if (won == true) {
+				history.GamesWon++;
+			}
+
+			switch (type) {
+			case Game.Types.LogicPuzzle:
+				history.LogicRawScore += game_score;
+				history.LogicPlayed++;
+				if (won) history.LogicWon++;
+				history.LogicScore = ScoreFormula (ref history, type, difficulty);
+				break;
+			case Game.Types.MemoryTrainer:
+				history.MemoryRawScore += game_score;
+				history.MemoryPlayed++;
+				if (won) history.MemoryWon++;
+				history.MemoryScore = ScoreFormula (ref history, type, difficulty);
+				break;
+			case Game.Types.MathTrainer:
+				history.MathRawScore += game_score;
+				history.MathPlayed++;
+				if (won) history.MathWon++;
+				history.MathScore = ScoreFormula (ref history, type, difficulty);
+				break;
+			case Game.Types.VerbalAnalogy:
+				history.VerbalRawScore += game_score;
+				history.VerbalPlayed++;
+				if (won) history.VerbalWon++;
+				history.VerbalScore = ScoreFormula (ref history, type, difficulty);
+				break;
+			default:
+				throw new InvalidOperationException ("Invalid switch value");
+			}
+
+			history.TotalScore = 0;
+
+			// Updates total score taking only into account played game types
+			if (history.LogicScore >= 0) {
+				history.TotalScore += history.LogicScore;
+				components++;
+			}
+
+			if (history.MemoryScore >= 0) {
+				history.TotalScore += history.MemoryScore;
+				components++;
+			}
+
+			if (history.MathScore >= 0) {
+				history.TotalScore += history.MathScore;
+				components++;
+			}
+
+			if (history.VerbalScore >= 0) {
+				history.TotalScore += history.VerbalScore;
+				components++;
+			}
+
+			history.TotalScore = history.TotalScore / components;
+		}
+
+		//
+		// Applies scoring formula to the session
+		//
+		static int ScoreFormula (ref GameSessionHistoryExtended history, Game.Types type, Game.Difficulty difficulty)
+		{
+			int logbase, scored, played, won;
+			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");
+			}
+
+			switch (type) {
+			case Game.Types.LogicPuzzle:
+				scored = history.LogicRawScore; 
+				played = history.LogicPlayed;
+				won = history.LogicWon;
+				break;
+			case Game.Types.MemoryTrainer:
+				scored = history.MemoryRawScore; 
+				played = history.MemoryPlayed;
+				won = history.MemoryWon;
+				break;
+			case Game.Types.MathTrainer:
+				scored = history.MathRawScore; 
+				played = history.MathPlayed;
+				won = history.MathWon;
+				break;
+			case Game.Types.VerbalAnalogy:
+				scored = history.VerbalRawScore; 
+				played = history.VerbalPlayed;
+				won = history.VerbalWon;
+				break;
+			default:
+				throw new InvalidOperationException ("Invalid switch value");
+			}
+
+			// Simple percentage of games won vs played
+			score = scored > 0 ? scored / played * 10 : 0;
+
+			// Puts score of the game in prespective for the whole game
+			factor = Math.Log (won + 2, logbase); // +2 to avoid log 0
+
+			score = score * factor;
+
+			if (score > 100) score = 100;
+
+			return (int) score;
+		}
+	}
+}
diff --git a/src/Core/Main/Verbal/AnalogiesFactory.cs b/src/Core/Main/Verbal/AnalogiesFactory.cs
index b33604e..bad4b8b 100644
--- a/src/Core/Main/Verbal/AnalogiesFactory.cs
+++ b/src/Core/Main/Verbal/AnalogiesFactory.cs
@@ -51,18 +51,26 @@ namespace gbrainy.Core.Main.Verbal
 			return analogies_arrays [(int) type];
 		}
 
-		static public void Read ()
+		static void Read ()
+		{
+			Read (Defines.DATA_DIR + Defines.VERBAL_ANALOGIES);
+		}			
+
+		static public void Read (string file)
 		{
 			Analogy analogy;
 			string name;
 			List <string> answers;
 
-			try 
+			try
 			{
-				StreamReader myStream = new StreamReader (Defines.DATA_DIR + Defines.VERBAL_ANALOGIES);
+				StreamReader myStream = new StreamReader (file);
 				XmlTextReader reader = new XmlTextReader (myStream);
 				answers = new List <string> ();
 
+				for (int i = 0; i < (int) Analogy.Type.Last; i++)
+					analogies_arrays[i].Clear ();
+
 				analogy = new Analogy ();
 				while (reader.Read ())
 				{
@@ -155,7 +163,7 @@ namespace gbrainy.Core.Main.Verbal
 
 			catch (Exception e)
 			{
-				Console.WriteLine ("Error loading {0}. Exception {1}", Defines.DATA_DIR + Defines.VERBAL_ANALOGIES, e.Message);
+				Console.WriteLine ("Error loading {0}. Exception {1}", file, e.Message);
 			}
 		}
 	}
diff --git a/src/Core/Makefile.am b/src/Core/Makefile.am
index afebef2..27c9ae4 100644
--- a/src/Core/Makefile.am
+++ b/src/Core/Makefile.am
@@ -13,10 +13,14 @@ CSDISTFILES =  \
 		$(srcdir)/Main/Game.cs			\
 		$(srcdir)/Main/GameManager.cs		\
 		$(srcdir)/Main/GameSession.cs		\
+		$(srcdir)/Main/GameSessionHistory.cs	\
+		$(srcdir)/Main/GameSessionHistoryExtended.cs \
 		$(srcdir)/Main/GameTips.cs		\
-		$(srcdir)/Main/Memory.cs			\
+		$(srcdir)/Main/Memory.cs		\
 		$(srcdir)/Main/PlayerHistory.cs		\
+		$(srcdir)/Main/PlayerPersonalRecord.cs	\
 		$(srcdir)/Main/Preferences.cs		\
+		$(srcdir)/Main/Score.cs			\
 		$(srcdir)/Main/UpdateUIStateEventArgs.cs \
 		$(srcdir)/Main/Verbal/Analogies.cs 	\
 		$(srcdir)/Main/Verbal/AnalogiesFactory.cs	\
diff --git a/src/Core/Views/FinishView.cs b/src/Core/Views/FinishView.cs
index ef127b0..0a5a581 100644
--- a/src/Core/Views/FinishView.cs
+++ b/src/Core/Views/FinishView.cs
@@ -88,34 +88,34 @@ namespace gbrainy.Core.Views
 			gr.Stroke ();
 
 			x = x + space_x;
-			DrawBar (gr, x, y + area_h, bar_w, bar_h, session.TotalScore);
+			DrawBar (gr, x, y + area_h, bar_w, bar_h, session.History.TotalScore);
 			gr.DrawTextCentered (x + bar_w / 2, y + area_h + 0.03, Catalog.GetString ("Total"));
 
 			x = x + space_x * 2;
 
-			if (session.LogicScore >= 0)
-				DrawBar (gr, x, y + area_h, bar_w, bar_h, session.LogicScore);
+			if (session.History.LogicPlayed > 0)
+				DrawBar (gr, x, y + area_h, bar_w, bar_h, session.History.LogicScore);
 
 			gr.DrawTextCentered (x + bar_w / 2, y + area_h + 0.03, 	Catalog.GetString ("Logic")); 
 
 			x = x + space_x * 2;
 
-			if (session.MathScore >= 0)
-				DrawBar (gr, x, y + area_h, bar_w, bar_h, session.MathScore);
+			if (session.History.MathPlayed > 0)
+				DrawBar (gr, x, y + area_h, bar_w, bar_h, session.History.MathScore);
 
 			gr.DrawTextCentered (x + bar_w / 2, y + area_h + 0.03, Catalog.GetString ("Calculation"));
 
 			x = x + space_x * 2;
 
-			if (session.MemoryScore >= 0)
-				DrawBar (gr, x, y + area_h, bar_w, bar_h, session.MemoryScore);
+			if (session.History.MemoryPlayed > 0)
+				DrawBar (gr, x, y + area_h, bar_w, bar_h, session.History.MemoryScore);
 
 			gr.DrawTextCentered (x + bar_w / 2, y + area_h + 0.03, Catalog.GetString ("Memory"));
 
 			x = x + space_x * 2;
 
-			if (session.VerbalScore >= 0)
-				DrawBar (gr, x, y + area_h, bar_w, bar_h, session.VerbalScore);
+			if (session.History.VerbalPlayed > 0)
+				DrawBar (gr, x, y + area_h, bar_w, bar_h, session.History.VerbalScore);
 
 			gr.DrawTextCentered (x + bar_w / 2, y + area_h + 0.03, Catalog.GetString ("Verbal"));
 		}
@@ -124,7 +124,7 @@ namespace gbrainy.Core.Views
 		{
 			double y = 0.04, x = 0.05;
 			const double space_small = 0.02;
-			List <PlayerHistory.PersonalRecord> records;
+			List <PlayerPersonalRecord> records;
 			string s, tip;
 			double width, height;
 
@@ -143,9 +143,9 @@ namespace gbrainy.Core.Views
 	
 			s = session.Result;
 			if (s == string.Empty)
-				gr.ShowPangoText (String.Format (Catalog.GetString ("Games won: {0} ({1} played)"), session.GamesWon, session.GamesPlayed));
+				gr.ShowPangoText (String.Format (Catalog.GetString ("Games won: {0} ({1} played)"), session.History.GamesWon, session.History.GamesPlayed));
 			else
-				gr.ShowPangoText (String.Format (Catalog.GetString ("{0}. Games won: {1} ({2} played)"), s, session.GamesWon, session.GamesPlayed));
+				gr.ShowPangoText (String.Format (Catalog.GetString ("{0}. Games won: {1} ({2} played)"), s, session.History.GamesWon, session.History.GamesPlayed));
 
 			y += 0.06;
 			gr.MoveTo (x, y);
diff --git a/src/Core/Views/PlayerHistoryView.cs b/src/Core/Views/PlayerHistoryView.cs
index 61046ec..9882aa3 100644
--- a/src/Core/Views/PlayerHistoryView.cs
+++ b/src/Core/Views/PlayerHistoryView.cs
@@ -122,16 +122,16 @@ namespace gbrainy.Core.Views
 	
 			if (ShowLogic) {
 				cr.Color = logic_color;
-				cr.MoveTo (x, area_h - (area_h * history.Games[0].logic_score / 100));
+				cr.MoveTo (x, area_h - (area_h * history.Games[0].LogicScore / 100));
 
 				pos = 1;
 				for (int i = 1; i < history.Games.Count; i++)
 				{
-					if (history.Games[i].logic_score < 0)
+					if (history.Games[i].LogicScore < 0)
 						continue;
 
 					px = x + (ratio * pos);
-					py = y + area_h - (area_h * history.Games[i].logic_score / 100);
+					py = y + area_h - (area_h * history.Games[i].LogicScore / 100);
 					cr.LineTo (px, py);
 					pos++;
 				}
@@ -140,16 +140,16 @@ namespace gbrainy.Core.Views
 
 			if (ShowCalculation) {
 				cr.Color = math_color;
-				cr.MoveTo (x, area_h - (area_h * history.Games[0].math_score / 100));
+				cr.MoveTo (x, area_h - (area_h * history.Games[0].MathScore / 100));
 
 				pos = 1;
 				for (int i = 1; i < history.Games.Count; i++)
 				{
-					if (history.Games[i].math_score < 0)
+					if (history.Games[i].MathScore < 0)
 						continue;
 
 					px = x + (ratio * pos);
-					py = y + area_h - (area_h * history.Games[i].math_score / 100);
+					py = y + area_h - (area_h * history.Games[i].MathScore / 100);
 					cr.LineTo (px, py);
 					pos++;
 				}
@@ -158,16 +158,16 @@ namespace gbrainy.Core.Views
 
 			if (ShowMemory) {
 				cr.Color = memory_color;
-				cr.MoveTo (x, area_h - (area_h * history.Games[0].memory_score / 100));
+				cr.MoveTo (x, area_h - (area_h * history.Games[0].MemoryScore / 100));
 
 				pos = 1;
 				for (int i = 1; i < history.Games.Count; i++)
 				{
-					if (history.Games[i].memory_score < 0)
+					if (history.Games[i].MemoryScore < 0)
 						continue;
 
 					px = x + (ratio * pos);
-					py = y + area_h - (area_h * history.Games[i].memory_score / 100);
+					py = y + area_h - (area_h * history.Games[i].MemoryScore / 100);
 					cr.LineTo (px, py);
 					pos++;
 				}
@@ -176,16 +176,16 @@ namespace gbrainy.Core.Views
 
 			if (ShowVerbal) {
 				cr.Color = verbal_color;
-				cr.MoveTo (x, area_h - (area_h * history.Games[0].verbal_score / 100));
+				cr.MoveTo (x, area_h - (area_h * history.Games[0].VerbalScore / 100));
 
 				pos = 1;
 				for (int i = 1; i < history.Games.Count; i++)
 				{
-					if (history.Games[i].verbal_score < 0)
+					if (history.Games[i].VerbalScore < 0)
 						continue;
 
 					px = x + (ratio * i);
-					py = y + area_h - (area_h * history.Games[i].verbal_score / 100);
+					py = y + area_h - (area_h * history.Games[i].VerbalScore / 100);
 					cr.LineTo (px, py);
 					pos++;
 				}
@@ -194,16 +194,16 @@ namespace gbrainy.Core.Views
 
 			if (ShowTotal) {
 				cr.Color = total_color;
-				cr.MoveTo (x, area_h - (area_h * history.Games[0].total_score / 100));
+				cr.MoveTo (x, area_h - (area_h * history.Games[0].TotalScore / 100));
 
 				pos = 1;
 				for (int i = 1; i < history.Games.Count; i++)
 				{
-					if (history.Games[pos].total_score < 0)
+					if (history.Games[pos].TotalScore < 0)
 						continue;
 
 					px = x + (ratio * pos);
-					py = y + area_h - (area_h * history.Games[i].total_score / 100);
+					py = y + area_h - (area_h * history.Games[i].TotalScore / 100);
 					cr.LineTo (px, py);
 					pos++;
 				}
diff --git a/tests/Core/AnalogiesFactoryTest.cs b/tests/Core/AnalogiesFactoryTest.cs
new file mode 100644
index 0000000..b25cfa1
--- /dev/null
+++ b/tests/Core/AnalogiesFactoryTest.cs
@@ -0,0 +1,55 @@
+using System;
+using System.Collections.Generic;
+using NUnit.Framework;
+
+using gbrainy.Core.Main.Verbal;
+
+namespace gbrainyTest
+{
+	[TestFixture]
+	public class AnalogiesFactoryTest
+	{
+		[TestFixtureSetUp]
+		public void Construct ()
+		{
+			AnalogiesFactory.Read ("test_analogies.xml");
+		}
+
+		[Test]
+		public void MultipleOptionsWithIngore ()
+		{
+			Dictionary <int, Analogy> analogies;
+
+			// Checks also the <ignore> parameter
+			analogies = AnalogiesFactory.Get (Analogy.Type.MultipleOptions);
+			Assert.AreEqual (2, analogies.Count);
+		}
+
+		[Test]
+		public void PairOfWordsOptions ()
+		{
+			Dictionary <int, Analogy> analogies;
+
+			analogies = AnalogiesFactory.Get (Analogy.Type.PairOfWordsOptions);
+			Assert.AreEqual (1, analogies.Count);
+		}
+
+		[Test]
+		public void QuestionAnswer ()
+		{
+			Dictionary <int, Analogy> analogies;
+
+			analogies = AnalogiesFactory.Get (Analogy.Type.QuestionAnswer);
+			Assert.AreEqual (1, analogies.Count);
+		}
+
+		[Test]
+		public void PairOfWordsCompare ()
+		{
+			Dictionary <int, Analogy> analogies;
+
+			analogies = AnalogiesFactory.Get (Analogy.Type.PairOfWordsCompare);
+			Assert.AreEqual (2, analogies.Count);
+		}
+	}
+}
diff --git a/tests/Core/PlayerHistoryTest.cs b/tests/Core/PlayerHistoryTest.cs
new file mode 100644
index 0000000..74856c7
--- /dev/null
+++ b/tests/Core/PlayerHistoryTest.cs
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2010 Jordi Mas i Hernàndez <jmas softcatala org>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this program; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+using System;
+using System.Collections.Generic;
+using NUnit.Framework;
+
+using gbrainy.Core.Main;
+
+namespace gbrainyTest
+{
+	[TestFixture]
+	public class PlayerHistoryTest
+	{
+		PlayerHistory history;
+
+		[TestFixtureSetUp]
+		public void Construct ()
+		{
+			// Ignore gbrainy instance preferences
+			Preferences.LoadDefaultValues ();
+		}
+
+		[Test]
+		public void Clean ()
+		{
+			GameSessionHistory game = new GameSessionHistory ();
+			game.GamesPlayed = Preferences.GetIntValue (Preferences.MinPlayedGamesKey);
+
+			history = new PlayerHistory ();
+			history.ConfigPath = ".";
+			history.Clean ();
+			history.SaveGameSession (game);
+
+			Assert.AreEqual (1, history.Games.Count);
+			history.Clean ();
+			Assert.AreEqual (0, history.Games.Count);
+		}
+
+		[Test]
+		public void SaveLoad ()
+		{
+			GameSessionHistory game = new GameSessionHistory ();
+			game.GamesPlayed = Preferences.GetIntValue (Preferences.MinPlayedGamesKey);
+			game.MemoryScore = 20;
+
+			history = new PlayerHistory ();
+			history.ConfigPath = ".";
+			history.SaveGameSession (game);
+
+			history = new PlayerHistory ();
+			history.ConfigPath = ".";
+			history.Load ();
+
+			Assert.AreEqual (1, history.Games.Count);
+			Assert.AreEqual (20, history.Games[0].MemoryScore);
+		}
+	}
+}
diff --git a/tests/Core/PlayerPersonalRecordTest.cs b/tests/Core/PlayerPersonalRecordTest.cs
new file mode 100644
index 0000000..2720d11
--- /dev/null
+++ b/tests/Core/PlayerPersonalRecordTest.cs
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2010 Jordi Mas i Hernàndez <jmas softcatala org>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this program; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+using System;
+using System.Collections.Generic;
+using NUnit.Framework;
+
+using gbrainy.Core.Main;
+
+namespace gbrainyTest
+{
+	[TestFixture]
+	public class PlayerPersonalRecordTest
+	{
+		PlayerHistory history;
+
+		[TestFixtureSetUp]
+		public void Construct ()
+		{
+			// Ignore gbrainy instance preferences
+			Preferences.LoadDefaultValues ();
+		}
+
+		[Test]
+		public void MinGamesRecord ()
+		{
+			PlayerHistory history;
+
+			GameSessionHistory game = new GameSessionHistory ();
+			game.GamesPlayed = Preferences.GetIntValue (Preferences.MinPlayedGamesKey);
+
+			history = new PlayerHistory ();
+			history.ConfigPath = ".";
+			history.Clean ();
+
+			for (int i = 0; i < PlayerPersonalRecord.MIN_GAMES_RECORD - 2; i++)
+			{
+				history.SaveGameSession (game);
+			}
+
+			game.LogicScore = 10;
+			history.SaveGameSession (game);
+
+			Assert.AreEqual (0, history.GetLastGameRecords ().Count,
+				"Did not reach MinPlayedGamesKey, the game should not be a person record yet");
+
+			game.LogicScore = 30;
+			history.SaveGameSession (game);
+
+			Assert.AreEqual (1, history.GetLastGameRecords ().Count,
+				"We have just recorded a personal record");
+
+			game.LogicScore = 20;
+			history.SaveGameSession (game);
+
+			Assert.AreEqual (0, history.GetLastGameRecords ().Count,
+				"Score saved was lower than previous, no record");
+		}
+	}
+}
diff --git a/tests/Makefile.am b/tests/Makefile.am
new file mode 100644
index 0000000..7d6bea7
--- /dev/null
+++ b/tests/Makefile.am
@@ -0,0 +1,36 @@
+TARGET = gbrainy.Core.Test.dll
+
+CSFLAGS =				\
+	-debug				\
+	-define:DEBUG			\
+	-target:library
+
+CSFILES =					\
+	$(srcdir)/Core/AnalogiesFactoryTest.cs	\
+	$(srcdir)/Core/PlayerHistoryTest.cs	\
+	$(srcdir)/Core/PlayerPersonalRecordTest.cs
+
+ASSEMBLIES = \
+	$(NUNIT_LIBS)			\
+	$(MONO_ADDINS_LIBS)		\
+	-pkg:gbrainy
+
+MONO_PATH = .:$(top_builddir)/src:
+
+RESSOURCES =
+
+$(TARGET): $(CSFILES) $(top_builddir)/src/gbrainy.exe
+	$(CSC) -out:$@ $(CSFLAGS) $(CSFILES) $(ASSEMBLIES) $(RESSOURCES)
+
+test: $(TARGET)
+	MONO_PATH=$(MONO_PATH) $(NUNIT) $(TARGET) -nologo
+
+EXTRA_DIST = 				\
+	$(CSFILES)
+
+CLEANFILES = 				\
+	$(TARGET)			\
+	$(TARGET).mdb			\
+	TestResult.xml
+
+.PHONY: test
diff --git a/tests/README b/tests/README
new file mode 100644
index 0000000..5b195fe
--- /dev/null
+++ b/tests/README
@@ -0,0 +1,7 @@
+
+This is a collection of unit tests to verify that the key features of gbrainy
+behave as intended.
+
+To run the tests use:
+
+make rest
diff --git a/tests/test_analogies.xml b/tests/test_analogies.xml
new file mode 100644
index 0000000..86ace71
--- /dev/null
+++ b/tests/test_analogies.xml
@@ -0,0 +1,66 @@
+<analogies>
+	<!---
+			gbrainy Verbal Analogies test file
+	-->
+	
+	<!--- Multiple options  -->
+
+	<analogy>
+		<_question type = "MultipleOptions">Which of the following sports is the odd one?</_question>
+		<_tip>Think of the items used in the game.</_tip>
+		<_answer>Water polo</_answer>
+		<_answer>Basketball</_answer>
+		<_answer>Tennis</_answer>
+		<_answer correct = "yes">Cycling</_answer>
+		<_rationale>It is the only one that does not use a ball in the game.</_rationale>
+	</analogy>
+
+	<analogy>
+		<_question type = "MultipleOptions">The word 'taxidermist' is used to define a person that?</_question>
+		<_answer correct ="yes">Works with dead animals</_answer>
+		<_answer>Specializes in skin diseases</_answer>
+		<_answer>Suffers a skin disease</_answer>
+		<_answer>Works with leather</_answer>
+	</analogy>
+
+	<analogy>
+		<_question type = "MultipleOptions">&lt;ignore&gt;</_question>
+		<_answer correct ="yes">simplistic</_answer>
+		<_answer>erroneous</_answer>
+		<_answer>broken</_answer>
+		<_answer>unorthodox</_answer>
+	</analogy>
+
+	<!--- Pair of words options -->
+
+	<analogy>
+		<_question type = "PairOfWordsOptions">moratorium / payment</_question>
+		<_answer correct ="yes">reprieve / punishment</_answer>
+		<_answer>amnesty / prisoner</_answer>
+		<_answer>date / meeting</_answer>
+		<_answer>sentence / prison</_answer>
+	</analogy>
+
+	<!--- Pair of words compare  -->
+
+	<analogy>
+		<_question type = "PairOfWordsCompare">car / road | train</_question>
+		<_answer correct ="yes">track | railway</_answer>
+	</analogy>
+
+	<analogy>
+		<_question type = "PairOfWordsCompare">pediatrics / children | numismatics</_question>
+		<_answer correct ="yes">coins</_answer>
+	</analogy>
+
+	<!---	QuestionAnswer -->
+	
+	<analogy>
+		<_question>A restaurant is to a dinner like a park is to?</_question>
+		<_answer correct ="yes">picnic</_answer>
+	</analogy>
+
+
+</analogies>
+
+



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