TheAlgorithms/C++ 1.0.0
All the algorithms implemented in C++
Loading...
Searching...
No Matches
memory_game.cpp
Go to the documentation of this file.
1
17#include <algorithm>
18#include <cstdlib>
19#include <ctime>
20#include <iostream>
21#include <random>
22#include <vector>
23
24// `Sleep` is only available in Windows in milliseconds.
25// However, on Unix/Linux systems it is `sleep`, in seconds.
26#ifdef _WIN32
27#include <Windows.h>
28template <typename T>
29constexpr typename std::enable_if<std::is_integral<T>::value, void>::type SLEEP(
30 T milliseconds) {
31 Sleep(milliseconds * 1000);
32}
33#else
34#include <unistd.h>
35template <typename T>
36constexpr T SLEEP(T seconds) {
37 return sleep(seconds);
38}
39#endif
40
45namespace games {
51namespace memory_game {
61template <typename T>
62bool is_number(const T &input) {
63 if (std::cin.fail()) {
64 std::cin.clear();
65 std::cin.ignore(256, '\n');
66
67 return false;
68 }
69
70 return true;
71}
72
79template <typename T>
80void init(std::vector<T> *table) {
81 std::vector<char> letters(7);
82
83 // Decrease / increase the number of letters depending on the size.
84 if ((*table).size() == 10) { // 5x2
85 letters = {'A', 'E', 'Z', 'P', 'D'};
86 } else if ((*table).size() == 8) { // 4x2
87 letters = {'A', 'E', 'Z', 'D'};
88 } else if ((*table).size() == 14) { // 7x2
89 letters = {'A', 'E', 'Z', 'P', 'D', 'B', 'M'};
90 }
91
92 std::vector<char> pairs;
93 for (char letter : letters) {
94 pairs.push_back(letter);
95 pairs.push_back(letter);
96 }
97
98 std::shuffle(pairs.begin(), pairs.end(),
99 std::mt19937(std::random_device()()));
100
101 for (int i = 0; i < (*table).size(); i++) {
102 (*table)[i] = pairs[i];
103 }
104
105 std::cout << "All available types are: ";
106
107 for (int i = 0; i < letters.size(); i++) {
108 if (i == letters.size() - 1) {
109 std::cout << "and " << letters[i] << ".\n\n";
110 } else {
111 std::cout << letters[i] << ", ";
112 }
113 }
114}
115
122template <typename T>
123void print_table(const std::vector<T> &table) {
124 std::cout << "| ";
125 std::vector<T> table_print(table.size());
126
127 for (int i = 0; i < table.size(); i++) {
128 table_print[i] = ' ';
129
130 if (table[i] != 0) {
131 table_print[i] = table[i];
132 }
133 }
134
135 for (int i = 0; i < table.size(); i++) {
136 if (i % 5 == 0 && i != 0) {
137 std::cout << "\n| ";
138 }
139
140 std::cout << table_print[i] << " | ";
141 }
142}
143
144// Prototype function. This is needed as `ask_data` calls `reset_data`, and
145// `reset_data` calls `ask_data`.
146template <typename T>
147void reset_data(const std::vector<T> &, int *, int *, int *);
148
160template <typename T>
161void ask_data(const std::vector<T> &table, int *answer, int *old_answer,
162 int *memory_count) {
163 (*old_answer) = (*answer);
164 print_table(table);
165
166 std::cout << "\n\nType your response here (number index):\n";
167 std::cin >> (*answer);
168
169 if (!is_number((*answer))) {
170 std::cout << "\nYou must enter a valid number.\n\n";
171 reset_data(table, answer, old_answer, memory_count);
172 }
173
174 // Increase the memory count, which will be later on used for checking if
175 // the user has already answered two values.
176 (*memory_count)++;
177
178 if (((*answer) > table.size()) || ((*answer) < 1)) {
179 std::cout << "\nYou can't check a value that doesn't exist (or an "
180 "invalid number).\n\n";
181 reset_data(table, answer, old_answer, memory_count);
182 }
183
184 if ((*old_answer) == (*answer)) {
185 std::cout << "\nYou can't check the same value twice.\n\n";
186 reset_data(table, answer, old_answer, memory_count);
187 }
188
189 // If two matches are answered already, but the user checkes a non-answered
190 // and an answered value, the program will mark it as no match, however, we
191 // must not allow the user to check the same value twice.
192 if ((table[(*answer) - 1] != 0) &&
193 ((table[(*old_answer)] == 0) || (table[(*old_answer)] != 0))) {
194 std::cout << "\nYou can't check the same value twice.\n\n";
195 reset_data(table, answer, old_answer, memory_count);
196 }
197}
198
210template <typename T>
211void reset_data(const std::vector<T> &table, int *answer, int *old_answer,
212 int *memory_count) {
213 (*answer) = (*old_answer);
214 (*memory_count)--;
215
216 ask_data(table, answer, old_answer, memory_count);
217}
218
234template <typename T>
235bool match(const std::vector<T> &table, std::vector<T> *table_empty,
236 const int &answer, bool *first_time, int *old_answer,
237 int *memory_count) {
238 if ((*first_time) == true) {
239 return true;
240 }
241
242 // Search across the whole table and if the two values match, keep results,
243 // otherwise, hide 'em up.
244 for (int i = 0; i < table.size() + 1; i++) {
245 if (i == answer) {
246 if (table[i - 1] == table[(*old_answer) - 1]) {
247 (*first_time) = true;
248 (*memory_count) = 0;
249
250 (*old_answer) = 0;
251 return true;
252 } else {
253 std::cout << "\nNo match (value was " << table[i - 1]
254 << ", index is " << i << ").\n\n";
255
256 (*table_empty)[(*old_answer) - 1] = 0;
257 (*table_empty)[answer - 1] = 0;
258
259 (*first_time) = true;
260 (*memory_count) = 0;
261
262 (*old_answer) = 0;
263 return false;
264 }
265 }
266 }
267
268 return false;
269}
270
288template <typename T>
289void assign_results(std::vector<T> *table_empty, std::vector<T> *table,
290 int *answer, bool *first_time, int *old_answer,
291 int *memory_count) {
292 // Search through the entire table and if the answer matches the index, show
293 // the value. If it doesn't match, hide both the values. Don't forget to
294 // keep older values already answered.
295 for (int i = 0; i < (*table).size() + 1; i++) {
296 if (i == (*answer)) {
297 if (match((*table), table_empty, (*answer), first_time, old_answer,
298 memory_count) == true) {
299 (*table_empty)[i - 1] = (*table)[i - 1];
300 (*first_time) = true;
301 }
302 }
303 }
304
305 if ((*memory_count) == 1) {
306 (*first_time) = false;
307 (*memory_count) = 0;
308 }
309
310 char try_again = 'n';
311
312 // Has the user finished the game? Use a `for` loop, and if the table is
313 // full, ask the user if he wants to play again.
314 for (int i = 0; i < (*table).size() + 1; i++) {
315 if ((*table_empty)[i] == 0) {
316 break;
317 } else if (i == (*table).size() - 1) {
318 print_table((*table));
319
320 std::cout << "\n\nYou won. Congratulations! Do you want to play "
321 "again? (y/n)\n";
322 std::cout
323 << "Size " << (*table).size()
324 << " will be used. This can be changed by re-running the game.";
325 std::cin >> try_again;
326 if (try_again == 'y') {
327 // This is needed when checking if the user has two matches
328 // already.
329 for (int i = 0; i < (*table_empty).size(); i++) {
330 (*table_empty)[i] = 0;
331 }
332
333 init(table);
334 } else if (try_again == 'n') {
335 std::cout << "\nThanks for playing the game!\n";
336 SLEEP(3);
337
338 exit(0);
339 } else {
340 std::cout << "\nInvalid input (exitting...).\n";
341 SLEEP(3);
342
343 exit(0);
344 }
345 }
346 }
347
348 // Ask data again.
349 ask_data((*table_empty), answer, old_answer, memory_count);
350 assign_results(table_empty, table, answer, first_time, old_answer,
351 memory_count);
352}
353} // namespace memory_game
354} // namespace games
355
360int main() {
361 // Start randomizer. This changes the values every time.
362 std::srand(std::time(nullptr));
363
364 int size = 0;
365 int selection = 0;
366
367 int response = 0;
368 int old_answer = 0;
369
370 int memory_count =
371 0;
372 bool first_time = true;
374
375 std::cout << "\tMEMORY GAME\n";
376
377 do {
378 std::cout << "\n1. 4x2 (1)";
379 std::cout << "\n2. 5x2 (2)";
380 std::cout << "\n3. 7x2 (3)\n";
381
382 std::cout << "\nChoose table size: ";
383 std::cin >> selection;
384 } while ((selection < 1 || selection > 3) &&
385 (!games::memory_game::is_number(selection)));
386
387 switch (selection) {
388 case 1:
389 size = 8;
390 break;
391 case 2:
392 size = 10;
393 break;
394 case 3:
395 size = 14;
396 break;
397 default:
398 size = 10;
399 break;
400 }
401
402 std::vector<char> table(size);
403 std::vector<char> table_empty(size);
404
405 std::cout << "\n";
406
408 games::memory_game::ask_data(table_empty, &response, &old_answer,
409 &memory_count);
410 games::memory_game::assign_results(&table_empty, &table, &response,
411 &first_time, &old_answer, &memory_count);
412
413 return 0;
414}
constexpr T SLEEP(T seconds)
for sleep()
int main()
Main function.
bool is_number(const T &input)
Utility function to verify if the given input is a number or not. This is very useful to prevent the ...
bool match(const std::vector< T > &table, std::vector< T > *table_empty, const int &answer, bool *first_time, int *old_answer, int *memory_count)
Checks if the two values given by the user match.
void assign_results(std::vector< T > *table_empty, std::vector< T > *table, int *answer, bool *first_time, int *old_answer, int *memory_count)
Function to assign the results to the table.
void ask_data(const std::vector< T > &table, int *answer, int *old_answer, int *memory_count)
Function that asks the user for their input in the table they previously chose.
void print_table(const std::vector< T > &table)
Utility function to print the table.
void init(std::vector< T > *table)
Initializes the table with the letters.
void reset_data(const std::vector< T > &, int *, int *, int *)
Utility function that resets the data if the user enters an invalid value.
(Mini)game implementations.