Advertisement
Guest User

Untitled

a guest
Feb 26th, 2017
96
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 14.99 KB | None | 0 0
  1. /*
  2. Tic - Tac - Console
  3. by: @JackDraak - Playing around: just a humble hobbyist
  4. tinkerer, messing with C++ to have fun
  5. and maybe even learn something.
  6. This is my first working version of a one-player
  7. take on the the classic pen and paper (or should I
  8. say, "stick and dirt"?) game: tic - tac - toe
  9. [designed to now be played on the Windows Console.]
  10. Where to go from here:
  11. - improve formatting, use colour(?)
  12. - optimize source-code
  13. - separate into MFC class and Game class, use a header file....
  14. */
  15.  
  16. // Used for Input and Output.
  17. #include <iostream>
  18. #include <sstream>
  19.  
  20. // Used by A.I. player.
  21. #include <random>
  22.  
  23. // Used to treat the console display as a textbox.
  24. #include <windows.h>
  25. #include <stdio.h>
  26. #include <string.h>
  27. #include <stdlib.h>
  28.  
  29. // For quality randomness, use this Entropy.
  30. std::mt19937 Entropy = std::mt19937{ std::random_device{}() };
  31.  
  32. // The 9 cells of the traditional game grid will be box objects in memory.
  33. class box
  34. {
  35. private:
  36. char value;
  37. char identity;
  38. int screenColumn;
  39. int screenRow;
  40.  
  41. public:
  42. box();
  43. char GetValue();
  44. char GetID();
  45. int GetScreenColumn();
  46. int GetScreenRow();
  47. void Reset();
  48. void SetValue(char);
  49. void SetID(char);
  50. void SetScreenColumn(int);
  51. void SetScreenRow(int);
  52. };
  53.  
  54. // Default initializations for a new box object.
  55. box::box() { value = 'U'; identity = '0'; screenColumn = 0; screenRow = 0; return; }
  56.  
  57. // Box getters and setters.
  58. char box::GetValue() { return value; }
  59. char box::GetID() { return identity; }
  60. int box::GetScreenColumn() { return screenColumn; }
  61. int box::GetScreenRow() { return screenRow; }
  62. void box::Reset() { value = identity; return; }
  63. void box::SetValue(char newValue) { value = newValue; return; }
  64. void box::SetID(char newID) { identity = newID; return; }
  65. void box::SetScreenColumn(int sColumn) { screenColumn = sColumn; return; }
  66. void box::SetScreenRow(int sRow) { screenRow = sRow; return; }
  67.  
  68. // Global variables.
  69. char const winSets[8][3] = {
  70. { '1','2','3' }, // 0 row 1
  71. { '4','5','6' }, // 1 row 2
  72. { '7','8','9' }, // 2 row 3
  73. { '1','4','7' }, // 3 column 1
  74. { '2','5','8' }, // 4 column 2
  75. { '3','6','9' }, // 5 column 3
  76. { '1','5','9' }, // 6 backslash
  77. { '7','5','3' }, // 7 foreslash
  78. };
  79.  
  80. box gameBoard[3][3];
  81.  
  82. char activePlayer;
  83. char blockingPlay;
  84. char winingSet;
  85. char winner;
  86. bool playerXturn = false;
  87. int blockingSet;
  88.  
  89. // Method prototypes.
  90.  
  91. bool CheckForBlock();
  92. bool CheckForWin();
  93. bool Continue();
  94. bool ValidatePlay(char);
  95. char GetBlockingPlay(int);
  96. char GetPlay();
  97. char GetBoxValue(char);
  98. char Shuffle(char, char);
  99. int Shuffle(int);
  100. int Shuffle(int, int);
  101. void Home();
  102. void Home(bool);
  103. void Home(int, int);
  104. void InitBoard();
  105. void PauseForInput();
  106. void PrintEmptyBoard();
  107. void PrintValues();
  108. void SwitchPlayer();
  109.  
  110. // Switch active player from X to O or vice versa.
  111. void SwitchPlayer()
  112. {
  113. playerXturn = !playerXturn;
  114. if (playerXturn) activePlayer = 'X';
  115. else activePlayer = 'O';
  116. }
  117.  
  118. // Print the game box::value's into the game-grid on screen.
  119. void PrintValues()
  120. {
  121. for (int row = 0; row < 3; row++)
  122. {
  123. for (int column = 0; column < 3; column++)
  124. {
  125. int boxRow = gameBoard[row][column].GetScreenRow();
  126. int boxColumn = gameBoard[row][column].GetScreenColumn();
  127. char boxValue = gameBoard[row][column].GetValue();
  128.  
  129. // Output value to designated screen position.
  130. Home(boxRow, boxColumn);
  131. std::cout << boxValue;
  132. }
  133. }
  134. Home(10, 0);
  135. return;
  136. }
  137.  
  138. // Initialize gameBoard array with content (numbered boxes) and position information.
  139. void InitBoard()
  140. {
  141. // Assign game grid rows and columns to on-screen coordinates.
  142. int const R1 = 4;
  143. int const R2 = 6;
  144. int const R3 = 8;
  145. int const C1 = 17;
  146. int const C2 = 21;
  147. int const C3 = 25;
  148. char thisValue = '1';
  149. for (int row = 0; row < 3; row++)
  150. {
  151. for (int column = 0; column < 3; column++)
  152. {
  153. // Set initial value and reset ID <thisValue> of each box.
  154. gameBoard[row][column].SetID(thisValue);
  155. gameBoard[row][column].Reset();
  156.  
  157. // Set designated on-screen row of each box.
  158. if (row == 0) gameBoard[row][column].SetScreenRow(R1);
  159. else if (row == 1) gameBoard[row][column].SetScreenRow(R2);
  160. else if (row == 2) gameBoard[row][column].SetScreenRow(R3);
  161.  
  162. // Set designated on-screen column of each box.
  163. if (column == 0) gameBoard[row][column].SetScreenColumn(C1);
  164. else if (column == 1) gameBoard[row][column].SetScreenColumn(C2);
  165. else if (column == 2) gameBoard[row][column].SetScreenColumn(C3);
  166.  
  167. thisValue++;
  168. }
  169. } return;
  170. }
  171.  
  172. // Get a boxID <char 1-9> from the console user.
  173. char GetPlay()
  174. {
  175. std::string userInput;
  176. char position;
  177. bool outOfRange = true;
  178. do
  179. {
  180. getline(std::cin, userInput);
  181. std::stringstream thisStream(userInput);
  182. Home(15, 0); std::cout << " ";
  183. if (thisStream >> position) { break; }
  184. Home(15, 0); std::cout << " Invalid entry...";
  185. if (position >= '1' && position <= '9') outOfRange = false;
  186. } while (outOfRange);
  187. return position;
  188. }
  189.  
  190. // Clear and home the console, and put up an empty game board.
  191. void PrintEmptyBoard()
  192. {
  193. Home(true);
  194. std::cout << /// 10 20 30 4
  195. ///0123456789-123456789-123456789-123456///0
  196. "\n -------------------" ///1
  197. "\n Tic - Tac - Console" ///2
  198. "\n" ///3
  199. "\n | | " ///4
  200. "\n ---|---|---" ///5
  201. "\n | | " ///6
  202. "\n ---|---|---" ///7
  203. "\n | | " ///8
  204. "\n"; ///9
  205. Home(10, 0);
  206. return;
  207. }
  208.  
  209. // Use this function to "Home" the cursor to the top left of the console.
  210. void Home()
  211. {
  212. HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE);
  213. COORD coordScreen = { 0, 0 };
  214. SetConsoleCursorPosition(hConsole, coordScreen);
  215. return;
  216. }
  217.  
  218. // Use this function overload to "Home" the cursor to a specific row:column.
  219. void Home(int row, int column)
  220. {
  221. short thisRow = row;
  222. short thisColumn = column;
  223. HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE);
  224. COORD coordScreen = { thisColumn, thisRow };
  225. SetConsoleCursorPosition(hConsole, coordScreen);
  226. return;
  227. }
  228.  
  229. // Use this overload with a true or false argument for a "Clear and Home" effect in the console.
  230. void Home(bool clear)
  231. {
  232. HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE);
  233. COORD coordScreen = { 0, 0 };
  234. BOOL bSuccess;
  235. DWORD cCharsWritten;
  236. CONSOLE_SCREEN_BUFFER_INFO csbi;
  237. DWORD dwConSize;
  238. bSuccess = GetConsoleScreenBufferInfo(hConsole, &csbi);
  239. if (!bSuccess) return;
  240. dwConSize = csbi.dwSize.X * csbi.dwSize.Y;
  241. bSuccess = FillConsoleOutputCharacter(hConsole, (TCHAR) ' ', dwConSize, coordScreen, &cCharsWritten);
  242. if (!bSuccess) return;
  243. bSuccess = GetConsoleScreenBufferInfo(hConsole, &csbi);
  244. if (!bSuccess) return;
  245. bSuccess = FillConsoleOutputAttribute(hConsole, csbi.wAttributes, dwConSize, coordScreen, &cCharsWritten);
  246. if (!bSuccess) return;
  247. bSuccess = SetConsoleCursorPosition(hConsole, coordScreen);
  248. if (!bSuccess) return;
  249. }
  250.  
  251. // Return a random int from 0-max.
  252. int Shuffle(int max)
  253. {
  254. std::uniform_int_distribution<> zIndexDist(0, max);
  255. return zIndexDist(Entropy);
  256. }
  257.  
  258. // Return a random int from min-max.
  259. int Shuffle(int min, int max)
  260. {
  261. std::uniform_int_distribution<> zIndexDist(min, max);
  262. return zIndexDist(Entropy);
  263. }
  264.  
  265. // Return a random char from begin to end.
  266. char Shuffle(char begin, char end)
  267. {
  268. std::uniform_int_distribution<> zIndexDist(begin, end);
  269. return zIndexDist(Entropy);
  270. }
  271.  
  272. // Delay for console input.
  273. void PauseForInput()
  274. {
  275. std::string userInput;
  276. Home(10, 0); std::cout << " Please hit <Enter> to proceed... ";
  277. getline(std::cin, userInput);
  278. return;
  279. }
  280.  
  281. // True if continued play is requested, false if exit requested.
  282. bool Continue()
  283. {
  284. bool validInput = false;
  285. bool proceed = false;
  286. do
  287. {
  288. std::string userInput;
  289. Home(10, 0); std::cout << " Please enter (P)lay or (Q)uit then <Enter>, thank you. ";
  290. getline(std::cin, userInput);
  291. if (userInput[0] == 'p' || userInput[0] == 'P')
  292. {
  293. validInput = true;
  294. proceed = true;
  295. }
  296. else if (userInput[0] == 'q' || userInput[0] == 'Q')
  297. {
  298. validInput = true;
  299. }
  300. } while (!validInput);
  301. Home(10, 0); std::cout << " ";
  302. return proceed;
  303. }
  304.  
  305. // Take a box ID and attempt to claim it for the activePlayer.
  306. bool ValidatePlay(char id)
  307. {
  308. bool validity = false;
  309. for (int row = 0; row < 3; row++)
  310. {
  311. for (int column = 0; column < 3; column++)
  312. {
  313. if (id == gameBoard[row][column].GetID())
  314. {
  315. char boxValue = gameBoard[row][column].GetValue();
  316. if (boxValue == id)
  317. {
  318. gameBoard[row][column].SetValue(activePlayer);
  319. validity = true;
  320. }
  321. else if (boxValue == activePlayer && activePlayer == 'X')
  322. {
  323. Home(12, 0); std::cout << " ";
  324. Home(12, 0); std::cout << " Box " << id << "?!? You've already claimed that one! ";
  325. Home(10, 0);
  326. }
  327. else if (activePlayer == 'X' && boxValue != activePlayer)
  328. {
  329. Home(12, 0); std::cout << " ";
  330. Home(12, 0); std::cout << " Box " << id << "?!? Your opponent already claimed that one! ";
  331. Home(10, 0);
  332. }
  333. }
  334. }
  335. } return validity;
  336. }
  337.  
  338. // Return the box::value of a box by box-ID.
  339. char GetBoxValue(char id)
  340. {
  341. char value = '-';
  342. for (int row = 0; row < 3; row++)
  343. {
  344. for (int column = 0; column < 3; column++)
  345. {
  346. if (gameBoard[row][column].GetID() == id)
  347. {
  348. value = gameBoard[row][column].GetValue();
  349. return value;
  350. }
  351. }
  352. } return value;
  353. }
  354.  
  355. // Check winSets against gameBoard.
  356. bool CheckForWin()
  357. {
  358. bool won = false;
  359. char thisSet[3];
  360. for (int winSetRow = 0; winSetRow <= 7; winSetRow++)
  361. {
  362. for (int member = 0; member <= 2; member++)
  363. {
  364. thisSet[member] = winSets[winSetRow][member];
  365. }
  366. int xScore = 0;
  367. int oScore = 0;
  368. for (int member = 0; member <= 2; member++)
  369. {
  370. char thisValue = thisSet[member];
  371. char thisBoxValue = GetBoxValue(thisValue);
  372. if (thisBoxValue != thisValue)
  373. {
  374. if (thisBoxValue == 'X') xScore++;
  375. else oScore++;
  376. }
  377. }
  378. if (xScore == 3 || oScore == 3)
  379. {
  380. Home(16, 0); std::cout << " Congratulations, player " << activePlayer << " for the win! (WinPatternID:" << winSetRow << ")";
  381. won = true;
  382. }
  383. } return won;
  384. }
  385.  
  386. // Check gameBoard array, looking for any prescribed blocking play; when true, follow with GetBlockingPlay() on the identified set.
  387. bool CheckForBlock()
  388. {
  389. bool block = false;
  390. char thisSet[3];
  391. blockingSet = -1;
  392. for (int winSetRow = 0; winSetRow <= 7; winSetRow++)
  393. {
  394. for (int member = 0; member <= 2; member++)
  395. {
  396. thisSet[member] = winSets[winSetRow][member];
  397. }
  398. int xScore = 0;
  399. int oScore = 0;
  400. blockingPlay = 'U';
  401. for (int member = 0; member <= 2; member++)
  402. {
  403. char thisValue = thisSet[member];
  404. char thisBoxValue = GetBoxValue(thisValue);
  405. if (thisBoxValue != thisValue)
  406. {
  407. if (thisBoxValue == 'X') xScore++;
  408. else oScore++;
  409. }
  410. }
  411. if ((xScore == 2 && oScore == 0) || (xScore == 0 && oScore == 2))
  412. {
  413. blockingSet = winSetRow;
  414. block = true;
  415. }
  416. } return block;
  417. }
  418.  
  419. // To be used if CheckForBlock identifies a blockingSet in order to return the blockingPlay.
  420. char GetBlockingPlay(int blockingSet)
  421. {
  422. char block = 'E';
  423. char thisSet[3];
  424. char thisValue;
  425. for (int member = 0; member <= 2; member++)
  426. {
  427. thisSet[member] = winSets[blockingSet][member];
  428. thisValue = thisSet[member];
  429. char thisBoxValue = GetBoxValue(thisValue);
  430. if (thisBoxValue == thisValue) block = thisValue;
  431. } return block;
  432. }
  433.  
  434. // Application entry point.
  435. int main()
  436. {
  437. // Play (at least one) game of Tic-Tac-Toe
  438. do {
  439. PrintEmptyBoard();
  440. InitBoard();
  441. PrintValues();
  442. SwitchPlayer();
  443. if (activePlayer == 'O') SwitchPlayer(); // For simplicity, start each game with console player as X.
  444. blockingPlay = 'U';
  445. winner = '-';
  446. bool gameInProgress = true;
  447. int turnNumber = 1;
  448. do {
  449. // Player X plays.
  450. bool validMove = false;
  451. do {
  452. Home(10, 0); std::cout << " ";
  453. Home(10, 0); std::cout << " Make a move, player " << activePlayer << ": ";
  454. char thisPlay = GetPlay();
  455. validMove = ValidatePlay(thisPlay);
  456. } while (!validMove);
  457. PrintValues();
  458. gameInProgress = (!CheckForWin());
  459. if (turnNumber == 9 && gameInProgress) { winner = '-'; gameInProgress = false; }
  460. else
  461. {
  462. if (CheckForBlock()) blockingPlay = GetBlockingPlay(blockingSet);
  463. }
  464. turnNumber++;
  465. SwitchPlayer();
  466. // Player O plays.
  467. if (gameInProgress)
  468. {
  469. bool validRandom;
  470. if (!ValidatePlay(blockingPlay)) validRandom = false; else validRandom = true;
  471. if (!validRandom) do {
  472. char randomPlay = Shuffle('1', '9');
  473. validRandom = ValidatePlay(randomPlay);
  474. } while (!validRandom);
  475. PrintValues();
  476. gameInProgress = (!CheckForWin());
  477. turnNumber++;
  478. SwitchPlayer();
  479. }
  480. // Repeat X play then O play as needed.
  481. } while (turnNumber < 10 && gameInProgress);
  482. // Would you like to play a game? -War Games
  483. } while (Continue());
  484. // End Of Line -Tron
  485. return 0;
  486. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement