# Where to call AI_response?

This topic is 4673 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

## Recommended Posts

I'm having some troubles with Tic-Tac-Toe, that I have been unable to resolve. I have written the code for the player to place his piece; however, I am having issues with the computer's response. I have written a function for the computer's response, however I am not sure where to call it. Here's my code (cut down for efficiency): Implementation of the Grid class -
void Grid::blitFromInput()
{
}

void Grid::blitToRect(int x, int y, char turn)
{
int temp_x = x;
int temp_y = y;
x *= static_cast<int>(scale_factor / 1.5) + 1;
y *= static_cast<int>(scale_factor / 1.5) + 1;
if(!(theGrid[temp_x][temp_y] == 'X' || theGrid[temp_x][temp_y] == 'O')) {
if (turn == 'X') {
draw_sprite(g->buffer,x_bitmap, x, y);
} else {
draw_sprite(g->buffer,o_bitmap, x, y);
}
theGrid[temp_x][temp_y] = turn;
}

}

bool Grid::AI_response()
{
srand(time(0));
int x = rand()%3;
int y = rand()%3;
if(theGrid[x][y] == 0)
{
blitToRect(x, y, 'O');
return true;
}
else
{
for(int i = 0; i < 3; i++)
for(int j = 0; j < 3; j++)
{
if(theGrid[j] == 0) blitToRect(i, j, 'O');
break;
}
return true;
}
}


Main loop -
while ( !done )
{
g->update();
g->render();
if (key[ KEY_ESC ]) done = true;
while(speed_counter > 0)
{

if(key[KEY_RIGHT])// If the user hits the right key, change the picture's X coordinate
{
//my_pic_x ++;// Moving right so up the X coordinate by 1
}
else if(key[KEY_LEFT])// Ditto' - only for left key
{
//my_pic_x --;// Moving left, so lower the X coordinate by 1
}
else if(key[KEY_UP])// If the user hits the up key, change the picture's Y coordinate
{
//my_pic_y --;// Moving up, so lower the Y coordinate by 1
}
else if(key[KEY_DOWN])// Ditto' - only for down
{
//my_pic_y ++;// Moving down, so up the Y coordinate by 1
}
grid->blitFromInput();
speed_counter--;
}//End timer test
}


This is a logic problem, not a syntax or memory error. Thank you for your time.

##### Share on other sites
Wow, that code is indented weird. *At least it seems that way to me*

It seems to me you'd do it right after the while(speed_counter > 0) loop has exited.

##### Share on other sites
Originally I tried to put it right after the while like you said, but then it started making multiple moves in response to one of the player's moves.

##### Share on other sites
The APs solution seems right. Are you sure that you are reinitializing speed_counter after calling AI_response?

##### Share on other sites
If I do what the AP said, then the computer makes 4 moves before the player even makes 1.

##### Share on other sites
I don't see where you're keeping track of whose turn it is. Perhaps your computer is taking 4 turns because the player's not fast enough to jump in for his turn? Did you just not post this logic?

##### Share on other sites
You don't really need to keep track of whose turn it is.

You do need to call AI_response immediately after the "blitFromInput" IF the player actually made a move. So you need that function to communicate whether or not a move was actually entered (or, actually call AI_response itself, which is what I'd recommend). You don't need to return anything from AI_response, because the computer will always move when asked.

Also, you might want to do something to prevent the user from pressing multiple keys at once and getting all those boxes :) There is probably a good way to get rid of the repetition in that code, too. Are those constant values in a continuous range? If so, something like this should work:

