Hello, I am working on a Tic-Tac-Toe program in C++ which I want to have AI that will either win or draw every time, but will never lose. I think that this is within my grasp, though I am not at all an expert programmer.

I would really appreciate if someone would please read my code and tell me if it is ready to start on the AI, because at the moment I have been trying to write a program that will make it easy to put AI into. I have been trying to put in a variable or something that will keep track of the turn, starting at 1 and going up until 9, because if I did this then there would be no need to check for a win every turn, as the earliest a player can win is on the fifth turn. (If X goes first, as it always will in my program, it can win after it has played 3 times.) Also, I could forgo checking for a draw at all and simply end the game after the ninth turn.

I also would be grateful for any help with checking the wins, though this is not my biggest priority. The graphics library I am using is Allegro, in case anyone needs that information.

The code to my program is below. I can tell you that when you run the program, its window comes up on the screen (this is the opener) and asks if you want to be X or O, explaining that X goes first and O goes second. When you click one or the other, the window becomes a blank board. You are the only player, and so you can click on any square and your marker will appear there. This can be done until the board fills up, but the program does not terminate.

What I want to happen when it is run, is once you get to the board, if you are X then you can place your marker and then an O will appear in the computer's square of choice, or if you are O then the computer's X will appear letting you choose your square. When a player wins, the program will change to a win/lose-congratulation screen, from the point of view of the human. (if human loses the lose screen is presented.) As for draws, it will automatically go to a draw screen after Turn==9 or when all of the squares are filled.

The program is in three files: Main, Classes, Functions, and soon to be AI functions. Sorry for the abundance of it, I know that it is probably too complicated and that I need to clean it up a bit. Thanks anyway in advance!

/*Main*/
#include <allegro.h>
#include "Classes.h"
#include "Functions.h"

int main()
{
    allegro_init();
    install_keyboard();
    install_mouse();
    set_color_depth(24);
    set_gfx_mode(GFX_AUTODETECT, 480, 480, 0, 0);
    
    theBlank=create_bitmap(480, 480);
    theOpener=load_bitmap("Opener.bmp", NULL);
    theWin=load_bitmap("Wim.bmp", NULL);
    theLose=load_bitmap("Lose.bmp", NULL);
    theDraw=load_bitmap("Draw.bmp", NULL);
    theX=load_bitmap("X pic.bmp", NULL);
    theO=load_bitmap("O pic.bmp", NULL);
    
    show_mouse(screen);
    
    Opener();
    
    destroy_bitmap(theBlank);
    destroy_bitmap(theOpener);
    destroy_bitmap(theWin);
    destroy_bitmap(theLose);
    destroy_bitmap(theDraw);
    destroy_bitmap(theX);
    destroy_bitmap(theO);
    
    return 0;
}
END_OF_MAIN();
/*Class Declarations*/
#include <allegro.h>

int Square[9]={0, 0, 0, 0, 0, 0, 0, 0, 0};//This is used for occupation, wins, and losses
short box;//Global Variable for what Square[], use ONLY with GetPosition(), XMove(), and OMove()

/****************************************************************************************/
//When you put in the coordnates for the X or O to be drawn at, you must add two to each
//coordnate (eg. (0, 0) becomes (2, 2))

BITMAP *theBlank;
BITMAP *theOpener;
BITMAP *theWin;
BITMAP *theLose;
BITMAP *theDraw;
BITMAP *theX;
BITMAP *theO;

class XMarker//X
{
      public:
             //constructors
             XMarker(int Box);
             ~XMarker(){}
             
             void DrawX();
             
      private:
              int Xx;//For clarification, this is X's x coordnate.
              int Xy;
};

XMarker::XMarker(int Box)
{
     if(Box==0){Xx=481; Xy=481;}
     else if(Box==1){Xx=2; Xy=2;}
     else if(Box==2){Xx=162; Xy=2;}
     else if(Box==3){Xx=322; Xy=2;}
     else if(Box==4){Xx=2; Xy=162;}
     else if(Box==5){Xx=162; Xy=162;}
     else if(Box==6){Xx=322; Xy=162;}
     else if(Box==7){Xx=2; Xy=322;}
     else if(Box==8){Xx=162; Xy=322;}
     else if(Box==9){Xx=322; Xy=322;}
}

void XMarker::DrawX()
{
                acquire_screen();
                draw_sprite(screen, theX, Xx, Xy);
                release_screen();
}

/****************************************************************************************/

class OMarker//O
{
      public:
             //constructors
             OMarker(int Box);
             ~OMarker(){}
             
