[gbrainy] Initial support for XML defined games



commit 1d26f03a06f180aa2f32d83807e91590494e8813
Author: Jordi Mas <jmas softcatala org>
Date:   Sat Jun 5 09:34:41 2010 +0200

    Initial support for XML defined games

 src/Clients/Classical/Dialogs/CustomGameDialog.cs  |   59 ++--
 src/Clients/Classical/gbrainy.cs                   |    5 +-
 src/Core/Libraries/CairoContext.cs                 |    4 +-
 src/Core/Main/Game.cs                              |   29 ++-
 src/Core/Main/GameManager.cs                       |  427 +++++++++-----------
 src/Core/Main/GameSession.cs                       |    4 +-
 src/Core/Main/Verbal/Analogies.cs                  |   47 +--
 src/Core/Main/Verbal/AnalogiesFactory.cs           |    5 +-
 src/Core/Main/Verbal/AnalogiesMultipleOptions.cs   |   12 -
 .../Main/Verbal/AnalogiesPairOfWordsCompare.cs     |   11 -
 .../Main/Verbal/AnalogiesPairOfWordsOptions.cs     |   12 -
 src/Core/Main/Verbal/AnalogiesQuestionAnswer.cs    |   12 -
 src/Core/Main/Xml/GameXml.cs                       |  145 +++++++
 src/Core/Main/Xml/GameXmlDefinition.cs             |   96 +++++
 src/Core/Main/Xml/GameXmlFactory.cs                |  209 ++++++++++
 src/Core/Makefile.am                               |    3 +
 tests/Core/GameManagerTest.cs                      |   19 +-
 17 files changed, 728 insertions(+), 371 deletions(-)
---
diff --git a/src/Clients/Classical/Dialogs/CustomGameDialog.cs b/src/Clients/Classical/Dialogs/CustomGameDialog.cs
index fd759f0..9dc8642 100644
--- a/src/Clients/Classical/Dialogs/CustomGameDialog.cs
+++ b/src/Clients/Classical/Dialogs/CustomGameDialog.cs
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2007 Jordi Mas i Hernàndez <jmas softcatala org>
+ * Copyright (C) 2007-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
@@ -20,7 +20,7 @@
 using System;
 using Gtk;
 using Mono.Unix;
-using System.Collections;
+using System.Collections.Generic;
 
 using gbrainy.Core.Main;
 using gbrainy.Core.Libraries;
@@ -36,22 +36,23 @@ namespace gbrainy.Clients.Classical
 		CairoPreview drawing_area;
 		SimpleLabel question_label;
 		GameManager manager;
-		int ngames, npos;
-		Type [] custom_games;
+		GameManager.GameLocator [] games;
+		bool selection_done;
 
 		const int COL_ENABLED = 2;
 		const int COL_OBJECT = 3;
+		const int COL_INDEX = 4;
 
 		public CustomGameDialog (GameManager manager) : base ("CustomGameDialog.ui", "customgame")
 		{
 			Game game;
-			Type[] games;
 			GameManager gm;
 
+			selection_done = false;
 			this.manager = manager;
 			gm = new GameManager ();
 			gm.GameType = GameSession.Types.AllGames;
-			games = gm.CustomGames;
+			games = gm.AvailableGames;
 
 			drawing_area = new CairoPreview ();
 			preview_vbox.Add (drawing_area);
@@ -85,28 +86,33 @@ namespace gbrainy.Clients.Classical
 			treeview.AppendColumn (toggle_column);
 
 			if (games_store == null) {
-				games_store = new ListStore (typeof(string), typeof (string), typeof(bool), typeof (Game));
+				games_store = new ListStore (typeof(string), typeof (string), typeof(bool), typeof (Game), typeof (int));
 					 
 				// Data
 				string type;
 				for (int i = 0; i < games.Length; i++)
-				{	
-					game =  (Game) Activator.CreateInstance (games [i], true);
+				{
+					if (games [i].IsGame == false)
+						continue;
+
+					game = (Game) Activator.CreateInstance (games [i].TypeOf, true);
+					game.Variant = games [i].Variant;
 					type = GameTypesDescription.Get (game.Type);
-					games_store.AppendValues (game.Name, type, true, game);
+					games_store.AppendValues (game.Name, type, true, game, i);
 				}
 			}
 
 			treeview.Model = games_store;
-			game =  (Game) Activator.CreateInstance (games [0], true);
-			game.Initialize ();
+			game = (Game) Activator.CreateInstance (games [0].TypeOf, true);
+			game.Variant = 0;
+			game.Begin ();
 			drawing_area.puzzle = game;
 			question_label.Text = game.Question;
 			treeview.ColumnsAutosize ();
 		}
 
-		public int NumOfGames {
-			get { return ngames;}
+		public bool SelectionDone {
+			get { return selection_done;}
 		}
 
 		private void OnCursorChanged (object o, EventArgs args) 
@@ -123,7 +129,7 @@ namespace gbrainy.Clients.Classical
 			if (game.IsPreviewMode == false) 
 			{
 				game.IsPreviewMode = true;
-				game.Initialize ();
+				game.Begin ();
 			}
 
 			question_label.Text = game.Question;
@@ -160,33 +166,24 @@ namespace gbrainy.Clients.Classical
 
 		void OnOK (object sender, EventArgs args)
 		{
-			ngames = 0;
-			npos = 0;
-
-			games_store.Foreach (delegate (TreeModel model, TreePath path, TreeIter iter)  {
-				if ((bool) games_store.GetValue (iter, COL_ENABLED) == true)
-					ngames++;
+			List <int> play_list;
 
-				return false;
-			});
-
-			if (ngames == 0)
-				return;
+			play_list = new List <int> ();
 
-			custom_games = new Type [ngames];
 			games_store.Foreach (delegate (TreeModel model, TreePath path, TreeIter iter)  {
 				Game game = games_store.GetValue (iter, COL_OBJECT) as Game;
 				bool enabled = (bool) games_store.GetValue (iter, COL_ENABLED);
 
 				if (enabled == true) {
-					custom_games[npos] = game.GetType ();
-					npos++;
+					selection_done = true;
+					int idx = (int) games_store.GetValue (iter, COL_INDEX);
+					play_list.Add (idx);
 				}
 				return false;
 			});
 
-	
-			manager.CustomGames = custom_games;
+			if (selection_done == true)
+				manager.PlayList = play_list.ToArray ();
 		}
 
 		public class CairoPreview : DrawingArea 
diff --git a/src/Clients/Classical/gbrainy.cs b/src/Clients/Classical/gbrainy.cs
index 6bc2346..d6287da 100644
--- a/src/Clients/Classical/gbrainy.cs
+++ b/src/Clients/Classical/gbrainy.cs
@@ -522,14 +522,13 @@ namespace gbrainy.Clients.Classical
 
 		void OnCustomGame (object sender, EventArgs args)
 		{
-			ResponseType rslt;
 			CustomGameDialog dialog;
 
 			dialog = new CustomGameDialog (session.GameManager);
-			rslt = (Gtk.ResponseType) dialog.Run ();
+			dialog.Run ();
 			dialog.Destroy ();
 
-			if (rslt == ResponseType.Ok && dialog.NumOfGames > 0)
+			if (dialog.SelectionDone == true)
 				OnNewGame (GameSession.Types.Custom);
 		}
 
diff --git a/src/Core/Libraries/CairoContext.cs b/src/Core/Libraries/CairoContext.cs
index ddd434d..615df2d 100644
--- a/src/Core/Libraries/CairoContext.cs
+++ b/src/Core/Libraries/CairoContext.cs
@@ -256,9 +256,9 @@ namespace gbrainy.Core.Libraries
 					DrawImage (image, x, y, width, height);
 				}
 			}
