patterncppMinor
An unbeatable Tic-Tac-Toe game using exhaustive enumeration of all cases
Viewed 0 times
enumerationunbeatabletoealltictacgameusingexhaustivecases
Problem
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
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 >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>num>>num2;
while(num > 3 || num 3 || num2 >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>num>>num2;
while(num>3 || num 3 || num2 >num>>num2;
}
//Executes moves.
bobj.userMove(num, num2);
bobj.computerMove(2);
```
#include
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 >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>num>>num2;
while(num > 3 || num 3 || num2 >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>num>>num2;
while(num>3 || num 3 || num2 >num>>num2;
}
//Executes moves.
bobj.userMove(num, num2);
bobj.computerMove(2);
Solution
[..] it seems rather inefficient, as I literally have to type out every case of possible victory or defeat. [..] Everything is in one 500 line program.
Woah. Yes, that is inefficient, regarding developer time and developer annoyance. I'll attempt an answer now nonetheless. My answer consists of two parts: Part one is a (non-exhaustive) list of things that could be improved in your code. Part two is a implementation of the same idea by myself. In order to be able to show you absolutely fine code I'm posting my own code as another question. That way others can review and comment on it easily.
Review
Better not
There's a lot of discussion in this stackoverflow post.
and later
So a "place" on your board can be either
Member functions that don't modify data members...
... should be marked
But ...
Member functions that shouldn't even be members at all
Why is
Better make it a free function (with a descriptive name), like:
Names
Woah. Yes, that is inefficient, regarding developer time and developer annoyance. I'll attempt an answer now nonetheless. My answer consists of two parts: Part one is a (non-exhaustive) list of things that could be improved in your code. Part two is a implementation of the same idea by myself. In order to be able to show you absolutely fine code I'm posting my own code as another question. That way others can review and comment on it easily.
Review
Better not
using namespace std;There's a lot of discussion in this stackoverflow post.
char is a character, nothing else.char square[SIZE][SIZE], aiMark, userMark;and later
userMark = 'x', aiMark = 'o';
// a for loop setting all elements of sqaure to ' 'So a "place" on your board can be either
' ', 'x' or 'o'. And it can be the same as str[0] with char const * str = "BOooO";. At least according to the compiler, wo wouldn't stop you doing place[0][0] = str[0], because they're of the same type char. Instead of trying to enforce such a separation by "being careful" you should better let the compiler and the type system do this:enum class Mark {
X, O, None
};
// ...
Mark square[SIZE][SIZE];Member functions that don't modify data members...
... should be marked
const:// NO:
// void instructions();
// Yes (sort of):
void instructions() const;But ...
Member functions that shouldn't even be members at all
Why is
void instructions(); even a member function? It doesn't use / modify / need any data members / other member functions of Board at all.Better make it a free function (with a descriptive name), like:
void show_instruction_screen();Names
board.showBoard ? Redundant! Better: board.show(). Even better: stream
- Checking for win/loose/full:
std::count() or std::all_of/std::none_of`Code Snippets
char square[SIZE][SIZE], aiMark, userMark;userMark = 'x', aiMark = 'o';
// a for loop setting all elements of sqaure to ' 'enum class Mark {
X, O, None
};
// ...
Mark square[SIZE][SIZE];// NO:
// void instructions();
// Yes (sort of):
void instructions() const;void show_instruction_screen();Context
StackExchange Code Review Q#144799, answer score: 2
Revisions (0)
No revisions yet.