AI - Game PLaying (CODE-3)
Now we are going to code the main source file.
first add the following code.
#include<windows.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<CState> List;
CLList<CState*>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