海闊天空

          I'm on my way!
          隨筆 - 17, 文章 - 69, 評論 - 21, 引用 - 0
          數(shù)據(jù)加載中……

          五子棋AI算法

          五子棋的核心算法
          2009年04月22日 星期三 17:55
          五子棋是一種受大眾廣泛喜愛的游戲,其規(guī)則簡單,變化多端,非常富有趣味性和消遣性。這里設(shè)計和實(shí)現(xiàn)了一個人機(jī)對下的五子棋程序,采用了博弈樹的方法,應(yīng)用了剪枝和最大最小樹原理進(jìn)行搜索發(fā)現(xiàn)最好的下子位置。介紹五子棋程序的數(shù)據(jù)結(jié)構(gòu)、評分規(guī)則、勝負(fù)判斷方法和搜索算法過程。

           

           

          一、相關(guān)的數(shù)據(jù)結(jié)構(gòu)
              關(guān)于盤面情況的表示,以鏈表形式表示當(dāng)前盤面的情況,目的是可以允許用戶進(jìn)行悔棋、回退等操作。
              CList StepList;
              其中Step結(jié)構(gòu)的表示為:

              struct Step
              {
                int m; //m,n表示兩個坐標(biāo)值
                int n;
                char side; //side表示下子方
              };
          以數(shù)組形式保存當(dāng)前盤面的情況,
          目的是為了在顯示當(dāng)前盤面情況時使用:
          char FiveArea[FIVE_MAX_LINE][FIVE_MAX_LINE];

              其中FIVE_MAX_LINE表示盤面最大的行數(shù)。

              同時由于需要在遞歸搜索的過程中考慮時間和空間有效性,只找出就當(dāng)前情況來說相對比較好的幾個盤面,而不是對所有的可下子的位置都進(jìn)行搜索,這里用變量CountList來表示當(dāng)前搜索中可以選擇的所有新的盤面情況對象的集合:

          CList CountList;
              其中類CBoardSituiton為:
          class CBoardSituation
          {
          CList StepList; //每一步的列表
          char FiveArea[FIVE_MAX_LINE][FIVE_MAX_LINE];
          struct Step machineStep;    //機(jī)器所下的那一步
          double value; //該種盤面狀態(tài)所得到的分?jǐn)?shù)
          }

          二、評分規(guī)則
              對于下子的重要性評分,需要從六個位置來考慮當(dāng)前棋局的情況,分別為:-,¦,/,,//,\


              實(shí)際上需要考慮在這六個位置上某一方所形成的子的布局的情況,對于在還沒有子的地方落子以后的當(dāng)前局面的評分,主要是為了說明在這個地方下子的重要性程度,設(shè)定了一個簡單的規(guī)則來表示當(dāng)前棋面對機(jī)器方的分?jǐn)?shù)。

              基本的規(guī)則如下:

          判斷是否能成5, 如果是機(jī)器方的話給予100000分,如果是人方的話給予-100000 分;
          判斷是否能成活4或者是雙死4或者是死4活3,如果是機(jī)器方的話給予10000分,如果是人方的話給予-10000分;
          判斷是否已成雙活3,如果是機(jī)器方的話給予5000分,如果是人方的話給予-5000 分;
          判斷是否成死3活3,如果是機(jī)器方的話給予1000分,如果是人方的話給予-1000 分;
          判斷是否能成死4,如果是機(jī)器方的話給予500分,如果是人方的話給予-500分;
          判斷是否能成單活3,如果是機(jī)器方的話給予200分,如果是人方的話給予-200分;
          判斷是否已成雙活2,如果是機(jī)器方的話給予100分,如果是人方的話給予-100分;
          判斷是否能成死3,如果是機(jī)器方的話給予50分,如果是人方的話給予-50分;
          判斷是否能成雙活2,如果是機(jī)器方的話給予10分,如果是人方的話給予-10分;
          判斷是否能成活2,如果是機(jī)器方的話給予5分,如果是人方的話給予-5分;
          判斷是否能成死2,如果是機(jī)器方的話給予3分,如果是人方的話給予-3分。

              實(shí)際上對當(dāng)前的局面按照上面的規(guī)則的順序進(jìn)行比較,如果滿足某一條規(guī)則的話,就給該局面打分并保存,然后退出規(guī)則的匹配。注意這里的規(guī)則是根據(jù)一般的下棋規(guī)律的一個總結(jié),在實(shí)際運(yùn)行的時候,用戶可以添加規(guī)則和對評分機(jī)制加以修正。

          三、勝負(fù)判斷
              實(shí)際上,是根據(jù)當(dāng)前最后一個落子的情況來判斷勝負(fù)的。實(shí)際上需要從四個位置判斷,以該子為出發(fā)點(diǎn)的水平,豎直和兩條分別為 45度角和135度角的線,目的是看在這四個方向是否最后落子的一方構(gòu)成連續(xù)五個的棋子,如果是的話,就表示該盤棋局已經(jīng)分出勝負(fù)。具體見下面的圖示:


          四、搜索算法實(shí)現(xiàn)描述
              注意下面的核心的算法中的變量currentBoardSituation,表示當(dāng)前機(jī)器最新的盤面情況, CountList表示第一層子節(jié)點(diǎn)可以選擇的較好的盤面的集合。核心的算法如下:
          void MainDealFunction()
          {
          value=-MAXINT; //對初始根節(jié)點(diǎn)的value賦值
          CalSeveralGoodPlace(currentBoardSituation,CountList);
          //該函數(shù)是根據(jù)當(dāng)前的盤面情況來比較得到比較好的可以考慮的幾個盤面的情況,可以根據(jù)實(shí)際的得分情況選取分?jǐn)?shù)比較高的幾個盤面,也就是說在第一層節(jié)點(diǎn)選擇的時候采用貪婪算法,直接找出相對分?jǐn)?shù)比較高的幾個形成第一層節(jié)點(diǎn),目的是為了提高搜索速度和防止堆棧溢出。
          pos=CountList.GetHeadPosition();
          CBoardSituation* pBoard;
          for(i=0;ivalue=Search(pBoard,min,value,0);
          Value=Select(value,pBoard->value,max);
          //取value和pBoard->value中大的賦給根節(jié)點(diǎn)
          }
          for(i=0;ivalue)
          //找出那一個得到最高分的盤面
          {
              currentBoardSituation=pBoard;
              PlayerMode=min; //當(dāng)前下子方改為人
              Break;
          }
          }

              其中對于Search函數(shù)的表示如下:實(shí)際上核心的算法是一個剪枝過程,其中在這個搜索過程中相關(guān)的四個參數(shù)為:(1)當(dāng)前棋局情況;(2)當(dāng)前的下子方,可以是機(jī)器(max)或者是人(min);(3)父節(jié)點(diǎn)的值oldValue;(4)當(dāng)前的搜索深度depth。

          double Search(CBoardSituation&
          board,int mode,double oldvalue,int depth)
          {
          CList m_DeepList;
          if(deptholdvalue))==    TRUE)
                {
                    if(mode==max)
                      value=select(value,search(successor
                    Board,min,value,depth+1),max);
                    else
                      value=select(value,search(successor
                      Board,max,value,depth+1),min);
                }
                return value;
          }
          else
          {
          if ( goal(board)<>0)
          //這里goal(board)<>0表示已經(jīng)可以分出勝負(fù)
          return goal(board);
          else
          return evlation(board);
                  }
              }

              注意這里的goal(board)函數(shù)是用來判斷當(dāng)前盤面是否可以分出勝負(fù),而evlation(board)是對當(dāng)前的盤面從機(jī)器的角度進(jìn)行打分。

              下面是Select函數(shù)的介紹,這個函數(shù)的主要目的是根據(jù) PlayerMode情況,即是機(jī)器還是用戶來返回節(jié)點(diǎn)的應(yīng)有的值。

          double Select(double a,double b,int mode)
          {
          if(a>b && mode==max)&brvbar;&brvbar; (a< b && mode==min)
          return a;
          else
          return b;
          }

          五、小結(jié)
              在Windows操作系統(tǒng)下,用VC++實(shí)現(xiàn)了這個人機(jī)對戰(zhàn)的五子棋程序。和國內(nèi)許多只是采用規(guī)則或者只是采用簡單遞歸而沒有剪枝的那些程序相比,在智力上和時間有效性上都要好于這些程序。同時所討論的方法和設(shè)計過程為用戶設(shè)計其他的游戲(如象棋和圍棋等)提供了一個參考

          posted on 2009-07-13 18:46 石頭@ 閱讀(5755) 評論(0)  編輯  收藏 所屬分類: DS & Alg


          只有注冊用戶登錄后才能發(fā)表評論。


          網(wǎng)站導(dǎo)航:
           
          主站蜘蛛池模板: 韶山市| 呼图壁县| 隆化县| 陆丰市| 临桂县| 通州市| 罗平县| 保山市| 清水河县| 万载县| 富平县| 阜新市| 股票| 伊春市| 延边| 镇远县| 航空| 云安县| 潢川县| 涞源县| 库尔勒市| 卫辉市| 临桂县| 正蓝旗| 永寿县| 偃师市| 马山县| 乌苏市| 确山县| 湄潭县| 靖江市| 舟曲县| 武宣县| 阿勒泰市| 东台市| 盱眙县| 黑河市| 无为县| 江西省| 平邑县| 高州市|