2014.07.08 20:53ios
簡介:函數
Minimax策略描述的是二人在輪流操做的博弈中,盡力使本身的利益最大化(Max),使對手利益最小化(Min)的一種策略。編碼
這樣的遊戲有不少種,其中最典型的就是雙人棋牌類遊戲:中國象棋、五子棋、撲克牌等等。spa
這樣的遊戲的特色是:3d
1. 兩人交替操做,一方先開始調試
2. 兩人的操做互相獨立,沒有協做code
遊戲的種類實在太多,生活中到處是遊戲。blog
「遊戲」和「博弈」的英語釋義都是「game」,因此咱們其實能夠認爲理論上它們是一回事,只不過一個通俗,一個深奧。遊戲
圖示:ci
本次咱們使用書上給出的一個很是簡單的棋牌遊戲「三連棋」做爲例子,來解釋Minimax策略。
如圖:
給定一個3X3的棋盤,兩人輪流在上面畫「O」和「X」,誰能先將三個相同符號連成一條線(橫豎斜皆可),就算贏。
本次的代碼會實現這個遊戲的AI,容許你和程序來玩這個遊戲。運行程序後,你會發現不論你怎麼下棋,你都贏不了的。
由於這個遊戲是公平的,沒有必勝策略,所以電腦程序總能和你打成平局。
若是你很差好下棋,固然也會輸掉。
下面咱們開始討論Minimax策略的理論部分。
下面是一棵樹(分叉數量咱們不關心):
上面這棵樹叫「博弈樹」,英文是game tree。其中Max層和Min層交替出現。關於這棵樹有四點須要解釋:
1. Max層表示輪到我操做,指望個人利益最大化,因此標記爲Max層
2. Min層表示輪到對手操做,指望個人利益最小化,因此標記爲Min層
3. 節點中有一個數值,這個莫名其妙的數值是本章最難理解的東西——利益
4. 樹的底層老是葉子節點,此處每一個葉子節點都表示遊戲的一種結局,由於遊戲結束了,樹纔不會繼續延伸下去
利益是什麼?金錢,高考分數,身高體重,房子面積。總之說多了都是淚。
遊戲中的「利益」不能用一個「贏」字來歸納,不然你沒法着手去分析贏的辦法。
此處的利益,應該是具備多個參數的一個函數F(...),函數值越大,你離「贏」就越近。
好比下象棋,「贏」的定義是吃掉對手的「將」或者「帥」。因此你不能將「對手的將是否存在」做爲利益的判斷條件。
若是你手上如今有7個棋子,而對手只剩2個,那麼頗有可能你會贏,所以將雙方棋子的數量差做爲「利益」的標準,可能會更好。
一樣是象棋棋子,「車」的攻擊力巨大,「馬」的行動靈活,「士」的防護相當重要,所以不一樣的棋子爲你帶來的潛在利益各不相同。
正是因爲實際的遊戲如此複雜,想用一個包含了不少參數的函數F(...)來表達利益才顯得很是困難。
對於中國象棋之類的遊戲,這個估值函數能夠複雜到讓人吐血,也能夠很簡單。至於用戶能看到的區別,就是「電腦厲害死了」和「電腦弱爆了」。
至此,你要隨時記住一個遊戲中的兩個條件:
1. 贏的標準是什麼(走象棋,吃掉對方的將帥)
2. 爲了達到「贏」,我應該朝什麼方向努力(走象棋,努力吃掉對方的棋子)
那麼三連棋呢:
1. 贏的標準,三個棋子連成一條線
2. 爲了達到「贏」,你應該盡力把本身的棋子連在一塊兒
至此我已經不知道該怎麼繼續講了,由於我思考到這兒以後就直接開始編寫代碼了。
如今你能夠運行下面的代碼,試試和電腦下棋,並單步調試觀察運行過程。
若是你要本身編碼實現這個程序,請記住一條原則:你若是贏了電腦,那程序就是錯的。
在閱讀代碼的過程當中,請嘗試理解這個程序中是如何定義「利益」的。
實現:
感謝 @無聊的豆子君 指出代碼中的錯誤。
1 // Optimization for Minimax game strategy, using Alpha-Beta Pruning. 2 // You can watch over the 'function_call_count' variable. 3 #include <iostream> 4 #include <vector> 5 using namespace std; 6 7 int function_call_count; 8 9 bool computerWin(const vector<int> &board) 10 { 11 int i, j; 12 13 for (i = 0; i < 3; ++i) { 14 for (j = 0; j < 3; ++j) { 15 if (board[i * 3 + j] != -1) { 16 break; 17 } 18 } 19 if (j == 3) { 20 return true; 21 } 22 } 23 24 for (i = 0; i < 3; ++i) { 25 for (j = 0; j < 3; ++j) { 26 if (board[j * 3 + i] != -1) { 27 break; 28 } 29 } 30 if (j == 3) { 31 return true; 32 } 33 } 34 35 if (board[0] == board[4] && board[4] == board[8] && board[8] == -1) { 36 return true; 37 } 38 39 if (board[2] == board[4] && board[4] == board[6] && board[6] == -1) { 40 return true; 41 } 42 43 return false; 44 } 45 46 bool humanWin(const vector<int> &board) 47 { 48 int i, j; 49 50 for (i = 0; i < 3; ++i) { 51 for (j = 0; j < 3; ++j) { 52 if (board[i * 3 + j] != 1) { 53 break; 54 } 55 } 56 if (j == 3) { 57 return true; 58 } 59 } 60 61 for (i = 0; i < 3; ++i) { 62 for (j = 0; j < 3; ++j) { 63 if (board[j * 3 + i] != 1) { 64 break; 65 } 66 } 67 if (j == 3) { 68 return true; 69 } 70 } 71 72 if (board[0] == board[4] && board[4] == board[8] && board[8] == 1) { 73 return true; 74 } 75 76 if (board[2] == board[4] && board[4] == board[6] && board[6] == 1) { 77 return true; 78 } 79 80 return false; 81 } 82 83 bool fullBoard(const vector<int> &board) 84 { 85 for (int i = 0; i < 9; ++i) { 86 if (board[i] == 0) { 87 return false; 88 } 89 } 90 91 return true; 92 } 93 94 void findComputerMove(vector<int> &board, int &best_move, int &result, 95 int alpha, int beta) 96 { 97 void findHumanMove(vector<int> &, int &, int &, int, int); 98 int dc, i, response; 99 100 ++function_call_count; 101 best_move = -1; 102 103 if (fullBoard(board)) { 104 result = 0; 105 return; 106 } 107 108 if (humanWin(board)) { 109 result = 1; 110 return; 111 } 112 113 if (computerWin(board)) { 114 result = -1; 115 return; 116 } 117 118 result = alpha; 119 for (i = 0; i < 9 && result > beta; ++i) { 120 if (board[i] != 0) { 121 continue; 122 } 123 board[i] = -1; 124 findHumanMove(board, dc, response, result, beta); 125 board[i] = 0; 126 127 if (best_move == -1 || response < result) { 128 result = response; 129 best_move = i; 130 } 131 } 132 } 133 134 void findHumanMove(vector<int> &board, int &best_move, int &result, int alpha, 135 int beta) 136 { 137 void findComputerMove(vector<int> &, int &, int &, int, int); 138 int dc, i, response; 139 140 ++function_call_count; 141 best_move = -1; 142 143 if (fullBoard(board)) { 144 result = 0; 145 return; 146 } 147 148 if (computerWin(board)) { 149 result = -1; 150 return; 151 } 152 153 if (humanWin(board)) { 154 result = 1; 155 return; 156 } 157 158 result = beta; 159 for (i = 0; i < 9 && result < alpha; ++i) { 160 if (board[i] != 0) { 161 continue; 162 } 163 board[i] = 1; 164 findComputerMove(board, dc, response, alpha, result); 165 board[i] = 0; 166 167 if (best_move == -1 || response > result) { 168 result = response; 169 best_move = i; 170 } 171 } 172 } 173 174 void printBoard(const vector<int> &board) 175 { 176 cout << " 1 2 3" << endl; 177 int i, j; 178 179 for (i = 0; i < 3; ++i) { 180 cout << i + 1; 181 for (j = 0; j < 3; ++j) { 182 cout << ' '; 183 switch(board[i * 3 + j]) { 184 case -1: 185 cout << 'X'; 186 break; 187 case 0: 188 cout << '.'; 189 break; 190 case 1: 191 cout << 'O'; 192 break; 193 } 194 } 195 cout << endl; 196 } 197 } 198 199 int main() 200 { 201 vector<int> board; 202 int n; 203 int result; 204 205 board.resize(9, 0); 206 while (cin >> n) { 207 if (n < 0 || n >= 9 || board[n]) { 208 cout << "Invalid move" << endl; 209 continue; 210 } 211 212 board[n] = 1; 213 printBoard(board); 214 if (humanWin(board)) { 215 cout << "You win." << endl; 216 break; 217 } 218 219 if (fullBoard(board)) { 220 cout << "Draw." << endl; 221 break; 222 } 223 224 result = 1; 225 function_call_count = 0; 226 findComputerMove(board, n, result, 1, -1); 227 cout << "Number of function calls: " << function_call_count << endl; 228 board[n] = -1; 229 printBoard(board); 230 if (computerWin(board)) { 231 cout << "Computer win." << endl; 232 break; 233 } 234 235 if (fullBoard(board)) { 236 cout << "Draw." << endl; 237 break; 238 } 239 } 240 241 return 0; 242 }