• Source
    1. #include <stdlib.h>
    2. #include <iostream>
    3. #include <string>
    4. #include <ctime>
    5. #include <list>
    6. //#include <lt>;
    7. #include <unistd.h>
    8.  
    9.  
    10. static const int INFINITY = 1000000;
    11. //static const int NO_OF_CELLS = 9;
    12. // declare the possible states for the game session
    13. static enum {START, PLAYING, QUIT, OWIN, XWIN, DRAW} state;
    14.  
    15. // declare possible display mode
    16. static enum {REGULAR, PROGRESSIVE} display_mode;
    17.  
    18. // this stucture simply defines the characteristic of each player
    19. typedef struct {
    20. std::string name;
    21. char symbol;
    22. int move;
    23. int game_win;
    24. int draw_num;
    25. bool selected;
    26. bool win;
    27. } player;
    28.  
    29.  
    30. void display_board(); // display the current board position on the screen
    31. void seed_random_generator(); // seed the random generator (rand function) with the current time
    32. void get_move(); // get move for the current player, the computer also use that function
    33. void display_result(); // display the final result of the game on the screen
    34. void select_game_type(); // this function generate the menu for the game
    35. void get_player_symbol(); // get symbol (X, O) for the first player selected
    36. void get_player_name(); // gets the name of the current player
    37. void reset_player_name(); // reset the name of the players
    38. void set_game_statistic(); // this function makes update of the stististic for the game
    39. void display_game_statistic(); // this function display the statistic for the current games
    40. void reset_game_statistic(); // resets the game statistic
    41. void find_winner(); // this function finds the winner once the agme has ended
    42. void reset_winner(); // resets the winners before starting new game
    43. void update_board(); // this function makes an update of the current board position
    44. void update_screen(); // updates the screen
    45. void select_display_mode(); // selects different display style for displaying tictactoe board
    46. void set_game_level(); // will probably be be featured in the next version of this program
    47. void verify_move(); // verifies if the current move is legal
    48. void reset_state(); // resets the state of the game
    49. void reset_board(); // clears the board, removes all move that has been made
    50. void initialise_player_move(); // reinitialise players move
    51. void display_intro(); // display some text on the screen to introduce the game
    52. void display_game_progress(); // displays who made the last and also current position
    53. void update_game(); // makes updates for the current position
    54. void setup_game_screen(); // reset the dimension of the dos console window
    55. bool wrong_symbol(); // check if the symbols that are choose by players are valid
    56. bool wrong_selection(); // verifies the validity of current selection made by the player
    57. bool game_over(); // checks if the game is over
    58. bool free_square(); // verifies if the square chosen by the current player is free
    59.  
    60.  
    61. // generates the list of legal moves for the current position
    62. void generate_moves(char _board[25], std::list<int> &move_list);
    63.  
    64. // determin current game state (XWIN, OWIN, DRAW, PLAYING)
    65. void check_game_state(char board[25]);
    66.  
    67. // makes an evaluation of the current board position,
    68. // the function returns +INFINITY if the computer is winning,
    69. // -INFINITY if the computer is loosing or 0 if it is a draw
    70. int evaluate_position(char _board[25], player _player);
    71.  
    72.  
    73. // the three functions below implements the MiniMax algorithm
    74.  
    75. int MiniMax(char _board[25], player _player); // main function for MiniMax algorithm
    76. int MinMove(char _board[25], player _player); // helper function for MiniMax algorithm
    77. int MaxMove(char _board[25], player _player); // helper function for MiniMax algorithm
    78.  
    79. static player player1, player2, current_player; // 'player1' represent the first player and 'player2' the second one
    80. static std::string game_type; // 'human vs human', 'human vs computer', 'computer vs computer'
    81. static std::string prev_game_type; // variable for storing previous 'game type'
    82.  
    83. static char board[25] = {0}; // this variable is used to represent the board for the game
    84. static char cSymbol; // this variable represent the symbol for the current player
    85. static int nMove; // represents the last move made by the current player
    86.  
    87.  
    88. int main() {
    89. seed_random_generator();
    90. setup_game_screen();
    91.  
    92. display_intro();
    93. select_game_type();
    94.  
    95. if(state != QUIT) {
    96. get_player_name();
    97. get_player_symbol();
    98.  
    99. while(state != QUIT) {
    100. while(state == PLAYING) {
    101. initialise_player_move();
    102. get_move();
    103. update_game();
    104. }
    105.  
    106. if(game_over()) {
    107. find_winner();
    108. display_result();
    109. set_game_statistic();
    110. reset_state();
    111. reset_board();
    112. display_intro();
    113. }
    114.  
    115. select_game_type();
    116. }
    117. }
    118. return 0;
    119. }
    120.  
    121. // selects game type
    122. void select_game_type() {
    123. std::cout << " 1 - play a game against the computer" << std::endl;
    124. std::cout << " 7 - quit the program" << std::endl;
    125. std::cout << "\nselection: ";
    126. int choice;
    127. std::cin >> choice;
    128. if(!std::cin.good()) {
    129. std::cout << "please notice that you can only enter integers for the selection" << std::endl;
    130. update_screen();
    131. }
    132. switch(choice) {
    133. case 1:
    134. game_type = "human vs computer";
    135. break;
    136. case 7:
    137. state = QUIT;
    138. break;
    139. default:
    140. std::cout << "Invalid Selection." << std::endl;
    141. update_screen();
    142. }
    143. if((choice == 1)) {
    144. if(prev_game_type != "" && game_type != prev_game_type) {
    145. reset_game_statistic();
    146. reset_player_name();
    147. get_player_name();
    148. get_player_symbol();
    149. }
    150. if(game_type.length() > 0) {
    151. prev_game_type = game_type;
    152. }
    153. }
    154. }
    155.  
    156. // gets current player name
    157. void get_player_name() {
    158. std::cin.sync();
    159. if(game_type == "human vs computer") {
    160. std::cout << "\nplease enter your name: ";
    161. std::getline(std::cin, player1.name);
    162. if(player1.name.length() == 0) {
    163. get_player_name();
    164. }
    165. player2.name = "the computer";
    166. }
    167. }
    168.  
    169. void reset_player_name() {
    170. player1.name.erase();
    171. player2.name.erase();
    172. }
    173.  
    174. // gets symbol for the current player
    175. void get_player_symbol() {
    176. if(game_type == "human vs computer") {
    177. int selection = rand() % 2;
    178. //int selection = 1;
    179. if(selection == 0) {
    180. rand() % 2 == 0 ? player2.symbol = 'X' : player2.symbol = 'O';
    181. cSymbol = player2.symbol;
    182. player2.selected = 1;
    183. std::cout << player2.name << " will play \'" << player2.symbol << "\'" << std::endl;
    184. } else if(selection == 1) {
    185. std::cout << player1.name << " please enter your symbol (X, O): ";
    186. std::cin >> player1.symbol;
    187. player1.symbol = toupper(player1.symbol);
    188. cSymbol = player1.symbol;
    189. player1.selected = 1;
    190. }
    191. }
    192. if(!std::cin.good() || wrong_symbol()) {
    193. std::cout << "please notice that your symbol can only be X or O" << std::endl;
    194. system("pause");
    195. get_player_symbol();
    196. }
    197. if(!player2.selected) {
    198. player1.symbol == 'X' ? player2.symbol = 'O' : player2.symbol = 'X';
    199. player1.symbol == 'O' ? player2.symbol = 'X' : player2.symbol = 'O';
    200. } else if(!player1.selected) {
    201. player2.symbol == 'X' ? player1.symbol = 'O' : player1.symbol = 'X';
    202. player2.symbol == 'O' ? player1.symbol = 'X' : player1.symbol = 'O';
    203. }
    204. state = PLAYING;
    205. }
    206.  
    207. // gets move for the current player
    208. void get_move() {
    209. std::cin.sync();
    210. if(game_type == "human vs computer") {
    211. if(player1.selected) {
    212. std::cout << "\n" << player1.name << " please enter your move (1 - 9): ";
    213. std::cin >> player1.move;
    214. if(!std::cin.good()) {
    215. std::cin.clear();
    216. std::cin.sync();
    217. }
    218. nMove = player1.move;
    219. cSymbol = player1.symbol;
    220. current_player = player1;
    221. player1.selected = 0;
    222. player2.selected = 1;
    223. sleep(1);
    224. } else if(player2.selected) {
    225. player2.move = MiniMax(board, player2);
    226. nMove = player2.move;
    227. cSymbol = player2.symbol;
    228. current_player = player2;
    229. player1.selected = 1;
    230. player2.selected = 0;
    231. reset_state();
    232. sleep(1);
    233. }
    234. }
    235. verify_move();
    236. if(game_over()) {
    237. return;
    238. }
    239. }
    240.  
    241. // set game statististic for current match
    242. void set_game_statistic() {
    243. if(state == START) {
    244. player1.game_win = 0;
    245. player1.draw_num = 0;
    246. player1.win = 0;
    247. player2.game_win = 0;
    248. player2.draw_num = 0;
    249. player2.win = 0;
    250. } else if(state == XWIN || state == OWIN) {
    251. if(player1.win) {
    252. player1.game_win++;
    253. player1.win = 0;
    254. } else if(player2.win) {
    255. player2.game_win++;
    256. player2.win = 0;
    257. }
    258. } else if(state == DRAW) {
    259. player1.draw_num++;
    260. player2.draw_num++;
    261. }
    262. }
    263.  
    264. // resets game statistic
    265. void reset_game_statistic() {
    266. player1.game_win = 0;
    267. player1.draw_num = 0;
    268. player1.win = 0;
    269. player2.game_win = 0;
    270. player2.draw_num = 0;
    271. player2.win = 0;
    272. player1.selected = 0;
    273. player2.selected = 0;
    274. }
    275.  
    276. // displayes game statistic on the screen
    277. void display_game_statistic() {
    278. if(state != START) {
    279. std::cout << "\ngame statistic" << std::endl;
    280. std::cout << "==============" << std::endl;
    281. std::cout << player1.name << " has won " << player1.game_win << " game(s)." << std::endl;
    282. std::cout << player2.name << " has won " << player2.game_win << " game(s)." << std::endl;
    283. std::cout << player1.draw_num << " game(s) ended with a draw." << std::endl;
    284. }
    285. }
    286.  
    287. // finds the winner once the game is over
    288. void find_winner() {
    289. if(state == XWIN && player1.symbol == 'X') {
    290. player1.win = 1;
    291. } else if(state == OWIN && player1.symbol == 'O') {
    292. player1.win = 1;
    293. } else if(state == XWIN && player2.symbol == 'X') {
    294. player2.win = 1;
    295. } else if(state == OWIN && player2.symbol == 'O') {
    296. player2.win = 1;
    297. }
    298. }
    299.  
    300. // resets winners
    301. void reset_winner() {
    302. player1.win = 0;
    303. player2.win = 0;
    304. }
    305.  
    306. // verifies validity of symbols (X, O)
    307. bool wrong_symbol() {
    308. return (cSymbol != 'X' && cSymbol != 'O');
    309. }
    310.  
    311. // checks for wrong selection
    312. bool wrong_selection() {
    313. return !(nMove > 0 && nMove < 26);
    314. }
    315.  
    316. // reinitialise player moves
    317. void initialise_player_move() {
    318. player1.move = -1;
    319. player2.move = -1;
    320. }
    321.  
    322. // check for ending of the game
    323. bool game_over() {
    324. return (state == XWIN || state == OWIN || state == DRAW);
    325. }
    326.  
    327. // resets state of the game
    328. void reset_state() {
    329. state = PLAYING;
    330. }
    331.  
    332. // clears the board
    333. void reset_board() {
    334. for(int i = 0; i < 25; ++i) {
    335. board[i] = 0;
    336. }
    337. }
    338.  
    339. // updates currrent board position
    340. void update_board() {
    341. if(state == PLAYING) {
    342. if(player1.move != -1 && player2.move == -1) {
    343. board[player1.move - 1] = player1.symbol;
    344. } else if(player2.move != -1) {
    345. board[player2.move - 1] = player2.symbol;
    346. }
    347. }
    348. }
    349.  
    350.  
    351.  
    352.  
    353. // displays outcome the game on the screen
    354. void display_result() {
    355. if(player1.win) {
    356. std::cout << player1.name << " has won the game!" << std::endl;
    357. } else if(player2.win) {
    358. std::cout << player2.name << " has won the game!" << std::endl;
    359. } else if(player1.win == 0 && player2.win == 0) {
    360. std::cout << "no winner, this game is a draw." << std::endl;
    361. }
    362. system("pause");
    363. system("cls");
    364. }
    365.  
    366. // make updates of the current game
    367. void update_game() {
    368. update_board();
    369. display_game_progress();
    370. check_game_state(board);
    371. }
    372.  
    373. // checks is the square selected by the current player is free
    374. bool free_square() {
    375. if(player1.move != -1 && player2.move == -1) {
    376. return board[player1.move - 1] == 0;
    377. } else if(player2.move != -1) {
    378. return board[player2.move - 1] == 0;
    379. }
    380. return 0;
    381. }
    382.  
    383. // displays board on the screen
    384. void display_board() {
    385. std::cout << std::endl;
    386. /*for(int i = 0; i < 3;i++)
    387. {
    388. for(int j = 0; j < 3;j++)
    389. {
    390. std::cout << " " << board[i] << " | ";
    391. }
    392. std::cout << std::endl;
    393. //std::cout << "-----------" << std::endl;
    394. } */
    395. std::cout << " " << board[0] << " | " << board[1] << " | " << board[2] << " | " << board[3] << " | " << board[4] << std::endl;
    396. std::cout << "-----------" << std::endl;
    397. std::cout << " " << board[5] << " | " << board[6] << " | " << board[7] << " | " << board[7] << " | " << board[9] << std::endl;
    398. std::cout << "-----------" << std::endl;
    399. std::cout << " " << board[10] << " | " << board[11] << " | " << board[12] << " | " << board[13] << " | " << board[14] << std::endl;
    400. std::cout << "-----------" << std::endl;
    401. std::cout << " " << board[15] << " | " << board[16] << " | " << board[17] << " | " << board[18] << " | " << board[19] << std::endl;
    402. std::cout << "-----------" << std::endl;
    403. std::cout << " " << board[20] << " | " << board[21] << " | " << board[22] << " | " << board[23] << " | " << board[24] << std::endl;
    404.  
    405.  
    406.  
    407.  
    408.  
    409. std::cout << std::endl;
    410.  
    411. std::cin.sync();
    412. }
    413.  
    414. // displays the progress of the game
    415. void display_game_progress() {
    416. if(display_mode == PROGRESSIVE) {
    417. system("cls");
    418. display_intro();
    419. }
    420. std::cout << "\n\nboard position after " << current_player.name << "\'s move" << std::endl;
    421. display_board();
    422. }
    423.  
    424. // verifies validity of the current move
    425. // if the player makes an invalid move
    426. // the function will ask the player
    427. // to select another move
    428. void verify_move() {
    429. if(wrong_selection() || !free_square()) {
    430. std::cout << "Invalid Move." << std::endl;
    431. if(player2.move == -1) {
    432. player1.selected = 1;
    433. player2.selected = 0;
    434. } else {
    435. player1.selected = 0;
    436. player2.selected = 1;
    437. }
    438. system("pause");
    439. if(game_type == "human vs computer") {
    440. player1.selected = 1;
    441. player2.selected = 0;
    442. }
    443. get_move();
    444. }
    445. }
    446.  
    447. // seeds random generator with current time
    448. void seed_random_generator() {
    449. srand((unsigned) time(NULL));
    450. }
    451.  
    452. // displays intro for the game
    453. void display_intro() {
    454. std::cout << "\n=========================================" << std::endl;
    455. std::cout << " TicTacToe game v1.0 - Abhijay Vuyyuru " << std::endl;
    456. std::cout << "=========================================" << std::endl;
    457. }
    458.  
    459. // resize the dimension of the dos console window
    460. void setup_game_screen() {
    461. //system("mode con: cols=99 lines=300");
    462. }
    463.  
    464. // refresh game screen
    465. void update_screen() {
    466. system("pause");
    467. system("cls");
    468. std::cin.clear();
    469. std::cin.sync();
    470. display_intro();
    471. select_game_type();
    472. }
    473.  
    474. // determins if the current board position is final, if it is a win, draw
    475. void check_game_state(char board[25]) {
    476. if ((board[0] == cSymbol && board[1] == cSymbol && board[2] == cSymbol && board[3] == cSymbol && board[4] == cSymbol ) || (board[5] == cSymbol && board[6] == cSymbol && board[7] == cSymbol && board[8] == cSymbol && board[9] == cSymbol ) ||
    477. (board[10] == cSymbol && board[11] == cSymbol && board[12] == cSymbol && board[13] == cSymbol && board[14] == cSymbol ) ||
    478. (board[15] == cSymbol && board[16] == cSymbol && board[17] == cSymbol && board[18] == cSymbol && board[19] == cSymbol ) ||
    479. (board[20] == cSymbol && board[21] == cSymbol && board[22] == cSymbol && board[23] == cSymbol && board[24] == cSymbol ) ||
    480. (board[0] == cSymbol && board[5] == cSymbol && board[10] == cSymbol && board[15] == cSymbol && board[20] == cSymbol ) ||
    481. (board[1] == cSymbol && board[6] == cSymbol && board[11] == cSymbol && board[16] == cSymbol && board[21] == cSymbol ) ||
    482. (board[2] == cSymbol && board[7] == cSymbol && board[12] == cSymbol && board[17] == cSymbol && board[22] == cSymbol ) ||
    483. (board[3] == cSymbol && board[8] == cSymbol && board[13] == cSymbol && board[18] == cSymbol && board[23] == cSymbol ) ||
    484. (board[4] == cSymbol && board[9] == cSymbol && board[14] == cSymbol && board[19] == cSymbol && board[24] == cSymbol ) ||
    485. (board[0] == cSymbol && board[6] == cSymbol && board[12] == cSymbol && board[18] == cSymbol && board[24] == cSymbol ) ||
    486. (board[4] == cSymbol && board[8] == cSymbol && board[12] == cSymbol && board[16] == cSymbol && board[20] == cSymbol ) )
    487. {
    488. if(cSymbol == 'X') {
    489. state = XWIN;
    490. } else if(cSymbol == 'O') {
    491. state = OWIN;
    492. }
    493. }
    494. else {
    495. state = DRAW;
    496. for(int i = 0; i < 25; ++i) {
    497. if(board[i] == 0) {
    498. state = PLAYING;
    499. break;
    500. }
    501. }
    502. }
    503. }
    504.  
    505. // generates all the possible moves for the current board position
    506. void generate_moves(char _board[25], std::list<int> &move_list) {
    507. for(int i = 0; i < 25; ++i) {
    508. if(_board[i] == 0) {
    509. move_list.push_back(i);
    510. }
    511. }
    512. }
    513.  
    514. // evaluates the current board position
    515. int evaluate_position(char _board[25], player _player) {
    516. check_game_state(_board);
    517. if(game_over()) {
    518. if((state == XWIN && _player.symbol == 'X') ||
    519. (state == OWIN && _player.symbol == 'O')) {
    520. return +INFINITY;
    521. } else if((state == XWIN && _player.symbol == 'O') ||
    522. (state == OWIN && _player.symbol == 'X')) {
    523. return -INFINITY;
    524. } else if(state == DRAW) {
    525. return 0;
    526. }
    527. }
    528. return -1;
    529. }
    530.  
    531. // returns best move for the current computer player
    532. int MiniMax(char _board[25], player _player) {
    533. int best_val = -INFINITY, index = 0;
    534. std::list<int> move_list;
    535. char best_moves[25] = {0};
    536. generate_moves(_board, move_list);
    537. while(!move_list.empty()) {
    538. _board[move_list.front()] = _player.symbol;
    539. cSymbol = _player.symbol;
    540. int val = MinMove(_board, _player);
    541. if(val > best_val) {
    542. best_val = val;
    543. index = 0;
    544. best_moves[index] = move_list.front() + 1;
    545. } else if(val == best_val) {
    546. best_moves[++index] = move_list.front() + 1;
    547. }
    548. _board[move_list.front()] = 0;
    549. move_list.pop_front();
    550. }
    551. if(index > 0) {
    552. index = rand() % index;
    553. }
    554. return best_moves[index];
    555. }
    556.  
    557. // finds best move for 'min player'
    558. int MinMove(char _board[25], player _player) {
    559. int pos_value = evaluate_position(_board, _player);
    560. if(pos_value != -1) {
    561. return pos_value;
    562. }
    563. int best_val = +INFINITY;
    564. std::list<int> move_list;
    565. generate_moves(_board, move_list);
    566. while(!move_list.empty()) {
    567. _player.symbol == 'X' ? cSymbol = 'O' : cSymbol = 'X';
    568. _board[move_list.front()] = cSymbol;
    569. int val = MaxMove(_board, _player);
    570. if(val < best_val) {
    571. best_val = val;
    572. }
    573. _board[move_list.front()] = 0;
    574. move_list.pop_front();
    575. }
    576. return best_val;
    577. }
    578.  
    579. // finds best move for 'max player'
    580. int MaxMove(char _board[25], player _player) {
    581. int pos_value = evaluate_position(_board, _player);
    582. if(pos_value != -1) {
    583. return pos_value;
    584. }
    585. int best_val = -INFINITY;
    586. std::list<int> move_list;
    587. generate_moves(_board, move_list);
    588. while(!move_list.empty()) {
    589. _player.symbol == 'X' ? cSymbol = 'X' : cSymbol = 'O';
    590. _board[move_list.front()] = cSymbol;
    591. int val = MinMove(_board, _player);
    592. if(val > best_val) {
    593. best_val = val;
    594. }
    595. _board[move_list.front()] = 0;
    596. move_list.pop_front();
    597. }
    598. return best_val;
    599. }