-			catch (Exception)
+			catch (Exception e)
 			{
-				return;
+				Console.WriteLine ("CairoContext. Error '{0}' when drawing image from filename {1}", e.Message, filename);
 			}
 		}
 
diff --git a/src/Core/Main/Game.cs b/src/Core/Main/Game.cs
index beda11d..cb03dbf 100644
--- a/src/Core/Main/Game.cs
+++ b/src/Core/Main/Game.cs
@@ -39,6 +39,7 @@ namespace gbrainy.Core.Main
 			Easy			= 2,
 			Medium			= 4,
 			Master			= 8,
+			All			= Easy | Medium | Master,
 		}
 
 		[Flags]
@@ -75,6 +76,7 @@ namespace gbrainy.Core.Main
 		private Difficulty difficulty;
 		private ISynchronizeInvoke synchronize;
 		private List <Toolkit.Container> containers;
+		private int variant;
 
 		public event EventHandler DrawRequest;
 		public event EventHandler <UpdateUIStateEventArgs> UpdateUIElement;
@@ -82,10 +84,7 @@ namespace gbrainy.Core.Main
 
 		protected Game ()
 		{
-			random = new Random ();
-			default_color = new Cairo.Color (0, 0, 0);
 			difficulty = Difficulty.Medium;
-			containers = new List <Toolkit.Container> ();
 		}
 
 #region Methods to override in your own games
@@ -108,7 +107,12 @@ namespace gbrainy.Core.Main
 			get { return string.Empty;}
 		}
 
+		// TODO: This should be protected since we should use Begin
 		public abstract void Initialize ();
+
+		public virtual int Variants {
+			get { return 1;}
+		}
 #endregion
 
 #region Methods that you can optionally override
@@ -143,6 +147,25 @@ namespace gbrainy.Core.Main
 			get { return false;}
 		}
 #endregion
