rp-scrabble
Simple, terminal-based Scrabble game
game.cc
Go to the documentation of this file.
1 
4 #include <iostream>
5 #include <ctime>
6 #include <string>
7 #include <cstdlib>
8 #include <stdexcept>
9 #include "game.h"
10 #include "bag.h"
11 #include "board.h"
12 #include "player.h"
13 #include "utils.h"
14 #include "play.h"
15 #include "tile.h"
16 
17 using namespace std;
18 
23 {
24  char filenameBuffer[60];
25  time_t rawTime;
26  time(&rawTime);
27  strftime(filenameBuffer, 60, "-%F-scrabble.log", localtime(&rawTime));
28  gameID = RawTimeToString(rawTime);
29  logFilePath = LOG_PATH + gameID + string(filenameBuffer);
30 
31  // DEBUG_PRINT(" logFilePath", logFilePath);
32  cout << endl;
33 
34  gameBoard = new Board;
35  gameBag = new Bag;
36 
37  // Solitaire game by default
38  addPlayer(new Player(to_string(1)));
39 }
40 
45 {
46  if(gameBag) {
47  delete gameBag;
48  }
49 
50  if(gameBoard) {
51  delete gameBoard;
52  }
53 
54  for(Player* p : players) {
55  if(p) {
56  delete p;
57  }
58  }
59 }
60 
69 {
70  if(p) {
71  players.push_back(p);
72  }
73 }
74 
78 void Game::init()
79 {
80  string tempName;
81  char response;
82  int i, j;
83  bool complete = false;
84 
85  try {
86  log(logFilePath, "Log start\n");
87  }
88  catch(string err) {
89  BOLD_RED_FG(" " + err);
90  BOLD_RED_FG(" You can set the path of the log file in CMakeLists.txt\n");
91  BOLD_RED_FG(" Aborting\n");
92  exit(1);
93  }
94 
95  BOLD(" Welcome to Scrabble!");
96  cout << "\n";
97 
98  for(Player* p : players) {
99  cout << " Name of Player " + p->getName() + ": ";
100  cin >> tempName;
101  try {
102  log(logFilePath, "Player 1: "+ tempName);
103  }
104  catch(string err) {
105  BOLD_RED_FG(" " + err);
106  BOLD_RED_FG(" You can set the path of the log file in CMakeLists.txt\n");
107  BOLD_RED_FG(" Aborting\n");
108  exit(1);
109  }
110  p->setName(tempName);
111  }
112 
113  cout << " Would you like to add more players? (y/n)? ";
114  cin >> response;
115  if(response == 'y') {
116  while(!complete) {
117  cout << " How many more (max 3 more)? ";
118  cin >> i;
119  if(i > 0 && i < 4) {
120  for(j = 0; j < i; j++) {
121  cout << " Name of Player " + to_string(j + 2) + ": ";
122  cin >> tempName;
123  addPlayer(new Player(tempName));
124  try {
125  log(logFilePath, "Player " + to_string(j + i) + ": " + tempName);
126  }
127  catch(string err) {
128  BOLD_RED_FG(" " + err);
129  BOLD_RED_FG(" You can set the path of the log file in CMakeLists.txt\n");
130  BOLD_RED_FG(" Aborting\n");
131  exit(1);
132  }
133  }
134  complete = true;
135  }
136  else {
137  if(i != 0) {
138  BOLD(" You can only add upto 3 more players!\n");
139  }
140  else {
141  break;
142  }
143  }
144  }
145  }
146  else if(response == 'n') {
147  // do nothing, carry on
148  }
149  else {
150  BOLD_RED_FG(" Error: Invalid input\n");
151  init();
152  }
153  cout << "\n";
154 }
155 
168 bool Game::firstTurnCheck(string tileStr, int row, int col, char dir)
169 {
170  if(dir == 'h') {
171  for(unsigned long j = col; j < col + tileStr.length(); j++) {
172  if(j == 7 && row == 7) {
173  return true;
174  }
175  }
176  }
177  else if(dir == 'v') {
178  for(unsigned long i = row; i < row + tileStr.length(); i++) {
179  if(i == 7 && col == 7) {
180  return true;
181  }
182  }
183  }
184 
185  return false;
186 }
187 
197 {
198  string tempIn = "";
199  string input = "";
200 
201  BOLD(" Enter the tiles you want to place ");
202  cout << "(? for help, - to quit) ";
203  cin >> tempIn;
204  if(tempIn == "?") {
205  return "?";
206  }
207  else if(tempIn == ".") {
208  return ".";
209  }
210  else if(tempIn == "-") {
211  return "-";
212  }
213  else if(tempIn == "!") {
214  return "!";
215  }
216  else if(tempIn == "#") {
217  return "#";
218  }
219  else {
220  for(char ch : tempIn) {
221  if(!charPresent(alphabets, ch)) {
222  throw(string("Invalid character input\n"));
223  }
224  }
225  input.append(tempIn + "-");
226  }
227 
228  BOLD(" Enter the row where the first tile will go ");
229  cout << "(? for help, - to quit) ";
230  cin >> tempIn;
231  if(tempIn == "?") {
232  return "?";
233  }
234  else if(tempIn == ".") {
235  return ".";
236  }
237  else if(tempIn == "-") {
238  return "-";
239  }
240  else if(tempIn == "!") {
241  return "!";
242  }
243  else if(tempIn == "#") {
244  return "#";
245  }
246  else {
247  try {
248  stoi(tempIn);
249  }
250  catch(const invalid_argument& ia) {
251  throw(string("Invalid integer input\n"));
252  }
253  input.append(tempIn + "-");
254  }
255 
256  BOLD(" Enter the column where the first tile will go ");
257  cout << "(? for help, - to quit) ";
258  cin >> tempIn;
259  if(tempIn == "?") {
260  return "?";
261  }
262  else if(tempIn == ".") {
263  return ".";
264  }
265  else if(tempIn == "-") {
266  return "-";
267  }
268  else if(tempIn == "!") {
269  return "!";
270  }
271  else if(tempIn == "#") {
272  return "#";
273  }
274  else {
275  try {
276  stoi(tempIn);
277  }
278  catch(const invalid_argument& ia) {
279  throw(string("Invalid integer input"));
280  }
281  input.append(tempIn + "-");
282  }
283 
284  BOLD(" Enter the direction of placement ");
285  cout << "(? for help, - to quit) ";
286  cin >> tempIn;
287  if(tempIn == "?") {
288  return "?";
289  }
290  else if(tempIn == ".") {
291  return ".";
292  }
293  else if(tempIn == "-") {
294  return "-";
295  }
296  else if(tempIn == "!") {
297  return "!";
298  }
299  else if(tempIn == "#") {
300  return "#";
301  }
302  else {
303  if(tempIn.length() != 1 && (tempIn != "h" || tempIn != "v")) {
304  throw(string("Invalid direction\n"));
305  }
306  input.append(tempIn);
307  }
308 
309  return input;
310 }
311 
318 {
319  PALE_GREEN_FG("\n The tiles you want to place are entered in order of placement using the respective letter\n\n");
320  PALE_GREEN_FG(" The row and column of the square to place can be seen outside the edge of the board\n\n");
321  PALE_GREEN_FG(" The placement direction can either be 'v' (place tiles vertically downward one after the other) or\n");
322  PALE_GREEN_FG(" 'h' (place tiles horizontally from left to right one after the other)\n\n");
323 
324  BOLD(" Commands\n");
325  BOLD(" --------\n");
326 
327  BOLD_BRIGHT_GREEN_FG(" ? ");
328  cout << "Show this help text\t";
329  BOLD_BRIGHT_GREEN_FG(" . ");
330  cout << "Show the board\t";
331  BOLD_BRIGHT_GREEN_FG(" ! ");
332  cout << "Skip turn\n";
333  BOLD_BRIGHT_GREEN_FG(" # ");
334  cout << "Show scores\t\t";
335  BOLD_BRIGHT_GREEN_FG(" - ");
336  cout << "Quit the game\n\n";
337 
338  BOLD(" Board legend\n");
339  BOLD(" ------------\n");
340 
341  cout << " ";
342  RED_BG(" ");
343  cout << " Triple Word Score\t";
344 
345  cout << " ";
346  PINK_BG(" ");
347  cout << " Double Word Score\t";
348 
349  cout << " ";
350  DARK_BLUE_BG(" ");
351  cout << " Triple Letter Score\t";
352 
353  cout << " ";
354  LIGHT_BLUE_BG(" ");
355  cout << " Double Letter Score\n\n";
356 }
357 
363 void Game::run()
364 {
365  int row, col;
366  bool endTurn;
367  bool allEmpty = false;
368  bool playValid;
369  bool firstTurn = true;
370  string tileStr;
371  string in = "";
372  string tempIn = "";
373  char dir;
374  vector<string> parsed;
375 
376  init();
377 
378  // Fill up all Players racks
379  for(Player* p : players) {
380  p->draw(7, gameBag);
381  }
382 
383  try {
384  log(logFilePath, "\nGame start\n");
385  }
386  catch(string err) {
387  BOLD_RED_FG(" " + err);
388  BOLD_RED_FG(" You can set the path of the log file in CMakeLists.txt\n");
389  BOLD_RED_FG(" Aborting\n");
390  exit(1);
391  }
392 
393  // Main game loop
394  while(!allEmpty) {
395  for(Player* currPlayer : players) {
396  if(!currPlayer->rackIsEmpty()) {
397  plays.push_back(new Play(currPlayer));
398  Play* currPlay = plays.back();
399  row = col = 7;
400  endTurn = false;
401  tileStr = "";
402 
403  currPlayer->toggleTurn(); // Turn begins;
404  gameBoard->show();
405  BOLD(" Bag: ");
406  gameBag->show();
407  cout << "\n";
408  currPlayer->show();
409 
410  while(!endTurn) {
411  try {
412  BOLD(" " + currPlayer->getName());
413 
414  in = getInput();
415 
416  if(in == "?") {
417  printHelp();
418  }
419  else if(in == ".") {
420  gameBoard->show();
421  BOLD(" Bag: ");
422  gameBag->show();
423  cout << "\n";
424  currPlayer->show();
425  }
426  else if(in == "-") {
427  char c;
428  BOLD_RED_FG(" Are you sure you want to quit? (y/n) ");
429  cin >> c;
430  if(c == 'y') {
431  for(Player* p : players) {
432  log(logFilePath, p->getName() + ": " + to_string(p->getScore()));
433  }
434  exit(0);
435  }
436  else if(c == 'n') {
437  // Do nothing
438  }
439  else {
440  throw(string("Invalid input\n"));
441  }
442  }
443  else if(in == "!") {
444  char c;
445  BOLD_RED_FG(" Skip turn? (y/n) ");
446  cin >> c;
447  if(c == 'y') {
448  currPlayer->toggleTurn();
449  endTurn = !endTurn;
450  }
451  else if(c == 'n') {
452  // Do nothing
453  }
454  else {
455  throw(string("Invalid input\n"));
456  }
457  }
458  else if(in == "#") {
459  for(Player* p : players) {
460  p->showScore();
461  cout << "\t";
462  }
463  cout << "\n";
464  }
465  else {
466  vector<vector<Tile*>> connnectedWords;
467  vector<Tile*> tileStrVec;
468 
469  try {
470  log(logFilePath, in);
471  }
472  catch(string err) {
473  BOLD_RED_FG(" " + err);
474  BOLD_RED_FG(" You can set the path of the log file in CMakeLists.txt\n");
475  BOLD_RED_FG(" Aborting\n");
476  exit(1);
477  }
478 
479  parsed = parsePlay(in);
480 
481  tileStr = parsed[0];
482  row = stoi(parsed[1]);
483  col = stoi(parsed[2]);
484  dir = parsed[3][0];
485  playValid = currPlay->validate(tileStr, gameBoard, row, col, dir);
486 
487  if(firstTurn) {
488  if(!firstTurnCheck(tileStr, row, col, dir)) {
489  firstTurn = true;
490  BOLD_RED_FG(" This is the first turn of the game, please make sure the centre square is covered by your word\n");
491  }
492  else {
493  playValid = true;
494  firstTurn = false;
495  }
496  }
497 
498  if(playValid) {
499  tileStrVec = currPlayer->placeTileStr(tileStr, gameBoard, row, col, dir);
500  connnectedWords = currPlay->getWords(tileStrVec, gameBoard, row, col, dir);
501  currPlay->calculatePoints(connnectedWords, tileStrVec);
502 
503  currPlay->show();
504 
505  if(currPlay->confirmPlay()) {
506  currPlayer->updateScore(currPlay->getPointsMade());
507  currPlayer->draw(tileStr.length(), gameBag);
508  currPlayer->toggleTurn();
509  endTurn = !endTurn; // Turn ends
510  }
511  else {
512  for(Tile* t : tileStrVec) {
513  currPlayer->returnToRack(t, gameBoard);
514  }
515  currPlay->reset();
516  }
517  }
518  else {
519  BOLD_RED_FG(" You can't place a word there!\n");
520  }
521  }
522  }
523  catch(string ex) {
524  BOLD_RED_FG(" Error: " + ex);
525  }
526  }
527  }
528  // Find out whether all racks are empty
529  allEmpty = players.front()->rackIsEmpty();
530  for(Player* p : players) {
531  allEmpty = allEmpty && p->rackIsEmpty();
532  }
533  }
534  }
535 
536  BOLD(" You have placed all tiles!!! Final scores are-\n");
537  for(Player* p : players) {
538  log(logFilePath, "\n");
539  log(logFilePath, p->getName() + ": " + to_string(p->getScore()) + "\n");
540  BOLD_WHITE_FG(p->getName() + ": " + to_string(p->getScore()) + "\n");
541  }
542 }
Definition: bag.h:15
Definition: board.h:20
void run()
Definition: game.cc:363
bool firstTurnCheck(std::string str, int r, int c, char dir)
Definition: game.cc:168
void init()
Definition: game.cc:78
Game()
Definition: game.cc:22
std::string getInput()
Definition: game.cc:196
~Game()
Definition: game.cc:44
void addPlayer(Player *p)
Definition: game.cc:68
void printHelp()
Definition: game.cc:317
Definition: play.h:23
bool confirmPlay()
Definition: play.cc:363
void reset()
Definition: play.cc:387
void show()
Definition: play.cc:37
void calculatePoints(std::vector< std::vector< Tile * >> words, std::vector< Tile * > tileStrVec)
Definition: play.cc:296
int getPointsMade()
Definition: play.cc:351
std::vector< std::vector< Tile * > > getWords(std::vector< Tile * > tilesInStr, Board *b, int r, int c, char dir)
Definition: play.cc:175
bool validate(std::string tileStr, Board *b, int r, int c, char dir)
Definition: play.cc:90
Definition: player.h:19
Definition: tile.h:22
string RawTimeToString(const time_t &t)
Definition: utils.cc:127
void log(string logFilePath, string str)
Definition: utils.cc:104
std::vector< std::string > parsePlay(std::string in)
Definition: utils.cc:78
bool charPresent(string str, char ch)
Definition: utils.cc:62
void PINK_BG(std::string x)
Definition: utils.h:107
void PALE_GREEN_FG(std::string x)
Definition: utils.h:82
void RED_BG(std::string x)
Definition: utils.h:102
void BOLD_BRIGHT_GREEN_FG(std::string x)
Definition: utils.h:42
void DARK_BLUE_BG(std::string x)
Definition: utils.h:112
const std::string alphabets
Definition: utils.h:24
void BOLD_RED_FG(std::string x)
Definition: utils.h:77
void BOLD_WHITE_FG(std::string x)
Definition: utils.h:72
void BOLD(std::string x)
Definition: utils.h:37
void LIGHT_BLUE_BG(std::string x)
Definition: utils.h:117