五子棋人機對戰設計


一、人機對戰算法概述

人機對戰屬於一種弱人工智能算法,其核心是:當玩家落下一枚棋子后,計算出這枚棋子構成的所有棋型,找出威脅程度最大的棋型,並破解其產生的威脅。

五子棋中所能產生的棋型有很多,如果棋子至少有一邊被封死,這種棋型被稱為“死”棋型,反之被稱為“活”棋型。根據棋子的數量,棋型又可以細化為“活三”、“死三”、“活四”等不同的棋型。棋型的不同,產生的威脅程度不同,破解的方式也不同。以四枚棋子為例,列出所有棋型及其威脅值和破解方法,如圖4.16所示。

圖4.16  四個棋子所產生的棋型及其威脅值和破解方法

說明:棋型就是棋子在棋盤上組合成的形狀。

二、電腦自動處理用戶請求

玩家聯機對戰的時候,是在各自電腦中處理對方的指令。但是玩家與電腦對戰,則是在一台電腦中同時處理玩家和電腦的指令,這種情況下就不能再使用聯機對戰的命令處理邏輯了。在AI(電腦智能對戰算法)類中,編寫oprationHandler()方法,來專門處理玩家向電腦發來的指令。由於本程序取消了人機對戰中的“認輸”、“和棋”、“悔棋”命令,所以只處理玩家的“開始”命令即可。具體代碼如下。

