diff --git a/Board.cpp b/Board.cpp index 6750ab2..6f30d40 100644 --- a/Board.cpp +++ b/Board.cpp @@ -1,4 +1,7 @@ +#include +#include #include +#include #include #include "Board.h" @@ -6,7 +9,7 @@ using namespace std; Board::Board() { Piece* temp; - bool valid = false; + for (int i = 0; i < 2; ++i) { for (int j = 0; j < 8; ++j) { temp = new Piece(i, j, 'X'); @@ -22,14 +25,15 @@ Board::Board() { pieces.push_back(temp); } } + + valid, xtaken, otaken = false; } Board::Board(const Board& b) { - vector xp = b.getXPieces(); - vector op = b.getOPieces(); + vector xp = b.getTypePieces('X'); + vector op = b.getTypePieces('O'); Piece* temp; - //bool valid = false; - char tempturn = b.getTurnPls(); + char tempturn = b.getTurn(); turn = tempturn; for (int i = 0; i < xp.size(); ++i) { @@ -43,6 +47,8 @@ Board::Board(const Board& b) { opieces.push_back(temp); pieces.push_back(temp); } + + valid, xtaken, otaken = false; } //make this efficient! @@ -74,6 +80,8 @@ void Board::isTaken(int r, int c) { for(int x = 0; x < opieces.size(); ++x) { if (opieces[x]->getX() == r && opieces[x]->getY() == c) { opieces.erase(opieces.begin() + x); + cout << "otaken set to TRUE\n"; + otaken = true; } } } @@ -81,6 +89,8 @@ void Board::isTaken(int r, int c) { for(int x = 0; x < xpieces.size(); ++x) { if (xpieces[x]->getX() == r && xpieces[x]->getY() == c) { xpieces.erase(xpieces.begin() + x); + cout << "xtaken set to TRUE\n"; + xtaken = true; } } } @@ -90,6 +100,30 @@ void Board::isTaken(int r, int c) { } } +bool Board::checkTaken(char c){ + if (c == 'X'){ + return xtaken; + } + + else if (c == 'O'){ + return otaken; + } + + else + return false; +} + +vector Board::getTypePieces(char type) const{ + if (type == 'X') + return xpieces; + else if (type == 'O') + return opieces; + else{ + cout << "Invalid type!\n"; + return pieces; + } +} + moves Board::parse(string input){ input = myToUpper(input); @@ -136,20 +170,21 @@ bool Board::isGameOver(){ return false; } -string Board::whoWon(){ +char Board::whoWon(){ for (int i = 0; i < xpieces.size(); ++i){ if (xpieces[i]->getX() == 7){ - return "Player X wins!"; + return 'X'; } } for (int i = 0; i < opieces.size(); ++i){ if (opieces[i]->getX() == 0){ - return "Player O wins!"; + return 'O'; } } - return "ERROR: function whoWon() called incorrectly."; + cout << "ERROR: function whoWon() called incorrectly. Game is not over. \n"; + return 'a'; } void Board::changeTurns(){ @@ -157,6 +192,11 @@ void Board::changeTurns(){ else turn = 'O'; } +void Board::resetTaken(){ + xtaken = false; + otaken = false; +} + void Board::displayBoard(){ cout << "; A B C D E F G H"<& inputVec, Board inputBoard){ } int Board::evaluate(char max, char min){ - //right now just evaluating number of pieces - if (max == 'X'){ - return (xpieces.size() - opieces.size()); + vector maxPieces = getTypePieces(max); + vector minPieces = getTypePieces(min); + int reflector, val, x, y = 0; + Piece* temp; + + val += 2 * (maxPieces.size() - minPieces.size()); + + cout << (checkTaken(max)) << "\n"; + cout << (checkTaken(min)) << "\n"; + //check for taken conditions + if (checkTaken(min)){ + cout << "adding 10 to val\n"; + val += 10; } - - else if (max == 'O'){ - return (opieces.size() - xpieces.size()); + + if (checkTaken(max)){ + cout << "subtracting 10 from val\n"; + val -= 10; } - - else { - cout << "Error in evaluate: unidentified max, must be either 'X' or 'O'.\n"; - return 0; + + //ultimate condition! + if (isGameOver()){ + if (whoWon() == max) + val = INT_MAX / 2; + + else + val = INT_MIN / 2; } + + cout << "val: " << val << "\n"; + return val; } diff --git a/Board.h b/Board.h index 7af5bcf..13a820c 100644 --- a/Board.h +++ b/Board.h @@ -28,8 +28,8 @@ class Board { vector xpieces; vector opieces; vector pieces; + bool valid, xtaken, otaken; char turn = 'O'; - bool valid = false; public: Board(); @@ -40,14 +40,15 @@ public: bool isPiece(int r, int c); Piece* getPiece(int r, int c); void isTaken(int r, int c); - vector getXPieces() const { return xpieces; } - vector getOPieces() const { return opieces; } - char getTurnPls() const { return turn; } + bool checkTaken(char c); + vector getPieces() const { return pieces; } + vector getTypePieces(char type) const; + char getTurn() const { return turn; } moves parse(string input); - char getTurn() { return turn; } bool isGameOver(); - string whoWon(); + char whoWon(); void changeTurns(); + void resetTaken(); void displayBoard(); string boardToString(); int charToIntColumn(char input); diff --git a/Engine.cpp b/Engine.cpp index 185cb90..a3024de 100644 --- a/Engine.cpp +++ b/Engine.cpp @@ -2,6 +2,9 @@ #include #include #include +#include +#include +#include #include "Engine.h" Engine::Engine(){ @@ -13,13 +16,24 @@ void Engine::startGame(){ cout<<"WELCOME\n"; cout<<"1. Play against AI?\n"; - cout<<"2. Play against a human?\n"; + cout<<"2. Watch AI vs AI?\n"; cout<<"Enter choice: \n"; int choice = -1; cin >> choice; cout << "OK" << endl; + if (choice == 1) + userGame(); + else if (choice == 2) + AIGame(); + else { + cout << "Please enter a valid choice.\n"; + startGame(); + } +} + +void Engine::userGame(){ string move; bool gameOver = false; @@ -38,13 +52,14 @@ void Engine::startGame(){ if(b->isValid()){ b->changeTurns(); + b->resetTaken(); b->setValidFalse(); } } while(b->getTurn() == 'X' ){ - easyAI(); - //AI(3); + //easyAI(); + AI(3); } gameOver = b->isGameOver(); @@ -58,55 +73,114 @@ void Engine::startGame(){ cout << "Game over. " + s + " wins!\n\n"; } -void Engine::easyAI(){ - vector listOfMoves = b->viewPossibleMoves(); - - srand(time(NULL)); - int randomChoice = rand() % (listOfMoves.size()-1) - 0; - - b->move(listOfMoves[randomChoice]); - b->changeTurns(); +void Engine::AIGame(){ + bool gameOver = false; + + while (gameOver != true){ + gameOver = b->isGameOver(); + + while(b->getTurn() == 'O'){ + AI(3); + } + + b->displayBoard(); + sleep(1); + + while(b->getTurn() == 'X' ){ + AI(3); + } + + gameOver = b->isGameOver(); + } } void Engine::AI(int depth){ - Board* temp = new Board(*b); - moves empty; - MNode* root = new MNode(*temp, empty, 0); + Board* state = new Board(*b); + moves m; + MNode* root = new MNode(*state, m, 0); + createMMTree(root, depth, 1); + m = evaluateMMTree(root); - //only doing 1 branch right now because testing - /*for (int i = 0; i < listOfMoves.size(); ++i){ - minMax(b, listOfMoves[i]); - }*/ - - //remove this soon - //b->move(minMax(temp, listOfMoves[0], 0, 0)); - - b->changeTurns(); - b->displayBoard(); + printTree(0, root); + + b->move(m); + b->changeTurns(); + b->resetTaken(); } -moves Engine::minMax(MNode* node, int depth){ +void Engine::createMMTree(MNode* node, int depth, int alt){ + MNode* temp; + char max, min; Board current = node->getState(); vector listOfMoves = current.viewPossibleMoves(); - /* - if (current.isGameOver() == true){ - cout << "END OF PATH REACHED\n\n"; - return m; + + if (depth >= 0){ + for (int i = 0; i < listOfMoves.size(); ++i){ + if(current.getPiece(8 - listOfMoves[i].row, listOfMoves[i].column)->getType() == + current.getTurn() && current.isThisMovePossible(8 - listOfMoves[i].row, + listOfMoves[i].column, + listOfMoves[i].moveType)){ + current.resetTaken(); + current = current.move(listOfMoves[i]); + max = current.getTurn(); + current.changeTurns(); + min = current.getTurn(); + + if (current.checkTaken('X')) + cout << "xtaken true\n"; + if (current.checkTaken('O')) + cout << "otaken true\n"; + + temp = new MNode(current, listOfMoves[i], current.evaluate(max, min)); + + if (alt == 1) + temp->setType("max"); + else if (alt == 1) + temp->setType("min"); + + current.resetTaken(); + node->addChild(temp); + + createMMTree(temp, --depth, alt * -1); + + current.changeTurns(); + } + } } - else { - if(current.getPiece(8 - m.row, m.column)->getType() == current.getTurn() && current.isThisMovePossible(8 - m.row, m.column, m.moveType)){ - temp->move(m); - temp->evaluate('X', 'O'); - temp->changeTurns(); - } - else { - m = minMax(temp, listOfMoves[++r], c, r); - } - - vector listOfMoves = temp->viewPossibleMoves(); - minMax(temp, listOfMoves[0], 0, 0); - return m; - } - */ + return; } + +moves Engine::evaluateMMTree(MNode* node){ + //returns the move from children that maximizes evaluate() + MNode* temp; + moves m; + int max = INT_MIN; + int val; + vector children = node->getChildren(); + + for (auto &c : children){ + val = evaluateMMBranch(c, INT_MIN); + if(val > max){ + temp = c; + max = val; + } + } + + return temp->getMove(); +} + +int Engine::evaluateMMBranch(MNode* node, int max){ + if (node->getMMVal() > max) + max = node->getMMVal(); + + vector children = node->getChildren(); + + for (auto &c : children){ + evaluateMMBranch(c, max); + } + + //cout << "max: " << max << "\n"; + + return max; +} diff --git a/Engine.h b/Engine.h index 89f3327..fbcb212 100644 --- a/Engine.h +++ b/Engine.h @@ -12,7 +12,10 @@ public: Engine(); Board* getBoard() { return b; } void startGame(); - void easyAI(); + void userGame(); + void AIGame(); void AI(int depth); - moves minMax(MNode* node, int depth); + void createMMTree(MNode* node, int depth, int alt); + moves evaluateMMTree(MNode* node); + int evaluateMMBranch(MNode* node, int sum); }; diff --git a/MNode.cpp b/MNode.cpp index 8f4c410..8865b29 100755 --- a/MNode.cpp +++ b/MNode.cpp @@ -31,13 +31,13 @@ bool MNode::hasChildren(){ return false; } -void printTree(int depth, const MNode& n){ +void printTree(int depth, MNode* n){ vector children; - cout << "depth " << depth << " :" << n.getMMVal() << " "; - children = n.getChildren(); + cout << "depth " << depth << " : " << n->getMMVal() << "\n"; + children = n->getChildren(); //print out root for (int i = 0; i < children.size(); ++i){ - printTree(++depth, *children[i]); + printTree(++depth, children[i]); } } \ No newline at end of file diff --git a/MNode.h b/MNode.h index 1afe963..1db411e 100755 --- a/MNode.h +++ b/MNode.h @@ -11,6 +11,7 @@ class MNode { Board state; moves mvs; int minimax_val; + string type; //min or max public: MNode(); @@ -20,11 +21,13 @@ public: void addChild(MNode* n) { children.push_back(n); } int getMMVal() const { return minimax_val; } void setMMVal(int mmval); - Board getState() { return state; } + Board getState() const { return state; } void setState(Board s) { state = s; } - moves getMove() { return mvs; } + moves getMove() const { return mvs; } void setMove(moves m) { mvs = m; } + string getType() { return type; } + void setType(string t) { type = t; } bool hasChildren(); }; -void printTree(int depth, const MNode& n); \ No newline at end of file +void printTree(int depth, MNode* n); \ No newline at end of file diff --git a/Piece.cpp b/Piece.cpp index a438327..bd78a31 100644 --- a/Piece.cpp +++ b/Piece.cpp @@ -63,7 +63,3 @@ void Piece::moveRight(){ else cout << "Error: trying to move an empty piece left."; } - -void Piece::isTaken(){ - cout << getX() << " " << getY() << "\n\n"; -} diff --git a/Piece.h b/Piece.h index 82f6bed..4da1f83 100644 --- a/Piece.h +++ b/Piece.h @@ -20,5 +20,4 @@ public: void setY(int c){ y = c; } char getType(){ return type; } void makeEmpty(){ type = '_'; } - void isTaken(); -}; +}; diff --git a/test.cpp b/test.cpp index c26c406..8b1eb77 100644 --- a/test.cpp +++ b/test.cpp @@ -4,7 +4,6 @@ using namespace std; int main() { - //Board b; Engine e; e.startGame(); }