1 // Brain Party
2 // Copyright (C) 2010 Paul Hudson (http://www.tuxradar.com/brainparty)
3 
4 // Brain Party is free software; you can redistribute it and/or
5 // modify it under the terms of the GNU General Public License
6 // as published by the Free Software Foundation; either version 3
7 // of the 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
12 // GNU General Public License for more details.
13 
14 // You should have received a copy of the GNU General Public License
15 // along with this program; if not, write to the Free Software
16 // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
17 
18 #include "scrambled.h"
19 #include "Minigame.h"
20 
BPMiniGame_Scrambled(BPGame * game)21 BPMiniGame_Scrambled::BPMiniGame_Scrambled(BPGame* game) : BPMiniGame(game) {
22 	AnswersYOffset = 9;
23 
24 	CurrentLevel = Score = RandSeed = SelectedItem = TimeStarted = 0;
25 
26 	LastStateChange = -1;
27 
28 	sfcBackground = TheGame->LoadBitmap("scrambled", 320, 416);
29 
30 	sfcAnswer1 = sfcAnswer2 = sfcAnswer3 = sfcAnswer4 = NULL;
31 	TheQuestion = NULL;
32 
33 	GameTitle = "Scrambled!";
34 	GameHelp = "I'm going to scramble words and numbers by adding, removing and switching letters. You need to figure out what I've done, then tap the correct answers to my questions.";
35 	GameHelp2 = "Have letters been added, swapped or deleted? Has the whole word been reversed? Figure out what I changed to solve the problems!";
36 
37 	MiniGameType = PUZZLE;
38 
39 	// note: each of these words have no duplicate letters, eg "DEERS" is not allowed
40 	Words.Add(string("ACORN"));
41 	Words.Add(string("AFTER"));
42 	Words.Add(string("BEACH"));
43 	Words.Add(string("BEAST"));
44 	Words.Add(string("BLACK"));
45 	Words.Add(string("BLADE"));
46 	Words.Add(string("BLAST"));
47 	Words.Add(string("BLAZE"));
48 	Words.Add(string("BLOCK"));
49 	Words.Add(string("BRAIN"));
50 	Words.Add(string("BRAKE"));
51 	Words.Add(string("BRICK"));
52 	Words.Add(string("BROWN"));
53 	Words.Add(string("CARDS"));
54 	Words.Add(string("CHAMP"));
55 	Words.Add(string("CHILD"));
56 	Words.Add(string("CLAMP"));
57 	Words.Add(string("CLEAR"));
58 	Words.Add(string("CLEFT"));
59 	Words.Add(string("CLOSE"));
60 	Words.Add(string("CODER"));
61 	Words.Add(string("COULD"));
62 	Words.Add(string("CRASH"));
63 	Words.Add(string("DAIRY"));
64 	Words.Add(string("DIRTY"));
65 	Words.Add(string("DOZEN"));
66 	Words.Add(string("DRINK"));
67 	Words.Add(string("ENJOY"));
68 	Words.Add(string("FAITH"));
69 	Words.Add(string("FIELD"));
70 	Words.Add(string("FINAL"));
71 	Words.Add(string("FLASH"));
72 	Words.Add(string("FLASK"));
73 	Words.Add(string("FLING"));
74 	Words.Add(string("FLIRT"));
75 	Words.Add(string("FRESH"));
76 	Words.Add(string("FUDGE"));
77 	Words.Add(string("GIANT"));
78 	Words.Add(string("GLINT"));
79 	Words.Add(string("GLITZ"));
80 	Words.Add(string("GRAPH"));
81 	Words.Add(string("GROWL"));
82 	Words.Add(string("GUARD"));
83 	Words.Add(string("GUIDE"));
84 	Words.Add(string("HEART"));
85 	Words.Add(string("HEAVY"));
86 	Words.Add(string("HORSE"));
87 	Words.Add(string("JUICY"));
88 	Words.Add(string("LEAST"));
89 	Words.Add(string("LEMON"));
90 	Words.Add(string("MAGIC"));
91 	Words.Add(string("MEANS"));
92 	Words.Add(string("MICRO"));
93 	Words.Add(string("MISTY"));
94 	Words.Add(string("MODEL"));
95 	Words.Add(string("MOUTH"));
96 	Words.Add(string("MOVIE"));
97 	Words.Add(string("OTHER"));
98 	Words.Add(string("PAIRS"));
99 	Words.Add(string("PARKS"));
100 	Words.Add(string("PARTY"));
101 	Words.Add(string("PHASE"));
102 	Words.Add(string("PHONE"));
103 	Words.Add(string("PLACE"));
104 	Words.Add(string("PLANK"));
105 	Words.Add(string("PLANT"));
106 	Words.Add(string("POWER"));
107 	Words.Add(string("PRICE"));
108 	Words.Add(string("PRINT"));
109 	Words.Add(string("QUICK"));
110 	Words.Add(string("QUIET"));
111 	Words.Add(string("RATED"));
112 	Words.Add(string("RIGHT"));
113 	Words.Add(string("RIVAL"));
114 	Words.Add(string("ROYAL"));
115 	Words.Add(string("RUSTY"));
116 	Words.Add(string("SCALE"));
117 	Words.Add(string("SCORE"));
118 	Words.Add(string("SHAKE"));
119 	Words.Add(string("SHAVE"));
120 	Words.Add(string("SNORE"));
121 	Words.Add(string("SPARK"));
122 	Words.Add(string("SPEAR"));
123 	Words.Add(string("SPEND"));
124 	Words.Add(string("STAND"));
125 	Words.Add(string("STALK"));
126 	Words.Add(string("STORE"));
127 	Words.Add(string("SUGAR"));
128 	Words.Add(string("SWAMP"));
129 	Words.Add(string("SWIFT"));
130 	Words.Add(string("SWING"));
131 	Words.Add(string("THING"));
132 	Words.Add(string("TRAMP"));
133 	Words.Add(string("UNDER"));
134 	Words.Add(string("WATER"));
135 	Words.Add(string("WHARF"));
136 	Words.Add(string("WHILE"));
137 	Words.Add(string("WHITE"));
138 	Words.Add(string("WHOLE"));
139 	Words.Add(string("WRITE"));
140 
141 	Numbers.Add(string("0"));
142 	Numbers.Add(string("1"));
143 	Numbers.Add(string("2"));
144 	Numbers.Add(string("3"));
145 	Numbers.Add(string("4"));
146 	Numbers.Add(string("5"));
147 	Numbers.Add(string("6"));
148 	Numbers.Add(string("7"));
149 	Numbers.Add(string("8"));
150 	Numbers.Add(string("9"));
151 
152 	Letters.Add(string("A"));
153 	Letters.Add(string("B"));
154 	Letters.Add(string("C"));
155 	Letters.Add(string("D"));
156 	Letters.Add(string("E"));
157 	Letters.Add(string("F"));
158 	Letters.Add(string("G"));
159 	Letters.Add(string("H"));
160 	Letters.Add(string("J"));
161 	Letters.Add(string("K"));
162 	Letters.Add(string("L"));
163 	Letters.Add(string("M"));
164 	Letters.Add(string("N"));
165 	Letters.Add(string("P"));
166 	Letters.Add(string("Q"));
167 	Letters.Add(string("R"));
168 	Letters.Add(string("S"));
169 	Letters.Add(string("T"));
170 	Letters.Add(string("U"));
171 	Letters.Add(string("V"));
172 	Letters.Add(string("W"));
173 	Letters.Add(string("X"));
174 	Letters.Add(string("Y"));
175 	Letters.Add(string("Z"));
176 
177 	Positions.Add(0);
178 	Positions.Add(1);
179 	Positions.Add(2);
180 	Positions.Add(3);
181 	Positions.Add(4);
182 
183 	for (int i = 0; i < 4; ++i) {
184 		BPMiniGame_Scrambled_Answer* answer = new BPMiniGame_Scrambled_Answer();
185 		Answers.Add(answer);
186 	}
187 
188 	BPPoint point = BPPoint(29, 164);
189 	AnswerPositions.Add(point);
190 
191 	point = BPPoint(29, 222);
192 	AnswerPositions.Add(point);
193 
194 	point = BPPoint(29, 280);
195 	AnswerPositions.Add(point);
196 
197 	point = BPPoint(29, 337);
198 	AnswerPositions.Add(point);
199 
200 	LevelUp();
201 }
202 
~BPMiniGame_Scrambled()203 BPMiniGame_Scrambled::~BPMiniGame_Scrambled() {
204 	SAFE_DELETE(sfcBackground);
205 	Answers.Clear();
206 }
207 
Start()208 void BPMiniGame_Scrambled::Start() {
209 	TimeStarted = TheGame->TickCount;
210 }
211 
GetWeight()212 int BPMiniGame_Scrambled::GetWeight() {
213 	float TimePassed = (TheGame->TickCount - TimeStarted) / 1000.0f;
214 	return MinMax((Score * 75) - round(TimePassed * 4));
215 }
216 
Render()217 void BPMiniGame_Scrambled::Render() {
218 	TheGame->DrawImage(sfcBackground, 0, 0);
219 
220 	TheGame->DrawString(TheQuestion, BLACK, 29, 29);
221 
222 	for (int i = 0; i < Answers.Count; ++i) {
223 		BPMiniGame_Scrambled_Answer* answer = Answers[i];
224 		TheGame->DrawString(answer->Text, BLACK, answer->Pos.X, answer->Pos.Y + AnswersYOffset);
225 	}
226 
227 	if (GameState == CORRECT) {
228 		RenderCorrect();
229 	} else if (GameState == WRONG) {
230 		RenderWrong();
231 	}
232 }
233 
FlipLetters(string Word)234 string BPMiniGame_Scrambled::FlipLetters(string Word) {
235 	string retval = Word;
236 	reverse(retval.begin(), retval.end());
237 	return retval;
238 }
239 
RemoveLetters(string Word,int Pos)240 string BPMiniGame_Scrambled::RemoveLetters(string Word, int Pos) {
241 	string retval = Word;
242 
243 	retval = retval.erase(Pos, 1);
244 	return retval;
245 }
246 
SwitchLetters(string Word,int Pos)247 string BPMiniGame_Scrambled::SwitchLetters(string Word, int Pos) {
248 	string retval = Word;
249 	const char letter = retval[Pos];
250 
251 	if (Pos == retval.size() - 1) {
252 		// this was the last letter; insert it at the front
253 		retval[4] = retval[0];
254 		retval[0] = letter;
255 	} else {
256 		retval[Pos] = retval[Pos + 1];
257 		retval[Pos + 1] = letter;
258 	}
259 
260 	return retval;
261 }
262 
AddLetters(string Word,string Letter,int Pos)263 string BPMiniGame_Scrambled::AddLetters(string Word, string Letter, int Pos) {
264 	string retval = Word;
265 	retval.insert(Pos, Letter);
266 
267 	return retval;
268 }
269 
GetUniqueNumber(string Word1,string Word2)270 string BPMiniGame_Scrambled::GetUniqueNumber(string Word1, string Word2) {
271 	for (int i = 0; i < Numbers.Count; ++i) {
272 		string Number = Numbers[i];
273 		if (Word1.find(Number) == string::npos && Word2.find(Number) == string::npos) return Number;
274 	}
275 
276 	return "D"; // no unique numbers are around; send back a letter
277 }
278 
GetUniqueLetter(string Word1,string Word2)279 string BPMiniGame_Scrambled::GetUniqueLetter(string Word1, string Word2) {
280 	while (true) {
281 		string Letter = Letters[TheGame->RandomRange(0, Letters.Count - 1)];
282 		if (Word1.find(Letter) == string::npos && Word2.find(Letter) == string::npos) return Letter;
283 	}
284 }
285 
Tick()286 void BPMiniGame_Scrambled::Tick() {
287 	if (LastStateChange != -1 && LastStateChange + 500 < TheGame->TickCount) {
288 		LevelUp();
289 	}
290 }
291 
OnMouseDown()292 void BPMiniGame_Scrambled::OnMouseDown() {
293 
294 }
295 
OnMouseMove()296 void BPMiniGame_Scrambled::OnMouseMove() {
297 
298 }
299 
OnMouseUp()300 void BPMiniGame_Scrambled::OnMouseUp() {
301 	switch (GameState) {
302 		case CORRECT:
303 			LevelUp();
304 			break;
305 
306 		case WRONG:
307 			LevelUp();
308 			break;
309 
310 		case WAITING:
311 			for (int i = 0; i < Answers.Count; ++i) {
312 				BPMiniGame_Scrambled_Answer* answer = Answers[i];
313 				if (TheGame->PointOverRect(TouchEvent.X, TouchEvent.Y, answer->Pos.X, answer->Pos.Y, 264.0f, 47.0f)) {
314 					if (answer->Correct) {
315 						++Score;
316 						GameState = CORRECT;
317 						LastStateChange = TheGame->TickCount;
318 						TheGame->PlaySound("correct");
319 					} else {
320 						--Score;
321 						GameState = WRONG;
322 						LastStateChange = TheGame->TickCount;
323 						TheGame->PlaySound("wrong");
324 					}
325 
326 					return;
327 				}
328 			}
329 
330 			break;
331 	}
332 }
333 
LevelUp()334 void BPMiniGame_Scrambled::LevelUp() {
335 	LastStateChange = -1;
336 	GameState = WAITING;
337 
338 	if (!MarathonMode && CurrentLevel >= 10) {
339 		Success();
340 		return;
341 	}
342 
343 	++CurrentLevel;
344 
345 	Numbers.Shuffle();
346 	Words.Shuffle();
347 	Positions.Shuffle();
348 	Answers.Shuffle();
349 
350 	bool NumbersOnly = false;
351 
352 	switch (TheGame->RandomRange(0, 3)) {
353 		case 0:
354 		case 1:
355 			// word to number comparison
356 			ExampleWord = Words[0];
357 			TestWord.clear();
358 
359 			TestWord.append(Numbers[0]);
360 			TestWord.append(Numbers[1]);
361 			TestWord.append(Numbers[2]);
362 			TestWord.append(Numbers[3]);
363 			TestWord.append(Numbers[4]);
364 			break;
365 
366 		case 2:
367 			// word to word comparison
368 			ExampleWord = Words[0];
369 			TestWord = Words[1];
370 			break;
371 
372 		case 3:
373 			// number to number comparison
374 			NumbersOnly = true; // so we add a number in AddLetters() rather than adding a letter
375 
376 			ExampleWord.clear();
377 
378 			ExampleWord.append(Numbers[0]);
379 			ExampleWord.append(Numbers[1]);
380 			ExampleWord.append(Numbers[2]);
381 			ExampleWord.append(Numbers[3]);
382 			ExampleWord.append(Numbers[4]);
383 
384 			Numbers.Shuffle();
385 
386 			TestWord.clear();
387 
388 			TestWord.append(Numbers[0]);
389 			TestWord.append(Numbers[1]);
390 			TestWord.append(Numbers[2]);
391 			TestWord.append(Numbers[3]);
392 			TestWord.append(Numbers[4]);
393 
394 			break;
395 	}
396 
397 	switch (TheGame->RandomRange(0, 3)) {
398 		case 0:
399 		{
400 			string LetterToAdd;
401 
402 			if (NumbersOnly) {
403 				LetterToAdd = GetUniqueNumber(ExampleWord, TestWord);
404 			} else {
405 				LetterToAdd = GetUniqueLetter(ExampleWord, TestWord);
406 			}
407 
408 			ExampleWordAnswer = AddLetters(ExampleWord, LetterToAdd, Positions[0]);
409 			Answer1 = AddLetters(TestWord, LetterToAdd, Positions[0]);
410 			Answer2 = AddLetters(TestWord, LetterToAdd, Positions[1]);
411 			Answer3 = AddLetters(TestWord, LetterToAdd, Positions[2]);
412 			Answer4 = AddLetters(TestWord, LetterToAdd, Positions[3]);
413 
414 			break;
415 		}
416 
417 		case 1:
418 			ExampleWordAnswer = FlipLetters(ExampleWord);
419 			Answer1 = FlipLetters(TestWord);
420 
421 			// we're going to be cruel, and flip these words then switch a letter so they look vaguely right at first glance
422 			Answer2 = FlipLetters(SwitchLetters(TestWord, Positions[0]));
423 			Answer3 = FlipLetters(SwitchLetters(TestWord, Positions[1]));
424 			Answer4 = FlipLetters(SwitchLetters(TestWord, Positions[2]));
425 			break;
426 
427 		case 2:
428 			ExampleWordAnswer = RemoveLetters(ExampleWord, Positions[0]);
429 			Answer1 = RemoveLetters(TestWord, Positions[0]);
430 			Answer2 = RemoveLetters(TestWord, Positions[1]);
431 			Answer3 = RemoveLetters(TestWord, Positions[2]);
432 			Answer4 = RemoveLetters(TestWord, Positions[3]);
433 			break;
434 
435 		case 3:
436 			ExampleWordAnswer = SwitchLetters(ExampleWord, Positions[0]);
437 			Answer1 = SwitchLetters(TestWord, Positions[0]);
438 
439 			// to make this a bit harder, we're going to switch two letters in the others
440 			Answer2 = SwitchLetters(TestWord, Positions[0]);
441 			Answer2 = SwitchLetters(Answer2, Positions[1]);
442 
443 			Answer3 = SwitchLetters(TestWord, Positions[0]);
444 			Answer3 = SwitchLetters(Answer3, Positions[2]);
445 
446 			Answer4 = SwitchLetters(TestWord, Positions[0]);
447 			Answer4 = SwitchLetters(Answer4, Positions[3]);
448 			break;
449 	}
450 
451 	Answers[0]->Correct = true;
452 	Answers[1]->Correct = false;
453 	Answers[2]->Correct = false;
454 	Answers[3]->Correct = false;
455 
456 	TheGame->AllocString(&Answers[0]->Text, Answer1.c_str(), NORMAL, 264.0f, 47.0f, CENTRED);
457 	TheGame->AllocString(&Answers[1]->Text, Answer2.c_str(), NORMAL, 264.0f, 47.0f, CENTRED);
458 	TheGame->AllocString(&Answers[2]->Text, Answer3.c_str(), NORMAL, 264.0f, 47.0f, CENTRED);
459 	TheGame->AllocString(&Answers[3]->Text, Answer4.c_str(), NORMAL, 264.0f, 47.0f, CENTRED);
460 
461 	Answers.Shuffle();
462 
463 	Answers[0]->Pos = AnswerPositions[0];
464 	Answers[1]->Pos = AnswerPositions[1];
465 	Answers[2]->Pos = AnswerPositions[2];
466 	Answers[3]->Pos = AnswerPositions[3];
467 
468 	ostringstream question;
469 	question << "If " << ExampleWord.c_str() << " is " << ExampleWordAnswer.c_str() << " then " << TestWord.c_str() << " is...";
470 	TheGame->AllocString(&TheQuestion, question.str().c_str(), LARGE, 264.0f, 135.0f, CENTRED);
471 }
472 
SetMarathon()473 void BPMiniGame_Scrambled::SetMarathon() {
474 	MarathonMode = true;
475 	GameHelp = "I'm going to scramble words and numbers by adding, removing and switching letters. Can you figure out what's changed?";
476 }
477