14
14
#include < chrono>
15
15
#include < iostream>
16
16
#include < sstream>
17
+ #include < random> // Required for generating random numbers/integers in the RemoveTiles() function.
17
18
18
19
namespace Game {
19
20
namespace {
@@ -28,13 +29,24 @@ enum GameStatusFlag {
28
29
FLAG_ENDLESS_MODE,
29
30
FLAG_GAME_IS_ASKING_QUESTION_MODE,
30
31
FLAG_QUESTION_STAY_OR_QUIT,
32
+ FLAG_TILES_REMOVED, // Indicates if tiles have already been removed
31
33
MAX_NO_GAME_STATUS_FLAGS
32
34
};
33
35
34
36
using gamestatus_t = std::array<bool , MAX_NO_GAME_STATUS_FLAGS>;
35
37
36
38
using gamestatus_gameboard_t = std::tuple<gamestatus_t , GameBoard>;
37
39
40
+ /* *
41
+ * @brief Processes the game logic for the current game state.
42
+ *
43
+ * This function updates the game status and game board based on the current state.
44
+ * It handles tile movements, checks for game-winning conditions, and verifies if the game can continue.
45
+ * If the player cannot make any more moves, it prompts the player to remove random tiles before ending the game.
46
+ *
47
+ * @param gsgb A tuple containing the current game status and game board.
48
+ * @return gamestatus_gameboard_t A tuple containing the updated game status and game board.
49
+ */
38
50
gamestatus_gameboard_t process_gamelogic (gamestatus_gameboard_t gsgb) {
39
51
gamestatus_t gamestatus;
40
52
GameBoard gb;
@@ -53,7 +65,31 @@ gamestatus_gameboard_t process_gamelogic(gamestatus_gameboard_t gsgb) {
53
65
}
54
66
}
55
67
if (!canMoveOnGameboard (gb)) {
56
- gamestatus[FLAG_END_GAME] = true ;
68
+ if (gamestatus[FLAG_TILES_REMOVED] == false )
69
+ {
70
+ char input;
71
+ std::cout << " You lose. Do you want to remove random tiles (Y/N) ?" << std::endl;
72
+ std::cin >> input;
73
+
74
+ if (input == ' Y' || input == ' y' )
75
+ {
76
+ gamestatus[FLAG_TILES_REMOVED] = true ;
77
+ removeTiles (gb);
78
+ }
79
+ else if (input == ' N' || input == ' n' )
80
+ {
81
+ gamestatus[FLAG_END_GAME] = true ;
82
+ gamestatus[FLAG_START_MENU] = true ;
83
+ }
84
+ else
85
+ {
86
+ std::cout << " Invalid input." << std::endl;
87
+ }
88
+ }
89
+ else
90
+ {
91
+ gamestatus[FLAG_END_GAME] = true ;
92
+ }
57
93
}
58
94
return std::make_tuple (gamestatus, gb);
59
95
}
@@ -162,6 +198,21 @@ gamestatus_t update_one_shot_display_flags(gamestatus_t gamestatus) {
162
198
}
163
199
164
200
using bool_gamestatus_t = std::tuple<bool , gamestatus_t >;
201
+ /* *
202
+ * @brief Processes non-standard input commands and updates the game status accordingly.
203
+ *
204
+ * This function handles additional input commands that are not part of the standard gameplay.
205
+ * It updates the game status based on the input character received. The function supports saving
206
+ * the game, quitting endless mode, and returning to the main menu.
207
+ *
208
+ * @param c The input character received from the user.
209
+ * @param gamestatus The current game status flags.
210
+ * @return bool_gamestatus_t A tuple containing a boolean indicating if the keycode is invalid and the updated game status flags.
211
+ *
212
+ * @note Changes in the new version:
213
+ * - Added support for returning to the main menu with CODE_RETURN_TO_MENU.
214
+ * - When CODE_RETURN_TO_MENU is detected, the appropriate main menu flags are set.
215
+ */
165
216
bool_gamestatus_t check_input_other (char c, gamestatus_t gamestatus) {
166
217
using namespace Input ::Keypress::Code;
167
218
auto is_invalid_keycode{true };
@@ -178,6 +229,12 @@ bool_gamestatus_t check_input_other(char c, gamestatus_t gamestatus) {
178
229
is_invalid_keycode = false ;
179
230
}
180
231
break ;
232
+ case CODE_RETURN_TO_MENU:
233
+ // When CODE_RETURN_TO_MENU is detected, the main menu is set to start, and game flags are adjusted. mainmenustatus[FLAG_START_MENU] = true;
234
+ mainmenustatus[FLAG_START_GAME] = false ;
235
+ mainmenustatus[FLAG_CONTINUE_GAME] = false ;
236
+ is_invalid_keycode = false ;
237
+ break ;
181
238
}
182
239
return std::make_tuple (is_invalid_keycode, gamestatus);
183
240
}
@@ -267,6 +324,21 @@ bool continue_playing_game(std::istream &in_os) {
267
324
return true ;
268
325
}
269
326
327
+ /* *
328
+ * @brief Processes the current game status and updates the game loop control.
329
+ *
330
+ * This function evaluates the current game status flags and takes appropriate actions:
331
+ * - It handles winning conditions and prompts the user to continue playing.
332
+ * - It checks for the end of the game conditions.
333
+ * - It manages saving the game state by prompting the user for a filename.
334
+ * - It resets the question asking event trigger for a new loop cycle.
335
+ *
336
+ * @param gsgb A tuple containing the current game status and game board.
337
+ * @return bool_gamestatus_t A tuple containing a boolean indicating whether to continue the game loop and the updated game status flags.
338
+ *
339
+ * @note Changes in the new version:
340
+ * - Added a prompt asking the user to enter a filename when saving the game state.
341
+ */
270
342
bool_gamestatus_t process_gameStatus (gamestatus_gameboard_t gsgb) {
271
343
gamestatus_t gamestatus;
272
344
GameBoard gb;
@@ -288,7 +360,10 @@ bool_gamestatus_t process_gameStatus(gamestatus_gameboard_t gsgb) {
288
360
loop_again = false ;
289
361
}
290
362
if (gamestatus[FLAG_SAVED_GAME]) {
291
- Saver::saveGamePlayState (gb);
363
+ std::cout << " Please enter the filename to save the game state" << std::endl;
364
+ std::string filename;
365
+ std::cin >> filename;
366
+ Saver::saveGamePlayState (gb, filename);
292
367
}
293
368
294
369
// New loop cycle: reset question asking event trigger
@@ -361,13 +436,31 @@ std::string drawEndGameLoopGraphics(current_game_session_t finalgamestatus) {
361
436
return str_os.str ();
362
437
}
363
438
439
+ /* *
440
+ * @brief Runs the endless game loop until the game ends or the user returns to the menu.
441
+ *
442
+ * This function manages the continuous gameplay loop for the endless game mode.
443
+ * It repeatedly calls the solo game loop until the game ends or the user chooses to return to the menu.
444
+ * The game status and game board are updated accordingly in each iteration.
445
+ *
446
+ * @param currentBestScore The current best score achieved.
447
+ * @param cm The competition mode settings.
448
+ * @param gb The current game board state.
449
+ * @return GameBoard The final state of the game board after the loop ends.
450
+ *
451
+ * @note Changes in the new version:
452
+ * - Added checks to monitor if the current state is set to GAME or MENU.
453
+ * - The loop will terminate if the user presses the M key and the state changes to MENU.
454
+ */
364
455
GameBoard endlessGameLoop (ull currentBestScore, competition_mode_t cm,
365
456
GameBoard gb) {
366
457
auto loop_again{true };
367
458
auto currentgamestatus =
368
459
std::make_tuple (currentBestScore, cm, gamestatus_t {}, gb);
369
-
370
- while (loop_again) {
460
+ // Monitor the loop to check if the current state is still set to GAME
461
+ // If not, then the M key was pressed, setting the current state to MENU
462
+ // Thus, we return to the menu
463
+ while (loop_again && ((mainmenustatus[FLAG_START_GAME] == true ) || (mainmenustatus[FLAG_CONTINUE_GAME] == true ))) {
371
464
std::tie (loop_again, currentgamestatus) = soloGameLoop (currentgamestatus);
372
465
}
373
466
@@ -423,8 +516,45 @@ void startGame() {
423
516
PreGameSetup::SetUpNewGame ();
424
517
}
425
518
426
- void continueGame () {
427
- PreGameSetup::ContinueOldGame ();
519
+ /* *
520
+ * @brief Continue a previously saved game.
521
+ *
522
+ * The ContinueOldGame function has been updated to accept a filename directly.
523
+ * This allows the user to load a specific save file instead of only the last saved game.
524
+ *
525
+ * @param filename The name of the file containing the saved game to load.
526
+ */
527
+ void continueGame (const std::string& filename) {
528
+ PreGameSetup::ContinueOldGame (filename);
529
+ }
530
+
531
+ /* *
532
+ * @brief Randomly removes two tiles from a GameBoard by setting their values to 0.
533
+ *
534
+ * This function uses a random number generator to select and remove two non-empty tiles
535
+ * from the given GameBoard by setting their values to 0. The process continues until
536
+ * exactly two tiles are removed.
537
+ *
538
+ * @param gb The GameBoard object from which tiles will be removed.
539
+ */
540
+ void removeTiles (GameBoard& gb) {
541
+ // Seed with a real random value, if available
542
+ std::random_device rd;
543
+ std::mt19937 gen (rd ());
544
+
545
+ auto & [playsize, tiles] = gb.gbda ;
546
+ std::uniform_int_distribution<> dis (0 , tiles.size () - 1 );
547
+
548
+ int tiles_to_remove = 2 ;
549
+ while (tiles_to_remove > 0 ) {
550
+ int random_index = dis (gen);
551
+ auto & tile = tiles[random_index];
552
+
553
+ if (tile.value != 0 ) { // Ensure it's not already an empty tile
554
+ tile.value = 0 ; // Remove the tile
555
+ --tiles_to_remove;
556
+ }
557
+ }
428
558
}
429
559
430
560
} // namespace Game
0 commit comments