-
Creating a good computer AI for a tic tac toe game.
I'm almost done with this little mini project that I've been doing for a couple of days and I was wondering how I can go about creating a computer AI given the way that I structured my code. Also, suggestions how I can make my code look better is always welcomed.
main.cpp
Code:
#include <iostream>
#include "Board.h"
#include <limits>
#include <cctype>
using namespace std;
int main()
{
// Welcoming message.
cout << "Welcome users to my very own tic-tac-toe game. " << endl;
// Object to access the class functions
Board process;
char response1; // A variable to gather input from player1 if they want to quit the game or not
char response2; // A variable to gather input from player2 if they want to quit the game or not
int tileCounter = 0; // Represents how many tiles have been used.
int playerWins1 = 0; // How many times player1 won a match.
int PlayerLose1 = 0; // How many times Player1 lost a match.
int playerLose2 = 0; // How many times player 2 lost a match.
int playerWins2 = 0; // How many times player2 won a match.
int tie = 0; // How many times a match ended up in a tie.
while(toupper(response1) != 'N' || toupper(response2) != 'N')
{
process.choosingLetter(); // Players choose their letter for the game.
cout << endl << endl;
process.draw(); // Creates the board.
tileCounter = 0; // reinitializes after each game.
// Game loop
// If there are no 3 matches, keep playing.
while(!process.isGameOver(tileCounter,playerWins1,PlayerLose1, playerWins2, playerLose2, tie)) // If there are no 3 matches, keep playing.
{
cout << endl;
process.playerMove1(); // Outputs player1's move.
cout << endl << endl << endl;
process.draw(); // Call the board again to see the changes.
cout << endl;
tileCounter++;
if(process.isGameOver(tileCounter,playerWins1,PlayerLose1, playerWins2, playerLose2, tie) == true)
{
break;
}
process.playerMove2(); // Outputs player2's move.
cout << endl << endl;
process.draw();
tileCounter++;
}
process.playAgain(response1, response2);
}
process.scoreBoard(playerWins1,PlayerLose1, playerWins2, playerLose2, tie);
return 0;
}
board.h
Code:
#ifndef BOARD_H
#define BOARD_H
class Board
{
public:
Board();
void draw();
void choosingLetter();
void playerMove1();
void playerMove2();
void playerInputCheck1();
void playerInputCheck2();
void playerLocationCheck1();
void playerLocationCheck2();
bool isGameOver(int &, int &, int &, int &, int &, int &);
void refresh();
void playAgain(char &, char &);
void scoreBoard(int &, int &, int &, int &, int &);
private:
char board[3][3];
char PlayerLetter1; // The letter that player 1 will be.
char PlayerLetter2; // The letter that player 2 will be
std::string playerInput1; // Input from player1.
std::string playerInput2; // Input from player2.
};
#endif
board.cpp
Code:
#include <iostream>
#include <limits>
#include "Board.h"
using namespace std;
Board::Board()
{
for(unsigned int i = 0; i < 3; i++)
for(unsigned int j = 0; j < 3; j++)
board[i][j] = ' ';
}
void Board::draw()
{
for(unsigned int i = 0; i < 3; i++)
{
cout << "\t\t";
for(unsigned int j = 0; j < 3; j++)
{
// Display the board.
cout << "|" << board[i][j] << "|";
}
cout << endl; // Create a space between each row and column.
}
}
void Board::choosingLetter()
{
// Prompt the user to choose the letter X or O
cout << "\nPlayer 1, which letter would you like to start as? X or O? ";
cout << endl;
cout << "\n*Note X or O are the only acceptable inputs. ";
cin >> PlayerLetter1;
cin.ignore(numeric_limits<streamsize>::max(), '\n');
// Check for valid input.
while(PlayerLetter1 != 'X' && PlayerLetter1 != 'O')
{
cerr << "\nError, invalid input. ";
cin >> PlayerLetter1;
cin.ignore(numeric_limits<streamsize>::max(), '\n');
}
// If player 1 chooses X, then player2 will be O and vice versa.
if(PlayerLetter1 == 'X')
PlayerLetter2 = 'O';
else if(PlayerLetter1 == 'O')
PlayerLetter2 = 'X';
}
void Board::playerMove1()
{
cout << endl;
// Commands that dictates where the letter will be placed.
cout << "\t\t" << "Legend Key" << endl << endl;
cout << "UL=UpperLeft UC=UpperCenter UR=UpperRight " << endl;
cout << "LC=LeftCenter MC=MiddleCenter RC=RightCenter " << endl;
cout << "BL=BottomLeft BM=BottomMiddle BR=BottomRight " << endl;
cout << endl;
// Prompting Player1.
cout << "\nPlayer 1, pick a field. ";
// Checks for valid letter placement and non duplicate locations.
playerLocationCheck1();
}
void Board::playerMove2()
{
cout << endl;
// Commands that dictates where the letter will be placed.
cout << "\t\t" << "Legend Key" << endl << endl;
cout << "UL=UpperLeft UC=UpperCenter UR=UpperRight " << endl;
cout << "LC=LeftCenter MC=MiddleCenter RC=RightCenter " << endl;
cout << "BL=BottomLeft BM=BottomMiddle BR=BottomRight " << endl;
cout << endl;
// Prompting Player2.
cout << "\nPlayer 2, pick a field. ";
// Checks for valid letter placement and non duplicate locations.
playerLocationCheck2();
}
void Board::playerInputCheck1()
{
cin >> playerInput1;
cin.ignore(numeric_limits<streamsize>::max(), '\n');
while(playerInput1 != "UL" && playerInput1 != "UC" && playerInput1 != "UR"
&& playerInput1 != "LC" && playerInput1 != "MC" && playerInput1 != "RC"
&& playerInput1 != "BL" && playerInput1 != "BM" && playerInput1 != "BR")
{
cerr << "\nError, unknown command entered. Read the legend key. ";
cin >> playerInput1;
cin.ignore(numeric_limits<streamsize>::max(), '\n');
}
}
void Board::playerInputCheck2()
{
cin >> playerInput2;
cin.ignore(numeric_limits<streamsize>::max(), '\n');
while(playerInput2 != "UL" && playerInput2 != "UC" && playerInput2 != "UR"
&& playerInput2 != "LC" && playerInput2 != "MC" && playerInput2 != "RC"
&& playerInput2 != "BL" && playerInput2 != "BM" && playerInput2 != "BR")
{
cerr << "\nError, unknown command entered. Read the legend key. ";
cin >> playerInput2;
cin.ignore(numeric_limits<streamsize>::max(), '\n');
}
}
void Board::playerLocationCheck1()
{
bool isEmpty;
while(!isEmpty)
{
// Checks for valid commands.
playerInputCheck1();
if(playerInput1=="UL" && (board[0][0] != 'X' && board[0][0] != 'O'))
{
board[0][0] = PlayerLetter1;
isEmpty = true;
}
else if(playerInput1=="UC" && (board[0][1] != 'X' && board[0][1] != 'O'))
{
board[0][1] = PlayerLetter1;
isEmpty = true;
}
else if(playerInput1=="UR" && (board[0][2] != 'X' && board[0][2] != 'O'))
{
board[0][2] = PlayerLetter1;
isEmpty = true;
}
else if(playerInput1=="LC" && (board[1][0] != 'X' && board[1][0] != 'O'))
{
board[1][0] = PlayerLetter1;
isEmpty = true;
}
else if(playerInput1=="MC" && (board[1][1] != 'X' && board[1][1] != 'O'))
{
board[1][1] = PlayerLetter1;
isEmpty = true;
}
else if(playerInput1=="RC" && (board[1][2] != 'X' && board[1][2] != 'O'))
{
board[1][2] = PlayerLetter1;
isEmpty = true;
}
else if(playerInput1=="BL" && (board[2][0] != 'X' && board[2][0] != 'O'))
{
board[2][0] = PlayerLetter1;
isEmpty = true;
}
else if(playerInput1=="BM" && (board[2][1] != 'X' && board[2][1] != 'O'))
{
board[2][1] = PlayerLetter1;
isEmpty = true;
}
else if(playerInput1=="BR" && (board[2][2] != 'X' && board[2][2] != 'O'))
{
board[2][2] = PlayerLetter1;
isEmpty = true;
}
else
{
cerr << "\nError, this position has already been taken Try again. ";
isEmpty = false;
}
}
}
void Board::playerLocationCheck2()
{
bool isEmpty;
while(!isEmpty)
{
// Checks for valid commands.
playerInputCheck2();
if(playerInput2=="UL" && (board[0][0] != 'X' && board[0][0] != 'O'))
{
board[0][0] = PlayerLetter2;
isEmpty = true;
}
else if(playerInput2=="UC" && (board[0][1] != 'X' && board[0][1] != 'O'))
{
board[0][1] = PlayerLetter2;
isEmpty = true;
}
else if(playerInput2=="UR" && (board[0][2] != 'X' && board[0][2] != 'O'))
{
board[0][2] = PlayerLetter2;
isEmpty = true;
}
else if(playerInput2=="LC" && (board[1][0] != 'X' && board[1][0] != 'O'))
{
board[1][0] = PlayerLetter2;
isEmpty = true;
}
else if(playerInput2=="MC" && (board[1][1] != 'X' && board[1][1] != 'O'))
{
board[1][1] = PlayerLetter2;
isEmpty = true;
}
else if(playerInput2=="RC" && (board[1][2] != 'X' && board[1][2] != 'O'))
{
board[1][2] = PlayerLetter2;
isEmpty = true;
}
else if(playerInput2=="BL" && (board[2][0] != 'X' && board[2][0] != 'O'))
{
board[2][0] = PlayerLetter2;
isEmpty = true;
}
else if(playerInput2=="BM" && (board[2][1] != 'X' && board[2][1] != 'O'))
{
board[2][1] = PlayerLetter2;
isEmpty = true;
}
else if(playerInput2=="BR" && (board[2][2] != 'X' && board[2][2] != 'O'))
{
board[2][2] = PlayerLetter2;
isEmpty = true;
}
else
{
cerr << "\nError, this position has already been taken Try again. ";
isEmpty = false;
}
}
}
bool Board::isGameOver(int &tileCounter, int &playerWins1, int &PlayerLose1, int &playerWins2, int &playerLose2, int &tie)
{
bool gameOver1 = false; // If there are 3 X's.
bool gameOver2 = false; // If there are 3 O's.
bool tieGame = false; // If there are no more tiles.
bool noGameOver; // If there are no 3 X's or 3 O's.
if(board[0][0] == 'X' && board[0][1] == 'X' && board [0][2] == 'X')
{
gameOver1 = true;
}
else if(board[1][0] == 'X' && board[1][1] == 'X' && board [1][2] == 'X')
{
gameOver1 = true;
}
else if(board[2][0] == 'X' && board[2][1] == 'X' && board [2][2] == 'X')
{
gameOver1 = true;
}
else if(board[0][0] == 'X' && board[1][0] == 'X' && board [2][0] == 'X')
{
gameOver1 = true;
}
else if(board[0][1] == 'X' && board[1][1] == 'X' && board [2][1] == 'X')
{
gameOver1 = true;
}
else if(board[0][2] == 'X' && board[1][2] == 'X' && board [2][2] == 'X')
{
gameOver1 = true;
}
else if(board[0][0] == 'X' && board[1][1] == 'X' && board [1][2] == 'X')
{
gameOver1 = true;
}
else if(board[0][2] == 'X' && board[1][1] == 'X' && board [2][0] == 'X')
{
gameOver1 = true;
}
else if(board[0][0] == 'O' && board[0][1] == 'O' && board [0][2] == 'O')
{
gameOver2 = true;
}
else if(board[1][0] == 'O' && board[1][1] == 'O' && board [1][2] == 'O')
{
gameOver2 = true;
}
else if(board[2][0] == 'O' && board[2][1] == 'O' && board [2][2] == 'O')
{
gameOver2 = true;
}
else if(board[0][0] == 'O' && board[1][0] == 'O' && board [2][0] == 'O')
{
gameOver2 = true;
}
else if(board[0][1] == 'O' && board[1][1] == 'O' && board [2][1] == 'O')
{
gameOver2 = true;
}
else if(board[0][2] == 'O' && board[1][2] == 'O' && board [2][2] == 'O')
{
gameOver2 = true;
}
else if(board[0][0] == 'O' && board[1][1] == 'O' && board [1][2] == 'O')
{
gameOver2 = true;
}
else if(board[0][2] == 'O' && board[1][1] == 'O' && board [2][0] == 'O')
{
gameOver2 = true;
}
if(gameOver1 == true)
{
if(PlayerLetter1 == 'X')
{
playerWins1++;
playerLose2++;
cout << "Game over! Player 1 is the victor! " << endl;
return gameOver1;
}
else if(PlayerLetter2 == 'X')
{
playerWins2++;
PlayerLose1++;
cout << "Game over! Player 2 is the victor! " << endl;
return gameOver1;
}
}
else if(gameOver2 == true)
{
if(PlayerLetter1 == 'O')
{
playerWins1++;
playerLose2++;
cout << "Game over! Player 1 is the victor! " << endl;
return gameOver2;
}
else if(PlayerLetter2 == 'O')
{
playerWins2++;
PlayerLose1++;
cout << "Game over! Player 2 is the victor! " << endl;
return gameOver2;
}
}
else if(tileCounter == 9)
{
tie++;
tieGame = true;
cout << "Tie game! No one wins! " << endl;
return tieGame;
}
else if(gameOver1 && gameOver2 == false)
{
noGameOver = false;
}
return noGameOver;
}
void Board::refresh()
{
char newTile = ' '; // gets rid of all the X's and O's
for(unsigned int i = 0; i < 3; i++)
for(unsigned int j = 0; j < 3; j++)
board[i][j] = newTile;
}
void Board::playAgain(char &playerResponse1, char &playerResponse2)
{
cout << "\n\nWould you like to play again player1? [y/n] ";
cin >> playerResponse1;
cin.ignore(numeric_limits<streamsize>::max(), '\n');
while(toupper(playerResponse1) != 'Y' && toupper(playerResponse1) != 'N')
{
cerr << "\nError, Invalid response. ";
cin >> playerResponse1;
cin.ignore(numeric_limits<streamsize>::max(), '\n');
}
cout << "\n\nWould you like to play again player2? [y/n] ";
cin >> playerResponse2;
cin.ignore(numeric_limits<streamsize>::max(), '\n');
while(toupper(playerResponse2) != 'Y' && toupper(playerResponse2) != 'N')
{
cerr << "\nError, Invalid response. ";
cin >> playerResponse2;
cin.ignore(numeric_limits<streamsize>::max(), '\n');
}
if(toupper(playerResponse1) == 'Y' && toupper(playerResponse2) == 'Y')
{
cout << "\n\nCreating a new board to play on. " << endl;
refresh(); // refreshes the board.
}
else
cout << "\n\nThanks for playing! " << endl << endl << endl;
}
void Board::scoreBoard(int &playerWins1, int &PlayerLose1, int &playerWins2, int &playerLose2, int &tie)
{
cout << "\t\t\tScoreBoard " << endl << endl;
cout << "--------------------------------------------------------------" << endl;
cout << " \t\tWins\t\tLosses " << endl << endl;
cout << "player 1: " << "\t\t\t" << playerWins1 << "\t\t" << PlayerLose1 << endl;
cout << "player 2: " << "\t\t\t" << playerWins2 << "\t\t" << playerLose2 << endl << endl;
cout << "Number of tie games: " << tie << endl;
cout << "--------------------------------------------------------------" << endl;
}
-
Re: Creating a good computer AI for a tic tac toe game.
Quote:
Originally Posted by
Freddy Kreuger
how I can go about creating a computer AI given the way that I structured my code.
What do you mean by "good computer AI" and how does this property depend on code structure?
There's very little structure in your code actually. Except from the Board class you haven't defined a single own type. Your code is more C than C++.
Splitting a C++ classes into .h and .cpp files is not how you give structure to code in C++. You do it by defining your own classes (and structs).
Forget about the .h / .cpp split. It's of no importance really. Put everything in .h and let the compiler do the rest.
-
Re: Creating a good computer AI for a tic tac toe game.
1.) What I mean by a good computer AI is an AI that won't let the user win by randomly placing on X's or O's on the board(it would place an X or an O right when the user is about to win).
2.) What I'm saying when I say "code structure", I'm asking how can incorporate a computer ai without overhauling or changing the structure of my program.(I', talking about the way the program looks and all the functions and variable that are inside the program).
3.) In what way is my program more c oriented than c++ oriented? Is the functions or libraries that I'm using that make more c than anything? Please explain.
-
Re: Creating a good computer AI for a tic tac toe game.
Quote:
Originally Posted by
Freddy Kreuger
1.) What I mean by a good computer AI is an AI that won't let the user win by randomly placing on X's or O's on the board(it would place an X or an O right when the user is about to win).
2.) What I'm saying when I say "code structure", I'm asking how can incorporate a computer ai without overhauling or changing the structure of my program.(I', talking about the way the program looks and all the functions and variable that are inside the program).
3.) In what way is my program more c oriented than c++ oriented? Is the functions or libraries that I'm using that make more c than anything? Please explain.
Your program is mostly C because you're not defining own classes.
Okay you have the Board class but one swallow doesn't make a summer as they say. :)
And avoid plaititues like "good computer AI". It's unprofessional.
-
Re: Creating a good computer AI for a tic tac toe game.
Ok, if you may, can you offer a mini example of how you would've effectively defined my class and properly implement it(I've done most of the work. All I'm asking for is an example so that I can learn.)?
-
Re: Creating a good computer AI for a tic tac toe game.
Quote:
Originally Posted by
Freddy Kreuger
Ok, if you may, can you offer a mini example of how you would've effectively defined my class and properly implement it(I've done most of the work. All I'm asking for is an example so that I can learn.)?
You'll have to be a little patient.
In due time your teacher will introduce what makes C++ more than C.
Later you may even be asked to rewrite this very first of your programs to reflect what you've learned later in the course. Then you will have graduated from the procedural to the OO and functional paradigms of programing.
Good luck.
-
Re: Creating a good computer AI for a tic tac toe game.
That's all well and good, but the main reason I made this thread was to ask how I can incorporate a computer AI in my program. o you have any pointers on that.
-
Re: Creating a good computer AI for a tic tac toe game.
Quote:
Originally Posted by
Freddy Kreuger
That's all well and good, but the main reason I made this thread was to ask how I can incorporate a computer AI in my program. o you have any pointers on that.
So you want this to be solved,
"1.) What I mean by a good computer AI is an AI that won't let the user win by randomly placing on X's or O's on the board(it would place an X or an O right when the user is about to win)."
What's the problem then? Your program's just a few lines. What's preventing you from just doing what you want?
What kind of help do you want? A kick in the butt to take the plunge and get going?
-
Re: Creating a good computer AI for a tic tac toe game.
A general structure on how you usually incorporate a computer AI would be a good start.
-
Re: Creating a good computer AI for a tic tac toe game.
What's also getting to me is that I'm using commands(string like commands to be exact) to dictate where the X or O will be placed(If you go back to my board.cpp, you would see that I'm using commands like UL, UC, UR, that represents the location of the tile) and I don't know how to program the computer to know that the tile has been taken(I'm pretty sure by now you see can that I have never done this before).
-
Re: Creating a good computer AI for a tic tac toe game.
I'm sorry but I haven't the faintest idea of what you're asking.
You're talking like someone who hasn't written this program but wants to modify it without any knowledge of the programming process.
I'm out.
-
Re: Creating a good computer AI for a tic tac toe game.
ok then, instead of dealing with the AI, I was thinking of cleaning the up all the if/else if statements. The pseudocode that I thought is this
for (int i = 0; i < rowSize; i++)
{
if(if the tile is blank)
{
place letter
}
else
output-This spot has been taken Try again.
Ask the user to pick another command(like UL, UC etc).
}
-
Re: Creating a good computer AI for a tic tac toe game.
Code:
while(toupper(response1) != 'N' || toupper(response2) != 'N')
This is your first issue with this code.
If you are trying to go down the ai route, then the program 'learns' from its mistakes so that next time it doesn't make that 'mistake' and tries a different solution.
If you have a class called Board, then this class would be expected to deal with displaying the board, registering a move etc. I would'nt expect it to deal with play again (better in main()) etc. Another class say TicTacToe could be derived from class Board that deals with checking validity of moves, making a move etc.
-
Re: Creating a good computer AI for a tic tac toe game.
Well I don't really see that code is an issue. The reason why is because I want the user to have the option of playing with a computer of a real person. That is why I have two responses that prompt each user if they wish to play again. If one of them doesn't want to play again the game ends. Now, I simply can use another game loop that is for AI's only and then I'll have two game loops that are for real players and a computer AI.
-
Re: Creating a good computer AI for a tic tac toe game.
That code is an issue because for the first time around the while loop, response1 and response2 haven't been initialised and could contain any value!
Code:
char response1 = 'Y'; // A variable to gather input from player1 if they want to quit the game or not
char response2 = 'Y'; // A variable to gather input from player2 if they want to quit the game or not
-
Re: Creating a good computer AI for a tic tac toe game.
Ok I see your point. Regardless of the AI that I'm trying to do, my focus as of now is to get rid of all the if/else in the playerlocation functions and put them into a simple for loop that checks if the tile has an X or an O. If the tile does, then the location has been taken. If the tile doesn't, then the move is valid. I also have to take into consideration that I'm using string commands that dictate where the letter will be place. This is the problem that I'm facing. I don't how to check if the tile is empty and that the command that they picked correspond to where they're trying to place the letter and put it into a simple for loop.
-
Re: Creating a good computer AI for a tic tac toe game.
One way to check is via using a map. A possible test program using this method is
Code:
#include <unordered_map>
#include <iostream>
#include <string>
using namespace std;
struct point
{
int r;
int c;
point(int rr, int cc) : r(rr), c(cc){}
};
string toupper(const string& st)
{
string tu;
for (auto ch : st)
tu += toupper(ch);
return tu;
}
const unordered_map<string, point> msp{ make_pair("UL", point(0, 0)), make_pair("UC", point(0, 1)), make_pair("UR", point(0, 2)),
make_pair("LC", point(1, 0)), make_pair("MC", point(1, 1)), make_pair("RC", point(1, 2)),
make_pair("BL", point(2, 0)), make_pair("BC", point(2, 1)), make_pair("BR", point(2,2))};
int main()
{
char board[3][3] = { 0 };
string inp;
auto mit = msp.cbegin();
for (int cnt = 0; cnt < 9;) {
while ((cout << "Enter location: ") && (cin >> inp) && ((mit = msp.find(toupper(inp))) == msp.end()))
cout << "Invalid location" << endl;
char &pos = board[mit->second.r][mit->second.c];
if (pos > ' ')
cout << "Space taken!" << endl;
else {
pos = 'X';
cnt++;
}
}
}
-
Re: Creating a good computer AI for a tic tac toe game.
This isn't really an AI problem. The game is pretty simple. You just need to define the rules. First look to see if you can win. Second see if the user's about to win and block him. If neither of those look for where you've already gone and try to make a sequence that will let you win. Define the rules and the approach before you write code.
-
Re: Creating a good computer AI for a tic tac toe game.
Quote:
Originally Posted by
Freddy Kreuger
Ok I see your point. Regardless of the AI that I'm trying to do, my focus as of now is to get rid of all the if/else in the playerlocation functions and put them into a simple for loop that checks if the tile has an X or an O. If the tile does, then the location has been taken. If the tile doesn't, then the move is valid. I also have to take into consideration that I'm using string commands that dictate where the letter will be place. This is the problem that I'm facing. I don't how to check if the tile is empty and that the command that they picked correspond to where they're trying to place the letter and put it into a simple for loop.
Your code is very long winded with almost duplicate sections of code. There are easier ways to check for a win or a draw using an array of winning positions. There is also no need to have separate code for each player. There just needs to be code for a player which alternates between 'x' and 'o'. A possible way could be
Code:
#include <unordered_map>
#include <iostream>
#include <string>
using namespace std;
struct point
{
int r;
int c;
point(int rr, int cc) : r(rr), c(cc){}
};
const string lft[3] { "T", "M", "B" };
const string top[3] { "L", "C", "R" };
const char sym[2] { 'X', 'O' };
const unordered_map<string, point> msp {make_pair(lft[0] + top[0], point(0, 0)),
make_pair(lft[0] + top[1], point(0, 1)),
make_pair(lft[0] + top[2], point(0, 2)),
make_pair(lft[1] + top[0], point(1, 0)),
make_pair(lft[1] + top[1], point(1, 1)),
make_pair(lft[1] + top[2], point(1, 2)),
make_pair(lft[2] + top[0], point(2, 0)),
make_pair(lft[2] + top[1], point(2, 1)),
make_pair(lft[2] + top[2], point(2, 2)) };
const point win[8][3] {{ { 0, 0 }, { 0, 1 }, { 0, 2 } },
{ { 1, 0 }, { 1, 1 }, { 1, 2 } },
{ { 2, 0 }, { 2, 1 }, { 2, 2 } },
{ { 0, 0 }, { 1, 0 }, { 2, 0 } },
{ { 0, 1 }, { 1, 1 }, { 2, 1 } },
{ { 0, 2 }, { 1, 2 }, { 2, 2 } },
{ { 0, 0 }, { 1, 1 }, { 2, 2 } },
{ { 0, 2 }, { 1, 1 }, { 2, 0 } } };
string toupper(const string& st)
{
string tu;
for (auto ch : st)
tu += toupper(ch);
return tu;
}
void draw(char board[3][3])
{
cout << endl;
for (int t = 0; t < 3; ++t)
cout << " " << top[t];
cout << endl << endl;
for (int rr = 0; rr < 3; ++rr) {
if (rr > 0)
cout << " -----------" << endl;
for (int cc = 0; cc < 3; ++cc) {
if (cc > 0)
cout << " |";
else
cout << lft[rr] << " ";
cout << " " << board[rr][cc];
}
cout << endl;
}
}
int main()
{
char again = 'n';
do {
char board[3][3] = { 0 };
string inp;
auto mit = msp.cbegin();
char gotwin = ' ';
draw(board);
for (int cnt = 0, play = 0; (cnt < 9) && (gotwin == ' ');) {
while ((cout << "\nEnter location for player " << play + 1 << " [" << sym[play] << "]: ") && (cin >> inp) && ((mit = msp.find(toupper(inp))) == msp.end()))
cout << "Invalid location";
char &pos = board[mit->second.r][mit->second.c];
if (pos > ' ')
cout << "Space taken!" << endl;
else {
pos = sym[play];
++cnt;
play = (play + 1) % 2;
draw(board);
for (int w = 0, b; (w < 8) && (gotwin == ' '); ++w)
if (((b = board[win[w][0].r][win[w][0].c]) > ' ') && (b == board[win[w][1].r][win[w][1].c]) && (b == board[win[w][2].r][win[w][2].c]))
gotwin = b;
if (gotwin != ' ')
cout << "\nPlayer " << (gotwin == 'X' ? "1" : "2") << " wins!" << endl;
}
}
if (gotwin == ' ')
cout << "\nDraw!" << endl;
while ((cout << "\nPlay again? [Y, N]: ") && (cin >> again) && ((again = toupper(again)) != 'N' && (again != 'Y')))
cout << "Please enter 'Y' or 'N': ";
} while (again == 'Y');
}
If you want it as a class, I'll leave that as an exercise! :)
-
Re: Creating a good computer AI for a tic tac toe game.
It looks like a lot of effort (and typing) went into text processing when there is an easier way.
Look at your numeric keypad. Does it look like a tic-tac-toe board? Here are your “commands”:
7 8 9
4 5 6
1 2 3
This simplifies the input and eliminates case sensitivity.
You could keep the board in one-dimensional array where those “commands” are indices into it.
As was noted, I would split your Board class into two: Game and Board, where Game coordinates the play and Board only manages the board.
But first I’d try to remove redundancy. How is playerInputCheck1() different from playerInputCheck2()? Could they be combined?
Same for playerLocationCheck1() and playerLocationCheck2(). If a tile is available for Player1, it should be available for Player2 as well – right?
Your game counters are also redundant: playerWins1 must be equal to playerLose2 and vice versa (or you have a problem).
As for the structure – I would construct an instance of a Game object in main(), let it initialize what’s needed and call , for example, its Play() method.
The board may belong to Game.
Play() may clear the board, choose X or O for the players and alternate moves, possibly checking for the end-of-game status after each move.
Then you may have a base player class with one virtual method Move() returning a valid tile index for that move, and two derived classes for a person and a computer.
Person’s Move() would prompt for an input and validate it, while computer’s Move() will be implemented as your AI.
-
Re: Creating a good computer AI for a tic tac toe game.
Quote:
Originally Posted by
VladimirF
As was noted, I would split your Board class into two: Game and Board, where Game coordinates the play and Board only manages the board.
To add to that, I'd add an abstract class called "Player", which would have different implementations, such as "HumanPlayer" "DumbAI" "SmartAI" etc...
A Game would be made of 2 Players and a Board. The game queries the players for the moves, and manages their turns etc.
-
Re: Creating a good computer AI for a tic tac toe game.
This doesn't need an AI.
There is a simple set of steps that will either make the computer win or make sure it doesn't loose and make the game end in a draw.
follow each step taking a box
1) if you can make 3 in a row/column/diagonal, take that box and win
2) if player has 2 in a row/column/diagonal, block the player by taking whichever box prevents 3
3) play the middle piece if available
4) if any row/column ONLY has boxes of your own, pick another box on that row/column, prefer corners. (disregard diagonals here)
5) pick any corner box
6) pick any side box
That isn't an "AI" per se, it's a playing strategy. In this case the strategy can never loose and forces a draw unless the player makes a mistake.
Some games have winnable strategies. Some (like chess) don't and need elaborate AI's to even achieve semi decent play.
There can be things in between where there are "good" (but beatable) strategies once you figure out how to counter the "AI" and where there are multiple strategies the computer can follow (even where a computer can change strategy mid game).
From a pure technical POV, a AI is not based on strategy rules but instead "learns" as it goes by avoiding to make the mistakes that caused it to loose in previous games.
There are very few actual AI's out there, most are simple strategy rules, brute force approaches (looking only a few steps ahead), or even random with discarding 'Obvious' bad moves.