             void DrawO();
             
      private:
              int Ox;//This is O's x coordnate, not an ox.
              int Oy;
};

OMarker::OMarker(int Box)
{
     if(Box==0){Ox=481; Oy-481;}
     else if(Box==1){Ox=2; Oy=2;}
     else if(Box==2){Ox=162; Oy=2;}
     else if(Box==3){Ox=322; Oy=2;}
     else if(Box==4){Ox=2; Oy=162;}
     else if(Box==5){Ox=162; Oy=162;}
     else if(Box==6){Ox=322; Oy=162;}
     else if(Box==7){Ox=2; Oy=322;}
     else if(Box==8){Ox=162; Oy=322;}
     else if(Box==9){Ox=322; Oy=322;}
}

void OMarker::DrawO()
{
                acquire_screen();
                draw_sprite(screen, theO, Ox, Oy);
                release_screen();
}
/*Function Declarations*/
#include <allegro.h>

void GetPosition()
{
    if((mouse_b & 1) && (mouse_x > 0 && mouse_x < 160) && (mouse_y > 0 && mouse_y < 160) && (Square[0]==0)){
                box=1;}
    else if((mouse_b & 1) && (mouse_x > 160 && mouse_x < 320) && (mouse_y > 0 && mouse_y < 160) && (Square[1]==0)){
                box=2;}
    else if((mouse_b & 1) && (mouse_x > 320 && mouse_x < 480) && (mouse_y > 0 && mouse_y < 160) && (Square[2]==0)){
                box=3;}
    else if((mouse_b & 1) && (mouse_x > 0 && mouse_x < 160) && (mouse_y > 160 && mouse_y < 320) && (Square[3]==0)){
                box=4;}
    else if((mouse_b & 1) && (mouse_x > 160 && mouse_x < 320) && (mouse_y > 160 && mouse_y < 320) && (Square[4]==0)){
                box=5;}
    else if((mouse_b & 1) && (mouse_x > 320 && mouse_x < 480) && (mouse_y > 160 && mouse_y < 320) && (Square[5]==0)){
                box=6;}
    else if((mouse_b & 1) && (mouse_x > 0 && mouse_x < 160) && (mouse_y > 320 && mouse_y < 480) && (Square[6]==0)){
                box=7;}
    else if((mouse_b & 1) && (mouse_x > 160 && mouse_x < 320) && (mouse_y > 320 && mouse_y < 480) && (Square[7]==0)){
                box=8;}
    else if((mouse_b & 1) && (mouse_x > 320 && mouse_x < 480) && (mouse_y > 320 && mouse_y < 480) && (Square[8]==0)){
                box=9;}
}

void Check(short order)
{
     if((Square[0]==order) && (Square[1]==order) && (Square[2]==order)){//Top Row
                acquire_screen();
                while(!key[KEY_ESC]){draw_sprite(screen, theWin, 0, 0);}
                release_screen();}
     else if((Square[3]==order) && (Square[4]==order) && (Square[5]==order)){//Middle Row
                acquire_screen();
                while(!key[KEY_ESC]){draw_sprite(screen, theWin, 0, 0);}
                release_screen();}
     else if((Square[6]==order) && (Square[7]==order) && (Square[8]==order)){//Bottom Row
                acquire_screen();
                while(!key[KEY_ESC]){draw_sprite(screen, theWin, 0, 0);}
                release_screen();}
     else if((Square[0]==order) && (Square[3]==order) && (Square[6]==order)){//Left Column
                acquire_screen();
                while(!key[KEY_ESC]){draw_sprite(screen, theWin, 0, 0);}
                release_screen();}
     else if((Square[1]==order) && (Square[4]==order) && (Square[7]==order)){//Middle Column
                acquire_screen();
                while(!key[KEY_ESC]){draw_sprite(screen, theWin, 0, 0);}
                release_screen();}
     else if((Square[2]==order) && (Square[5]==order) && (Square[8]==order)){//Right Column
                acquire_screen();
                while(!key[KEY_ESC]){draw_sprite(screen, theWin, 0, 0);}
                release_screen();}
     else if((Square[0]==order) && (Square[4]==order) && (Square[8]==order)){//Down Diagonal
                acquire_screen();
                while(!key[KEY_ESC]){draw_sprite(screen, theWin, 0, 0);}
                release_screen();}
     else if((Square[6]==order) && (Square[4]==order) && (Square[2]==order)){//Up Diagonal
                acquire_screen();
                while(!key[KEY_ESC]){draw_sprite(screen, theWin, 0, 0);}
                release_screen();}
}