01 publicvoid oprationHandler(Object messageObj) { 02        intcode = (Integer) messageObj; // 獲取命令代碼
03        switch (code) {                                        // 判斷命令
04        case ChessPanel.OPRATION_START_MACHINE:                 // 如果是玩家請求開始游戲
05            frame.getChessPanel1().setTowardsStart(true);         // 設置AI的游戲開始狀態為true
06            break; 07    }

三、電腦判斷落棋點

電腦判斷落棋點總共分四個步驟:第一步,遍歷整個棋盤,獲取每個棋子坐標;第二步,以一個棋子為中心,獲取其四個直線方向排列的所有棋子;第三步,判斷此直線排列的棋子中是否出現了有威脅的棋型;第四部,根據棋型庫給出的棋型和破解方法,記錄其威脅值和破解方案。下面將詳細介紹這四個步驟是如何實現的。

1. 遍歷整個棋盤,尋找落棋點

執行AI類中forEach()方法,此方法可以獲取電腦最終給定的下棋位置。遍歷整個棋盤,查看每一個棋子所能形成的棋型,找出其中威脅最大的,根據棋型庫給出的破解位置找出落棋點。具體代碼如下:

01 privateint[] forEach() { 02        intx = -1, y = -1;                                        // 將要下的棋子坐標
03        intthreat = 0;                                            // 棋盤上出現的最大威脅值
04        byte[][] chessmanArray = gobangModel1.getChessmanArray();    // 獲得棋盤
05        for (inti = 0; i< 15; i++) {                            // 遍歷棋盤行
06            for (intj = 0; j< 15; j++) {                        // 遍歷棋盤列
07                if (chessmanArray[i][j] > 0) {                    // 如果此處有白棋子
08                    // 捕捉每個棋子形成的棋型
09                    inttmp[] = catchChessModle(i, j, chessmanArray); 10                    // 如果存在比當前最大威脅值還要大的威脅值,則記錄此處落子坐標
11                    if (tmp[0] >threat) { 12                        threat = tmp[0];                            // 更新最大威脅值
13                        x = tmp[1];                                // 更新落子橫坐標
14                        y = tmp[2];                                // 更新落子縱坐標
15 } 16 } 17 } 18 } 19        returnnewint[] { x, y };                                // 返回橫縱坐標組成的一維數組
20    }

2. 判斷棋子構成的棋型

forEach()方法中調用了catchChessModle()方法,這個方法以一枚棋子為原點向四種方位延伸,分別判斷這四條線上可能產生具有威脅的棋型,並根據棋型庫給出的破解位置,換算成棋盤坐標,並返回成結果數組。具體代碼如下:

01    privateint[] catchChessModle(intx, inty, byte[][] chessmanArray) { 02        // 索引0:記錄此位置的棋子可產生的最大威脅值或優勢值
03        // 索引1:對應下棋橫坐標
04        // 索引2:對應下棋橫坐標
05        intposition[] = newint[3]; 06        // 創建以被捕捉棋子為中心的四個方向形成的棋型
07        // 以參數點為中心點,保存四個方向的棋型,方向分別為 — | \ /
08        intmodel[][] = newint[4][11]; 09        for (inttmp[] : model) {                                // 遍歷數組
10            Arrays.fill(tmp, boundary);                        // 將數組填充為邊界常量
11 } 12        // 把參數點放入每行的中心部位
13        model[0][5] = model[1][5] = model[2][5] = model[3][5] = chessmanArray[x][y]; 14        // 以該棋子為中心,向兩邊走5步
15        for (inti = 1; i<= 5; i++) { 16            // 水平方向棋型
17            if (x - i>= 0) {                                // 如果沒有走出邊界
18                model[0][5 - i] = chessmanArray[x - i][y];        // 將左側棋子記錄到水平棋型當中
19 } 20            if (x + i<= 14) {                                // 如果沒有走出邊界
21                model[0][5 + i] = chessmanArray[x + i][y];        // 將右側棋子記錄到水平棋型當中
22 } 23    
24            // 垂直方向棋型
25            if (y - i>= 0) {                                // 如果沒有走出邊界
26                model[1][5 - i] = chessmanArray[x][y - i];        // 將上方棋子記錄到垂直棋型當中
27 } 28            if (y + i<= 14) {                                // 如果沒有走出邊界
29                model[1][5 + i] = chessmanArray[x][y + i];        // 將下方棋子記錄到垂直棋型當中
30 } 31    
32            // 反斜杠方向棋型
33            if (x - i>= 0 &&y + i<= 14) {                    // 如果沒有走出邊界
34                // 將左下方棋子記錄到反斜棋型當中
35                model[2][5 - i] = chessmanArray[x - i][y + i]; 36 } 37            if (x + i<= 14 &&y - i>= 0) {                    // 如果沒有走出邊界
38                // 將右上方棋子記錄到反斜棋型當中
39                model[2][5 + i] = chessmanArray[x + i][y - i]; 40 } 41    
42            // 正斜杠方向棋型
43            if (x - i>= 0 &&y - i>= 0) {                    // 如果沒有走出邊界
44                // 將左上方棋子記錄到正斜棋型當中
45                model[3][5 - i] = chessmanArray[x - i][y - i]; 46 } 47            if (x + i<= 14 &&y + i<= 14) {                    // 如果沒有走出邊界
48                // 將右下方棋子記錄到正斜棋型當中
49                model[3][5 + i] = chessmanArray[x + i][y + i]; 50 } 51 } 52        intscore = 0;                        // 記錄最大評分(威脅值)
53        intdirection = -1;                    // 記錄最大評分的方向(model數組一維下標)
54        intindex = 0;                        // 記錄坐標偏移量(judgeModle()方法給予的破解位置)
55        for (inti = 0; i<model.length; i++) {    // 遍歷棋型數組
56            intgetResult[] = judgeModle(model[i]);    // 針對此方向棋型,給予破解方案
57            if (score<getResult[1]) {        // 如果出現比當前最大威脅值還要大的威脅
58                score = getResult[1];            // 更新最大分(威脅值)
59                // 被捕捉的棋子在模型中的索引為5,getResult[0]為破解方案中的下棋索引位置
60                // getResult[0] - 5 = 破解位置距離被捕捉的棋子的索引位置
61                index = getResult[0] - 5; 62                direction = i;                // 記錄此棋型的方向
63 } 64 } 65        switch (direction) {                    // 判斷最大威脅值所在的方向
66        case 0:                                // 如果是水平方向
67            x += index;                        // 下棋的位置是原位置向右(或向左)偏移index的值
68            break; 69        case 1:                                // 如果是垂直方向
70            y += index;                        // 下棋的位置是原位置向下(或向上)偏移index的值
71            break; 72        case 2:                                // 如果是反斜方向
73            x += index;                        // 下棋的位置是原位置向右(或向左)偏移index的值
74            y -= index;                        // 下棋的位置是原位置向上(或向下)偏移index的值
75            break; 76        case 3:                                // 如果是正斜方向
77            x += index;                        // 下棋的位置是原位置向右(或向左)偏移index的值
78            y += index;                        // 下棋的位置是原位置向下(或向上)偏移index的值
79            break; 80 } 81        position[0] = score;                    // 記錄此棋子的最大評分(威脅值)
82        position[1] = x;                        // 記錄對應下棋橫坐標
83        position[2] = y;                        // 記錄對應下棋縱坐標
84    
85        returnposition;                        // 返回結果數組
86    }

3. 獲取棋型威脅值及其破解方法

catchChessModle()中調用了judgeModle()方法,這個方法可以給出具體棋型的威脅值和相應破解位置。首先將一行棋子轉換為字符串,然后查找字符串中是否出現與棋型庫中相匹配的棋型,找出其中威脅值最大棋型,記錄其的威脅值和落棋點並返回成結果數組。具體代碼如下:

01 publicint[] judgeModle(intmodel[]) { 02        intpiont[] = newint[2];                        // 初始化返回結果數組
03        intscore = 0;                                // 記錄最大評分
04        StringBuffer sb = new StringBuffer();        // 准備將棋型數組保存為字符串的StringBuffer
05        for (intnum : model) {                        // 遍歷數組,將數組變成字符串
06            if (num == GobangModel.BLACK_CHESSMAN) {    // 如果是黑子
07                num = 4;                                // 改為其他數字,以免負號會占字符
08 } 09            sb.append(num);                            // 字符串添加此數字
10 } 11        Object library[][] = getModelLibrary();        // 獲取棋型庫中所有棋型及其解決方案
12        for (inti = 0; i<library.length; i++) {        // 遍歷棋型庫
13            String chessModel = (String) library[i][0];// 獲取庫中棋型
14            intmodelIndex = -1;        // 臨時變量,用於保存某棋型在字符串中出現的索引位置
15            // 如果存在此棋型,則將棋型出現的位置付給modelIndex
16            if ((modelIndex = sb.indexOf(chessModel)) != -1) { 17                intscoreInLib = (int) library[i][1];    // 獲取棋型評分
18                intstepIndex = (int) library[i][2];    // 獲取對應的下棋位置
19                if (score<scoreInLib) {                // 如果出現更高的評分
20                    score = scoreInLib;                // 更新最大分
21                    // 記錄應該(在這一行中)下棋的實際索引位置。
22                    // 棋型在字符串中的索引位置 + 棋型給出的解決位置 = 字符串中的解決位置
23                    piont[0] = modelIndex + stepIndex; 24                    piont[1] = score;                // 記錄最大分數
25 } 26 } 27 } 28        returnpiont;                                // 返回結果數組
29    }

4. 棋型庫

judgeModle ()方法中調用了getModelLibrary()方法,該方法用於獲取棋型庫中所有棋型及其解決方案,這個棋型庫可以自己搭建,也可以《Java項目實戰入門》中的源碼。

 

本文摘自明日科技出版的《Java項目開發實戰入門》,轉載請注明出處!!!

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM