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.Text.RegularExpressions; 20 using System.Text; 21 22 using gbrainy.Core.Services; 23 24 namespace gbrainy.Core.Main 25 { 26 public class GameAnswer 27 { 28 static char separator = '|'; 29 const int MAX_POSSIBLE_ANSWER = 8; 30 string correct; 31 ITranslations translations; 32 GameAnswer()33 public GameAnswer () 34 { 35 CheckAttributes = GameAnswerCheckAttributes.Trim | GameAnswerCheckAttributes.IgnoreCase; 36 CheckExpression = ".+"; 37 } 38 39 // This is a property because the object can be constructed and use it 40 // Only text related functions needed. Perhaps, a possible to do extract class pattern 41 public ITranslations Translations { 42 set { translations = value; } 43 get { return translations; } 44 } 45 46 public char Separator { 47 get { return separator; } 48 } 49 50 // Correct answer as shown to the user. Usually equals to Correct, but can be different 51 // when the answer contains multiple options (e.g. 1 | 2 shown as 1 and 2). 52 public string CorrectShow { get; set; } 53 54 // This is the correct answer used for validating the answer (a | b) 55 public string Correct { 56 get { return correct;} 57 set { 58 correct = value; 59 if (CorrectShow == null) // Set default answer to show 60 CorrectShow = correct; 61 } 62 } 63 64 public string CheckExpression { get; set; } 65 public bool Draw { get; set; } 66 67 public GameAnswerCheckAttributes CheckAttributes { get; set; } 68 GetMultiOptionsExpression()69 public string GetMultiOptionsExpression () 70 { 71 StringBuilder str = new StringBuilder (); 72 str.Append ("["); 73 for (int i = 0; i < MAX_POSSIBLE_ANSWER; i++) 74 str.Append (GetMultiOptionInternal (i)); 75 76 str.Append ("]"); 77 return str.ToString (); 78 } 79 80 // Index of the option (A, B) and answer (dog, cat) SetMultiOptionAnswer(int multioption, string answer)81 public void SetMultiOptionAnswer (int multioption, string answer) 82 { 83 if (String.IsNullOrEmpty (answer) == true) 84 throw new InvalidOperationException ("Both options should be defined"); 85 86 string option = GetMultiOption (multioption); 87 88 Correct = option + Separator + answer; 89 CorrectShow = option; 90 } 91 GetMultiOption(int answer)92 public string GetMultiOption (int answer) 93 { 94 bool multioption; 95 96 multioption = (CheckAttributes & GameAnswerCheckAttributes.MultiOption) == GameAnswerCheckAttributes.MultiOption; 97 98 if (multioption == false) 99 throw new InvalidOperationException ("Cannot call Multioption API if the game does not have the multioption attribute"); 100 101 return GetMultiOptionInternal (answer); 102 } 103 GetMultiOptionInternal(int answer)104 string GetMultiOptionInternal (int answer) 105 { 106 switch (answer) { 107 // Translators Note 108 // The following series of answers may need to be adapted 109 // in cultures with alphabets different to the Latin one. 110 // The idea is to enumerate a sequence of possible answers 111 // For languages represented with the Latin alphabet use 112 // the same than English 113 case 0: // First possible answer for a series (e.g.: Figure A) 114 return Translations.GetString ("A"); 115 case 1: // Second possible answer for a series 116 return Translations.GetString ("B"); 117 case 2: // Third possible answer for a series 118 return Translations.GetString ("C"); 119 case 3: // Fourth possible answer for a series 120 return Translations.GetString ("D"); 121 case 4: // Fifth possible answer for a series 122 return Translations.GetString ("E"); 123 case 5: // Sixth possible answer for a series 124 return Translations.GetString ("F"); 125 case 6: // Seventh possible answer for a series 126 return Translations.GetString ("G"); 127 case 7: // Eighth possible answer for a series 128 return Translations.GetString ("H"); 129 // When adding new items update MAX_POSSIBLE_ANSWER accordingly 130 default: 131 throw new ArgumentOutOfRangeException ("Do not have an option for this answer"); 132 } 133 } 134 135 // A string of for format "A, B or C GetMultiOptionsPossibleAnswers(int num_answers)136 public string GetMultiOptionsPossibleAnswers (int num_answers) 137 { 138 switch (num_answers) { 139 case 0: 140 case 1: 141 throw new InvalidOperationException ("You need more than 1 answer to select from"); 142 case 2: 143 // Translators. This is the list of valid answers, like A or B. 144 return String.Format (Translations.GetString ("{0} or {1}"), 145 GetMultiOption (0), GetMultiOption (1)); 146 case 3: 147 // Translators. This is the list of valid answers, like A, B or C. 148 return String.Format (Translations.GetString ("{0}, {1} or {2}"), 149 GetMultiOption (0), GetMultiOption (1), GetMultiOption (2)); 150 case 4: 151 // Translators. This is the list of valid answers, like A, B, C or D. 152 return String.Format (Translations.GetString ("{0}, {1}, {2} or {3}"), 153 GetMultiOption (0), GetMultiOption (1), GetMultiOption (2), GetMultiOption (3)); 154 default: 155 throw new InvalidOperationException ("Number of multiple options not supported"); 156 } 157 } 158 GetFigureName(int answer)159 public string GetFigureName (int answer) 160 { 161 return String.Format (Translations.GetString ("Figure {0}"), GetMultiOptionInternal (answer)); 162 } 163 CheckAnswer(string answer)164 public bool CheckAnswer (string answer) 165 { 166 Regex regex; 167 Match match; 168 bool ignore_case, ignore_spaces; 169 int matched_all_in_order = 0; 170 171 if (String.IsNullOrEmpty (answer)) 172 return false; 173 174 ignore_case = (CheckAttributes & GameAnswerCheckAttributes.IgnoreCase) == GameAnswerCheckAttributes.IgnoreCase; 175 ignore_spaces = (CheckAttributes & GameAnswerCheckAttributes.IgnoreSpaces) == GameAnswerCheckAttributes.IgnoreSpaces; 176 177 if (ignore_case == true) // This necessary to make pattern selection (e.g. [a-z]) case insensitive 178 regex = new Regex (CheckExpression, RegexOptions.IgnoreCase); 179 else 180 regex = new Regex (CheckExpression); 181 182 string [] right_answers = Correct.Split (Separator); 183 184 for (int i = 0; i < right_answers.Length; i++) 185 { 186 right_answers [i] = right_answers[i].Trim (); 187 188 if (ignore_spaces) 189 right_answers [i] = RemoveWhiteSpace (right_answers [i]); 190 } 191 192 if ((CheckAttributes & GameAnswerCheckAttributes.Trim) == GameAnswerCheckAttributes.Trim) 193 answer = answer.Trim (); 194 195 if (ignore_spaces) 196 answer = RemoveWhiteSpace (answer); 197 198 // All strings from the list of expected answers (two numbers: 22 | 44) must present in the answer 199 if ((CheckAttributes & GameAnswerCheckAttributes.MatchAll) == GameAnswerCheckAttributes.MatchAll || 200 (CheckAttributes & GameAnswerCheckAttributes.MatchAllInOrder) == GameAnswerCheckAttributes.MatchAllInOrder) 201 { 202 int pos = 0; 203 match = regex.Match (answer); 204 while (String.IsNullOrEmpty (match.Value) == false) 205 { 206 bool matched = false; 207 if ((CheckAttributes & GameAnswerCheckAttributes.MatchAll) == GameAnswerCheckAttributes.MatchAll) 208 { 209 for (int i = 0; i < right_answers.Length; i++) 210 { 211 if (String.Compare (match.Value, right_answers[i], ignore_case) == 0) 212 { 213 right_answers[i] = null; 214 matched = true; 215 break; 216 } 217 } 218 if (matched == false) 219 return false; 220 } 221 else //MatchAllInOrder 222 { 223 if (String.Compare (match.Value, right_answers[pos++], ignore_case) == 0) 224 matched_all_in_order++; 225 } 226 match = match.NextMatch (); 227 } 228 229 if ((CheckAttributes & GameAnswerCheckAttributes.MatchAllInOrder) == GameAnswerCheckAttributes.MatchAllInOrder && 230 matched_all_in_order == right_answers.Length) 231 return true; 232 233 // Have all the expected answers been matched? 234 for (int i = 0; i < right_answers.Length; i++) 235 { 236 if (right_answers[i] != null) 237 return false; 238 } 239 240 return true; 241 } 242 else // Any string from the list of possible answers (answer1 | answer2) present in the answer will do it 243 { 244 foreach (string s in right_answers) 245 { 246 match = regex.Match (answer); 247 if (String.Compare (match.Value, s, ignore_case) == 0) 248 return true; 249 } 250 } 251 return false; 252 } 253 RemoveWhiteSpace(string source)254 static string RemoveWhiteSpace (string source) 255 { 256 StringBuilder str = new StringBuilder (); 257 for (int n = 0; n < source.Length; n++) 258 { 259 if (char.IsWhiteSpace (source [n]) == false) 260 str.Append (source[n]); 261 } 262 return str.ToString (); 263 } 264 } 265 } 266