for (int i = 0; i < 9; i++) {  if(key[KEY_1_PAD + i]) {    // i == 0 -> key 1 pressed    // i == 8 -> key 9 pressed    // So we do some div and mod arithmetic to get the right row/column...    blitToRect(i % 3, 2 - (i/3), 'X');    // then tell the AI response to kick in...    AI_response();    // and bail out, because we want to ignore multiple keypresses at once    return;  }}

Also, you'll probably want to check for illegal moves (i.e. on an already-occupied square). Just silently return for those; they'll probably happen a fair bit if the user holds down a key...

Also, I'm wondering why that division by 1.5 isn't incorporated into your (presumably constant) "scale_factor"...

##### Share on other sites
Quote:
 Also, you'll probably want to check for illegal moves (i.e. on an already-occupied square). Just silently return for those; they'll probably happen a fair bit if the user holds down a key...

Doesn't my code already check for the illegal move? Is there a problem with the way that I do it?

##### Share on other sites
Oh, you have it handled in blitToRect(), sorry.

... It might have been better to write the console version first to get the game logic worked out, and then fit graphical stuff into that framework :/

##### Share on other sites
I have the graphical issues resolved, it is just the logical issues that I am having problems with. I don't have problems with the game graphics, just the game logic. So it wouldn't really matter whether I did text-based or graphical, because I would still have the same question.

##### Share on other sites
Well yes, but game logic is often easier to sort out when you do it first and don't have to embed it into the existing rest-of-everything. [smile]

##### Share on other sites
Quote:
 Original post by ZahlmanWell yes, but game logic is often easier to sort out when you do it first and don't have to embed it into the existing rest-of-everything. [smile]

I have this almost working, but it's just a minor logic error somewhere that is causing AI_response() to be called more than once in a turn.

##### Share on other sites
I think the problem is that the main game loop instructs
the computer to make its move regardless if the player made a move
or not.

the following code might work:

int Grid::blitFromInput()         {    if(key[NULL])              // the user isnt pressing anything...    {        return 0;              // the user didnt make a move.    }        if(key[KEY_1_PAD])	blitToRect(0, 2, 'X');	if(key[KEY_2_PAD])	blitToRect(1, 2, 'X');	if(key[KEY_3_PAD])	blitToRect(2, 2, 'X');	if(key[KEY_4_PAD])	blitToRect(0, 1, 'X');	if(key[KEY_5_PAD])	blitToRect(1, 1, 'X');	if(key[KEY_6_PAD])	blitToRect(2, 1, 'X');	if(key[KEY_7_PAD])	blitToRect(0, 0, 'X');	if(key[KEY_8_PAD])	blitToRect(1, 0, 'X');	if(key[KEY_9_PAD])	blitToRect(2, 0, 'X');        return 1;}

and then in the main loop, you might put something like this:

        // Call AI_response() if move was made        if(blitFromInput()) AI_response();

##### Share on other sites
I'm starting to think that there's a problem with my AI_response() function. Here it is:

bool Grid::AI_response(){	srand(time(0));		int x = rand()%3;	int y = rand()%3;	if(theGrid[x][y] == 0)	{		blitToRect(x, y, 'O');		return true;	}	else	{		for(int i = 0; i < 3; i++)			for(int j = 0; j < 3; j++)			{				if(theGrid[j] == 0) blitToRect(i, j, 'O');				return true;			}	}	return true;}

What it's supposed to do is randomly pick a square and, if that square is taken, it should go through all the squares until it finds an empty one, where it will place the piece. It's not supposed to be smart.

##### Share on other sites
I've gone through my code several times, using the "teddy-bear" approach (explain the code line-by-line to a teddy-bear). However, I am unable to find the error. Presumably, it is in AI_response().

Help is greatly appreciated.

##### Share on other sites
Hey CodeTitan, did you try AcePilot's solution? I went through your code and was about to tell you the same thing before I saw his post.

##### Share on other sites
Quote:
 Original post by wasted_druidHey CodeTitan, did you try AcePilot's solution? I went through your code and was about to tell you the same thing before I saw his post.

I did try AcePilot's solution, however, the computer still makes several moves before the player does. I am wondering how it is possible for the AI_response() to be called before the player even tries to make a move.

##### Share on other sites
Hmmm... his solution will let the computer make a movie if ANY key was pressed, not just the keypad.... it's close though. Try throwing a switch and in each case of a keypad key return true, otherwise false...

##### Share on other sites
Quote:
 Original post by wasted_druidHmmm... his solution will let the computer make a movie if ANY key was pressed, not just the keypad.... it's close though. Try throwing a switch and in each case of a keypad key return true, otherwise false...

OK, I'll try that. Thanks! I hope it works...

##### Share on other sites
Ok, so now my blitFromInput function looks like this:

int Grid::blitFromInput()         {    if(key[NULL])              // the user isnt pressing anything...    {        return 0;              // the user didnt make a move.    }    if(key[KEY_1_PAD]){	blitToRect(0, 2, 'X');return true;}	if(key[KEY_2_PAD]){	blitToRect(1, 2, 'X');return true;}	if(key[KEY_3_PAD]){	blitToRect(2, 2, 'X');return true;}	if(key[KEY_4_PAD]){	blitToRect(0, 1, 'X');return true;}	if(key[KEY_5_PAD]){	blitToRect(1, 1, 'X');return true;}	if(key[KEY_6_PAD]){	blitToRect(2, 1, 'X');return true;}	if(key[KEY_7_PAD]){	blitToRect(0, 0, 'X');return true;}	if(key[KEY_8_PAD]){	blitToRect(1, 0, 'X');return true;}	if(key[KEY_9_PAD]){	blitToRect(2, 0, 'X');return true;}}

Nope, it still doesn't work. Although this is progress...[smile].

##### Share on other sites
How does your call to AI_Response look now?

*EDIT* And remember to return false after that string of if statements.

##### Share on other sites
My call to AI_response() looks like this:
if(grid->blitFromInput()) grid->AI_response();

So my blitFromInput() looks like this now, after some slight editation:

bool Grid::blitFromInput()         {    if(key[NULL])              // the user isnt pressing anything...    {        return false;              // the user didnt make a move.    }        if(key[KEY_1_PAD]){	blitToRect(0, 2, 'X');return true;}        if(key[KEY_2_PAD]){	blitToRect(1, 2, 'X');return true;}	if(key[KEY_3_PAD]){	blitToRect(2, 2, 'X');return true;}	if(key[KEY_4_PAD]){	blitToRect(0, 1, 'X');return true;}	if(key[KEY_5_PAD]){	blitToRect(1, 1, 'X');return true;}	if(key[KEY_6_PAD]){	blitToRect(2, 1, 'X');return true;}	if(key[KEY_7_PAD]){	blitToRect(0, 0, 'X');return true;}	if(key[KEY_8_PAD]){	blitToRect(1, 0, 'X');return true;}	if(key[KEY_9_PAD]){	blitToRect(2, 0, 'X');return true;}	return false;}

##### Share on other sites
Okay. All good stuff. If it doesn't work still, I have one more suggestion, then you've got me stumped. Change blitToRect to return a boolean. True on a valid move false on an invalid one. Then change

to

if(key[KEY_1_PAD]){if(blitToRect(0, 2, 'X')) return true; else return false;}

I think the computer might read the same keypress through multiple loops since you're just checking for keystate and not for a keydown event.

##### Share on other sites
Ok, I'll try that.

##### Share on other sites
Quote:
 Original post by CodeTitanI have this almost working, but it's just a minor logic error somewhere that is causing AI_response() to be called more than once in a turn.

Aha! I think I figured it out!
bool Grid::AI_response(){	srand(time(0));		int x = rand()%3;	int y = rand()%3;	if(theGrid[x][y] == 0)	{		blitToRect(x, y, 'O');		return true;	}	else	{		for(int i = 0; i < 3; i++)			for(int j = 0; j < 3; j++)			{				if(theGrid[j] == 0) blitToRect(i, j, 'O');				break;   // Only breaks you out of Inner Loop!			}		return true;	}}

The break statement in AI_response() just breaks you out of the INNER LOOP! The outer loop keeps going resulting in multiple turns for the AI for each player turn!

Bow before my debugging prowess. :-)