1 /*
2  * Copyright (C) 2007-2011 Jordi Mas i Hernàndez <jmas@softcatala.org>
3  *
4  * This program is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU General Public License as
6  * published by the Free Software Foundation; either version 2 of the
7  * License, or (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public
15  * License along with this program; if not, see <http://www.gnu.org/licenses/>.
16  */
17 
18 using System;
19 using System.Linq;
20 using System.Collections.Generic;
21 
22 namespace gbrainy.Core.Main
23 {
24 	// Manages a list of games to played within a session based on difficulty, game types and other parameters
25 	public class GameSessionPlayList
26 	{
27 		IEnumerator <int> enumerator;
28 		List <int> play_list; // Play list for the Selected difficulty, game types
29 		GameDifficulty difficulty;
30 		bool color_blind;
31 		bool dirty;
32 		GameSession.Types game_type;
33 		GameManager manager;
34 
GameSessionPlayList(GameManager manager)35 		public GameSessionPlayList (GameManager manager)
36 		{
37 			this.manager = manager;
38 			play_list = new List <int> ();
39 			game_type = GameSession.Types.AllGames;
40 			difficulty = GameDifficulty.Medium;
41 			RandomOrder = true;
42 			dirty = true;
43 		}
44 
45 		public bool ColorBlind {
46 			get { return color_blind;}
47 			set {
48 				if (color_blind == value)
49 					return;
50 
51 				color_blind = value;
52 				dirty = true;
53 			}
54 		}
55 
56 		public GameDifficulty Difficulty {
57 			set {
58 				if (difficulty == value)
59 					return;
60 
61 				difficulty = value;
62 				dirty = true;
63 			}
64 			get { return difficulty; }
65 		}
66 
67 		public GameManager GameManager {
68 			get { return manager;}
69 			set {
70 				if (manager == value)
71 					return;
72 
73 				manager = value;
74 				dirty = true;
75 			}
76 		}
77 
78 		public GameSession.Types GameType {
79 			get {return game_type; }
80 			set {
81 				if (game_type == value)
82 					return;
83 
84 				game_type = value;
85 				dirty = true;
86 			}
87 		}
88 
89 		// Indicates if the PlayList for CustomGames is delivered in RandomOrder
90  		public bool RandomOrder { get; set; }
91 
92 		// Establish the PlayList (the indices of the array to available games)
93 		public int [] PlayList {
94 			get { return play_list.ToArray ();}
95 			set {
96 				play_list = new List <int> (value);
97 				dirty = false;
98 				UpdateEnumerator ();
99 			}
100 		}
101 
BuildListIfIsDirty()102 		void BuildListIfIsDirty ()
103 		{
104 			if (dirty == false)
105 				return;
106 
107 			if ((game_type & GameSession.Types.Custom) != GameSession.Types.Custom)
108 				BuildPlayList (manager.AvailableGames);
109 
110 			dirty = false;
111 		}
112 
113 		// Taking a GameLocator list builds the play_list
BuildPlayList(GameLocator [] all_games)114 		void BuildPlayList (GameLocator [] all_games)
115 		{
116 			if ((game_type & GameSession.Types.Custom) == GameSession.Types.Custom)
117 				throw new InvalidOperationException ();
118 
119 			ArrayListIndicesRandom indices = new ArrayListIndicesRandom (all_games.Length);
120 			indices.Initialize ();
121 			play_list.Clear ();
122 
123 			// Decide which game types are part of the list
124 			bool logic, memory, calculation, verbal;
125 			if ((game_type & GameSession.Types.AllGames) == GameSession.Types.AllGames)
126 			{
127 				logic = memory = calculation = verbal = true;
128 			}
129 			else
130 			{
131 				logic = (game_type & GameSession.Types.LogicPuzzles) == GameSession.Types.LogicPuzzles;
132 				calculation = (game_type & GameSession.Types.Calculation) == GameSession.Types.Calculation;
133 				memory = (game_type & GameSession.Types.Memory) == GameSession.Types.Memory;
134 				verbal = (game_type & GameSession.Types.VerbalAnalogies) == GameSession.Types.VerbalAnalogies;
135 			}
136 
137 			// Create item arrays for games types
138 			List <int> logic_indices = new List <int> ();
139 			List <int> calculation_indices = new List <int> ();
140 			List <int> memory_indices = new List <int> ();
141 			List <int> verbal_indices = new List <int> ();
142 
143 			if (logic)
144 				logic_indices.AddRange (indices.Where (a => all_games[a].GameType == GameTypes.LogicPuzzle));
145 
146 			if (memory)
147 				memory_indices.AddRange (indices.Where (a => all_games[a].GameType == GameTypes.Memory));
148 
149 			if (calculation)
150 				calculation_indices.AddRange (indices.Where (a => all_games[a].GameType == GameTypes.Calculation));
151 
152 			if (verbal)
153 				verbal_indices.AddRange (indices.Where (a => all_games[a].GameType == GameTypes.VerbalAnalogy));
154 
155 			CreateListWithDistributedGameTypes (logic_indices, calculation_indices, memory_indices, verbal_indices);
156 			enumerator = play_list.GetEnumerator ();
157 		}
158 
CreateListWithDistributedGameTypes(List <int> logic_indices, List <int> calculation_indices, List <int> memory_indices, List <int> verbal_indices)159 		void CreateListWithDistributedGameTypes (List <int> logic_indices, List <int> calculation_indices, List <int> memory_indices,
160 			List <int> verbal_indices)
161 		{
162 			int total = logic_indices.Count + memory_indices.Count + calculation_indices.Count + verbal_indices.Count;
163 			int pos_logic, pos_memory, pos_calculation, pos_verbal;
164 			Random random = new Random ();
165 
166 			pos_logic = pos_memory = pos_calculation = pos_verbal = 0;
167 
168 			while (play_list.Count < total)
169 			{
170 				switch (random.Next (3)) {
171 				case 0:
172 					if (pos_calculation < calculation_indices.Count) play_list.Add (calculation_indices[pos_calculation++]);
173 					if (pos_logic < logic_indices.Count) play_list.Add (logic_indices[pos_logic++]);
174 					if (pos_memory < memory_indices.Count) play_list.Add (memory_indices[pos_memory++]);
175 					if (pos_verbal < verbal_indices.Count) play_list.Add (verbal_indices[pos_verbal++]);
176 					break;
177 				case 1:
178 					if (pos_memory < memory_indices.Count) play_list.Add (memory_indices[pos_memory++]);
179 					if (pos_calculation < calculation_indices.Count) play_list.Add (calculation_indices[pos_calculation++]);
180 					if (pos_verbal < verbal_indices.Count) play_list.Add (verbal_indices[pos_verbal++]);
181 					if (pos_logic < logic_indices.Count) play_list.Add (logic_indices[pos_logic++]);
182 					break;
183 				case 2:
184 					if (pos_calculation < calculation_indices.Count) play_list.Add (calculation_indices[pos_calculation++]);
185 					if (pos_verbal < verbal_indices.Count) play_list.Add (verbal_indices[pos_verbal++]);
186 					if (pos_memory < memory_indices.Count) play_list.Add (memory_indices[pos_memory++]);
187 					if (pos_logic < logic_indices.Count) play_list.Add (logic_indices[pos_logic++]);
188 					break;
189 				}
190 			}
191 		}
192 
UpdateEnumerator()193 		void UpdateEnumerator ()
194 		{
195 			if (RandomOrder == true)
196 			{
197 				ArrayListIndicesRandom random = new ArrayListIndicesRandom (play_list.Count);
198 				random.RandomizeFromArray (play_list);
199 				enumerator = random.GetEnumerator ();
200 			}
201 			else
202 				enumerator = play_list.GetEnumerator ();
203 		}
204 
GetPuzzle()205 		public Game GetPuzzle ()
206 		{
207 			Game puzzle, first = null;
208 
209 			BuildListIfIsDirty ();
210 
211 			while (true) {
212 
213 				if (enumerator.MoveNext () == false) { // All the games have been played, restart again
214 					enumerator = play_list.GetEnumerator ();
215 					enumerator.MoveNext ();
216 				}
217 
218 				puzzle = (Game) Activator.CreateInstance ((Type) manager.AvailableGames [enumerator.Current].TypeOf, true);
219 				puzzle.Variant = manager.AvailableGames [enumerator.Current].Variant;
220 
221 				if (first != null && first.GetType () == puzzle.GetType ())
222 					break;
223 
224 				if (puzzle.IsPlayable == false)
225 					continue;
226 
227 				if (ColorBlind == true && puzzle.UsesColors == true)
228 					continue;
229 
230 				if (first == null)
231 					first = puzzle;
232 
233 				if ((puzzle.Difficulty & difficulty) == difficulty)
234 					break;
235 			}
236 
237 			puzzle.CurrentDifficulty = Difficulty;
238 			return puzzle;
239 		}
240 	}
241 }
242