Library tutorials & articles

AI 2 - Game Playing (Artificial intelligence)

CODE 3

AI - Game PLaying (CODE-3)

Now we are going to code the main source file.
first add the following code.

#include&ltwindows.h>// for setting the console cursor positon

void gotoxy(int,int);

#include<iostream.h> // Console Input / Output
void PrintBoard(int,int);

#include"State.h" // State Class and State Constants
#include"TLList.h" // Linked List (storeage)

// Current Real State.
CState RealState;

// function prototypes
void MainLoop();
void HumanMove();
void ComputerMove();
double Think(int);
void cls();

The above is just the include list, function prototypes and global objects. Now we need to code the following functions

  • void main() Display a welcome message, and set a game in motion
  • void MainLoop() Loop between human and computer move, until the game is over
  • void HumanMove() get input from the human, and make the requested move
  • void ComputerMove() allow the computer to think, then make a move
  • double Think(int n) evalute game outcome if the next move is n
  • void PrintBoard(int x, int y) print the board to the screen
  • void gotoxy(int x, int y) set console cursor position
  • void cls() clear the screen

Lets strt with the simple functions
void cls() {
    system("cls");
}

void gotoxy(int x, int y) {

    COORD Position = {x,y};
    SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), Position);
}

void PrintBoard(int x, int y) {

    RealState._PrintBoard(x,y);
}

Now for the main loop
While the game is not over (the state is not a draw, and not a win)
Print the board to the screen, then call Human move or computer move, depending on who's turn it is void MainLoop() {

    char UserInput=0;

    while(!(RealState.IsDraw() || RealState.IsWin())) {

        cls();
        if(RealState.GetThisMove() == ComputerMarker) {
            PrintBoard(0,5);
            HumanMove();
        }
        else {
            PrintBoard(0,5);
            ComputerMove();
        }
    }

    // end of game !
}

Now the Human move.Prompt the user for input, validate the input (make sure the move is legal)Once a valid responce has been entered, perform the move void HumanMove() {

    int UserInput=0;
    bool Valid=false;

    gotoxy(0,0);
    cout << "Your Move" << endl;

    while(!Valid) {
        gotoxy(0,1);
        cout << "Where you Wanna go ? :";
        cin >> UserInput;

        if(UserInput < 9 && UserInput >=0) {
            if(RealState.IsMoveLegal(UserInput)) {
                Valid = true;
            }
            else {
                cout << "Invalid Move !" << endl;
            }
        }
        else {
            cout << "Invalid Move !" << endl;
        }
    }
    RealState.MakeMove(UserInput);
}

ComputerMove()
This function Thinks about every possible next move, then selects the next move which returned the highest score and performs it on the main board void ComputerMove() {

    int n=0;
    double HighScore = 0; // Lower than the lowest possible average.
    int HighMove = 0;
    double Results[9] = {0,0,0,0,0,0,0,0,0};

    gotoxy(0,0);

    cout << "My Move\nThinking...." << endl;

    for(n=0; n<9; n++) {
        Results[n] = Think(n); // think about all possible next moves
    }

    gotoxy(0,15);
    // which is the Highest legal move ???
    for(n=0; n<9; n++) {
        if((HighScore < Results[n]) && RealState.IsMoveLegal(n)) {
            HighScore = Results[n];
            HighMove = n;
        }
    }

    gotoxy(0,2);
    cout << "I will move to " << HighMove << endl;
    Sleep(1500);
    RealState.MakeMove(HighMove);
}