+
+		public void Begin ()
+		{
+			random = new Random ();
+			default_color = new Cairo.Color (0, 0, 0);
+			containers = new List <Toolkit.Container> ();
+			Initialize ();
+		}
+
+		public virtual int Variant {
+			protected get { return variant; }
+			set {
+				if (value < 0 || value > Variants)
+					throw new ArgumentOutOfRangeException (String.Format ("Variant out of range {0}", value));
+
+				variant = value; 
+			}
+		}
+
 		// Builds a text answer for the puzzle
 		public virtual string Answer {
 			get {
diff --git a/src/Core/Main/GameManager.cs b/src/Core/Main/GameManager.cs
index da76d76..a3d1031 100644
--- a/src/Core/Main/GameManager.cs
+++ b/src/Core/Main/GameManager.cs
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2007-2009 Jordi Mas i Hernàndez <jmas softcatala org>
+ * Copyright (C) 2007-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
@@ -41,58 +41,7 @@ namespace gbrainy.Core.Main
 {
 	public class GameManager
 	{
-		// Serves analogies as their container class is still not exhausted
-		// This is used to make sure that the analogies are not repeated within a game session
-		public class AnalogiesManager
-		{
-			List <Analogies> analogies;
-
-			public AnalogiesManager (Type [] types)
-			{
-				analogies = new List <Analogies> ();
-			
-				foreach (Type type in types)
-				{
-					Analogies analogy;
-
-					analogy = (Analogies) Activator.CreateInstance (type, true);
-					analogies.Add (analogy);
-				}
-			}
-
-			public void Initialize ()
-			{
-				foreach (Analogies analogy in analogies)
-					analogy.CurrentIndex = 0;
-			}
-
-			public Type [] AvailableTypes {
-				get {
-					List <Type> types = new List <Type> ();
-
-					foreach (Analogies analogy in analogies)
-					{
-						if (analogy.List.Count > 0)
-							types.Add (analogy.GetType ());
-					}
-
-					return types.ToArray ();
-				}
-			}
-
-			public bool IsExhausted {
-				get {
-					foreach (Analogies analogy in analogies)
-					{
-						if (analogy.IsExhausted == false)
-							return false;
-					}
-					return true;
-				}
-			}
-		}
-
-		static Type[] VerbalAnalogiesInternal = new Type[] 
+		static Type[] VerbalAnalogiesInternal = new Type[]
 		{
 			typeof (AnalogiesQuestionAnswer),
 			typeof (AnalogiesMultipleOptions),
@@ -100,69 +49,79 @@ namespace gbrainy.Core.Main
 			typeof (AnalogiesPairOfWordsCompare),
 		};
 
+		public class GameLocator
+		{
+			public Type TypeOf { get; set; }
+			public int Variant { get; set; }
+			public GameTypes GameType { get; set; }
+			public bool IsGame { get; set; }
+
+			public GameLocator (Type type, int variant, GameTypes game_type, bool isGame)
+			{
+				TypeOf = type;
+				Variant = variant;
+				GameType = game_type;
+				IsGame = isGame;
+			}
+		}
+
 		bool once;
 		GameSession.Types game_type;
 		ArrayListIndicesRandom list;
-		IEnumerator enumerator;
-		List <Type> games;
+		IEnumerator <int> enumerator;
 		Game.Difficulty difficulty;
-		List <Type> LogicPuzzles;
-		List <Type> CalculationTrainers;
-		List <Type> MemoryTrainers;
-		List <Type> VerbalAnalogies;
-		AnalogiesManager analogies_manager;
-	
+
+		List <GameLocator> available_games; 	// List of all available games in the system
+		List <int> play_list;  		// Play list for the Selected difficulty, game types
+		int cnt_logic, cnt_memory, cnt_calculation, cnt_verbal;
+
 		public GameManager ()
 		{
 			game_type = GameSession.Types.None;
 			difficulty = Game.Difficulty.Medium;
-			games = new List <Type> ();
-			VerbalAnalogies = new List <Type> ();
-			analogies_manager = new AnalogiesManager (VerbalAnalogiesInternal);
-
-			foreach (Type type in analogies_manager.AvailableTypes)
-				VerbalAnalogies.Add (type);			
+			available_games = new List <GameLocator> ();
+			play_list = new List <int> ();
+			cnt_logic = cnt_memory = cnt_calculation = cnt_verbal = 0;
 
-			LoadAssemblyGame ();
+			GamesXmlFactory.Read ();
 
-			if (LogicPuzzles == null)
-				LogicPuzzles = new List <Type> ();
+			LoadAssemblyGames ();
 
-			if (MemoryTrainers == null)
-				MemoryTrainers = new List <Type> ();
+			// Load Analogies
+			cnt_verbal += AddGamesAndVariations (VerbalAnalogiesInternal);
 
-			if (CalculationTrainers == null)
-				CalculationTrainers = new List <Type> ();
+			// Load defined XML games
+			cnt_logic += LoadXmlGames ();
 
 			LoadPlugins ();
 
 			if (once == false) {
 				once = true;
-				Console.WriteLine (Catalog.GetString ("Games registered: {0}: {1} logic puzzles, {2} calculation trainers, {3} memory trainers, {4} verbal analogies"), 
-					LogicPuzzles.Count + CalculationTrainers.Count + MemoryTrainers.Count + VerbalAnalogies.Count,
-					LogicPuzzles.Count, CalculationTrainers.Count, MemoryTrainers.Count, VerbalAnalogies.Count);
+				Console.WriteLine (Catalog.GetString ("Games registered: {0}: {1} logic puzzles, {2} calculation trainers, {3} memory trainers, {4} verbal analogies"),
+					cnt_logic + cnt_memory + cnt_calculation + cnt_verbal,
+					cnt_logic, cnt_calculation, cnt_memory, cnt_verbal);
 			}
 #if PDF_DUMP
 			GeneratePDF ();
 #endif
 		}
-	
-		public GameTypes AvailableGames {
+
+		public GameTypes AvailableGameTypes {
 			get {
 				GameTypes types = GameTypes.None;
 
-				if (LogicPuzzles.Count > 0)
+				if (cnt_logic > 0)
 					types |= GameTypes.LogicPuzzle;
 
-				if (CalculationTrainers.Count > 0)
+				if (cnt_calculation > 0)
 					types |= GameTypes.MathTrainer;
 
-				if (MemoryTrainers.Count > 0)
+				if (cnt_memory > 0)
 					types |= GameTypes.MemoryTrainer;
 
-				if (analogies_manager.AvailableTypes.Length > 0)
+				if (cnt_verbal > 0)
 					types |= GameTypes.VerbalAnalogy;
-			
+
 				return types;
 			}
 		}
@@ -172,54 +131,41 @@ namespace gbrainy.Core.Main
 			set {
 				if (game_type == value)
 					return;
-			
+
 				game_type = value;
-				BuildGameList ();
+
+				if ((game_type & GameSession.Types.Custom) != GameSession.Types.Custom)
+					BuildPlayList (available_games);
 			}
 		}
 
 		public Game.Difficulty Difficulty {
 			set {
+				if (difficulty == value)
+					return;
+
 				difficulty = value;
-				BuildGameList ();
-			}
-			get {
-				return difficulty;
+				BuildPlayList (available_games);
 			}
+			get { return difficulty; }
 		}
 
-		// Used from CustomGameDialog only
-		public Type[] CustomGames {
-			get { 
-				Type[] list = new Type [LogicPuzzles.Count + CalculationTrainers.Count + MemoryTrainers.Count + VerbalAnalogies.Count];
-				int idx = 0;
-
-				for (int i = 0; i < LogicPuzzles.Count; i++, idx++)
-					list[idx] = LogicPuzzles [i];
-
-				for (int i = 0; i < CalculationTrainers.Count; i++, idx++)
-					list[idx] = CalculationTrainers [i];
-
-				for (int i = 0; i < MemoryTrainers.Count; i++, idx++)
-					list[idx] = MemoryTrainers [i];
-
-				for (int i = 0; i < VerbalAnalogies.Count; i++, idx++)
-					list[idx] = VerbalAnalogies [i];
-
-				return list;
-			}
+		// Establish the PlayList (the indices of the array to available games)
+		public int [] PlayList {
+			get { return play_list.ToArray ();}
 			set {
-				games = new List <Type> (value.Length);
-				for (int i = 0; i < value.Length; i++)
-					games.Add (value[i]);
-
-				list = new ArrayListIndicesRandom (games.Count);
-				Initialize ();
+				play_list = new List <int> (value);
+				enumerator = play_list.GetEnumerator ();
 			}
 		}
 
+		// Returns all the games available for playing
+		public GameLocator [] AvailableGames {
+			get { return available_games.ToArray (); }
+		}
+
 		// Dynamic load of the gbrainy.Games.Dll assembly
-		void LoadAssemblyGame ()
+		void LoadAssemblyGames ()
 		{
 			const string ASSEMBLY = "gbrainy.Games.dll";
 			const string CLASS = "gbrainy.Games.GameList";
@@ -240,7 +186,7 @@ namespace gbrainy.Core.Main
 
 				asem = Assembly.LoadFrom (System.IO.Path.Combine (asm_dir, ASSEMBLY));
 
-				foreach (Type t in asem.GetTypes()) 
+				foreach (Type t in asem.GetTypes())
 				{
 					if (t.FullName == CLASS)
 					{
@@ -253,15 +199,15 @@ namespace gbrainy.Core.Main
 
 				prop = type.GetProperty (LOGIC_METHOD);
 				if (prop != null)
-					LogicPuzzles = new List <Type> ((Type []) prop.GetValue (obj, null));
+					cnt_logic += AddGamesAndVariations ((Type []) prop.GetValue (obj, null));
 
 				prop = type.GetProperty (MEMORY_METHOD);
 				if (prop != null)
-					MemoryTrainers = new List <Type> ((Type []) prop.GetValue (obj, null));
+					cnt_memory += AddGamesAndVariations ((Type []) prop.GetValue (obj, null));
 
 				prop = type.GetProperty (CALCULATION_METHOD);
 				if (prop != null)
-					CalculationTrainers = new List <Type> ((Type []) prop.GetValue (obj, null));
+					cnt_calculation += AddGamesAndVariations ((Type []) prop.GetValue (obj, null));
 			}
 
 			catch (Exception e)
@@ -270,15 +216,52 @@ namespace gbrainy.Core.Main
 			}
 		}
 
+		// Adds all the games and its variants into the available games list
+		int AddGamesAndVariations (Type [] types)
+		{
+			Game game;
+			int cnt = 0;
+
+			foreach (Type type in types)
+			{
+				game = (Game) Activator.CreateInstance (type, true);
+				for (int i = 0; i < game.Variants; i++)
+				{
+					available_games.Add (new GameLocator (type, i, game.Type, game.Variants == 1));
+				}
+				cnt += game.Variants;
+			}
+			return cnt;
+		}
+
+		// XML are stored using the Variant as a pointer to the game + the internal variant
+		int LoadXmlGames ()
+		{
+			Type type = typeof (GameXml);
+			int cnt = 0;
+
+			foreach (GameXmlDefinition game in GamesXmlFactory.Definitions)
+			{
+				available_games.Add (new GameLocator (type, cnt++, game.Type, true));
+				for (int i = 0; i < game.Variants.Count; i++)
+				{
+					available_games.Add (new GameLocator (type, cnt++, game.Type, false));
+				}
+			}
+			return cnt;
+		}
+
 		void LoadPlugins ()
 		{
 
 	#if MONO_ADDINS
+
 			try {
 				ExtensionNodeList addins;
 				Game game;
+				Type [] type = new Type [1];
 				string dir = System.IO.Path.Combine (Environment.GetFolderPath (Environment.SpecialFolder.ApplicationData), "gbrainy");
-		
+
 				AddinManager.Initialize (dir);
 				Console.WriteLine ("Pluggin database:" + dir);
 				AddinManager.Registry.Update (null);
@@ -288,28 +271,32 @@ namespace gbrainy.Core.Main
 				foreach (TypeExtensionNode node in addins) {
 					game = (Game) node.CreateInstance ();
 					Console.WriteLine ("Loading external logic game: {0}", game);
-					LogicPuzzles.Add (game.GetType ());
+					type [0] = game.GetType ();
+					AddGamesAndVariations (type);
 				}
-		
+
 				addins = AddinManager.GetExtensionNodes ("/gbrainy/games/memory");
 				foreach (TypeExtensionNode node in addins) {
 					game = (Game) node.CreateInstance ();
 					Console.WriteLine ("Loading external memory game: {0}", game);
-					MemoryTrainers.Add (game.GetType ());
+					type [0] = game.GetType ();
+					AddGamesAndVariations (type);
 				}
 
 				addins = AddinManager.GetExtensionNodes ("/gbrainy/games/calculation");
 				foreach (TypeExtensionNode node in addins) {
 					game = (Game) node.CreateInstance ();
 					Console.WriteLine ("Loading external calculation game: {0}", game);
-					CalculationTrainers.Add (game.GetType ());
+					type [0] = game.GetType ();
+					AddGamesAndVariations (type);
 				}
 
 				addins = AddinManager.GetExtensionNodes ("/gbrainy/games/verbal");
 				foreach (TypeExtensionNode node in addins) {
 					game = (Game) node.CreateInstance ();
 					Console.WriteLine ("Loading external verbal analogy game: {0}", game);
-					VerbalAnalogies.Add (game.GetType ());
+					type [0] = game.GetType ();
+					AddGamesAndVariations (type);
 				}
 			}
 			catch (Exception e)
@@ -319,140 +306,119 @@ namespace gbrainy.Core.Main
 	#endif
 		}
 
-		void BuildGameList ()
+		// Taking a GameLocator list builds the play_list
+		void BuildPlayList (List <GameLocator> all_games)
 		{
-			analogies_manager.Initialize ();
-
-			if (GameType == GameSession.Types.Custom)
-				return;
+			if ((game_type & GameSession.Types.Custom) == GameSession.Types.Custom)
+				throw new InvalidOperationException ();
 
-			games.Clear ();
-			Random random = new Random ();
+			play_list.Clear ();
 
-			// For all games, 1/4 of the total are logic, 1/4 Memory, 1/4 calculation, 1/4 verbal analogies
-			if (((game_type & GameSession.Types.AllGames) == GameSession.Types.AllGames) &&
-				LogicPuzzles.Count > 0 && MemoryTrainers.Count > 0 &&
-				CalculationTrainers.Count > 0 && VerbalAnalogies.Count > 0) {
-			
-				int idx_cal = 0, idx_mem = 0, idx_verb = 0;
-				ArrayListIndicesRandom idx_logic = new ArrayListIndicesRandom (LogicPuzzles.Count);
-				ArrayListIndicesRandom idx_memory = new ArrayListIndicesRandom (MemoryTrainers.Count);
-				ArrayListIndicesRandom idx_calculation = new ArrayListIndicesRandom (CalculationTrainers.Count);
-				ArrayListIndicesRandom idx_verbal = new ArrayListIndicesRandom (VerbalAnalogies.Count);
-
-				games.Clear ();
-				idx_memory.Initialize ();
-				idx_logic.Initialize ();
-				idx_calculation.Initialize ();
-				idx_verbal.Initialize ();
-
-				for (int i = 0; i < LogicPuzzles.Count; i++, idx_mem++, idx_cal++, idx_verb++) {
-
-					if (idx_cal == CalculationTrainers.Count) {
-						idx_cal = 0;
-						idx_calculation.Initialize ();
-					}
+			ArrayListIndicesRandom indices = new ArrayListIndicesRandom (all_games.Count);
+			indices.Initialize ();
 
-					if (idx_mem == MemoryTrainers.Count) {
-						idx_mem = 0;
-						idx_memory.Initialize ();
-					}
-
-					if (idx_verb == VerbalAnalogies.Count) {
-						idx_verb = 0;
-						idx_verbal.Initialize ();
-					}
+			List <int> logic_indices = new List <int> (cnt_logic);
+			List <int> calculation_indices = new List <int> (cnt_calculation);
+			List <int> memory_indices = new List <int> (cnt_memory);
+			List <int> verbal_indices = new List <int> (cnt_verbal);
+			bool logic, memory, calculation, verbal;
 
-					switch (random.Next (3)) {
-					case 0:
-						games.Add (CalculationTrainers [idx_calculation[idx_cal]]);
-						games.Add (LogicPuzzles [idx_logic[i]]);
-						games.Add (MemoryTrainers [idx_memory[idx_mem]]);
-						games.Add (VerbalAnalogies [idx_verbal[idx_verb]]);
-						break;
-					case 1:
-						games.Add (MemoryTrainers [idx_memory[idx_mem]]);
-						games.Add (CalculationTrainers [idx_calculation[idx_cal]]);
-						games.Add (VerbalAnalogies [idx_verbal[idx_verb]]);
-						games.Add (LogicPuzzles [idx_logic[i]]);
-						break;
-					case 2:
-						games.Add (CalculationTrainers [idx_calculation[idx_cal]]);
-						games.Add (VerbalAnalogies [idx_verbal[idx_verb]]);
-						games.Add (MemoryTrainers [idx_memory[idx_mem]]);
-						games.Add (LogicPuzzles [idx_logic[i]]);
-						break;
-					}
-				}
-			} else {
+			// Decide which game_types are part of the game
+			if ((game_type & GameSession.Types.AllGames) == GameSession.Types.AllGames)
+			{
+				logic = memory = calculation = verbal = true;
+			}
+			else
+			{
+				logic = (game_type & GameSession.Types.LogicPuzzles) == GameSession.Types.LogicPuzzles;
+				calculation = (game_type & GameSession.Types.CalculationTrainers) == GameSession.Types.CalculationTrainers;
+				memory = (game_type & GameSession.Types.MemoryTrainers) == GameSession.Types.MemoryTrainers;
+				verbal = (game_type & GameSession.Types.VerbalAnalogies) == GameSession.Types.VerbalAnalogies;
+			}
 
-				if ((game_type & GameSession.Types.LogicPuzzles) == GameSession.Types.LogicPuzzles) {
-					for (int i = 0; i < LogicPuzzles.Count; i++)
-						games.Add (LogicPuzzles [i]);
+			// Create arrays by game type
+			for (int n = 0; n < all_games.Count; n++)
+			{
+				switch (all_games [indices [n]].GameType) {
+				case GameTypes.LogicPuzzle:
+					if (logic)
+						logic_indices.Add (indices [n]);
+					break;
+				case GameTypes.MemoryTrainer:
+					if (memory)
+						memory_indices.Add (indices [n]);
+					break;
+				case GameTypes.MathTrainer:
+					if (calculation)
+						calculation_indices.Add (indices [n]);
+					break;
+				case GameTypes.VerbalAnalogy:
+					if (verbal)
+						verbal_indices.Add (indices [n]);
+					break;
+				default:
+					throw new InvalidOperationException ("Unknown value");
 				}
+			}
 
-				if ((game_type & GameSession.Types.CalculationTrainers) == GameSession.Types.CalculationTrainers) {
-					for (int i = 0; i < CalculationTrainers.Count; i++)
-						games.Add (CalculationTrainers [i]);
-				}
+			int total = logic_indices.Count + memory_indices.Count + calculation_indices.Count + verbal_indices.Count;
+			int pos_logic, pos_memory, pos_calculation, pos_verbal;
+			Random random = new Random ();
 
-				if ((game_type & GameSession.Types.MemoryTrainers) == GameSession.Types.MemoryTrainers) {
-					for (int i = 0; i < MemoryTrainers.Count; i++)
-						games.Add (MemoryTrainers [i]);
-				}
+			pos_logic = pos_memory = pos_calculation = pos_verbal = 0;
 
-				if ((game_type & GameSession.Types.VerbalAnalogies) == GameSession.Types.VerbalAnalogies) {
-					for (int i = 0; i < VerbalAnalogies.Count; i++)
-						games.Add (VerbalAnalogies [i]);
+			while (play_list.Count < total)
+			{
+				switch (random.Next (3)) {
+				case 0:
+					if (pos_calculation < calculation_indices.Count) play_list.Add (calculation_indices[pos_calculation++]);
+					if (pos_logic < logic_indices.Count) play_list.Add (logic_indices[pos_logic++]);
+					if (pos_memory < memory_indices.Count) play_list.Add (memory_indices[pos_memory++]);
+					if (pos_verbal < verbal_indices.Count) play_list.Add (verbal_indices[pos_verbal++]);
+					break;
+				case 1:
+					if (pos_memory < memory_indices.Count) play_list.Add (memory_indices[pos_memory++]);
+					if (pos_calculation < calculation_indices.Count) play_list.Add (calculation_indices[pos_calculation++]);
+					if (pos_verbal < verbal_indices.Count) play_list.Add (verbal_indices[pos_verbal++]);
+					if (pos_logic < logic_indices.Count) play_list.Add (logic_indices[pos_logic++]);
+					break;
+				case 2:
+					if (pos_calculation < calculation_indices.Count) play_list.Add (calculation_indices[pos_calculation++]);
+					if (pos_verbal < verbal_indices.Count) play_list.Add (verbal_indices[pos_verbal++]);
+					if (pos_memory < memory_indices.Count) play_list.Add (memory_indices[pos_memory++]);
+					if (pos_logic < logic_indices.Count) play_list.Add (logic_indices[pos_logic++]);
+					break;
 				}
-
 			}
 
-			list = new ArrayListIndicesRandom (games.Count);
-			Initialize ();
+			enumerator = play_list.GetEnumerator ();
 		}
 
-		void Initialize ()
-		{
-			if ((game_type & GameSession.Types.AllGames) == GameSession.Types.AllGames) { // The game list has been already randomized
-				list.Clear ();
-				for (int i = 0; i < games.Count; i++)
-					list.Add (i);
-			} else
-				list.Initialize ();
-
-			enumerator = list.GetEnumerator ();
-		}
-	
 		public Game GetPuzzle ()
 		{
 			Game puzzle, first = null;
 
 			while (true) {
 
-				if (enumerator.MoveNext () == false) { // All the games have been played, restart again 
-					Initialize ();
+				if (enumerator.MoveNext () == false) { // All the games have been played, restart again
+					enumerator = play_list.GetEnumerator ();
 					enumerator.MoveNext ();
-
-					if (analogies_manager.IsExhausted == true)
-						analogies_manager.Initialize ();
 				}
-				puzzle =  (Game) Activator.CreateInstance ((Type) games [(int) enumerator.Current], true);
+
+				puzzle =  (Game) Activator.CreateInstance ((Type) available_games [enumerator.Current].TypeOf, true);
 				//puzzle =  (Game) Activator.CreateInstance (LogicPuzzles [37], true);
 
+				puzzle.Variant = available_games [enumerator.Current].Variant;
+
 				if (first != null && first.GetType () == puzzle.GetType ())
 					break;
 
 				if (puzzle.IsPlayable == false)
 					continue;
-		
+
 				if ((Preferences.GetBoolValue (Preferences.ColorBlindKey) == true) && puzzle.UsesColors == true)
 					continue;
 
-				Analogies analogy = puzzle as Analogies;
-				if (analogy != null && analogy.IsExhausted == true)
-					continue;
-				
 				if (first == null)
 					first = puzzle;
 
@@ -463,7 +429,6 @@ namespace gbrainy.Core.Main
 			puzzle.CurrentDifficulty = Difficulty;
 			return puzzle;
 		}
-
 #if PDF_DUMP
 		// Generates a single PDF document with all the puzzles contained in gbrainy (4 games per page)
 		public void GeneratePDF ()
@@ -472,7 +437,7 @@ namespace gbrainy.Core.Main
 			Game puzzle;
 			game_type = GameSession.Types.AllGames;
 			Type [] allgames = CustomGames;
-		
+
 			for (int i = 0; i < allgames.Length; i++)
 				games.Add (allgames [i]);
 
@@ -486,7 +451,7 @@ namespace gbrainy.Core.Main
 				cnt++;
 				cr.Save ();
 				cr.Translate (x, y);
-				cr.Rectangle (0, 0, width, height);;	
+				cr.Rectangle (0, 0, width, height);;
 				cr.Clip ();
 				cr.Save ();
 				puzzle.DrawPreview (cr, width, height, false);
diff --git a/src/Core/Main/GameSession.cs b/src/Core/Main/GameSession.cs
index e09ce73..c176560 100644
--- a/src/Core/Main/GameSession.cs
+++ b/src/Core/Main/GameSession.cs
@@ -91,7 +91,7 @@ namespace gbrainy.Core.Main
 		}
 
 		public GameTypes AvailableGames {
-			get { return game_manager.AvailableGames; }
+			get { return game_manager.AvailableGameTypes; }
 		}
 
 		public PlayerHistory PlayerHistory { 
@@ -249,7 +249,7 @@ namespace gbrainy.Core.Main
 			CurrentGame.DrawRequest += GameDrawRequest;
 			CurrentGame.UpdateUIElement += GameUpdateUIElement;
 
-			CurrentGame.Initialize ();
+			CurrentGame.Begin ();
 
 			CurrentGame.GameTime = TimeSpan.Zero;
 			Status = SessionStatus.Playing;
diff --git a/src/Core/Main/Verbal/Analogies.cs b/src/Core/Main/Verbal/Analogies.cs
index a4df6c7..c393d9c 100644
--- a/src/Core/Main/Verbal/Analogies.cs
+++ b/src/Core/Main/Verbal/Analogies.cs
@@ -51,10 +51,6 @@ namespace gbrainy.Core.Main.Verbal
 			}
 		}
 
-		public override bool IsPlayable {
-			get { return List.Count > 0;}
-		}
-
 		public override string Answer {
 			get {
 				string str;
@@ -106,56 +102,23 @@ namespace gbrainy.Core.Main.Verbal
 			get { return GameTypes.VerbalAnalogy;}
 		}
 
-		public abstract ArrayListIndicesRandom Indices {
-			get;
-			set;
-		}
-
-		public abstract int CurrentIndex {
+		public abstract Dictionary <int, Analogy> List {
 			get;
-			set;
-		}
-
-		// Returns true when this game manager has no more games to server
-		public bool IsExhausted {
-			get { return CurrentIndex + 1 >= List.Count;}
 		}
 
-		public abstract Dictionary <int, Analogy> List {
-			get;
+		public override int Variants {
+			get { return List.Count;}
 		}
 
 		public Analogy GetNext ()
 		{
-			int idx;
 			Analogy analogy; // Holds a deep copy
 			Analogy analogy_ref; // Holds reference to the object
 			ArrayListIndicesRandom indices = null;
 			int new_right = 0;
 			bool localized = true;
 
-			if (List.Count == 0)
-				return null;
-
-			if (Indices == null || CurrentIndex + 1 >= List.Count) {
-				Indices = new ArrayListIndicesRandom (List.Count);
-				Indices.Initialize ();
-			}
-			else
-				CurrentIndex++;
-
-			idx = Indices [CurrentIndex];
-		
-			try
-			{
-				List.TryGetValue (idx, out analogy_ref);
-			}
-
-			catch (KeyNotFoundException)
-			{
-				return null;
-			}
-
+			List.TryGetValue (Variant, out analogy_ref);
 			analogy = analogy_ref.Copy ();
 
 			if (analogy.answers != null) { // Randomize answers order
@@ -195,7 +158,7 @@ namespace gbrainy.Core.Main.Verbal
 			} else {
 
 				// Get analogy again
-				List.TryGetValue (idx, out analogy_ref);
+				List.TryGetValue (Variant, out analogy_ref);
 				analogy = analogy_ref.Copy ();
 
 				if (analogy.answers != null) { // Randomize answers order
diff --git a/src/Core/Main/Verbal/AnalogiesFactory.cs b/src/Core/Main/Verbal/AnalogiesFactory.cs
index bad4b8b..5f6d953 100644
--- a/src/Core/Main/Verbal/AnalogiesFactory.cs
+++ b/src/Core/Main/Verbal/AnalogiesFactory.cs
@@ -22,7 +22,6 @@ using System.Xml;
 using System.IO;
 using System.Collections.Generic;
 
-using Cairo;
 using Mono.Unix;
 
 namespace gbrainy.Core.Main.Verbal
@@ -53,7 +52,7 @@ namespace gbrainy.Core.Main.Verbal
 
 		static void Read ()
 		{
-			Read (Defines.DATA_DIR + Defines.VERBAL_ANALOGIES);
+			Read (Path.Combine (Defines.DATA_DIR, Defines.VERBAL_ANALOGIES));
 		}			
 
 		static public void Read (string file)
@@ -163,7 +162,7 @@ namespace gbrainy.Core.Main.Verbal
 
 			catch (Exception e)
 			{
-				Console.WriteLine ("Error loading {0}. Exception {1}", file, e.Message);
+				Console.WriteLine ("AnalogiesFactory. Error loading file: {0}", e.Message);
 			}
 		}
 	}
diff --git a/src/Core/Main/Verbal/AnalogiesMultipleOptions.cs b/src/Core/Main/Verbal/AnalogiesMultipleOptions.cs
index 6bceef4..2050dbc 100644
--- a/src/Core/Main/Verbal/AnalogiesMultipleOptions.cs
+++ b/src/Core/Main/Verbal/AnalogiesMultipleOptions.cs
@@ -32,8 +32,6 @@ namespace gbrainy.Core.Main.Verbal
 	public class AnalogiesMultipleOptions : Analogies
 	{
 		static protected Dictionary <int, Analogy> analogies;
-		static protected ArrayListIndicesRandom analogies_indices;
-		static protected int analogies_index = 0;
 
 		public AnalogiesMultipleOptions ()
 		{
@@ -74,16 +72,6 @@ namespace gbrainy.Core.Main.Verbal
 			}
 		}
 
-		public override ArrayListIndicesRandom Indices {
-			get { return analogies_indices; }
-			set { analogies_indices = value; }
-		}
-
-		public override int CurrentIndex {
-			get { return analogies_index; }
-			set { analogies_index = value; }
-		}
-
 		public override Dictionary <int, Analogy> List {
 			get { return analogies; }
 		}
diff --git a/src/Core/Main/Verbal/AnalogiesPairOfWordsCompare.cs b/src/Core/Main/Verbal/AnalogiesPairOfWordsCompare.cs
index 93b53c5..f0ec6dd 100644
--- a/src/Core/Main/Verbal/AnalogiesPairOfWordsCompare.cs
+++ b/src/Core/Main/Verbal/AnalogiesPairOfWordsCompare.cs
@@ -32,7 +32,6 @@ namespace gbrainy.Core.Main.Verbal
 	{
 		static protected Dictionary <int, Analogy> analogies;
 		static protected ArrayListIndicesRandom analogies_indices;
-		static protected int analogies_index = 0;
 
 		string samples, sample;
 
@@ -46,16 +45,6 @@ namespace gbrainy.Core.Main.Verbal
 			get { return Catalog.GetString ("Pair of words compare");}
 		}
 
-		public override ArrayListIndicesRandom Indices {
-			get { return analogies_indices; }
-			set { analogies_indices = value; }
-		}
-
-		public override int CurrentIndex {
-			get { return analogies_index; }
-			set { analogies_index = value; }
-		}
-
 		public override Dictionary <int, Analogy> List {
 			get { return analogies; }
 		}
diff --git a/src/Core/Main/Verbal/AnalogiesPairOfWordsOptions.cs b/src/Core/Main/Verbal/AnalogiesPairOfWordsOptions.cs
index 2d27207..5210fa2 100644
--- a/src/Core/Main/Verbal/AnalogiesPairOfWordsOptions.cs
+++ b/src/Core/Main/Verbal/AnalogiesPairOfWordsOptions.cs
@@ -31,8 +31,6 @@ namespace gbrainy.Core.Main.Verbal
 	public class AnalogiesPairOfWordsOptions : Analogies
 	{
 		static protected Dictionary <int, Analogy> analogies;
-		static protected ArrayListIndicesRandom analogies_indices;
-		static protected int analogies_index = 0;
 
 		public AnalogiesPairOfWordsOptions ()
 		{
@@ -44,16 +42,6 @@ namespace gbrainy.Core.Main.Verbal
 			get { return Catalog.GetString ("Pair of words");}
 		}
 
-		public override ArrayListIndicesRandom Indices {
-			get { return analogies_indices; }
-			set { analogies_indices = value; }
-		}
-
-		public override int CurrentIndex {
-			get { return analogies_index; }
-			set { analogies_index = value; }
-		}
-
 		public override Dictionary <int, Analogy> List {
 			get { return analogies; }
 		}
diff --git a/src/Core/Main/Verbal/AnalogiesQuestionAnswer.cs b/src/Core/Main/Verbal/AnalogiesQuestionAnswer.cs
index 963cf36..cfcbf05 100644
--- a/src/Core/Main/Verbal/AnalogiesQuestionAnswer.cs
+++ b/src/Core/Main/Verbal/AnalogiesQuestionAnswer.cs
@@ -29,8 +29,6 @@ namespace gbrainy.Core.Main.Verbal
 	public class AnalogiesQuestionAnswer : Analogies
 	{
 		static protected Dictionary <int, Analogy> analogies;
-		static protected ArrayListIndicesRandom analogies_indices;
-		static protected int analogies_index = 0;
 
 		public AnalogiesQuestionAnswer ()
 		{
@@ -42,16 +40,6 @@ namespace gbrainy.Core.Main.Verbal
 			get { return Catalog.GetString ("Question and answer");}
 		}
 
-		public override ArrayListIndicesRandom Indices {
-			get { return analogies_indices; }
-			set { analogies_indices = value; }
-		}
-
-		public override int CurrentIndex {
-			get { return analogies_index; }
-			set { analogies_index = value; }
-		}
-
 		public override Dictionary <int, Analogy> List {
 			get { return analogies; }
 		}
diff --git a/src/Core/Main/Xml/GameXml.cs b/src/Core/Main/Xml/GameXml.cs
new file mode 100644
index 0000000..af5f9e5
--- /dev/null
+++ b/src/Core/Main/Xml/GameXml.cs
@@ -0,0 +1,145 @@
+/*
+ * 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.ComponentModel;
+using System.Collections.Generic;
+using System.IO;
+using Mono.Unix;
+
+namespace gbrainy.Core.Main
+{
+	public class GameXml : Game
+	{
+		// Every GameXml instance is capable of locating any XML defined game 
+		// This struct translates from a Variant that is global to all games
+		// to a specific game + variant
+		public struct DefinitionLocator
+		{
+			public int Game { get; set; }
+			public int Variant { get; set; }
+
+			public DefinitionLocator (int game, int variant)
+			{
+				Game = game;
+				Variant = variant;
+			}
+		};
+
+		// Shared with all instances
+		static List <GameXmlDefinition> games;
+		static List <DefinitionLocator> locators;
+
+		DefinitionLocator current;
+		GameXmlDefinition game;
+
+		static public List <GameXmlDefinition> Definitions {
+			set {
+				games = value;
+				locators = new List <DefinitionLocator> ();
+			}
+		}
+
+		public override GameTypes Type {
+			get { return game.Type;}
+		}
+
+		public override string Name {
+			get { return game.Name; }
+		}
+
+		public override string Question {
+			get {
+				if (game.Variants.Count > 0 && game.Variants[current.Variant].Question != null)
+					return game.Variants[current.Variant].Question;
+				else
+					return game.Question;
+			}
+		}
+
+		public override string Rationale {
+			get {
+				if (game.Variants.Count > 0 && game.Variants[current.Variant].Rationale != null)
+					return game.Variants[current.Variant].Rationale;
+				else
+					return game.Rationale;
+			}
+		}
+
+		public override string Tip {
+			get {
+				if (game.Variants.Count > 0 && game.Variants[current.Variant].Tip != null)
+					return game.Variants[current.Variant].Tip;
+				else
+					return game.Tip;
+			}
+		}
+
+		public override void Initialize ()
+		{
+			if (game.Variants.Count > 0 && game.Variants[current.Variant].Answer != null)
+				right_answer = game.Variants[current.Variant].Answer;
+			else
+				right_answer = game.Answer;
+		}
+
+		public override int Variants {
+			get {
+				if (locators.Count == 0)
+					BuildLocationList ();
+
+				return locators.Count;
+			}
+		}
+
+		public override int Variant {
+			set {
+				base.Variant = value;
+
+				DefinitionLocator locator;
+
+				locator = locators [Variant];
+				current.Game = locator.Game;
+				current.Variant = locator.Variant;
+				game = games [locator.Game];
+			}
+		}
+
+		public override void Draw (CairoContextEx gr, int area_width, int area_height, bool rtl)
+		{
+			base.Draw (gr, area_width, area_height, rtl);
+
+			if (String.IsNullOrEmpty (game.Image.Filename) == false)
+				gr.DrawImageFromFile (Path.Combine (Defines.DATA_DIR, game.Image.Filename),
+					game.Image.X, game.Image.Y, game.Image.Width, game.Image.Height);
+		}
+
+		void BuildLocationList ()
+		{
+			locators.Clear ();
+
+			for (int game = 0; game < games.Count; game++)
+			{
+				locators.Add (new DefinitionLocator (game, 0));
+				for (int variant = 0; variant < games[game].Variants.Count; variant++)
+					locators.Add (new DefinitionLocator (game, variant));
+			}
+		}
+	}
+}
diff --git a/src/Core/Main/Xml/GameXmlDefinition.cs b/src/Core/Main/Xml/GameXmlDefinition.cs
new file mode 100644
index 0000000..49339de
--- /dev/null
+++ b/src/Core/Main/Xml/GameXmlDefinition.cs
@@ -0,0 +1,96 @@
+/*
+ * 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.Text;
+using System.Collections.Generic;
+
+namespace gbrainy.Core.Main
+{
+	public class GameXmlDefinitionVariant
+	{
+		public struct SVGImage
+		{
+			public string Filename { get; set; }
+			public double X { get; set; }
+			public double Y { get; set; }
+			public double Width { get; set; }
+			public double Height { get; set; }
+		};
+
+		public string Question { get; set; }
+		public string Tip { get; set; }
+		public string Rationale { get; set; }
+		public string Answer { get; set; }
+
+		public SVGImage Image;
+
+		public override string ToString ()
+		{
+			StringBuilder str = new StringBuilder ();
+
+			str.AppendLine ("Question: " + Question);
+			str.AppendLine ("Tip: " + Tip);
+			str.AppendLine ("Rationale: " + Rationale);
+			str.AppendLine ("Answer: " + Answer);
+
+			return str.ToString ();
+		}
+	}
+
+	// Container for a game defined in an Xml file
+	public class GameXmlDefinition : GameXmlDefinitionVariant
+	{
+		public string Name { get; set; }
+		public Game.Difficulty Difficulty { get; set; }
+		public GameTypes Type { get; set; }
+
+		public List <GameXmlDefinitionVariant> Variants { get; set; }
+
+		public GameXmlDefinition ()
+		{
+			Difficulty = Game.Difficulty.Medium;
+			Type = GameTypes.LogicPuzzle; // TODO: temporary, should be mandatory in games.xml
+			Variants = new List <GameXmlDefinitionVariant> ();
+		}
+
+		public void NewVariant ()
+		{
+			Variants.Add (new GameXmlDefinitionVariant ());
+		}
+
+		public override string ToString ()
+		{
+			StringBuilder str = new StringBuilder ();
+
+			str.AppendLine ("Name: " + Name);
+			str.AppendLine ("Difficulty: " + Difficulty);
+			str.AppendLine (base.ToString ());
+
+			foreach (GameXmlDefinitionVariant variant in Variants)
+			{
+				str.AppendLine ("---");
+				str.AppendLine (variant.ToString ());
+				str.AppendLine ("---");
+			}
+
+			return str.ToString ();
+		}
+	}
+}
diff --git a/src/Core/Main/Xml/GameXmlFactory.cs b/src/Core/Main/Xml/GameXmlFactory.cs
new file mode 100644
index 0000000..23a697b
--- /dev/null
+++ b/src/Core/Main/Xml/GameXmlFactory.cs
@@ -0,0 +1,209 @@
+/*
+ * 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.Xml;
+using System.IO;
+using System.Collections.Generic;
+
+using Mono.Unix;
+
+namespace gbrainy.Core.Main
+{
+	static public class GamesXmlFactory
+	{
+		static List <GameXmlDefinition> games;
+		static bool read = false;
+
+		static GamesXmlFactory ()
+		{
+			games = new List <GameXmlDefinition> ();
+		}
+
+		static public List <GameXmlDefinition> Definitions {
+			get {
+				Read ();
+				return games;
+			}
+		}
+
+		static public void Read ()
+		{
+			Read (Path.Combine (Defines.DATA_DIR, "games.xml"));
+		}
+
+		static void Read (string file)
+		{
+			GameXmlDefinition game;
+			string name, str;
+			List <string> answers;
+			bool processing_variant = false;
+			int variant = 0;
+
+			if (read == true)
+				return;
+
+			try
+			{
+				StreamReader myStream = new StreamReader (file);
+				XmlTextReader reader = new XmlTextReader (myStream);
+				game = null;
+
+				while (reader.Read ())
+				{
+					name = reader.Name.ToLower ();
+					switch (name) {
+					case "game":
+						if (reader.NodeType == XmlNodeType.Element) {
+							game = new GameXmlDefinition ();
+						} else if (reader.NodeType == XmlNodeType.EndElement) {
+							games.Add (game);
+						}
+						break;
+					case "_name":
+						if (reader.NodeType != XmlNodeType.Element)
+							break;
+
+						game.Name = reader.ReadElementString ();
+						break;
+					case "_difficulty":
+						if (reader.NodeType != XmlNodeType.Element)
+							break;
+
+						str = reader.ReadElementString ();
+						str.Trim ();
+
+						switch (str) {
+						case "Easy":
+							game.Difficulty = Game.Difficulty.Easy;
+							break;
+						case "Medium":
+							game.Difficulty = Game.Difficulty.Medium;
+							break;
+						case "Master":
+							game.Difficulty = Game.Difficulty.Master;
+							break;
+						case "All":
+							game.Difficulty = Game.Difficulty.All;
+							break;
+						default:
+							Console.WriteLine ("GameXmlFactory. Unknown difficulty level: {0}", str);
+							break;
+						}
+
+						break;
+					case "svg":
+						if (reader.NodeType != XmlNodeType.Element)
+							break;
+
+						game.Image.Filename = reader.GetAttribute ("file");
+
+						str = reader.GetAttribute ("X");
+						if (String.IsNullOrEmpty (str) == false)
+							game.Image.X = Double.Parse (str);
+						else
+							game.Image.X = 0.1;
+
+						str = reader.GetAttribute ("Y");
+						if (String.IsNullOrEmpty (str) == false)
+							game.Image.Y = Double.Parse (str);
+						else
+							game.Image.Y = 0.1;
+
+						str = reader.GetAttribute ("width");
+						if (String.IsNullOrEmpty (str) == false)
+							game.Image.Width = Double.Parse (str);
+						else
+							game.Image.Width = 0.8;
+
+						str = reader.GetAttribute ("height");
+						if (String.IsNullOrEmpty (str) == false)
+							game.Image.Height = Double.Parse (str);
+						else
+							game.Image.Height = 0.8;
+
+						break;
+					case "_question":
+						if (reader.NodeType != XmlNodeType.Element)
+							break;
+
+						if (processing_variant)
+							game.Variants[variant].Question = reader.ReadElementString ();
+						else
+							game.Question = reader.ReadElementString ();
+
+						break;
+					case "_rationale":
+						if (reader.NodeType != XmlNodeType.Element)
+							break;
+
+						if (processing_variant)
+							game.Variants[variant].Rationale = reader.ReadElementString ();
+						else
+							game.Rationale = reader.ReadElementString ();
+
+						break;
+					case "_answer":
+						if (reader.NodeType != XmlNodeType.Element)
+							break;
+
+						if (processing_variant)
+							game.Variants[variant].Answer = reader.ReadElementString ();
+						else
+							game.Answer = reader.ReadElementString ();
+
+						break;
+					case "_tip":
+						if (reader.NodeType != XmlNodeType.Element)
+							break;
+
+						if (processing_variant)
+							game.Variants[variant].Tip = reader.ReadElementString ();
+						else
+							game.Tip = reader.ReadElementString ();
+
+						break;
+					case "variant":
+						if (reader.NodeType == XmlNodeType.Element) {
+							game.NewVariant ();
+							variant = game.Variants.Count - 1;
+							processing_variant = true;
+						} else if (reader.NodeType == XmlNodeType.EndElement) {
+							processing_variant = false;
+						}
+						break;
+					default:
+						break;
+					}
+				}
+
+				reader.Close ();
+				read = true;
+
+				GameXml.Definitions = games;
+			}
+
+			catch (Exception e)
+			{
+				read = true;
+				Console.WriteLine ("GameXmlFactory. Error loading: {0}", e.Message);
+			}
+		}
+	}
+}
diff --git a/src/Core/Makefile.am b/src/Core/Makefile.am
index 01495b3..4e20481 100644
--- a/src/Core/Makefile.am
+++ b/src/Core/Makefile.am
@@ -18,6 +18,9 @@ CSDISTFILES =  \
 		$(srcdir)/Main/GameSessionHistoryExtended.cs \
 		$(srcdir)/Main/GameTypes.cs		\
 		$(srcdir)/Main/GameTips.cs		\
+		$(srcdir)/Main/Xml/GameXml.cs		\
+		$(srcdir)/Main/Xml/GameXmlFactory.cs	\
+		$(srcdir)/Main/Xml/GameXmlDefinition.cs	\
 		$(srcdir)/Main/Memory.cs		\
 		$(srcdir)/Main/PlayerHistory.cs		\
 		$(srcdir)/Main/PlayerPersonalRecord.cs	\
diff --git a/tests/Core/GameManagerTest.cs b/tests/Core/GameManagerTest.cs
index 1f7d246..48900a2 100644
--- a/tests/Core/GameManagerTest.cs
+++ b/tests/Core/GameManagerTest.cs
@@ -40,11 +40,12 @@ namespace gbrainyTest
 			int notip = 0;
 			GameManager manager = new GameManager ();
 
-			Type[] games = manager.CustomGames;
+			GameManager.GameLocator [] games = manager.AvailableGames;
 
-			foreach (Type type in games)
+			foreach (GameManager.GameLocator locator in games)
 			{
-				Game game = (Game) Activator.CreateInstance (type, true);
+				Game game = (Game) Activator.CreateInstance (locator.TypeOf, true);
+				game.Variant = locator.Variant;
 				if (game.TipString == String.Empty)
 				{
 					notip++;
@@ -59,19 +60,23 @@ namespace gbrainyTest
 		{
 			Dictionary <string, bool> dictionary;
 			GameManager manager = new GameManager ();
-			Type[] games = manager.CustomGames;
-
+			GameManager.GameLocator [] games = manager.AvailableGames;
 			dictionary = new Dictionary <string, bool> (games.Length);
 
-			foreach (Type type in games)
+			foreach (GameManager.GameLocator locator in games)
 			{
-				Game game = (Game) Activator.CreateInstance (type, true);
+				if (locator.IsGame == false)
+					continue;
 
+				Game game = (Game) Activator.CreateInstance (locator.TypeOf, true);
+				game.Variant = locator.Variant;
+			
 				Assert.AreEqual (false, dictionary.ContainsKey (game.Name),
 					String.Format ("Game name {0} is duplicated", game.Name));
 
 				dictionary.Add (game.Name, true);
 			}
+
 		}
 	}
 }



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