1. 程式人生 > >博弈演算法實現三子棋

博弈演算法實現三子棋

用博弈樹演算法實現井字棋遊戲。 井字棋遊戲是一種簡單的棋類遊戲,在3*3的棋盤上,兩人輪流下子,誰的棋子先連成3顆一條直線,誰就贏了,可以橫著、豎著、斜著。博弈樹演算法是用搜索來解決這類問題的演算法,井字棋遊戲步數較少,很容易用博弈樹演算法實現

#include<iostream>
#include<stdlib.h>
#include<stdio.h>
using namespace std;

int num = 0;
int p, q;
int tmpQP[3][3]; //表示棋盤資料的臨時陣列,其中的元素0表示該格為空,
int cur[3][3];

const
int depth = 3; //搜尋樹的最大深度 void Init() { for (int i = 0; i<3; i++) for (int j = 0; j<3; j++) { cur[i][j] = 0; } } void PrintQP() //列印當棋盤格局的函式 { for (int i = 0; i<3; i++) { for (int j = 0; j<3; j++) cout << cur[i][j] << '\t'; cout
<< endl;//換行 } } int CheckWin() //有人贏了嗎?返回0表示沒有人贏,返回-1表示人贏了,返回1表示計算機贏了 { for (int i = 0; i<3; i++) { if (cur[i][0] == 1 && cur[i][1] == 1 && cur[i][2] == 1) return 1; if (cur[i][0] == -1 && cur[i][1] == -1 && cur[i][2] == -1) return
-1; } for (int i = 0; i<3; i++) { if (cur[0][i] == 1 && cur[1][i] == 1 && cur[2][i] == 1) return 1; if (cur[0][i] == -1 && cur[1][i] == -1 && cur[2][i] == -1) return -1; } if ((cur[0][0] == 1 && cur[1][1] == 1 && cur[2][2] == 1) || (cur[2][0] == 1 && cur[1][1] == 1 && cur[0][2] == 1)) return 1; if ((cur[0][0] == -1 && cur[1][1] == -1 && cur[2][2] == -1) || (cur[2][0] == -1 && cur[1][1] == -1 && cur[0][2] == -1)) return -1; return 0; } int value()//評估函式 { p = 0; q = 0; //將棋盤中的空格填滿自己的棋子,既將棋盤陣列中的0變為1 for (int i = 0; i<3; i++) for (int j = 0; j<3; j++) if (cur[i][j] == 0) tmpQP[i][j] = 1; else tmpQP[i][j] = cur[i][j]; //電腦一方 //計算每一行中有多少行的棋子連成3個的 for (int i = 0; i<3; i++) p += (tmpQP[i][0] + tmpQP[i][1] + tmpQP[i][2]) / 3; //計算每一列中有多少列的棋子連成3個的 for (int i = 0; i<3; i++) p += (tmpQP[0][i] + tmpQP[1][i] + tmpQP[2][i]) / 3; //斜行有沒有連成3個的? p += (tmpQP[0][0] + tmpQP[1][1] + tmpQP[2][2]) / 3; p += (tmpQP[2][0] + tmpQP[1][1] + tmpQP[0][2]) / 3; //將棋盤中的空格填滿對方的棋子,既將棋盤陣列中的0變為-1 for (int i = 0; i<3; i++) for (int j = 0; j<3; j++) if (cur[i][j] == 0)tmpQP[i][j] = -1; else tmpQP[i][j] = cur[i][j]; //對方 //計算每一行中有多少行的棋子連成3個的 for (int i = 0; i<3; i++) q += (tmpQP[i][0] + tmpQP[i][1] + tmpQP[i][2]) / 3; //計算每一列中有多少列的棋子連成3個的 for (int i = 0; i<3; i++) q += (tmpQP[0][i] + tmpQP[1][i] + tmpQP[2][i]) / 3; //斜行有沒有連成3個的? q += (tmpQP[0][0] + tmpQP[1][1] + tmpQP[2][2]) / 3; q += (tmpQP[2][0] + tmpQP[1][1] + tmpQP[0][2]) / 3; return p + q; } //極大結點下界為A,極小結點上界為B int cut(int &val, int dep, bool max) //主演算法部分,實現A-B剪枝的演算法,val為上一層的評價值,dep為搜尋深度,max記錄上一層是否為極大層 { if (dep == depth || dep + num == 9) //如果搜尋深度達到最大深度,或者深度加上當前棋子數已經達到9,就直接呼叫評價函式 { return value(); } int i, j, flag, temp; bool out = false; //out記錄是否剪枝,初始為false if (CheckWin() == 1) //如果使用者玩家輸了,就置上一層的評價值為無窮(用很大的值代表無窮) { val = 10000; return 0; } if (max) //如果上一層是極大層,本層則需要是極小層,記錄flag為無窮大;反之,則為記錄為負無窮大 flag = 10000; //flag記錄本層節點的極值 else flag = -10000; for (i = 0; i<3 && !out; i++) //兩重迴圈,遍歷棋盤所有位置 { for (j = 0; j<3 && !out; j++) { if (cur[i][j] == 0) //如果該位置上沒有棋子 { if (max) //並且為上一層為極大層,即本層為極小層,輪到使用者玩家走了。 { cur[i][j] = -1; //該位置填上使用者玩家棋子 if (CheckWin() == -1) //如果使用者玩家贏了 temp = -10000; //置棋盤評價值為負無窮 else temp = cut(flag, dep + 1, !max); //否則繼續呼叫ab剪枝函式 if (temp<flag) //如果下一步棋盤的評價值小於本層節點的極值,則置本層極值為更小者,即後輩結點極小值<=祖先節點極大值 flag = temp; if (flag <= val) //如果本層的極值已經小於上一層的評價值,則不需要搜尋下去,剪枝 A剪枝 out = true; } else //如果上一層為極小層,演算法與上面剛好相反 { cur[i][j] = 1;//該位置填上電腦玩家棋子 if (CheckWin() == 1)//如果電腦玩家贏了,置棋盤評價值為正無窮 temp = 10000; else temp = cut(flag, dep + 1, !max); // 否則繼續呼叫ab剪枝函式 if (temp>flag) //如果下一步棋盤的評價值大於本層節點的極值,則置本層極值為更小者,即後輩結點極大值>=祖先節點極小值 flag = temp; if (flag >= val) //如果本層的極值已經大於上一層的評價值,則不需要搜尋下去,剪枝 B剪枝 out = true; } cur[i][j] = 0; //把模擬下的一步棋還原,回溯 } } } if (max) //根據上一層是否為極大層,用本層的極值修改上一層的評價值 { if (flag>val) val = flag; } else { if (flag<val) val = flag; } return flag; //函式返回的是本層的極值 } //使用者通過此函式來輸入落子的位置, //比如,使用者輸入31,則表示使用者在第3行第1列落子。 void UserInput() { int pos, x, y; L1: cout << "Please input your qizi (xy):\n "; cin >> pos; x = pos / 10, y = pos % 10; if (x>0 && x<4 && y>0 && y<4 && cur[x - 1][y - 1] == 0) { cur[x - 1][y - 1] = -1; } else { cout << "Input Error!\n"; goto L1; } } //主程式 int main() { int m = -10000, val = -10000, dep = 1; //m 用來存放最大的val int x_pos, y_pos; //記錄最佳走步的座標 Init(); cout << "Qipan: " << endl; PrintQP(); char IsFirst; cout << "Do you want do first?(y/n)"; cin >> IsFirst; while (IsFirst != 'y'&&IsFirst != 'n') { cout << "ERROR!" << "Do you want do first?(y/n)"; cin >> IsFirst; } if (IsFirst == 'y') { L4: // 人先走 UserInput(); PrintQP(); cout << endl; num++; value(); if (q == 0) { cout << "DOWN GAME!" << endl; system("pause"); return 0; } if (CheckWin() == -1) { cout << "You Win! GAME OVER." << endl; system("pause"); return 0; } for (int x = 0; x<3; x++) for (int y = 0; y<3; y++){ if (cur[x][y] == 0){ cur[x][y] = 1; cut(val, dep, 1); if (CheckWin() == 1) { cout << "The computer put the qizi at:" << x + 1 << y + 1 << endl; PrintQP(); cout << "The computer WIN! GAME OVER." << endl; system("pause"); return 0; } if (val>m) { m = val; x_pos = x; y_pos = y; } val = -10000; cur[x][y] = 0; } } cur[x_pos][y_pos] = 1; val = -10000; m = -10000; dep = 1; cout << "The computer put the qizi at:" << x_pos + 1 << y_pos + 1 << endl; PrintQP(); cout << endl; num++; value(); if (q == 0) { cout << "DOWN GAME!" << endl; system("pause"); return 0; } goto L4; } else { // 計算機先走 L5: for (int x = 0; x<3; x++) for (int y = 0; y<3; y++){ if (cur[x][y] == 0){ cur[x][y] = 1; cut(val, dep, 1); if (CheckWin() == 1) { cout << "The computer put the qizi at:" << x + 1 << y + 1 << endl; PrintQP(); cout << "The computer WIN! GAME OVER." << endl; system("pause"); return 0; } if (val>m) { m = val; x_pos = x; y_pos = y; } val = -10000; cur[x][y] = 0; } } cur[x_pos][y_pos] = 1; val = -10000; m = -10000; dep = 1; cout << "The computer put the qizi at:" << x_pos + 1 << y_pos + 1 << endl; PrintQP(); cout << endl; num++; value(); if (p == 0) { cout << "DOWN GAME!" << endl; system("pause"); return 0; } UserInput(); PrintQP(); cout << endl; num++; value(); if (p == 0) { cout << "DOWN GAME!" << endl; system("pause"); return 0; } if (CheckWin() == -1) { cout << "Conguatulations! You Win! GAME OVER." << endl; system("pause"); return 0; } goto L5; } system("pause"); return 0; }