I am a beginner programmer and I've been trying to complete an unbeatable tic-tac-toe program, my code is below. The program itself works, but it seems rather inefficient, as I literally have to type out every case of possible victory or defeat. Any suggestions on how to make the code more efficient, or general suggestions, are appreciated! Everything is in one 500 line program.
#include <iostream>
using namespace std;
const short SIZE = 3;
class Board
{
private:
char square[SIZE][SIZE], aiMark, userMark;
public:
Board();
void instructions();
void showBoard();
//Gets user pre-approved user move.
void userMove(short n, short n2)
{square[n][n2] = userMark;}
bool checker(short n, short n2);
void computerMove(short n);
bool win();
bool lose();
bool full();
void clear();
};
//The constructor initializes each space (square) to a blank space and the markers to x and o.
Board::Board()
{
userMark = 'x', aiMark = 'o';
for(short i = 0; i < SIZE; i++){
for(short j = 0; j < SIZE; j++){
square[i][j] = ' ';
}
}
}
//Outlines the basic instructions and coordinate system.
void Board::instructions(){
cout<<"This is a Tic Tac Toe progam. All of the standard rules of Tic Tac Toe apply (i.e. three in a row is a victory).\n"
<<"A player may make a move by inputting the coordinance of the point they would like "
<<"to mark (ex. type \"0 0\" for the first space).\nAn outline of the table "
<<"and coordinacnes are below:\n"
<<"\n0 0 | 0 1 | 0 2\n"
<<"----|-----|----\n"
<<"1 0 | 1 1 | 1 2\n"
<<"----|-----|----\n"
<<"2 0 | 2 1 | 2 2\n\n";
cout<<"Player 1 will make the first move, followed by Player 2, until the game ends. "
<<"You are the x's, while the computer is the o's.\n";
cout<<"Press enter when you are ready to continue. ";
cin.get();
}
//Shows the current state of the board.
void Board::showBoard()
{
cout<<square[0][0]<<" | "<<square[0][1]<<" | "<<square[0][2]<<endl
<<"---|---|---"<<endl
<<square[1][0]<<" | "<<square[1][1]<<" | "<<square[1][2]<<endl
<<"---|---|---"<<endl
<<square[2][0]<<" | "<<square[2][1]<<" | "<<square[2][2]<<endl;
}
//Checks to see if either the computer or user is making a move that has already been made.
bool Board::checker(short n, short n2)
{
if(square[n][n2] == aiMark || square[n][n2] == userMark){
return false;
} else {
return true;
}
}
//A random assignment is made, then based on game type (i.e. if user is Player 1) a
//specialized move is selected, then if any cases of imminent defeat or victory are detected
//it responds accordingly.
void Board::computerMove(short n)
{
if(win()){
cout<<"You have won!\n";
return;
}
if(full()){
cout<<"You have tied the computer!\n";
return;
}
short r, r2, r3;
double seed;
seed = time(0);
srand(seed);
r = 0 + rand() % 3;
r2 = 0 + rand() % 3;
//Starts off with a random move assignment in the event that no cases fit.
while(!checker(r, r2)){
r = 0 + rand() % 3;
r2 = 0 + rand() % 3;
}
switch(n){
//This code is employs strategies reserved specifically for when the computer moves second.
case 1:
if(checker(1,1) && (square[0][0] == userMark || square[0][2] == userMark || square[2][0] == userMark || square[2][2] == userMark)){
//If the user begins in a corner AI moves to the center.
r = 1, r2 = 1;
} else if (square[1][1] == aiMark){
//After AI moves to the center AI attempts to occupy an edge.
if(checker(0,1)){
r = 0, r2 = 1;
} else if (checker(1,0)){
r = 1, r2 = 0;
} else if (checker(1,2)){
r = 1, r2 = 2;
} else if (checker(2,1)){
r = 2, r2 = 1;
}
} else if (square[1][1] == userMark){
//If the user begins in the center the AI moves to a corner.
if(checker(0,0)){
r = 0, r2 = 0;
} else if (checker(0,2)){
r = 0, r2 = 2;
} else if (checker(2,0)){
r = 2, r2 = 0;
} else if (checker(2,2)){
r = 2, r2 = 2;
}
} else if (square[0][1] == userMark || square[1][0] == userMark || square[1][2] == userMark || square[2][1] == userMark){
//If the user starts on the side the AI moves to the center.
r=1, r2=1;
}
break;
//This code is employs strategies reserved specifically for when the computer moves first.
case 2:
if (checker(0,0) && checker(0,2) && checker(2,0) && checker(2,2)){
//Makes sure that the computer starts in a corner.
r3 = 0 + rand() % 4;
if(r3 == 0){
r = 0, r2 = 0;
} else if (r3 == 1){
r = 0, r2 = 2;
} else if (r3 == 2){
r = 2, r2 = 0;
} else if (r3 == 3){
r = 2, r2 = 2;
}
} else if (square[1][1] == userMark){
//If the user moves to the center AI moves to the opposite corner.
if(square[0][0] == aiMark && checker(2,2)){
r = 2, r2 = 2;
} else if (square[0][2] == aiMark && checker(2,0)){
r = 2, r2 = 0;
} else if (square[2][0] == aiMark && checker(0,2)){
r = 0, r2 = 2;
} else if (square[2][2] == aiMark && checker(0,0)){
r = 0, r2 = 0;
}
} else if (square[1][1] != userMark){
//If the user does not move to the center AI moves to another corner which ensures a win.
if(checker(0,0)){
r = 0, r2 = 0;
} else if (checker(0,2)){
r = 0, r2 = 2;
} else if (checker(2,0)){
r = 2, r2 = 0;
} else if (checker(2,2)){
r = 2, r2 = 2;
}
}
break;
}
//CODE FOR CHECKING IMMINENT USER VICTORY BEGINS HERE.
//Checks diagonals.
if(square[2][0] == userMark && square[1][1] == userMark && square[0][2] == ' '){
r = 0, r2 = 2;
} else if (square[2][0] == ' ' && square[1][1] == userMark && square[0][2] == userMark){
r = 2, r2 = 0;
} else if (square[0][0] == ' ' && square[1][1] == userMark && square[2][2] == userMark){
r = 0, r2 = 0;
} else if (square[0][0] == userMark && square[1][1] == userMark && square[2][2] == ' '){
r = 2, r2 = 2;
} else if (square[0][0] == userMark && square[1][1] == ' ' && square[2][2] == userMark){
r = 1, r2 = 1;
} else if (square[2][0] == userMark && square[1][1] == ' ' && square[0][2] == userMark){
r = 1, r2 = 1;
}
//Checks all vertical possibilities.
if(square[0][0] == userMark && square[1][0] == userMark && square[2][0] == ' '){
r = 2, r2 = 0;
} else if (square[0][0] == userMark && square[1][0] == ' ' && square[2][0] == userMark){
r = 1, r2 = 0;
} else if (square[0][0] == ' ' && square[1][0] == userMark && square[2][0] == userMark){
r = 0, r2 = 0;
} else if (square[0][1] == userMark && square[1][1] == userMark && square[2][1] == ' '){
r = 2, r2 = 1;
} else if (square[0][1] == userMark && square[1][1] == ' ' && square[2][1] == userMark){
r = 1, r2 = 1;
} else if (square[0][1] == ' ' && square[1][1] == userMark && square[2][1] == userMark){
r = 0, r2 = 1;
} else if (square[0][2] == userMark && square[1][2] == userMark && square[2][2] == ' '){
r = 2, r2 = 2;
} else if (square[0][2] == userMark && square[1][2] == ' ' && square[2][2] == userMark){
r = 1, r2 = 2;
} else if (square[0][2] == ' ' && square[1][2] == userMark && square[2][2] == userMark){
r = 0, r2 = 2;
}
//Checks all horizontal possibilities.
if(square[0][0] == userMark && square[0][1] == userMark && square[0][2] == ' '){
r = 0, r2 = 2;
} else if (square[0][0] == userMark && square[0][1] == ' ' && square[0][2] == userMark){
r = 0, r2 = 1;
} else if (square[0][0] == ' ' && square[0][1] == userMark && square[0][2] == userMark){
r = 0, r2 = 0;
} else if (square[1][0] == userMark && square[1][1] == userMark && square[1][2] == ' '){
r = 1, r2 = 2;
} else if (square[1][0] == userMark && square[1][1] == ' ' && square[1][2] == userMark){
r = 1, r2 = 1;
} else if (square[1][0] == ' ' && square[1][1] == userMark && square[1][2] == userMark){
r = 1, r2 = 0;
} else if (square[2][0] == userMark && square[2][1] == userMark && square[2][2] == ' '){
r = 2, r2 = 2;
} else if (square[2][0] == userMark && square[2][1] == ' ' && square[2][2] == userMark){
r = 2, r2 = 1;
} else if (square[2][0] == ' ' && square[2][1] == userMark && square[2][2] == userMark){
r = 2, r2 = 0;
}
//CODE FOR CHECKING IMMINENT USER VICTORY ENDS HERE.
//CODE FOR CHECKING IMMINENT COMPUTER VICTORY BEGINS HERE.
//Checks diagonals.
if(square[2][0] == aiMark && square[1][1] == aiMark && square[0][2] == ' '){
r = 0, r2 = 2;
} else if (square[2][0] == ' ' && square[1][1] == aiMark && square[0][2] == aiMark){
r = 2, r2 = 0;
} else if (square[0][0] == ' ' && square[1][1] == aiMark && square[2][2] == aiMark){
r = 0, r2 = 0;
} else if (square[0][0] == aiMark && square[1][1] == aiMark && square[2][2] == ' '){
r = 2, r2 = 2;
} else if (square[0][0] == aiMark && square[1][1] == ' ' && square[2][2] == aiMark){
r = 1, r2 = 1;
} else if (square[2][0] == aiMark && square[1][1] == ' ' && square[0][2] == aiMark){
r = 1, r2 = 1;
}
//Checks all vertical possibilities.
if(square[0][0] == aiMark && square[1][0] == aiMark && square[2][0] == ' '){
r = 2, r2 = 0;
} else if (square[0][0] == aiMark && square[1][0] == ' ' && square[2][0] == aiMark){
r = 1, r2 = 0;
} else if (square[0][0] == ' ' && square[1][0] == aiMark && square[2][0] == aiMark){
r = 0, r2 = 0;
} else if (square[0][1] == aiMark && square[1][1] == aiMark && square[2][1] == ' '){
r = 2, r2 = 1;
} else if (square[0][1] == aiMark && square[1][1] == ' ' && square[2][1] == aiMark){
r = 1, r2 = 1;
} else if (square[0][1] == ' ' && square[1][1] == aiMark && square[2][1] == aiMark){
r = 0, r2 = 1;
} else if (square[0][2] == aiMark && square[1][2] == aiMark && square[2][2] == ' '){
r = 2, r2 = 2;
} else if (square[0][2] == aiMark && square[1][2] == ' ' && square[2][2] == aiMark){
r = 1, r2 = 2;
} else if (square[0][2] == ' ' && square[1][2] == aiMark && square[2][2] == aiMark){
r = 0, r2 = 2;
}
//Checks all horizontal possibilities.
if(square[0][0] == aiMark && square[0][1] == aiMark && square[0][2] == ' '){
r = 0, r2 = 2;
} else if (square[0][0] == aiMark && square[0][1] == ' ' && square[0][2] == aiMark){
r = 0, r2 = 1;
} else if (square[0][0] == ' ' && square[0][1] == aiMark && square[0][2] == aiMark){
r = 0, r2 = 0;
} else if (square[1][0] == aiMark && square[1][1] == aiMark && square[1][2] == ' '){
r = 1, r2 = 2;
} else if (square[1][0] == aiMark && square[1][1] == ' ' && square[1][2] == aiMark){
r = 1, r2 = 1;
} else if (square[1][0] == ' ' && square[1][1] == aiMark && square[1][2] == aiMark){
r = 1, r2 = 0;
} else if (square[2][0] == aiMark && square[2][1] == aiMark && square[2][2] == ' '){
r = 2, r2 = 2;
} else if (square[2][0] == aiMark && square[2][1] == ' ' && square[2][2] == aiMark){
r = 2, r2 = 1;
} else if (square[2][0] == ' ' && square[2][1] == aiMark && square[2][2] == aiMark){
r = 2, r2 = 0;
}
//CODE FOR CHECKING IMMINENT COMPUTER VICTORY ENDS HERE.
square[r][r2] = aiMark;
if(lose()){
cout<<"You have lost!\n";
return;
}
if(full()){
cout<<"You have tied the computer!\n";
return;
}
if(!full() && !lose()){
cout<<"Computer move: "<<r<<" "<<r2<<endl;
}
}
//Checks to see if the user has made three moves in a row.
bool Board::win()
{
if(square[0][0] == userMark && square[0][1] == userMark && square[0][2] == userMark){
return true;
} else if (square[1][0] == userMark && square[1][1] == userMark && square[1][2] == userMark){
return true;
} else if (square[2][0] == userMark && square[2][1] == userMark && square[2][2] == userMark){
return true;
} else if (square[0][0] == userMark && square[1][0] == userMark && square[2][0] == userMark){
return true;
} else if (square[0][1] == userMark && square[1][1] == userMark && square[2][1] == userMark){
return true;
} else if (square[0][2] == userMark && square[1][2] == userMark && square[2][2] == userMark){
return true;
} else if (square[0][0] == userMark && square[1][1] == userMark && square[2][2] == userMark){
return true;
} else if (square[0][2] == userMark && square[1][1] == userMark && square[2][0] == userMark){
return true;
} else {
return false;
}
}
//Checks to see if computer has made three moves in a row.
bool Board::lose()
{
if(square[0][0] == aiMark && square[0][1] == aiMark && square[0][2] == aiMark){
return true;
} else if (square[1][0] == aiMark && square[1][1] == aiMark && square[1][2] == aiMark){
return true;
} else if (square[2][0] == aiMark && square[2][1] == aiMark && square[2][2] == aiMark){
return true;
} else if (square[0][0] == aiMark && square[1][0] == aiMark && square[2][0] == aiMark){
return true;
} else if (square[0][1] == aiMark && square[1][1] == aiMark && square[2][1] == aiMark){
return true;
} else if (square[0][2] == aiMark && square[1][2] == aiMark && square[2][2] == aiMark){
return true;
} else if (square[0][0] == aiMark && square[1][1] == aiMark && square[2][2] == aiMark){
return true;
} else if (square[0][2] == aiMark && square[1][1] == aiMark && square[2][0] == aiMark){
return true;
} else {
return false;
}
}
//Checks to see if all the squares are full; if they are, it returns false to indicate a tie.
bool Board::full()
{
short count = 0;
for(short i = 0; i < SIZE; i++){
for(short j = 0; j < SIZE; j++){
if(square[i][j] == aiMark || square[i][j] == userMark){
count++;
} else {
continue;
}
}
}
if(count == 9){
return true;
} else {
return false;
}
}
//Clears board in event that it is not the first game.
void Board::clear()
{
for(short i = 0; i < SIZE; i++){
for(short j = 0; j < SIZE; j++){
square[i][j] = ' ';
}
}
}
int main()
{
Board bobj;
short choice, num, num2, count = 0;
char choice2;
//Displays instructions for users (only first time).
bobj.instructions();
do {
count++;
cout<<endl<<"Game "<<count<<"\n";
for(short i = 0; i < 26; i++){
cout<<"-";
}
cout<<endl;
cout<<"Select Player 1:\n"
<<"1. User\n2. AI\nEnter choice (ex. 1 or 2): ";
cin>>choice;
//Determines which code to run based on who is Player 1
//(i.e. runs case 1 if user is Player 1 and case 2 if AI is Player 1).
switch(choice){
//Runs when user is Player 1.
case 1:
cout<<"The game has begun. You are Player 1. ";
do {
cout<<"Enter your move as a coordinance: ";
cin>>num>>num2;
while(num > 3 || num < 0 || num2 > 3 || num2 < 0 || !bobj.checker(num, num2)){
cout<<"ERROR: you have not entered a valid coordinance.\n";
cout<<"Enter your move as a coordinance: ";
cin>>num>>num2;
}
//Executes moves.
bobj.userMove(num, num2);
bobj.computerMove(1);
bobj.showBoard();
} while(!bobj.lose() && !bobj.win() && !bobj.full());
break;
//Runs when AI is Player 1.
case 2:
cout<<"The game has begun. The computer is Player 1.\n";
//First computer move.
bobj.computerMove(2);
bobj.showBoard();
do {
cout<<"Enter your move as a coordinance: ";
cin>>num>>num2;
while(num>3 || num < 0 || num2 > 3 || num2 < 0 || !bobj.checker(num, num2)){
cout<<"ERROR: you have not entered a valid coordinance.\n";
cout<<"Enter your move as a coordinance: ";
cin>>num>>num2;
}
//Executes moves.
bobj.userMove(num, num2);
bobj.computerMove(2);
bobj.showBoard();
} while (!bobj.lose() && !bobj.win() && !bobj.full());
break;
default:
//If user does not enter 1 or 2 runs this code.
cout<<"You have not entered a valid option.\n";
break;
}
bobj.clear();
cout<<"Would you like to try again? Enter y/n: ";
cin>>choice2;
} while (choice2 != 'n' && choice2 != 'N');
cout<<"Thanks for playing!\n";
return 0;
}