My friend is kind of new to coding and he decided to write a console tic-tac-toe game. He then showed me his code and I thought it would be cool to optimize it a bit and rewrite in my own way. I tried my best but I'm pretty new to C++ and I'm not super experienced in optimizing things so I'd like to know about ways to improve my code in both performance and/or readability with the preference being on performance.
#include <iostream>
#include <stdlib.h>
#include <sstream>
using namespace std;
char board[3][3] = {{'1', '2', '3'}, {'4', '5', '6'}, {'7', '8', '9'}};
string choice;
int row, rows[3], col, columns[3], diagonal, anti_diagonal, intchoice;
bool turn = true; // True = X turn, False = O turn
void display_board()
{
cout << "\nPlayer 1[X] Player 2[O]\n";
cout << " | | \n";
cout << " " << board[0][0] << " | " << board[0][1] << " | " << board[0][2] << " \n";
cout << "_____|_____|_____\n";
cout << " | | \n";
cout << " " << board[1][0] << " | " << board[1][1] << " | " << board[1][2] << " \n";
cout << "_____|_____|_____\n";
cout << " | | \n";
cout << " " << board[2][0] << " | " << board[2][1] << " | " << board[2][2] << " \n";
cout << " | | \n";
}
bool isint(string str) // Input check for an int value
{
for (int i = 0; i < str.length(); i++)
if (!isdigit(str[i]))
return false;
return true;
}
void player_turn()
{
while (true)
{
if (turn)
cout << "Player - 1 [X] turn : ";
else if (!turn)
cout << "Player - 2 [0] turn : ";
getline(cin, choice); // Prevents multiple turns in one input
if (!isint(choice))
continue;
stringstream(choice) >> intchoice;
switch (intchoice)
{
case 1: row = 0; col = 0;break;
case 2: row = 0; col = 1; break;
case 3: row = 0; col = 2; break;
case 4: row = 1; col = 0; break;
case 5: row = 1; col = 1; break;
case 6: row = 1; col = 2; break;
case 7: row = 2; col = 0; break;
case 8: row = 2; col = 1; break;
case 9: row = 2; col = 2; break;
default: cout << "Invalid Move\n"; continue;
}
if (board[row][col] == 'X' || board[row][col] == 'O')
cout << "Occupied cell\n";
else
{
int change = turn ? 1 : -1; // Increment or Decrement value for win check
board[row][col] = turn ? 'X' : 'O';
rows[row] += change;
columns[col] += change;
if (row == col)
diagonal += change;
if (row == (2 - col))
anti_diagonal += change;
turn = !turn; // Switch turns
return;
}
}
}
bool gameover()
{
for (int i = 0; i < 3; i++)
if (rows[i] > 2 || rows[i] < -2 || columns[i] > 2 || columns[i] < -2 || diagonal > 2 || diagonal < -2 || anti_diagonal > 2 || anti_diagonal < -2)
return true;
return false;
}
int main()
{
cout << "T I C K -- T A C -- T O E -- G A M E";
display_board();
for (int i = 0; i < 9; i++)
{
player_turn();
display_board();
if (gameover())
break;
}
if (!gameover())
cout << "Draw";
else if (turn)
cout << "Player 'O' has won";
else if (!turn)
cout << "Player 'X' has won";
}
Here's a couple changes I myself made:
I changed
turn
to abool
value to make it easy to invert and compare (before it was achar
containing either'X'
or'O'
).I removed
bool draw
and instead I check if the game isn't over after there are no turns left (before it was a part ofgameover()
function and changed values each time the function was called).Instead of checking the whole input to be made out of digits I search for the closest non-digit value and go back to the start of the loop.
Instead of comparing all the values from the
board
array I created 4 extra variables:rows[3], columns[3], diagonal, anti_diagonal
. Those variables change their value each time a turn is made by incrementing (if it's X's turn) or decrementing (if O's turn). At the end those values are used to check for win conditions. For examplerows[0]
would represent the 1st row and the value can only be > 2 if there are 3 X's or < -2 if there are 3 O's.
Those are all the major changes I made alongside some minor cleanup like removing unnecessary repeating checks.
P.S. At the time of writing this question I noticed that in my gameover()
function the if
statement inside of the for
loop also checks for diagonal
and anti_diagonal
values even tho they only need to be checked once so that might be something worth changing.
cout << "T I C K -- T A C -- T O E -- G A M E";
spell Tic/Tick differently. Any particular reason for that? \$\endgroup\$cout
at the start of themain
func. Everything else was written by me so I doubt that it violates publishing rights. \$\endgroup\$