Now, take this with a grain of salt, because I have never done any AI before. This was my solution in C++ (which was about 800 lines :o )
I had methods like makeMove, and checkWin, which would call other (private) methods that would return the appropriate row or colum. I did that by going through each row, column, and diagonal, and if there were exactly 2 spaces taken up by the opponant, I returned it. After my makeMove() function had the correct row/column/diagonal, I simply went through it, looking for the empty spot (which I then filled with my player).
So, following that idea, my AI makeMove() function looks like this:
bool CBoard::makeMove()
{
//int chances = rand() % 2;
bool moveMade = false;
int *spot;
if(!moveMade)
{
spot = checkOffence();
if(spot != NULL)
{
//std::cout << "spot[0] = " << spot[0] << std::endl;
//std::cout << "spot[1] = " << spot[1] << std::endl;
Board::board[spot[0]][spot[1]] = player;
//std::cout << "offensive move made" << std::endl;
moveMade = true;
delete [] spot;
}
}
if(!moveMade)
{
spot = checkDefence();
if(spot != NULL)
{
Board::board[spot[0]][spot[1]] = player;
//std::cout << "defensive move" << std::endl;
moveMade = true;
delete [] spot;
}
}
if(!moveMade)
{
makeRandomMove();
//std::cout << "random move" << std::endl;
}
return true;
}
So, as you can (hopefully) see, first I look for an offensive move, then I look for a defensive move, and then I make a random move. The methods my makeMove() calls are all private.
The problem with my solution is that at the beginning of the game, the AI will often make a random move in a bad position, such as on a center-spot of a column on the right or left side.
Therefore, I'd like to add to my makeMove() function to first check to see if they can make a move in the center, or corner, and
then make a random move.
Like I said, this could well be (and probably is) a bad solution. But it's always helpful to look at things from different angles.