Think(int n): This function is the most important part of the AI.
It basically takes current state of the game, and expends to all the possible end game outcomes that could occus if the computer made the move 'n' from the current game state. It then remembers all the end game states, and calls their passback() function, triggering the domino effect within the objects (they all calll there parents passback(n) function) When the minni-max algorithm is finished, we simply return the score at the top of the tree ([the currentgame + n] move state). here is how it is done double Think(int n) {

    // data needs to STAY in memory, (no poping, only pushing)
    // instead of poping from the left, simply move the left read-from position
    // and use the overloaded [] operator for retrieving states

    double LeftReadPoint = 0; // start at very left

    CLList&ltCState&gt List;
    CLList&ltCState*>Terminal;
    // initiate current state to equal global real state
    CState Current(&RealState, n);

    // move is not legal, exit function
    if(!RealState.IsMoveLegal(n)) { return 0; }

    // set parent to 0, this is the top of the list
    Current.setParent(0); // stop passing back here

    // add the inital state to the expansion list
    List.Push(Current, Right);

    // continue while there are still more states to expand
    while(LeftReadPoint < List.GetTotalLinks()) {

        // is this state a terminal state ???
        if(List[LeftReadPoint].IsDraw() || List[LeftReadPoint].IsWin()) {
            // YES, add its address to the terminal pointer for later use
            // (initiate pass back later)
            Terminal.Push(&List[LeftReadPoint], Right);
        }
        else {
            // NO, not terminal, expand it, and put its children to the list
            for(int n=0; n<9; n++) {
                // loop through possible nexy moves
                if(List[LeftReadPoint].IsMoveLegal(n)) {
                    // only expand legal moves
                    // expand state at left read pos of list
                    // use dual parameter constuctor to make new state
                    // see dual parameter constructor of state class for more info
                    List.Push(CState(&List[LeftReadPoint], n), Right);
                }
            }
        }
        // next time, read the next state in the list
        LeftReadPoint++;
    }

    // The loop has terminated, our tree is fully drawn.
    // now use all the terminal states we remembered
    // go through terminal states, starting the PassBack domino effect.

    while(Terminal.GetTotalLinks() != 0) {

        Terminal.Pop(Right)->PassBack();
    }

    // return the score at the top of the tree
    return List[0].GetMyScore();
}

Now for the main() function,
Display a welcome message, Ask who is going first, kick off the main loop, congratulate the winner, then ask if the human wants to play again, nice and easy. lol, isnt that a slogan of sum shampoo comercial ?? anyway, back on the topic. void main() {

    // re initalise CState if this is not the first game
    RealState = CState();

    char UserInput=0;

    cls();

    gotoxy(25,10);
    cout << "****************************" << endl;
    gotoxy(25,11);
    cout << "* Tic Tac Toe, By Qwijibow *" << endl;
    gotoxy(25,12);
    cout << "****************************" << endl;
    gotoxy(0,0);

    Sleep(3000);

    // loop forever
    while(1) {

        RealState = CState(); // reinitalise

        cls();

        // loop until the human enters a valid character         while(UserInput != 'C' && UserInput != 'c' && UserInput != 'H' && UserInput != 'h') {
            cout << "Who is going to go first ? Human or Computer (H/C):";
            cin >> UserInput;
        }

        cls();

        // set up the real state for the first player         if(UserInput == 'C' || UserInput == 'c') {
            RealState.SetThisMove(HumanMarker);
        }
        else {
            RealState.SetThisMove(ComputerMarker);
        }

        // main loop, the game plays out
        MainLoop();
        // the game is now over
        PrintBoard(0,5);

        gotoxy(0,12);

        // congratulate the winner         if(RealState.IsDraw()) {
            cout << "A Draw" << endl;
        }
        else {
            if(RealState.GetThisMove() == HumanMarker) {
                cout << "You Win" << endl;
            }
            else {
                cout << "I Win" << endl;
            }
        }

        cout << "Wanna play again ??? (Y/N): ";

        cin >> UserInput;

        if(UserInput=='N' || UserInput == 'n') {
            // the human gives up, terminate forever loop             break;
        }
    }
}

1 more page, the conclusion

Comments

  1. 12 Sep 2005 at 07:49

    wher do i get the windows.h header file? the main.cpp code doesnt seem to work and has quite a few errors. help please!

  2. 07 Aug 2005 at 17:45

    after you get this code to run i need to know how to be able to save to a file and replay the game play before. if the player is starting a new game he/she should be prompted to enter their name and it should be pass to a constructor of the tictactoe board object . if the user wishes to save the game they should be prompted to enter te name of the ouput file. the output should have the player info and as well as the different moves that make up the game.
    i might need two sperate files containing the header and implementation of the player class , file representing the playing  board and the file containing the main program.    


    I really need some help on getting this to work and any help with code or pseudocode to implement this will be appreaciated. can i be notify at oyetih@yahoo.com  

  3. 01 Jan 1999 at 00:00

    This thread is for discussions of AI 2 - Game Playing (Artificial intelligence).

Leave a comment

Sign in or Join us (it's free).

AddThis

Related discussion

Events coming up

  • Dec 6

    Developing AJAX Web Applications with Castle Monorail

    London, United Kingdom

    Monorail is the model-view-controller engine of the Castle Project, bringing many of the best ideas of Ruby on Rails to the .NET world. In this talk, David De Florinier and Gojko Adzic show how Monorail makes it easy to develop .NET based AJAX applications, and how to use the Castle Project to build Web 2.0 applications effectively. Come to this session if you are a .NET web developer. Everyone is welcome!