簡單的井字棋 AI DEMO | MaxMin演算法
阿新 • • 發佈:2020-12-14
在“類與物件”實訓課上,有一道附加題讓我們用 OOP 做一個的井字棋模擬程式,要求中電腦是隨機落子的,這樣顯然不是很優雅。回憶起以前學的對抗搜尋(這裡叫 MaxMin 演算法),我繼續給遊戲中的電腦一方寫了個 AI。由於井字棋遊戲運算規模很小,大部分的剪枝手段變得比較雞肋,但以此為引搜尋了一些資料,瞭解一些有趣的計算機博弈論知識,有機會再繼續探究一下。
待改進的點:
-
判定勝利的方法比較蠢,可以用迴圈代替
-
沒有對搜尋樹的預計深度進行評估,使 AI 做出最 “快” 的策略
-
程式碼可讀性不強,不符合工程程式碼規範
#include <bits/stdc++.h> using namespace std; class MyChess { private: char A[3][3]; int step; public: MyChess(); void DispChessboard(); void PlayerMove(); void ComputerMove(); bool isFull(); int MaxSearch(); int MinSearch(); int checkWin(); }; MyChess::MyChess() { memset(A,0,sizeof(A)); step=0; } void MyChess::DispChessboard() { cout<<"-------------------\n"; for (int i=0;i<3;i++) { cout<<"| "<<A[i][0]<<" | "<<A[i][1]<<" | "<<A[i][2]<<" |\n"; cout<<"-------------------\n"; } } void MyChess::PlayerMove() { int x,y; cout<<"Step "<<++step<<" piece(x,y): "; cin>>x>>y; while (A[x][y] || x>2 || x<0 || y>2 || y<0) { cout<<"ERROR!! piece(x,y): "; cin>>x>>y; } A[x][y]='O'; } void MyChess::ComputerMove() { // 模擬 AI 的選擇過程 int x,y,score=1; for (int i=0;i<3;i++) { for (int j=0;j<3;j++) { if (!A[i][j]) { A[i][j]='X'; int temp=MaxSearch(); if (score>temp) x=i,y=j,score=temp; A[i][j]=0; } } } A[x][y]='X'; } int MyChess::MaxSearch() { // 人類執子時,希望找到權值最大的子節點 int ret=-1,sta=checkWin(); if (sta) return sta; if (isFull()) return 0; for (int i=0;i<3;i++) for (int j=0;j<3;j++) if (!A[i][j]) A[i][j]='O',ret=max(ret,MinSearch()),A[i][j]=0; return ret; } int MyChess::MinSearch() { // 電腦執子時,希望找找到權值最小的子節點 int ret=1,sta=checkWin(); if (sta) return sta; if (isFull()) return 0; for (int i=0;i<3;i++) for (int j=0;j<3;j++) if (!A[i][j]) A[i][j]='X',ret=min(ret,MaxSearch()),A[i][j]=0; return ret; } bool MyChess::isFull() { // 平局判定 for (int i=0;i<3;i++) for (int j=0;j<3;j++) if (!A[i][j]) return false; return true; } int MyChess::checkWin() { for (int i=0;i<3;i++) if (A[i][0]=='O' && A[i][1]=='O' && A[i][2]=='O') return 1; for (int i=0;i<3;i++) if (A[0][i]=='O' && A[1][i]=='O' && A[2][i]=='O') return 1; for (int i=0;i<3;i++) if (A[i][0]=='X' && A[i][1]=='X' && A[i][2]=='X') return -1; for (int i=0;i<3;i++) if (A[0][i]=='X' && A[1][i]=='X' && A[2][i]=='X') return -1; if (A[0][0]=='O' && A[1][1]=='O' && A[2][2]=='O') return 1; if (A[2][0]=='O' && A[1][1]=='O' && A[0][2]=='O') return 1; if (A[0][0]=='X' && A[1][1]=='X' && A[2][2]=='X') return -1; if (A[2][0]=='X' && A[1][1]=='X' && A[0][2]=='X') return -1; return 0; } int main() { MyChess Ch; int now=1; Ch.DispChessboard(); while (!Ch.isFull()) { if (now&1) { Ch.PlayerMove(); if (Ch.checkWin()==1) { Ch.DispChessboard(),cout<<"You win!\n"; break; } else if (Ch.isFull()) { Ch.DispChessboard(),cout<<"Gme tie!\n"; break; } } else { Ch.ComputerMove(); Ch.DispChessboard(); if (Ch.checkWin()==-1) { cout<<"Computer win!\n"; break; } } now^=1; } return 0; }