void XMove()
{
     GetPosition();
     XMarker X1(box);
     X1.DrawX();
     Square[-1+box]==1;
}

void OMove()
{
     GetPosition();
     OMarker O1(box);
     O1.DrawO();
     Square[-1+box]==2;
}

void DrawBoard()
{
    acquire_screen();
    
    draw_sprite(screen, theBlank, 0, 0);//Blank background to get rid of theOpener
     
    line(screen, 160, 0, 160, 480, makecol(255, 255, 255));//Left Vertical
    line(screen, 320, 0, 320, 480, makecol(255, 255, 255));//Right Vertical
    line(screen, 0, 160, 480, 160, makecol(255, 255, 255));//Top Horizontal
    line(screen, 0, 320, 480, 320, makecol(255, 255, 255));//Bottom Horizontal

    release_screen();
}

void Opener()
{
     acquire_screen();
     draw_sprite(screen, theOpener, 0, 0);
     release_screen();
     
     while(!key[KEY_ESC]){
     if((mouse_b & 1) && (mouse_x > 80 && mouse_x < 185) && (mouse_y > 345 && mouse_y < 466)){
                 DrawBoard();
                 rest(150);
                 while(!key[KEY_ESC]){XMove();}}
     else if((mouse_b & 1) && (mouse_x > 286 && mouse_x < 400) && (mouse_y > 345 && mouse_y < 466)){
                 DrawBoard();
                 rest(150);
                 while(!key[KEY_ESC]){OMove();}}
     }
}

PS: To explain all of the Terms and Conditions on the opening screen, I am doing this for a school project and as I will be using human subjects to test the program, it is possible that I will have to have them read all of that information and agree to play.

Recommended Answers

All 3 Replies

Well generally, you will always have the ability of a loss. Even the computer defined AI will screw up alot of times. To make it so that it will always win or always draw are almost impossible. There will always be a chance that the Human Opponent will win, wether by chance or knowledge, there is always the possibility of that.

Well generally, you will always have the ability of a loss. Even the computer defined AI will screw up alot of times. To make it so that it will always win or always draw are almost impossible. There will always be a chance that the Human Opponent will win, wether by chance or knowledge, there is always the possibility of that.

This is most certainely incorrect. It is possible to either win or draw every time. As a matter of fact not too long ago A member of PFO set up and online bot vs bot tic tac toe game to challenge AI coding skills of the locals. I believe Sane leaves his bot competing on the servers all of the time. It allows you to program your AI in several languages after it was re written by a bunch of different people. You can download all of the DIfferent API and use this as a method to write some AI that will always win / draw. Since Sane's AI is perfect you will never beat him, but if you get to a point here you never lose then you are sorted!

Here is a link to the page that contains all the information
http://www.programmingforums.org/showthread.php?t=15451&highlight=Tic-Tac-Toe

Also some very helpful links on how to always win or draw a game of tic tac toe.

http://ostermiller.org/tictactoeexpert.html
http://en.wikipedia.org/wiki/Tic-Tac-Toe
http://www.mathrec.org/old/2002jan/solutions.html

Chris

hi everyone,

@shinedevil:
Actually this is possible, and has been done before. I don't want to waste words so here is the link to an unbeatable tic tac toe program: http://ostermiller.org/calc/tictactoe.html Choose the Expert computer level and try to beat it.

@Freaky_Chris:
Thanks very much! I took a look at all the sites you recommened and they are great!

A lot of the code that is on this post is now out of date, as i recently cleaned everything up a lot, and began the AI. It is still very long though, and now as I am working on the AI, there is a whole other file, but I dont think I will post the code unless anyone asks me to because of its length. The only problem I have is this:

When the AI goes first and goes to a corner, and then the human goes to the opposite corner
[X][ ][ ]
[ ][ ][ ]
[ ][ ][O]
then the Ai should go to one of the remaining corners. sometimes it does, but other times it goes to both. The only solution I can think of is as about as crude as it gets, is to draw a blank spot over the second corner and pretend nothing ever happened. Unfortunately, I doubt that anyone will be able to help me with this problem withought running the code or, and I cant upload the .exe file for the game on here.
I am still open to more suggestions for AI however!

Thanks again!

Be a part of the DaniWeb community

We're a friendly, industry-focused community of developers, IT pros, digital marketers, and technology enthusiasts meeting, networking, learning, and sharing knowledge.