AI五子棋的改进版本来啦~~
我们发现,原版的AI五子棋如果调成4的话,非常之慢!!下面给出原版的链接
AI五子棋(原版本)
因此我对其进行了改进,由于正常人下五子棋不会东下一颗棋,西下一颗棋。
因此我们可以大幅度缩小搜索的范围,只要搜索已经下了的棋子的周围就可以了(2×2或3×3)。
下面的程序会是2×2的4,尽管还是有点慢,但相比原程序快很多。
另外,关于人机强度、人机耗时的修改,我放在原版的链接里了
我们可以做一个对比:让这两个程序同时运行4,会发现:改进后的程序用了1:17下了一步,但是原版的用了3:04(4GB内存,i3cpu情况下)
#include #include #include #include #include #include #include using namespace std; int minx=14,miny=14,maxx=0,maxy=0; void color(int x) { switch(x) { case 1: SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), FOREGROUND_INTENSITY |FOREGROUND_RED ); break; case 2: SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), FOREGROUND_INTENSITY |FOREGROUND_BLUE ); break; case 3: SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), FOREGROUND_INTENSITY |FOREGROUND_GREEN); break; case 4: SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), FOREGROUND_INTENSITY |FOREGROUND_RED |FOREGROUND_BLUE ); break; case 5: SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), FOREGROUND_INTENSITY |FOREGROUND_RED |FOREGROUND_GREEN); break; case 6: SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), FOREGROUND_INTENSITY |FOREGROUND_BLUE |FOREGROUND_GREEN); break; case 7: SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), FOREGROUND_INTENSITY |FOREGROUND_GREEN|FOREGROUND_BLUE |FOREGROUND_RED); break; default: SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), FOREGROUND_INTENSITY |FOREGROUND_GREEN|FOREGROUND_BLUE |FOREGROUND_RED); break; } } class GameTree { public: class Node { public: enum State :uint8_t/*节省内存*/ { SPACE, BLACK, WHITE }; private: friend class GameTree; static const uint8_t BOARDSIZE = 15; int value;//叶节点表示估价函数的结果,MAX节点表示α值,MIN节点表示β值 int evaluateValue;//估价函数计算的结果,用于优化 unsigned short depth;//深度 uint8_t lastX, lastY;//上一次落子的xy坐标 Node* father;//父亲节点 std::vector children;//子节点 State **board;//棋盘 int Evaluate()const { //估价函数 static const auto EvaluateSome = [](State **board, uint8_t beginX, uint8_t endX, uint8_t beginY, uint8_t endY) { static const auto EvaluateList = [](const std::array& v) { //假定自己是白方 //判断颜色并记录棋子个数 State lastColor = SPACE; uint8_t bitList = 0;//将棋链以二进制形式表示,如01101 for (State i : v) { if (i != lastColor) { if (lastColor == SPACE)//遇到的第一个棋子 lastColor = i; else//有不同颜色的棋子 return 0; } if (i != SPACE) bitList = bitList * 2 + 1; } int result = 0; switch (bitList) { case 0://00000 result = 0; break; case 1://00001 case 2://00010 case 4://00100 case 8://01000 case 16://10000 result = 5; break; case 3://00011 case 24://11000 result = 80; break; case 6://00110 case 12://01100 result = 100; break; case 10://01010 result = 80; break; case 5://00101 case 20://10100 result = 60; break; case 9://01001 case 18://10010 result = 20; break; case 17://10001 result = 10; break; case 7://00111 case 28://11100 result = 800; break; case 14://01110 result = 1000; break; case 13://01101 case 26://11010 case 11://01011 case 22://10110 result = 800; break; case 19://10011 case 21://10101 case 25://11001 result = 600; break; case 15://01111 case 30://11110 result = 10000; break; case 29://11101 case 23://10111 result = 8000; break; case 27://11011 result = 6000; break; case 31://11111 return lastColor == WHITE ? INT_MAX : INT_MIN; } return lastColor == WHITE ? result : -result;//对手返回负值,我方返回正值 }; int result = 0; for (uint8_t i = beginX; i = 4) { std::arrayv; for (uint8_t k = 0; k board, beginX, endX, beginY, endY) + father->evaluateValue; } public: //非根节点的构造函数 Node(Node* nf, uint8_t x, uint8_t y) :father(nf), lastX(x), lastY(y), depth(nf->depth + 1), value(0), board(new State* [BOARDSIZE]) { father->children.push_back(this); for (int i = 0; i board[i], BOARDSIZE * sizeof(State)); } board[lastX][lastY] = IsMaxNode() ? BLACK : WHITE; evaluateValue = Evaluate(); for (int i = 0; i evaluateValue == INT_MAX || this->evaluateValue == INT_MIN) { this->value = this->evaluateValue; return; } bool created = false;//记录是否new出新的Node,如果没有就不用排序了。 if (!board) { //不是根节点 board = new State * [BOARDSIZE]; for (int i = 0; i board[i], BOARDSIZE * sizeof(State)); } board[lastX][lastY] = IsMaxNode() ? BLACK : WHITE; } for (int i = max(minx-2,0); i = 2) { //若剩余深度小于2,则下一层肯定没有搜索过 for (Node* child : this->children) { if (child->lastX == i && child->lastY == j) { //已经被搜索过 flag = true; break; } } } if (!flag) { new Node(this, i, j); minx=min(minx,i); maxx=max(maxx,i); miny=min(miny,j); maxy=max(maxy,j); created = true; } } } } if (IsMaxNode()) { this->value = INT_MIN; if (created) { std::sort(this->children.begin(), this->children.end(), [](Node* a, Node* b) { return a->GetEvaluateValue() > b->GetEvaluateValue(); });//按照估价从大到小排序,增加剪枝的概率 } } else { this->value = INT_MAX; if (created) { std::sort(this->children.begin(), this->children.end(), [](Node* a, Node* b) { return a->GetEvaluateValue() GetEvaluateValue(); });//按照估价从小到大排序,增加剪枝的概率 } } auto ReleaseMemory = [this] { if (father) { //不是根节点 for (int i = 0; i children) { child->Search(_depth - 1); //α - β 剪枝 if (IsMaxNode()) { if (child->value > this->value) { this->value = child->value; if (this->father && this->value >= this->father->value && this != this->father->children.front()) { ReleaseMemory(); return;//剪枝 } } } else { //MIN节点 if (child->value value) { this->value = child->value; if (this->father && this->value father->value && this != this->father->children.front()) { ReleaseMemory(); return;//剪枝 } } } } ReleaseMemory(); } void DeleteAllButThis() { if (!father->board)//父节点不是根节点 throw std::invalid_argument("this->father必须是根节点!"); for (Node* n : father->children) { if (n != this) delete n; } board = new State * [BOARDSIZE]; for (int i = 0; i board[i], BOARDSIZE * sizeof(State)); delete[] father->board[i]; } delete[] father->board; board[lastX][lastY] = IsMaxNode() ? BLACK : WHITE; free(father);//避免调用析构函数 father = nullptr; } inline bool IsFull()const { return depth == BOARDSIZE * BOARDSIZE; } inline State GetWinner()const { if (this->value == INT_MAX) { return Node::WHITE; } else if (this->value == INT_MIN) { return Node::BLACK; } return Node::SPACE; } ~Node() { if (board) { for (int i = 0; i children) { //进入用户选择的分支 if (node->lastX == x && node->lastY == y) { node->DeleteAllButThis(); root = node; break; } } } else { //第一次开局 root = new Node(1, x, y); } root->Search(maxDepth); if (root->IsFull()) return std::make_pair(19, 19); for (Node* node : root->children) { //选择分值最大的 if (node->value == root->value) { node->DeleteAllButThis(); root = node; break; } } return std::make_pair(root->lastX, root->lastY); } Node::State GetWinner()const { return root->GetWinner(); } void Run() { while (1) { int x, y; do { color(7); std::cout > y >> x; y-=1; x-=1; } while (x = 15 || y >= 15 || (root && root->board[x][y] != Node::SPACE)); minx=min(minx,y); maxx=max(maxx,y); miny=min(miny,x); maxy=max(maxy,x); if (root) { for (Node* node : root->children) { //进入用户选择的分支 if (node->lastX == x && node->lastY == y) { node->DeleteAllButThis(); root = node; break; } } } else { //第一次开局 root = new Node(1, x, y); } system("cls"); for (int i = 0; i for (int j = 0; j color(6); if (i==0 && j color(5); std::cout color(2); std::cout color(7); std::cout std::cout std::cout //不能用root-value==0判断平局,因为平局一定为0,但为0不一定平局 std::cout //选择分值最大的 if (node-value == root-value) { node-DeleteAllButThis(); root = node; break; } } system("cls"); for (int i = 0; i for (int j = 0; j color(6); if (i==0 && j color(5); std::cout color(2); std::cout color(7); std::cout std::cout std::cout //不能用root-value==0判断平局,因为平局一定为0,但为0不一定平局 std::cout delete root; } }; int main() { cout for (int j = 0; j color(6); if (i==0 && j color(5); std::cout