完全自制的五子棋人機對戰游戲(VC++實現)


五子棋工作文檔

1說明:

      這個程序在創建初期的時候是有一個寫的比較亂的文檔的,但是很可惜回學校的時候沒有帶回來……所以現在趕緊整理一下,不然再過一段時間就忘干凈了。

       最初這個程序是受老同學所托做的,一開始的時候要求要人人對戰和人機對戰,但是大家都很明白,所謂的人人對戰就是簡單那的GDI繪圖罷了,那些基礎函數用好了自然沒問題。而人機對戰則需要一定的棋盤分析能力,做起來還是很復雜的。當時受時間限制,第一個版本是我用了兩天時間做的一個人人對戰,直接就給她發過去了,用來應付她的實習,因為我當時也不確定人機對戰能不能做出來。不過之后我一直在做,畢竟之前沒做過,算是一次嘗試。之后貌似過了9天吧,才完成了核心函數:GetAIPoint。用這么長時間一個是因為沒做過另外當時在家里還要幫家里干活,刨去干活加上打游戲的時間,平均下來每天的編碼時間不到3個小時。不過去我還是用了不少的時間來思考棋盤的分析的。走了不少彎路,吸取了不少教訓,感覺收獲還是挺大的。但是比較悲劇的是,我后來發現這個程序有內存泄露問題,問題貌似處在DrawChess函數里,因為無棋子的重繪並不會增加內存總量,看官若有興趣就幫我找找看吧,我是沒找到到底哪里出了問題……

       程序運行截圖演示:

 

2程序主要數據結構以及函數:

 1 //使用結構體有利於以后的數據擴展
 2 /*
 3 status 參數是用來表示當前這個點的狀態的,0表示白子,1表示黑子 -1表示尚無子
 4 后兩個參數是用來追蹤前一個點的,用於悔棋
 5 */
 6 typedef struct 
 7 {
 8     INT status;
 9     //悔棋 專用
10     INT PrePointx;
11     INT PrePointy;
12     INT nVisit_flag;
13 }Chess;
14 typedef struct
15 {
16     POINT startpos;//起始地點
17     POINT endpos;//終止地點
18     INT length;//長度
19     INT ChessType;//黑白子的辨別
20     INT EffectLevel;//棋子線的影響力,這個值的優先級判定應該和長度相關聯進行判斷,可以考慮通過使用一個公式來計算
21 }ChessLine;
22 // Forward declarations of functions included in this code module:
23 ATOM                MyRegisterClass(HINSTANCE hInstance);
24 BOOL                InitInstance(HINSTANCE, int);
25 LRESULT CALLBACK    WndProc(HWND, UINT, WPARAM, LPARAM);
26 INT_PTR CALLBACK    About(HWND, UINT, WPARAM, LPARAM);
27 INT g_nbase_x = 300;
28 INT g_nbase_y = 10;
29 Chess g_ChessTable[CHESS_LINE_NUM][CHESS_LINE_NUM];//作為全局變量的數據表
30 BOOL w_b_turn = 0;//下棋順序的控制變量
31 INT nxPosForChessTable = -1;//悔棋專用
32 INT nyPosForChessTable = -1;//悔棋專用
33 INT nRestart_Flag;//默認初始化的值為0,應該是重啟游戲的標志位
34 ChessLine BestLine;//白黑的最長有效線即可
35 INT DrawMode = 0;//0常規模式 1調試模式
36 INT PlayMode = 0;//游戲模式,分為人人對戰0和人機對戰1
37 //使用vector等模板時,還需要注意命名空間的問題
38 std::vector<ChessLine> w_ChessLineBuffer;//這個變量用於存儲所有的棋子線,白色
39 std::vector<ChessLine> b_ChessLineBuffer;//黑色
40 
41 void DrawTable(HDC hdc, int base_x = 0, int base_y = 0);
42 void WinRectConvert(RECT * rect);
43 void DrawChess(HDC hdc, int x, int y, int w_or_b = 0);//0為白子,1為黑子 
44 void GlobalInitial();//全局初始化函數
45 void DrwaChessOnTable(HDC hdc);
46 INT IsWin(int x, int y);
47 INT TellWhoWin(HWND hWnd, INT n, RECT * rect);
48 void BkBitmap(HDC hdc, RECT * rect);
49 void DrawInfo(HDC hdc, ChessLine * cl, INT length);
50 void GetALLLine(INT w_or_b);//根據棋盤全局信息來獲取對應顏色的最大長度線
51 INT GetMaxValCLAddr(ChessLine * parray,  INT N);//返回最大值的數字地址
52 ChessLine * GetChessMaxSubLine(INT x, INT y, INT nColor, BOOL IfRtnVal);//獲取單個點的最長線函數
53 void AddIntoBuf(ChessLine * pcl,INT w_or_b);
54 void ChessLineInitial(ChessLine * pcl, POINT * pstartpos, INT n, INT nColor);
55 inline void DeleteCL(ChessLine * pcl);
56 void DrawVecInfo(HDC hdc, std::vector<ChessLine> * pvcl);
57 ChessLine * GetBestLine(INT nColor);
58 INT GetValidSEDirection(POINT SP, POINT EP);//獲取有效的方向,返回值 0,1,2,3分別對應2-6, 3-7, 4-0,5-1,  
59 POINT GetAIPoint();//根據GetBestLine返回的黑白兩棋子線情況來判斷棋子的位置
60 POINT GetSinglePoint();
61 INT IsValidSinglePoint(int x, int y);

 

可以看到,好多好多的函數~我現在也覺得有點頭暈,不過這樣就更有必要對這個程序進行整理了。

3 程序調用層析如下:

繪圖消息:

 

鼠標左鍵消息:

 

         剛才意外的發現了我在家的時候做的那個文檔,雖然比較亂,但是還是較好的體現了一部分我的設計思想歷程,等會貼在最后面的附錄中。

         上面的函數層次圖主要還是為了表明函數之間的調用關系,這樣便於理清之間的功能關系。另外我發現在設計數據類型時使用結構體或者類是一個非常好的選擇,在數據擴充上有很大的優勢,我在這個工程中就因此受益。

4 AI部分核心函數設計部分思想

       首先就是如何看待棋盤上的棋子,如何根據棋盤上的棋子來分析出一個比較合適點作為下一步的選擇。

       我的程序中棋盤的大小是15*15的,這個還是那個同學和我說的,最初我設計的是19*19的,因為家里的顯示器分辨率比較高,放得下。X軸和Y軸各15條線,棋盤的棋子可以簡單的通過一個15*15的結構數組來表示,每個結構表示棋子的狀態。這個結構如下:

 1 /*
 2 
 3 status 參數是用來表示當前這個點的狀態的,0表示白子,1表示黑子 -1表示尚無子
 4 
 5 后兩個參數是用來追蹤前一個點的,用於悔棋
 6 
 7 */
 8 
 9 typedef struct
10 
11 {
12 
13          INT status;
14 
15          //悔棋 專用
16 
17          INT PrePointx;
18 
19          INT PrePointy;
20 
21          INT nVisit_flag;
22 
23 }Chess;

 

上面的這個結構中,status的作用就不多說了,后面的參數用處還是挺有意思的。PrePoint如其意思一樣,就是之前的那個點。第一個點的這個參數的值均為-1,用來標識無效點。而第1個點之后的所有的點這個參數的值均為前一點的坐標值。我覺得我的這個設計除了有點浪費內存,別的嘛,效率和效果還都是挺不錯的。這樣只要根據這兩個值,就能按照原路找回之前的點,從而實現悔棋以及其一套連續的操作。

最后一個參數是廢棄的,之前想通過每個點做一個標記來實現點的方向的記錄,不過后來的代碼實現表明這個是比較困難的,有更好的方法來實現,也就是我的程序中現在所使用的方法。

 1 typedef struct
 2 
 3 {
 4 
 5          POINT startpos;//起始地點
 6 
 7          POINT endpos;//終止地點
 8 
 9          INT length;//長度
10 
11          INT ChessType;//黑白子的辨別
12 
13          INT EffectLevel;//棋子線的影響力,這個值的優先級判定應該和長度相關聯進行判斷,可以考慮通過使用一個公式來計算
14 
15 }ChessLine;

上面的這個結構是用來表示棋子線的結構,其組成包括:起點和終點,長度,棋子的顏色,以及棋子線兩端是否有效。

而棋子線的優先級也可以通過一個很簡單的公式計算出來,即優先級為length + EffectLevel的結果來表示。需要注意的是,EffectLevel的值是不會等於0的,這個在線檢查函數中專門進行了處理,因為EffectLeve==0,意味着這條線是一條廢線,兩端都被堵死了,直接拋棄。

由上面的兩個結構你可以初步了解我對於整個棋盤上信息的抽象方法。

5 人人對戰的核心函數(IsWin)

在進行人人對戰的時候,核心函數其實就是要對棋盤上的棋子進行分析,判斷是否存在已經大於或等於長度為5的棋子線。

棋盤上每一個點,都可以分為4個方向,或者8個小方向。

最簡單的想法就是對棋盤上每一個點都進行計算,如果存在這樣一個點,就獲取其顏色,然后返回就可以了,由此即可判斷出誰贏了。但是仔細想想,這樣完全沒必要,因為能贏與否,與剛下的點是必然有聯系的。所以在進行檢測的時候,只需要檢測當前剛剛下的這個點就足夠了。想明白了沒?這樣一來,效率非常高,完全避免了無謂的操作。

IsWin函數的參數是棋盤上的坐標,然后通過坐標值訪問全局變量棋盤二維數組,做四個方向的檢查,從-4到+4的坐標偏移。針對越界情況專門進行了處理。但是不排除存在bug。

人人對戰的核心函數就這樣,沒別的。在AI模式下,這個函數依舊能夠用來判斷輸贏結果。

6 人機對戰核心函數POINT GetAIPoint();

根據上面的函數層次圖,能夠知道在這個函數中調用了4個重要的功能函數,先看下GetAiPoint函數的部分源代碼:

 1 POINT GetAIPoint()//根據GetBestLine返回的黑白兩棋子線情況來判斷棋子的位置
 2 
 3 {
 4 
 5          //先獲取全部的線。
 6 
 7          GetALLLine(0);
 8 
 9          GetALLLine(1);
10 
11          //這里曾造成內存泄露,原因是返回路徑會切斷刪除函數的調用
12 
13          ChessLine * pw_cl = GetBestLine(0);//白子 人方
14 
15          ChessLine * pb_cl = GetBestLine(1);//黑子 AI
16 
17          ChessLine * pfinal_cl;
18 
19          POINT rtnpos = {-1, -1};
20 
21          if(pw_cl != NULL && pb_cl != NULL)
22 
23          {
24 
25                    //防守優先
26 
27                    if(pw_cl->EffectLevel + pw_cl->length >= pb_cl->EffectLevel + pb_cl->length)
28 
29                             pfinal_cl = pw_cl;
30 
31                    else
32 
33                             pfinal_cl = pb_cl;
34 
35          }
36 
37          else if(pw_cl == NULL && pb_cl != NULL)
38 
39                    pfinal_cl = pb_cl;
40 
41          else if(pb_cl == NULL && pw_cl != NULL)
42 
43                    pfinal_cl = pw_cl;
44 
45          else //在上面的兩個ChessLine都獲取不到的時候,需要做的是,嘗試去獲取一個單獨的點。
46 
47          {
48 
49                    POINT SingleFinalPoint = GetSinglePoint();
50 
51                    return SingleFinalPoint;
52 
53          }

最先調用的函數是GetAllLine函數。這個函數的功能是查找全部的有效的線並將其添加到棋子線的vector容器中。參數是棋子顏色,0表示白色,1表示黑色。

看下這個函數的代碼:

 1 void GetALLLine(INT w_or_b)//這個函數應該只處理一個點
 2 
 3 {
 4 
 5          //現在看,不用進行8個方向的查詢,而是只需要做4個方向的查詢即可,比如:1234,剩下的0567用其他的點來檢測
 6 
 7          //八個方向為上下左右以及其45度角
 8 
 9          //8時鍾方向,上位0,順時針,從0 - 7
10 
11          /*               
12 
13                    7       0       1
14 
15                    6                 2
16 
17                    5       4       3
18 
19          */
20 
21          //這兩個變量都設計為數組,是因為8個方向的數據,都存儲處理還是可以的
22 
23          //一種比較節約空間的方法是設置臨時變量,存儲當前結果,與上一結果相比,這樣就不需要8個變量,而僅僅是兩個了。
24 
25          ChessLine * pCL;
26 
27  
28 
29          INT MaxLength = 0;
30 
31          POINT MaxStartPos = {0};
32 
33          POINT MaxEndPos = {0};
34 
35          //memset(ArrayLength, 0, sizeof(ArrayLength));//嘿,看代碼的,你應該知道我這么用是合法的吧?
36 
37          //這段代碼中有一部分代碼應該函數化
38 
39          if(0 == w_or_b)
40 
41                    w_ChessLineBuffer.clear();
42 
43          else
44 
45                    b_ChessLineBuffer.clear();
46 
47          for(int i = 0;i < CHESS_LINE_NUM;++ i)
48 
49          {
50 
51                    for(int j = 0;j < CHESS_LINE_NUM; ++ j)
52 
53                    {
54 
55                             pCL = GetChessMaxSubLine(i, j, w_or_b, FALSE);
56 
57                             if(pCL == NULL)
58 
59                                      continue;
60 
61                             if(pCL->length > MaxLength)
62 
63                             {
64 
65                                      MaxLength = pCL->length;
66 
67                                      MaxStartPos = pCL->startpos;
68 
69                                      MaxEndPos = pCL->endpos;
70 
71                             }       
72 
73                             DeleteCL(pCL);
74 
75                    }
76 
77          }
78 
79 }

 

代碼中的注釋可以好好的看一看,在方向的選擇上,8個小方向,只要每個點都能一次查找其中的4個方向,就已經足夠了。因為剩余4個方向的查找會被低地址點的查找覆蓋掉,代碼實際結果表明也是這樣的。

另外這個函數中,還調用了一個較為關鍵的函數:GetChessMaxSubLine。這個函數可以說是一個功能很強大的,實現承上啟下作用的一個函數。在這個函數中,完成了單個點的棋子線查找,避免重疊的棋子線,以及將合法的棋子線添加到容器的重要工作,這里每一步都很關鍵,直接決定棋盤數據抽象的效率和有效性。對於這個函數,我修改了數次,才最終確定下來。這個函數中使用了不少的技巧,讀的時候要好好看注釋。在GetAllLine函數中存在一定的代碼冗余,起因就是過多次的修改。

GetAllLine函數調用后,會將對應顏色的有效棋子線全部放到對應顏色棋子的vector容器中,確實做到了get all lines。

接下來調用的函數是GetBestLine函數。這個函數的功能就很簡單了,就是遍歷vector容器,獲取到最好的一條線。

那么此時你應該會有疑問了:如何判定一條線的好壞?

首先要說明的是,對於兩端都被堵住了的線,是不存在於vector容器中的。因為這樣的線一點用都沒有。從vector中讀出一條線的結構體之后,可以根據線長度和線影響力這兩個成員變量的和來進行衡量的。長度不用多解釋,線影響力就是棋子線兩端的可下點的數目。這個是五子棋中比較有趣的特點,根據這兩個值的和,就能很有效的得到一條線的優先級了。然后依此來獲取到整個容器中最好的線。這就是GetBestLine函數的功能。

在獲取到最佳線之后,需要對黑子最佳線和白字最佳線進行對比。這里我在AI設計中優先防守,所以只要黑子線不大於白字,就確定白子最佳線為要進行下一步處理的線。(白子為AI棋子)

在獲取了要進一步處理的線之后,只要根據這條線得到一個合法的點就可以了。這個沒太多可說的了,調用GetValidSEDirection后獲取到方向,然后根據始發點和終點進行相應的地址偏移就可以了。

其實這里有一個很有趣的地方,就是我根本就沒怎么關注最佳線到底是人方下的還是AI的,但是一樣能實現其功能。因為獲取到最佳線之后,如果是AI線,那么就能進一步擴大優勢;如果是人方的線,就能夠對其進行堵截。巧妙吧?

至此GetAiPoint函數的核心思想套路已經講差不多了,至少我這個發明人算是想起來了整體的構架~

7 GetSinglePoint是干什么用的?

兩點一線,如果只是單獨的一個點,是不能算成線的哦~所以對於單個且獨立的棋子點,並沒有作為線來計算並加入到容器中。但是在剛剛下棋的時候,毫無疑問只有一個點……這個時候用GetBestLine函數獲取到的指針都是空的,怎么辦?

為了應對這種情況,我專門設計了GetSinglePoint函數來解決問題。在GetAiPoint函數中可以看到,在兩個線指針都是空的時候,會調用GetSinglePoint函數從棋盤二維數組中專門找一個獨立的點,然后返回這個點周邊的一個有效的坐標值,而且需要注意的是,這個坐標是有效范圍內隨機的!為了實現這個我還頗為費了一點心思呢。看看GetSinglePoint函數:

 1 POINT GetSinglePoint()
 2 {
 3     //所謂singlepoint,就是8個相鄰點中沒有任何一點是同色點。
 4     //函數返回值為從0-7中的有效點中的一個隨機點
 5     INT npos;
 6     POINT rtnpoint = {-1, -1};
 7     for(int i = 0;i < CHESS_LINE_NUM;++ i)
 8     {
 9         for(int j = 0;j < CHESS_LINE_NUM;++ j)
10         {
11             if(g_ChessTable[i][j].status != -1)
12             {
13                 npos = IsValidSinglePoint(i, j);
14                 if(npos == -1)
15                     continue;
16                 switch(npos)
17                 {
18                 //這里的代碼直接return,就不用再break了
19                 case 0:
20                     rtnpoint.x = i - 1;
21                     rtnpoint.y = j - 1;
22                     break;
23                 case 1:
24                     rtnpoint.x = i;
25                     rtnpoint.y = j - 1;
26                     break;
27                 case 2:
28                     rtnpoint.x = i + 1;
29                     rtnpoint.y = j - 1;
30                     break;
31                 case 3:
32                     rtnpoint.x = i - 1;
33                     rtnpoint.y = j;
34                     break;
35                 case 4:
36                     rtnpoint.x = i + 1;
37                     rtnpoint.y = j;
38                     break;
39                 case 5:
40                     rtnpoint.x = i - 1;
41                     rtnpoint.y = j + 1;
42                     break;
43                 case 6:
44                     rtnpoint.x = i;
45                     rtnpoint.y = j + 1;
46                     break;
47                 case 7:
48                     rtnpoint.x = i + 1;
49                     rtnpoint.y = j + 1;
50                     break;
51                 }
52                 return rtnpoint;
53             }
54         }
55     }
56     return rtnpoint;
57 }

從中還能發現又調用了一個函數:IsValidSinglePoint。如果點合法,會返回一個隨機的方向值,0-7,即8個小方向。若非法,則返回-1。

接下來再看這個函數實現:

 1 INT IsValidSinglePoint(int x, int y)
 2 {
 3     assert(x >= 0 && y >=0 && x < CHESS_LINE_NUM && y < CHESS_LINE_NUM);
 4     char checkflag[8] = {0};//純標記位
 5     if(x - 1 >= 0)//一次查三個點
 6     {
 7         if(y - 1 >= 0)
 8         {
 9             if(g_ChessTable[x - 1][y - 1].status == -1)
10                 checkflag[0] = 1;
11         }
12         if(g_ChessTable[x - 1][y].status == -1)
13             checkflag[3] = 1;
14         if(y + 1 < CHESS_LINE_NUM)
15         {
16             if(g_ChessTable[x - 1][y + 1].status == -1)
17                 checkflag[5] = 1;
18         }
19     }
20     if(y - 1 >= 0 && g_ChessTable[x][y - 1].status == -1)
21             checkflag[1] = 1;
22     if(y + 1 < CHESS_LINE_NUM && g_ChessTable[x][y + 1].status == -1)
23 
24     {
25             checkflag[6] = 1;
26     }
27     if(x + 1 < CHESS_LINE_NUM)
28     {
29         if(g_ChessTable[x + 1][y].status == -1)
30             checkflag[4] = 1;
31         if(y + 1 < CHESS_LINE_NUM)
32         {
33             if(g_ChessTable[x + 1][y + 1].status == -1)
34                 checkflag[7] = 1;
35         }
36         if(y - 1 >= 0)
37         {
38             if(g_ChessTable[x + 1][y - 1].status == -1)
39                 checkflag[2] = 1;
40         }
41     }
42     /*調試部分
43     INT nrtn = 0;
44     for(int i = 0;i < 8;++ i)
45     {
46         if(checkflag[i] == 1)
47         {
48             nrtn |= 1 << (i * 4);
49         }
50     }*/
51     INT nCounterofValidPoint = 0;
52     for(int i = 0;i < 8;++ i)
53     {
54         if(checkflag[i] == 1)
55             nCounterofValidPoint ++;
56     }
57     if(!nCounterofValidPoint)
58         return -1;
59     srand(time(0));
60     INT nUpSection = rand() % 8;//在這倒是正好能用作地址上限
61     INT UpSearch =  nUpSection;
62     INT DownSearch = nUpSection;
63     for(;UpSearch < 8 || DownSearch >= 0;)
64     {
65         if(UpSearch < 8)
66         {
67             if(checkflag[UpSearch] == 1)
68                 return UpSearch;
69             else
70                 UpSearch ++;
71         }
72         if(DownSearch >= 0)
73         {
74             if(checkflag[DownSearch] == 1)
75                 return DownSearch;
76             else
77                 DownSearch --;
78         }
79     }
80 }

看起來一個功能簡單的函數,其實要做的操作還是不少的。因為除了要將合法的點對號入座,還要以隨機的形式取出來,代碼並不是很簡單。

由此,整個工程AI的核心實現基本介紹完畢。

附錄A 比較雜亂的最初版工作日記

五子棋工作日記

20130716創建

棋盤布局:

       初步估計為19*19的布局,這樣應該差不多。

       每個棋子的大小尺寸暫時設計為30*30個像素,應該可以的。

       期盼的網格大小為35*35,棋子放置在棋盤焦點上。

      

數據表示:

       除了棋盤布局之外,還需要一個棋盤上的數據表示矩陣,-1表示可以下,0表示白子,1表示黑子。

       並且需要處理

坐標轉換問題:

       首先,畫出來的棋盤經過了基礎坐標的偏移。

       目前的問題是,坐標對應不上。鼠標坐標的位置是基於表格的。

 

坐標對應很簡單,方案如下:

       首先,按正常的思路去畫坐標,然后,在網格的范圍中來正常的畫出棋子,棋子的坐標為左上角,但是,要畫在網格中間。

       鼠標點擊上,依舊要以網格作為確定范圍,點擊后在相應位置畫出棋子。

       以上的任務完成之后呢,效果應該是:

       用鼠標點擊網格,在對應的網格的中間畫出棋子。

上述完成后,只要簡單一步:將制表函數的頂點坐標向右下角方向偏移半個網格長度。

       然后下棋的效果就出來了

 

Win32 SDK背景圖片的處理經驗

       之前給程序貼圖片,用的都是MFC的類來進行操作。今天用了一把SDK,感覺,還是挺不錯的。代碼只有簡簡單單的這么幾行,具體如下:

HDC htmpdc = CreateCompatibleDC(hdc);

//HBITMAP hbitmap = CreateCompatibleBitmap(hdc, rect->right - rect->right, rect->bottom - rect->top);

HBITMAP hPicBitmap = LoadBitmap(hInst, MAKEINTRESOURCE(IDB_BKBITMAP));

   SelectObject(htmpdc, hPicBitmap);

   BitBlt(hdc, 0, 0, rect->right - rect->left, rect->bottom - rect->top, htmpdc, 300, 200, SRCCOPY);

   DeleteObject(hPicBitmap);

   DeleteDC(htmpdc);

首先,先調用CreateCompatibleBitmap函數來創建一個memory DC。然后再調用LoadBitmap函數獲取資源中的一張圖片,這個函數調用完成后,會獲取到一個位圖句柄。

接下來將其選入內存DC中。

最后調用BitBlt函數,把數據復制到我們從beginpaint函數中得到的hdc里面。

最后清理工作。

 

接下來應該做一下我自己的AI了。

五子棋AI思路:

首先,遇到未堵塞的對方三連點要立刻進行封堵。

在自己有優勢不如對方的時候,對對方進行封堵。

在優勢相當或者大於對方的時候,進行進攻。

 

優勢的判斷問題:

如何確定自己是優勢還是劣勢?

優勢應該為自己方可用的多連節點數多於對方的可用多連節點數。

 

判斷可用多連節點

這個剛剛做完,其實對一個點的檢查,只要滿足其中8個方向里4個防線就可以了,方向如下:

//8時時鍾方向,上為0 順時針,從0 - 7

   /*   

      7  0  1

      6     2

      5  4  3

   */

我在做的時候只做了其中的2 3 4 5其中的四個方向。

方向查找代碼:

萬惡的unicode……

//2方¤?向¨°

           INT nRight = StartPos.x + 1;

           while(nRight < CHESS_LINE_NUM && g_ChessTable[nRight][StartPos.y].status == w_or_b)

           {

              ArrayLength[0]++;

              nRight++;//向¨°右®¨°查¨¦找¨°

           }

           //保À¡ê存ä?對?應®|的Ì?點Ì?

           ArrayEndPos[0].x = nRight - 1;

           ArrayEndPos[0].y = StartPos.y;

           //3方¤?向¨°

           INT nRightDownOffset = 1;//右®¨°下?方¤?向¨°的Ì?偏?移°?地Ì?址¡¤

           while(StartPos.x + nRightDownOffset < CHESS_LINE_NUM && \

                StartPos.y + nRightDownOffset < CHESS_LINE_NUM && \

                g_ChessTable[StartPos.x + nRightDownOffset][StartPos.y + nRightDownOffset].status == w_or_b)

           {

              ArrayLength[1]++;

              nRightDownOffset++;

           }

           //保À¡ê存ä?對?應®|的Ì?點Ì?

           ArrayEndPos[1].x = StartPos.x + nRightDownOffset - 1;

           ArrayEndPos[1].y = StartPos.y + nRightDownOffset - 1;

           //4方¤?向¨°

           INT nDown = StartPos.y + 1;

           while(nDown < CHESS_LINE_NUM && g_ChessTable[StartPos.x][nDown].status == w_or_b)

           {  

              ArrayLength[2]++;

              nDown++;//向¨°下?查¨¦找¨°

           }

           //保À¡ê存ä?對?應®|的Ì?點Ì?

           ArrayEndPos[2].x = StartPos.x;

           ArrayEndPos[2].y = nDown - 1;

           //5方¤?向¨°

           INT nLeftDownOffset = 1;//左Á¨®下?方¤?向¨°偏?移°?地Ì?址¡¤,ê?x -;ê?y +

           while(StartPos.x + nLeftDownOffset < CHESS_LINE_NUM && \

                StartPos.y + nLeftDownOffset < CHESS_LINE_NUM && \

                g_ChessTable[StartPos.x - nLeftDownOffset][StartPos.y + nLeftDownOffset].status == w_or_b)

           {

              ArrayLength[3]++;

              nLeftDownOffset++;

           }

           ArrayEndPos[3].x = StartPos.x - (nLeftDownOffset - 1);//為a了¢?邏?輯-清?楚t,ê?就¨ª先¨¨這a么¡ä寫¡ä了¢?

           ArrayEndPos[3].y = StartPos.y + nLeftDownOffset - 1;

 

           INT MaxLengthAddr = GetMaxValnAddr(ArrayLength, 4);

           if(MaxLengthAddr == -1)

              return;

   現在在棋盤數據掃描上,已經能夠按照要求獲取到最長的有效棋子線了,但是,還不能對最長棋子線的兩端是否封閉進行檢測。

   初步估計要做的工作是在獲取當前點的最長棋子線后,根據其索引地址或者斜率計算的方式計算出來其可擴展方向,然后再判斷擴展方向上是否有對方的棋子或者己方的棋子占據,有點小復雜。

   另外現在的棋子長度線檢測是針對所有的線全部進行半規模檢測,也就是只檢查幫個方向,由此,倒也可以在一定程度上提高效率。

   之前的那種遞歸算法,也不是不可以,但是,那是另外一個思路了。我這個效率低一點,但是代碼還比較好寫。

 

2013-7-23 22:07

剛才遇到了一個溢出錯誤,但是中斷代碼中並沒有提示,給了我很大的困惑,因為在代碼中並沒有提示說異常出在了什么地方。

不過在調試信息的輸出欄中,我看到了有關於vector的異常信息,位置在932行處。我去看了之后,發現了如下的代碼:

#if _ITERATOR_DEBUG_LEVEL == 2

      if (size() <= _Pos)

        {  // report error

        _DEBUG_ERROR("vector subscript out of range");

        _SCL_SECURE_OUT_OF_RANGE;

        }

_DEBUG_ERROR就是932行之所在。

第一次看到的時候並沒有很放在心上,但是后來我發現,這段代碼的意思,就是訪問越界的一個判斷。

常規數組並沒有提供這個功能,但是,作為泛型編程模板的vector,提供了這個能力。而我的代碼觸發這個異常的原因近乎可笑,是在復制代碼的時候,有一個數忘記了更改,也就是0和1之差別

就是這個數的差別,會在白子線少於黑子線的時候,導致對白子線數組的越界訪問。就這么簡單。

 

現在做AI,代碼漸漸的已經膨脹到了900行,但是,我還真是沒什么欣喜的感覺。代碼越多,越難維護。看着現在的這個代碼,感覺,別人估計是看不懂的。

附錄B程序代碼

   1 // WuZiQi20130716.cpp : Defines the entry point for the application.
   2 
   3 //
   4 
   5 /*
   6 
   7 曲敬原創建於2013年07月16日
   8 
   9 */
  10 
  11 #include "stdafx.h"
  12 
  13 #include "WuZiQi20130716.h"
  14 
  15 #include <vector>//沒轍,容器還是C++好用,純SDK程序算是破產了
  16 
  17 #include <assert.h>
  18 
  19 #include <ctime>
  20 
  21 #include <cstdlib>
  22 
  23 #define MAX_LOADSTRING 100
  24 
  25 #define TABLE_SQUARE_LENGTH 35
  26 
  27 #define CHESS_LENGTH 30
  28 
  29 #define CHESS_LINE_NUM 15
  30 
  31 // Global Variables:
  32 
  33 HINSTANCE hInst;                                                                          // current instance
  34 
  35 TCHAR szTitle[MAX_LOADSTRING];                                              // The title bar text
  36 
  37 TCHAR szWindowClass[MAX_LOADSTRING];                      // the main window class name
  38 
  39 //使用結構體有利於以后的數據擴展
  40 
  41 /*
  42 
  43 status 參數是用來表示當前這個點的狀態的,0表示白子,1表示黑子 -1表示尚無子
  44 
  45 后兩個參數是用來追蹤前一個點的,用於悔棋
  46 
  47 */
  48 
  49 typedef struct
  50 
  51 {
  52 
  53          INT status;
  54 
  55          //悔棋 專用
  56 
  57          INT PrePointx;
  58 
  59          INT PrePointy;
  60 
  61          INT nVisit_flag;
  62 
  63 }Chess;
  64 
  65 typedef struct
  66 
  67 {
  68 
  69          POINT startpos;//起始地點
  70 
  71          POINT endpos;//終止地點
  72 
  73          INT length;//長度
  74 
  75          INT ChessType;//黑白子的辨別
  76 
  77          INT EffectLevel;//棋子線的影響力,這個值的優先級判定應該和長度相關聯進行判斷,可以考慮通過使用一個公式來計算
  78 
  79 }ChessLine;
  80 
  81 // Forward declarations of functions included in this code module:
  82 
  83 ATOM                                     MyRegisterClass(HINSTANCE hInstance);
  84 
  85 BOOL                                      InitInstance(HINSTANCE, int);
  86 
  87 LRESULT CALLBACK      WndProc(HWND, UINT, WPARAM, LPARAM);
  88 
  89 INT_PTR CALLBACK      About(HWND, UINT, WPARAM, LPARAM);
  90 
  91 INT g_nbase_x = 300;
  92 
  93 INT g_nbase_y = 10;
  94 
  95 Chess g_ChessTable[CHESS_LINE_NUM][CHESS_LINE_NUM];//作為全局變量的數據表
  96 
  97 BOOL w_b_turn = 0;//下棋順序的控制變量
  98 
  99 INT nxPosForChessTable = -1;//悔棋專用
 100 
 101 INT nyPosForChessTable = -1;//悔棋專用
 102 
 103 INT nRestart_Flag;//默認初始化的值為0,應該是重啟游戲的標志位
 104 
 105 ChessLine BestLine;//白黑的最長有效線即可
 106 
 107 INT DrawMode = 0;//0常規模式 1調試模式
 108 
 109 INT PlayMode = 0;//游戲模式,分為人人對戰0和人機對戰1
 110 
 111 //使用vector等模板時,還需要注意命名空間的問題
 112 
 113 std::vector<ChessLine> w_ChessLineBuffer;//這個變量用於存儲所有的棋子線,白色
 114 
 115 std::vector<ChessLine> b_ChessLineBuffer;//黑色
 116 
 117  
 118 
 119 void DrawTable(HDC hdc, int base_x = 0, int base_y = 0);
 120 
 121 void WinRectConvert(RECT * rect);
 122 
 123 void DrawChess(HDC hdc, int x, int y, int w_or_b = 0);//0為白子,1為黑子
 124 
 125 void GlobalInitial();//全局初始化函數
 126 
 127 void DrwaChessOnTable(HDC hdc);
 128 
 129 INT IsWin(int x, int y);
 130 
 131 INT TellWhoWin(HWND hWnd, INT n, RECT * rect);
 132 
 133 void BkBitmap(HDC hdc, RECT * rect);
 134 
 135 void DrawInfo(HDC hdc, ChessLine * cl, INT length);
 136 
 137 void GetALLLine(INT w_or_b);//根據棋盤全局信息來獲取對應顏色的最大長度線
 138 
 139 INT GetMaxValCLAddr(ChessLine * parray,  INT N);//返回最大值的數字地址
 140 
 141 ChessLine * GetChessMaxSubLine(INT x, INT y, INT nColor, BOOL IfRtnVal);//獲取單個點的最長線函數
 142 
 143 void AddIntoBuf(ChessLine * pcl,INT w_or_b);
 144 
 145 void ChessLineInitial(ChessLine * pcl, POINT * pstartpos, INT n, INT nColor);
 146 
 147 inline void DeleteCL(ChessLine * pcl);
 148 
 149 void DrawVecInfo(HDC hdc, std::vector<ChessLine> * pvcl);
 150 
 151 ChessLine * GetBestLine(INT nColor);
 152 
 153 INT GetValidSEDirection(POINT SP, POINT EP);//獲取有效的方向,返回值 0,1,2,3分別對應2-6, 3-7, 4-0,5-1, 
 154 
 155 POINT GetAIPoint();//根據GetBestLine返回的黑白兩棋子線情況來判斷棋子的位置
 156 
 157 POINT GetSinglePoint();
 158 
 159 INT IsValidSinglePoint(int x, int y);
 160 
 161  
 162 
 163 int APIENTRY _tWinMain(HINSTANCE hInstance,
 164 
 165                      HINSTANCE hPrevInstance,
 166 
 167                      LPTSTR    lpCmdLine,
 168 
 169                      int       nCmdShow)
 170 
 171 {
 172 
 173          UNREFERENCED_PARAMETER(hPrevInstance);
 174 
 175          UNREFERENCED_PARAMETER(lpCmdLine);
 176 
 177  
 178 
 179          // TODO: Place code here.
 180 
 181          MSG msg;
 182 
 183          HACCEL hAccelTable;
 184 
 185  
 186 
 187          // Initialize global strings
 188 
 189          LoadString(hInstance, IDS_APP_TITLE, szTitle, MAX_LOADSTRING);
 190 
 191          LoadString(hInstance, IDC_WUZIQI20130716, szWindowClass, MAX_LOADSTRING);
 192 
 193          MyRegisterClass(hInstance);
 194 
 195  
 196 
 197          // Perform application initialization:
 198 
 199          if (!InitInstance (hInstance, nCmdShow))
 200 
 201          {
 202 
 203                    return FALSE;
 204 
 205          }
 206 
 207  
 208 
 209          hAccelTable = LoadAccelerators(hInstance, MAKEINTRESOURCE(IDC_WUZIQI20130716));
 210 
 211  
 212 
 213          // Main message loop:
 214 
 215          while (GetMessage(&msg, NULL, 0, 0))
 216 
 217          {
 218 
 219                    if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg))
 220 
 221                    {
 222 
 223                             TranslateMessage(&msg);
 224 
 225                             DispatchMessage(&msg);
 226 
 227                    }
 228 
 229          }
 230 
 231  
 232 
 233          return (int) msg.wParam;
 234 
 235 }
 236 
 237  
 238 
 239  
 240 
 241  
 242 
 243 //
 244 
 245 //  FUNCTION: MyRegisterClass()
 246 
 247 //
 248 
 249 //  PURPOSE: Registers the window class.
 250 
 251 //
 252 
 253 //  COMMENTS:
 254 
 255 //
 256 
 257 //    This function and its usage are only necessary if you want this code
 258 
 259 //    to be compatible with Win32 systems prior to the 'RegisterClassEx'
 260 
 261 //    function that was added to Windows 95. It is important to call this function
 262 
 263 //    so that the application will get 'well formed' small icons associated
 264 
 265 //    with it.
 266 
 267 //
 268 
 269 ATOM MyRegisterClass(HINSTANCE hInstance)
 270 
 271 {
 272 
 273          WNDCLASSEX wcex;
 274 
 275  
 276 
 277          wcex.cbSize = sizeof(WNDCLASSEX);
 278 
 279  
 280 
 281          wcex.style                     = CS_HREDRAW | CS_VREDRAW;
 282 
 283          wcex.lpfnWndProc        = WndProc;
 284 
 285          wcex.cbClsExtra            = 0;
 286 
 287          wcex.cbWndExtra                   = 0;
 288 
 289          wcex.hInstance             = hInstance;
 290 
 291          wcex.hIcon                    = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_WUZIQI20130716));
 292 
 293          wcex.hCursor                = LoadCursor(NULL, IDC_ARROW);
 294 
 295          wcex.hbrBackground     = (HBRUSH)(COLOR_WINDOW+1);
 296 
 297          wcex.lpszMenuName   = MAKEINTRESOURCE(IDC_WUZIQI20130716);
 298 
 299          wcex.lpszClassName    = szWindowClass;
 300 
 301          wcex.hIconSm               = LoadIcon(wcex.hInstance, MAKEINTRESOURCE(IDI_SMALL));
 302 
 303  
 304 
 305          return RegisterClassEx(&wcex);
 306 
 307 }
 308 
 309  
 310 
 311 //
 312 
 313 //   FUNCTION: InitInstance(HINSTANCE, int)
 314 
 315 //
 316 
 317 //   PURPOSE: Saves instance handle and creates main window
 318 
 319 //
 320 
 321 //   COMMENTS:
 322 
 323 //
 324 
 325 //        In this function, we save the instance handle in a global variable and
 326 
 327 //        create and display the main program window.
 328 
 329 //
 330 
 331 BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)
 332 
 333 {
 334 
 335    HWND hWnd;
 336 
 337  
 338 
 339    hInst = hInstance; // Store instance handle in our global variable
 340 
 341  
 342 
 343    hWnd = CreateWindow(szWindowClass, szTitle, WS_OVERLAPPEDWINDOW,
 344 
 345       CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, NULL, NULL, hInstance, NULL);
 346 
 347  
 348 
 349    if (!hWnd)
 350 
 351    {
 352 
 353       return FALSE;
 354 
 355    }
 356 
 357  
 358 
 359    ShowWindow(hWnd, nCmdShow);
 360 
 361    UpdateWindow(hWnd);
 362 
 363  
 364 
 365    return TRUE;
 366 
 367 }
 368 
 369  
 370 
 371 //
 372 
 373 //  FUNCTION: WndProc(HWND, UINT, WPARAM, LPARAM)
 374 
 375 //
 376 
 377 //  PURPOSE:  Processes messages for the main window.
 378 
 379 //
 380 
 381 //  WM_COMMAND    - process the application menu
 382 
 383 //  WM_PAINT    - Paint the main window
 384 
 385 //  WM_DESTROY       - post a quit message and return
 386 
 387 //
 388 
 389 //
 390 
 391 LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
 392 
 393 {
 394 
 395          int wmId, wmEvent;
 396 
 397          PAINTSTRUCT ps;
 398 
 399          HDC hdc;
 400 
 401          RECT winrect;
 402 
 403          INT nlxPos;
 404 
 405          INT nlyPos;
 406 
 407          INT nDBPosx;
 408 
 409          INT nDBPosy;
 410 
 411          INT IORtmpx;//給IDM_OPTION_REGRET消息用的
 412 
 413          INT IORtmpy;
 414 
 415          POINT AIPoint;
 416 
 417          HMENU SubMenu;
 418 
 419          switch (message)
 420 
 421          {
 422 
 423          case WM_CREATE:
 424 
 425                    GlobalInitial();
 426 
 427                    break;
 428 
 429          case WM_COMMAND:
 430 
 431                    wmId    = LOWORD(wParam);
 432 
 433                    wmEvent = HIWORD(wParam);
 434 
 435                    // Parse the menu selections:
 436 
 437                    switch (wmId)
 438 
 439                    {
 440 
 441                    case IDM_ABOUT:
 442 
 443                             DialogBox(hInst, MAKEINTRESOURCE(IDD_ABOUTBOX), hWnd, About);
 444 
 445                             break;
 446 
 447                    case IDM_EXIT:
 448 
 449                             DestroyWindow(hWnd);
 450 
 451                             break;
 452 
 453                    case IDM_OPTION_REGRET:
 454 
 455                             //這一步是專門給悔棋用的
 456 
 457                             //根據當前節點的指向,進行退解
 458 
 459                             if(nxPosForChessTable < 0 ||  nyPosForChessTable < 0)
 460 
 461                                      break;
 462 
 463                             //下面這段代碼還挺好使的
 464 
 465                             IORtmpx = nxPosForChessTable;
 466 
 467                             IORtmpy = nyPosForChessTable;
 468 
 469                             g_ChessTable[IORtmpx][IORtmpy].status = -1;
 470 
 471                             nxPosForChessTable = g_ChessTable[IORtmpx][IORtmpy].PrePointx;
 472 
 473                             nyPosForChessTable = g_ChessTable[IORtmpx][IORtmpy].PrePointy;
 474 
 475                             //清理工作
 476 
 477                             g_ChessTable[IORtmpx][IORtmpy].PrePointx = -1;
 478 
 479                             g_ChessTable[IORtmpx][IORtmpy].PrePointy = -1;
 480 
 481                             (++w_b_turn) %= 2;//再次變成0或1
 482 
 483                             InvalidateRect(hWnd, NULL, TRUE);
 484 
 485                             break;
 486 
 487                    case ID_OPTION_PLAYMODE:
 488 
 489                             (++ PlayMode) %= 2;
 490 
 491                             SubMenu= GetSubMenu(GetMenu(hWnd), 1);
 492 
 493                             if(PlayMode == 1)
 494 
 495                                      CheckMenuItem(SubMenu, 1, MF_CHECKED|MF_BYPOSITION);
 496 
 497                             else
 498 
 499                                      CheckMenuItem(SubMenu, 1, MF_UNCHECKED|MF_BYPOSITION);
 500 
 501                             GlobalInitial();
 502 
 503                             InvalidateRect(hWnd, NULL, TRUE);
 504 
 505                             break;
 506 
 507                    case ID_OPTION_AIWATCH:
 508 
 509                             SubMenu= GetSubMenu(GetMenu(hWnd), 1);
 510 
 511                             (++ DrawMode) %= 2;
 512 
 513                             if(DrawMode == 1)
 514 
 515                                      CheckMenuItem(SubMenu, 2, MF_CHECKED|MF_BYPOSITION);
 516 
 517                             else
 518 
 519                                      CheckMenuItem(SubMenu, 2, MF_UNCHECKED|MF_BYPOSITION);
 520 
 521                             InvalidateRect(hWnd, NULL, TRUE);
 522 
 523                             break;
 524 
 525                    default:
 526 
 527                             return DefWindowProc(hWnd, message, wParam, lParam);
 528 
 529                    }
 530 
 531                    break;
 532 
 533          case WM_PAINT:
 534 
 535                    hdc = BeginPaint(hWnd, &ps);
 536 
 537                    GetWindowRect(hWnd, &winrect);
 538 
 539  
 540 
 541                    WinRectConvert(&winrect);
 542 
 543                    //防閃屏處理
 544 
 545                    //FillRect(hdc, &winrect, (HBRUSH)GetStockObject(WHITE_BRUSH));
 546 
 547                    BkBitmap(hdc, &winrect);
 548 
 549                   
 550 
 551                    //DrawChess(hdc, 10, 10, 0);
 552 
 553                    //根據棋盤對應數據來畫棋棋子
 554 
 555                    // TODO: Add any drawing code here...
 556 
 557                    EndPaint(hWnd, &ps);
 558 
 559                    break;
 560 
 561          case WM_ERASEBKGND:
 562 
 563                    //這塊代碼就是為了進行消息攔截,因為我並不需要把屏幕背景重新刷一遍,那樣會導致閃屏
 564 
 565                    break;
 566 
 567          case WM_DESTROY:
 568 
 569                    PostQuitMessage(0);
 570 
 571                    break;
 572 
 573          case WM_LBUTTONDOWN:
 574 
 575                    nlxPos = LOWORD(lParam) - g_nbase_x;
 576 
 577                    nlyPos = HIWORD(lParam) - g_nbase_y;
 578 
 579                    //部分初始化
 580 
 581                    GetWindowRect(hWnd, &winrect);
 582 
 583                    WinRectConvert(&winrect);
 584 
 585                    //做完了減法,一定要判斷結果是否依舊大於0;
 586 
 587                    if(nlxPos <= 0 || nlyPos <= 0)
 588 
 589                             return 0;
 590 
 591                    //這兩個除法主要是獲取左上角的坐標,用來轉換到棋盤數據對應的地址,同時下棋
 592 
 593                    nDBPosx = nlxPos / TABLE_SQUARE_LENGTH;
 594 
 595                    nDBPosy = nlyPos / TABLE_SQUARE_LENGTH;
 596 
 597                    if(nDBPosx >= CHESS_LINE_NUM || nDBPosy >= CHESS_LINE_NUM)
 598 
 599                             return 0;
 600 
 601                    //坐標判定有效之后,還需要對當前點的數據否有效進行檢測
 602 
 603                    if(g_ChessTable[nDBPosx][nDBPosy].status != -1)
 604 
 605                             return 0;
 606 
 607                    else
 608 
 609                    {
 610 
 611                             g_ChessTable[nDBPosx][nDBPosy].status = w_b_turn;
 612 
 613                             g_ChessTable[nDBPosx][nDBPosy].PrePointx = nxPosForChessTable;
 614 
 615                             g_ChessTable[nDBPosx][nDBPosy].PrePointy = nyPosForChessTable;
 616 
 617                             //復制完成后,再更新前點坐標
 618 
 619                             nxPosForChessTable = nDBPosx;
 620 
 621                             nyPosForChessTable = nDBPosy;
 622 
 623                             DrawChess(GetDC(hWnd), nDBPosx * TABLE_SQUARE_LENGTH + g_nbase_x, nDBPosy * TABLE_SQUARE_LENGTH + g_nbase_y, w_b_turn);
 624 
 625                             TellWhoWin(hWnd, IsWin(nDBPosx, nDBPosy), &winrect);
 626 
 627                    }
 628 
 629                    //這里我打算改成GetAIPoint函數執行全部的AI函數調用,包括相關的數據顯示
 630 
 631                    if(PlayMode)//1的時候執行人機對戰
 632 
 633                    {
 634 
 635                             AIPoint = GetAIPoint();
 636 
 637                             if(AIPoint.x != -1 && AIPoint.y != -1)
 638 
 639                                      g_ChessTable[AIPoint.x][AIPoint.y].status = ((++w_b_turn) %= 2);//順便執行了
 640 
 641                             g_ChessTable[AIPoint.x][AIPoint.y].PrePointx = nxPosForChessTable;
 642 
 643                             g_ChessTable[AIPoint.x][AIPoint.y].PrePointy = nyPosForChessTable;
 644 
 645                             //前點坐標更新
 646 
 647                             nxPosForChessTable = AIPoint.x;
 648 
 649                             nyPosForChessTable = AIPoint.y;
 650 
 651                             if(DrawMode == 0)
 652 
 653                                      DrawChess(GetDC(hWnd), AIPoint.x * TABLE_SQUARE_LENGTH + g_nbase_x, AIPoint.y * TABLE_SQUARE_LENGTH + g_nbase_y, w_b_turn);
 654 
 655                             else
 656 
 657                                      InvalidateRect(hWnd, NULL, TRUE);
 658 
 659                             TellWhoWin(hWnd, IsWin(AIPoint.x, AIPoint.y), &winrect);
 660 
 661                    }
 662 
 663                    //繪圖部分
 664 
 665                    (++w_b_turn) %= 2;//再次變成0或1;
 666 
 667  
 668 
 669                    break;
 670 
 671          default:
 672 
 673                    return DefWindowProc(hWnd, message, wParam, lParam);
 674 
 675          }
 676 
 677          return 0;
 678 
 679 }
 680 
 681  
 682 
 683 // Message handler for about box.
 684 
 685 INT_PTR CALLBACK About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
 686 
 687 {
 688 
 689          UNREFERENCED_PARAMETER(lParam);
 690 
 691          switch (message)
 692 
 693          {
 694 
 695          case WM_INITDIALOG:
 696 
 697                    return (INT_PTR)TRUE;
 698 
 699  
 700 
 701          case WM_COMMAND:
 702 
 703                    if (LOWORD(wParam) == IDOK || LOWORD(wParam) == IDCANCEL)
 704 
 705                    {
 706 
 707                             EndDialog(hDlg, LOWORD(wParam));
 708 
 709                             return (INT_PTR)TRUE;
 710 
 711                    }
 712 
 713                    break;
 714 
 715          }
 716 
 717          return (INT_PTR)FALSE;
 718 
 719 }
 720 
 721  
 722 
 723 void DrawTable(HDC hdc,  int base_x, int base_y)
 724 
 725 {
 726 
 727          int nsquarelength = TABLE_SQUARE_LENGTH;
 728 
 729          int nTableOffset = TABLE_SQUARE_LENGTH / 2;
 730 
 731          //畫豎表格
 732 
 733          for(int i = 0;i < CHESS_LINE_NUM;++ i)
 734 
 735          {
 736 
 737                    MoveToEx(hdc, i * nsquarelength + base_x + nTableOffset, base_y + nTableOffset, NULL);
 738 
 739                    LineTo(hdc, i * nsquarelength + base_x + nTableOffset, (CHESS_LINE_NUM - 1) * nsquarelength + base_y + nTableOffset);
 740 
 741          }
 742 
 743          //畫橫表格
 744 
 745          for(int i = 0;i < CHESS_LINE_NUM;++ i)
 746 
 747          {
 748 
 749                    MoveToEx(hdc, base_x + nTableOffset, i * nsquarelength + base_y + nTableOffset, NULL);
 750 
 751                    LineTo(hdc, (CHESS_LINE_NUM - 1) * nsquarelength + base_x + nTableOffset, i * nsquarelength + base_y + nTableOffset);
 752 
 753          }
 754 
 755 }
 756 
 757  
 758 
 759 void DrwaChessOnTable(HDC hdc)
 760 
 761 {
 762 
 763          for(int i = 0;i < CHESS_LINE_NUM;++ i)
 764 
 765          for(int j = 0;j < CHESS_LINE_NUM;++ j)
 766 
 767          {
 768 
 769                    if(g_ChessTable[i][j].status != -1)
 770 
 771                    {
 772 
 773                             DrawChess(hdc, i * TABLE_SQUARE_LENGTH + g_nbase_x, j * TABLE_SQUARE_LENGTH + g_nbase_y, g_ChessTable[i][j].status);
 774 
 775                    }
 776 
 777          }
 778 
 779 }
 780 
 781 void DrawChess(HDC hdc, int x, int y, int w_or_b)//0為白子,1為黑子
 782 
 783 {
 784 
 785          DWORD chesscolor;
 786 
 787          if(w_or_b == 0)
 788 
 789          {
 790 
 791                    chesscolor = RGB(255, 255, 255);//灰色,因為棋盤顏色背景還未選好
 792 
 793          }
 794 
 795          else
 796 
 797          {
 798 
 799                    chesscolor = RGB(0, 0, 0);
 800 
 801          }
 802 
 803          HBRUSH ChessBrush = CreateSolidBrush(chesscolor);
 804 
 805          HBRUSH OldBrush = (HBRUSH)SelectObject(hdc, ChessBrush);
 806 
 807          //下面這兩行的+2是根據效果手動確定的,效果還不錯。
 808 
 809          Ellipse(hdc, x + 2, y + 2, x + CHESS_LENGTH, y + CHESS_LENGTH);
 810 
 811          ChessBrush = (HBRUSH)SelectObject(hdc, OldBrush);
 812 
 813          assert(DeleteObject(ChessBrush) != 0);
 814 
 815 }
 816 
 817  
 818 
 819 void WinRectConvert(RECT * rect)
 820 
 821 {
 822 
 823          rect->bottom -= rect->top;
 824 
 825          rect->right -= rect->left;
 826 
 827          rect->left = 0;
 828 
 829          rect->top = 0;
 830 
 831 }
 832 
 833  
 834 
 835 void GlobalInitial()
 836 
 837 {
 838 
 839          //初始化19*19的結構數組
 840 
 841          for(int i = 0;i < CHESS_LINE_NUM;++ i)
 842 
 843          for(int j = 0;j < CHESS_LINE_NUM;++ j)
 844 
 845          {
 846 
 847                    g_ChessTable[i][j].status = -1;
 848 
 849                    //因為0 0 這個點是有效的坐標,因此初始化為-1用來表示無效點
 850 
 851                    g_ChessTable[i][j].PrePointx = -1;
 852 
 853                    g_ChessTable[i][j].PrePointy = -1;
 854 
 855                   
 856 
 857                    g_ChessTable[i][j].nVisit_flag = 0;//該參數表明此節點節點是否已經訪問過。0未訪問 1訪問
 858 
 859          }
 860 
 861          w_ChessLineBuffer.clear();
 862 
 863          b_ChessLineBuffer.clear();
 864 
 865          ;
 866 
 867 }
 868 
 869  
 870 
 871 INT IsWin(int x, int y)
 872 
 873 {
 874 
 875          //這個邏輯要仔細的想一下
 876 
 877          //首先 在這段代碼里 我很想說 如果每次都是對整個棋盤進行檢查,實在是太笨了。
 878 
 879          //畢竟每次要做的,僅僅是檢查當前這點關聯單位是否滿足條件,而且,只關心上一點的顏色即可
 880 
 881  
 882 
 883          int nTheColor = w_b_turn;
 884 
 885          int CheckCounter = 0;
 886 
 887          //行檢查
 888 
 889          int xStartPos;
 890 
 891          if(x - 4 >= 0)
 892 
 893                    xStartPos = x - 4;
 894 
 895          else
 896 
 897                    xStartPos = 0;
 898 
 899          int xEndPos;
 900 
 901          if(x + 4 < CHESS_LINE_NUM)
 902 
 903                    xEndPos = x + 4;
 904 
 905          else
 906 
 907                    xEndPos = (CHESS_LINE_NUM - 1);
 908 
 909          CheckCounter = 0;
 910 
 911          for(int i = xStartPos;i <= xEndPos;++ i)
 912 
 913          {
 914 
 915                    if(g_ChessTable[i][y].status == nTheColor)
 916 
 917                    {       
 918 
 919                             CheckCounter++;
 920 
 921                             if(CheckCounter >= 5)
 922 
 923                             {       
 924 
 925                                      CheckCounter = 0;
 926 
 927                                      return nTheColor;
 928 
 929                             }
 930 
 931                    }
 932 
 933                    else
 934 
 935                    {
 936 
 937                             CheckCounter = 0;
 938 
 939                    }
 940 
 941          }
 942 
 943          //列檢查
 944 
 945          int yStartPos;
 946 
 947          if(y - 4 >= 0)
 948 
 949                    yStartPos = y - 4;
 950 
 951          else
 952 
 953                    yStartPos = 0;
 954 
 955          int yEndPos;
 956 
 957          if(y + 4 < CHESS_LINE_NUM)
 958 
 959                    yEndPos = y + 4;
 960 
 961          else
 962 
 963                    yEndPos = (CHESS_LINE_NUM - 1);
 964 
 965          CheckCounter = 0;
 966 
 967          for(int i = yStartPos;i <= yEndPos;++ i)
 968 
 969          {
 970 
 971                    if(g_ChessTable[x][i].status == nTheColor)
 972 
 973                    {
 974 
 975                             CheckCounter++;
 976 
 977                             if(CheckCounter >= 5)
 978 
 979                             {       
 980 
 981                                      CheckCounter = 0;
 982 
 983                                      return nTheColor;
 984 
 985                             }
 986 
 987                    }
 988 
 989                    else
 990 
 991                    {
 992 
 993                             CheckCounter = 0;
 994 
 995                    }
 996 
 997          }
 998 
 999          //左上角到右下角檢查
1000 
1001          CheckCounter = 0;
1002 
1003          for(int i = -4;i <= 4;++ i)
1004 
1005          {
1006 
1007                    if(x + i < 0 || y + i < 0 || x + i >= CHESS_LINE_NUM || y + i >= CHESS_LINE_NUM)
1008 
1009                    {
1010 
1011                             continue;
1012 
1013                    }
1014 
1015                    else
1016 
1017                    {
1018 
1019                             if(g_ChessTable[x + i][y + i].status == nTheColor)
1020 
1021                             {
1022 
1023                                      CheckCounter ++;
1024 
1025                                      if(CheckCounter >= 5)
1026 
1027                                      {       
1028 
1029                                                CheckCounter = 0;
1030 
1031                                                return nTheColor;
1032 
1033                                      }
1034 
1035                             }
1036 
1037                             else
1038 
1039                             {
1040 
1041                                      CheckCounter = 0;
1042 
1043                             }
1044 
1045                    }
1046 
1047          }
1048 
1049          //右上角到左下角檢查
1050 
1051          CheckCounter = 0;
1052 
1053          for(int i = -4;i <= 4;++ i)
1054 
1055          {
1056 
1057                    if(x - i < 0 || y + i < 0 || x - i >= CHESS_LINE_NUM || y + i >= CHESS_LINE_NUM)
1058 
1059                    {
1060 
1061                             continue;
1062 
1063                    }
1064 
1065                    else
1066 
1067                    {
1068 
1069                             if(g_ChessTable[x - i][y + i].status == nTheColor)
1070 
1071                             {
1072 
1073                                      CheckCounter ++;
1074 
1075                                      if(CheckCounter >= 5)
1076 
1077                                      {       
1078 
1079                                                CheckCounter = 0;
1080 
1081                                                return nTheColor;
1082 
1083                                      }
1084 
1085                             }
1086 
1087                             else
1088 
1089                             {
1090 
1091                                      CheckCounter = 0;
1092 
1093                             }
1094 
1095                    }
1096 
1097          }
1098 
1099          return -1;
1100 
1101 }
1102 
1103  
1104 
1105 INT TellWhoWin(HWND hWnd, INT n, RECT * rect)
1106 
1107 {
1108 
1109          //SetBkMode(hdc, TRANSPARENT);這個透明參數,想了想 還是算了,背景不透明更好一點。
1110 
1111          /*把這段代碼注釋掉的原因是因為目前畫面做的還不夠好,這樣還不如直接使用messagebox函數
1112 
1113          rect->top += rect->bottom / 2;
1114 
1115          LOGFONT lf;
1116 
1117          memset(&lf, 0, sizeof(lf));
1118 
1119          lf.lfHeight = 50;
1120 
1121          HFONT hfont = CreateFontIndirect(&lf);
1122 
1123          HFONT OldFont = (HFONT)SelectObject(hdc, hfont);*/
1124 
1125          switch(n)
1126 
1127          {
1128 
1129          case 0:
1130 
1131                    //打出來白方勝
1132 
1133                    //DrawText(hdc, _T("白方勝"), 3, rect, DT_CENTER);
1134 
1135                    MessageBeep(-1);
1136 
1137                    MessageBox(hWnd, _T("白方勝"), _T("Notice"), 0);
1138 
1139                    break;
1140 
1141          case 1:
1142 
1143                    //DrawText(hdc, _T("黑方勝"), 3, rect, DT_CENTER);
1144 
1145                    MessageBeep(-1);
1146 
1147                    MessageBox(hWnd, _T("黑方勝"), _T("Notice"), 0);
1148 
1149                    //這個自然就是黑方勝了
1150 
1151                    break;
1152 
1153          default:
1154 
1155                    //DeleteObject(SelectObject(hdc,OldFont));
1156 
1157                    return 0;
1158 
1159                    break;//這個break雖然沒用,但是看着畢竟還是舒服點
1160 
1161          }
1162 
1163          //DeleteObject(SelectObject(hdc,OldFont));
1164 
1165          GlobalInitial();
1166 
1167          InvalidateRect(hWnd, NULL, TRUE);//擦寫屏幕
1168 
1169          //
1170 
1171          return 1;
1172 
1173 }
1174 
1175  
1176 
1177 void BkBitmap(HDC hdc, RECT * rect)
1178 
1179 {
1180 
1181          HDC htmpdc = CreateCompatibleDC(hdc);
1182 
1183          //HBITMAP hbitmap = CreateCompatibleBitmap(hdc, rect->right - rect->right, rect->bottom - rect->top);
1184 
1185          HBITMAP hPicBitmap = LoadBitmap(hInst, MAKEINTRESOURCE(IDB_BKBITMAP));
1186 
1187          HBITMAP OldBitmap = (HBITMAP)SelectObject(htmpdc, hPicBitmap);
1188 
1189  
1190 
1191          //代碼整合的嘗試
1192 
1193          DrawTable(htmpdc, g_nbase_x, g_nbase_y);
1194 
1195          DrwaChessOnTable(htmpdc);
1196 
1197  
1198 
1199          //調試專用
1200 
1201          SetBkMode(htmpdc, TRANSPARENT);
1202 
1203          //DrawInfo(htmpdc, MaxOfw_bLine, 2);
1204 
1205          if(DrawMode)
1206 
1207                    DrawVecInfo(htmpdc, &w_ChessLineBuffer);
1208 
1209  
1210 
1211          BitBlt(hdc, 0, 0, rect->right - rect->left, rect->bottom - rect->top, htmpdc, 0, 0, SRCCOPY);
1212 
1213          hPicBitmap = (HBITMAP)SelectObject(htmpdc, OldBitmap);
1214 
1215          DeleteObject(hPicBitmap);
1216 
1217          DeleteDC(htmpdc);
1218 
1219 }
1220 
1221  
1222 
1223 void DrawInfo(HDC hdc, ChessLine * cl, INT length)
1224 
1225 {
1226 
1227          TCHAR WORD[100];
1228 
1229          TCHAR TMPWORD[3];//三個應該就夠用了
1230 
1231          for(int i = 0;i < length;++ i)
1232 
1233          {
1234 
1235                    if(cl[i].ChessType == 0)
1236 
1237                             wcscpy(TMPWORD, _T("白方"));
1238 
1239                    else
1240 
1241                             wcscpy(TMPWORD, _T("黑方"));
1242 
1243                    wsprintf(WORD, _T("%s:StartPos x:%d y:%dEndPos x:%d y%d:%Length: %d"),
1244 
1245                             TMPWORD,
1246 
1247                             cl[i].startpos.x,
1248 
1249                             cl[i].startpos.y,
1250 
1251                             cl[i].endpos.x,
1252 
1253                             cl[i].endpos.y,
1254 
1255                             cl[i].length
1256 
1257                             );
1258 
1259                    TextOut(hdc, 0,i * 100, WORD, 3);
1260 
1261                    TextOut(hdc, 0,i * 100 + 20, WORD + 3,wcslen(WORD) - 3);
1262 
1263          }
1264 
1265 }
1266 
1267 POINT AIDeal(INT posx, INT posy)//因為大多數的變量都是全局變量,所以不需要很多參數。這兩個參數是剛剛按下的點
1268 
1269 {
1270 
1271          POINT tmppoint;
1272 
1273          return tmppoint;
1274 
1275 }
1276 
1277  
1278 
1279 void GetALLLine(INT w_or_b)//這個函數應該只處理一個點
1280 
1281 {
1282 
1283          //現在看,不用進行8個方向的查詢,而是只需要做4個方向的查詢即可,比如:1234,剩下的0567用其他的點來檢測
1284 
1285          //八個方向為上下左右以及其45度角
1286 
1287          //8時鍾方向,上位0,順時針,從0 - 7
1288 
1289          /*               
1290 
1291                    7       0       1
1292 
1293                    6                 2
1294 
1295                    5       4       3
1296 
1297          */
1298 
1299          /*這個方法是有缺陷的,正常方法應該是對每個點都進行遍歷,換言之,應該對點使用遞歸函數*/
1300 
1301          //0方向的全部線查找
1302 
1303         
1304 
1305          //這兩個變量都設計為數組,是因為8個方向的數據,都存儲處理還是可以的
1306 
1307          //一種比較節約空間的方法是設置臨時變量,存儲當前結果,與上一結果相比,這樣就不需要8個變量,而僅僅是兩個了。
1308 
1309          ChessLine * pCL;
1310 
1311  
1312 
1313          INT MaxLength = 0;
1314 
1315          POINT MaxStartPos = {0};
1316 
1317          POINT MaxEndPos = {0};
1318 
1319          //memset(ArrayLength, 0, sizeof(ArrayLength));//嘿,看代碼的,你應該知道我這么用是合法的吧?
1320 
1321          //這段代碼中有一部分代碼應該函數化
1322 
1323          if(0 == w_or_b)
1324 
1325                    w_ChessLineBuffer.clear();
1326 
1327          else
1328 
1329                    b_ChessLineBuffer.clear();
1330 
1331          for(int i = 0;i < CHESS_LINE_NUM;++ i)
1332 
1333          {
1334 
1335                    for(int j = 0;j < CHESS_LINE_NUM; ++ j)
1336 
1337                    {
1338 
1339                             pCL = GetChessMaxSubLine(i, j, w_or_b, FALSE);
1340 
1341                             if(pCL == NULL)
1342 
1343                                      continue;
1344 
1345                             if(pCL->length > MaxLength)
1346 
1347                             {
1348 
1349                                      MaxLength = pCL->length;
1350 
1351                                      MaxStartPos = pCL->startpos;
1352 
1353                                      MaxEndPos = pCL->endpos;
1354 
1355                             }       
1356 
1357                             DeleteCL(pCL);
1358 
1359                    }
1360 
1361          }
1362 
1363 }
1364 
1365 INT GetMaxValCLAddr(ChessLine * parray,  INT N)
1366 
1367 {
1368 
1369          if(parray == NULL && N <= 0)
1370 
1371                   return -1;//用來表示無效的數字
1372 
1373          INT maxval = parray[0].length;
1374 
1375          INT nrtnaddr = 0;
1376 
1377          for(int i = 1;i < N;++ i)
1378 
1379          {
1380 
1381                    if(maxval < parray[i].length)
1382 
1383                    {
1384 
1385                             maxval = parray[i].length;
1386 
1387                             nrtnaddr = i;
1388 
1389                    }
1390 
1391          }
1392 
1393          return nrtnaddr;
1394 
1395 }
1396 
1397  
1398 
1399 ChessLine * GetChessMaxSubLine(INT x, INT y, INT nColor, BOOL IfRtnVal)
1400 
1401 {
1402 
1403                    INT CheckNum = 4;
1404 
1405                    POINT StartPos;
1406 
1407                    ChessLine JudgeLine[8];
1408 
1409                    //判斷點是否合法
1410 
1411                    if(nColor != g_ChessTable[x][y].status)
1412 
1413                             return NULL;//放棄當前點      
1414 
1415                    //當前點合法后,開始8個方向的遍歷
1416 
1417                    //初始化
1418 
1419                    StartPos.x = x;
1420 
1421                    StartPos.y = y;
1422 
1423                    //一旦這個點被選入,初始長度肯定至少是1
1424 
1425                    ChessLineInitial(JudgeLine, &StartPos, 8, nColor);
1426 
1427                    JudgeLine[0].endpos = StartPos;
1428 
1429                    //2方向
1430 
1431                    INT nRight = StartPos.x + 1;
1432 
1433                    while(nRight < CHESS_LINE_NUM && g_ChessTable[nRight][StartPos.y].status == nColor)
1434 
1435                    {
1436 
1437                             JudgeLine[0].length++;
1438 
1439                             nRight++;//向右查找
1440 
1441                    }
1442 
1443                    //保存對應的點
1444 
1445                    JudgeLine[0].endpos.x = nRight - 1;
1446 
1447                    JudgeLine[0].endpos.y = StartPos.y;
1448 
1449                    //檢測線兩端的情況,數據存儲在Effectivelevel中
1450 
1451                    //線左端方向的查找
1452 
1453                    if(JudgeLine[0].startpos.x - 1 >= 0)//邊界判斷的前提條件
1454 
1455                    {
1456 
1457                             if(g_ChessTable[JudgeLine[0].startpos.x - 1][JudgeLine[0].startpos.y].status == -1)
1458 
1459                                      JudgeLine[0].EffectLevel ++;
1460 
1461                             else if(g_ChessTable[JudgeLine[0].startpos.x - 1][JudgeLine[0].startpos.y].status == nColor)
1462 
1463                             {
1464 
1465                                      //線點存在重復的線將被拋棄
1466 
1467                                      JudgeLine[0].length = 0;//這樣AddIntoBuf函數會自動拋棄該值
1468 
1469                             }
1470 
1471                    }
1472 
1473                    //線右端查找
1474 
1475                    if(JudgeLine[0].endpos.x + 1 < CHESS_LINE_NUM) 
1476 
1477                    {
1478 
1479                             if(g_ChessTable[JudgeLine[0].endpos.x + 1][JudgeLine[0].endpos.y].status == -1)
1480 
1481                                      JudgeLine[0].EffectLevel ++;
1482 
1483                             else if(g_ChessTable[JudgeLine[0].endpos.x + 1][JudgeLine[0].endpos.y].status == nColor)
1484 
1485                             {
1486 
1487                                      JudgeLine[0].length = 0;//這樣AddIntoBuf函數會自動拋棄該值
1488 
1489                             }
1490 
1491                    }
1492 
1493                    if(JudgeLine[0].EffectLevel != 0)
1494 
1495                             AddIntoBuf(&JudgeLine[0], nColor);
1496 
1497  
1498 
1499                    //3方向
1500 
1501                    INT nRightDownOffset = 1;//右下方向的偏移地址
1502 
1503                    while(StartPos.x + nRightDownOffset < CHESS_LINE_NUM && \
1504 
1505                               StartPos.y + nRightDownOffset < CHESS_LINE_NUM && \
1506 
1507                               g_ChessTable[StartPos.x + nRightDownOffset][StartPos.y + nRightDownOffset].status == nColor)
1508 
1509                    {
1510 
1511                             JudgeLine[1].length++;
1512 
1513                             nRightDownOffset++;
1514 
1515                    }
1516 
1517                    //保存對應的點
1518 
1519                    JudgeLine[1].endpos.x = StartPos.x + nRightDownOffset - 1;
1520 
1521                    JudgeLine[1].endpos.y = StartPos.y + nRightDownOffset - 1;
1522 
1523                    //右下和左上方向查找
1524 
1525                    if(JudgeLine[1].startpos.x - 1 >= 0 && JudgeLine[1].startpos.y - 1 >= 0)
1526 
1527                    {
1528 
1529                             if(g_ChessTable[JudgeLine[1].startpos.x - 1][JudgeLine[1].startpos.y - 1].status == -1)
1530 
1531                                      JudgeLine[1].EffectLevel ++;
1532 
1533                             else if(g_ChessTable[JudgeLine[1].startpos.x - 1][JudgeLine[1].startpos.y - 1].status == nColor)
1534 
1535                             {
1536 
1537                                      JudgeLine[1].length = 0;
1538 
1539                             }
1540 
1541                    }
1542 
1543                    if(JudgeLine[1].startpos.x + 1 < CHESS_LINE_NUM && JudgeLine[1].startpos.y + 1 < CHESS_LINE_NUM)
1544 
1545                    {
1546 
1547                             if(g_ChessTable[JudgeLine[1].endpos.x + 1][JudgeLine[1].endpos.y + 1].status == -1)
1548 
1549                                      JudgeLine[1].EffectLevel ++;
1550 
1551                             else if(g_ChessTable[JudgeLine[1].endpos.x + 1][JudgeLine[1].endpos.y + 1].status == nColor)
1552 
1553                             {
1554 
1555                                      JudgeLine[1].length = 0;
1556 
1557                             }
1558 
1559                    }
1560 
1561                    if(JudgeLine[1].EffectLevel != 0)
1562 
1563                             AddIntoBuf(&JudgeLine[1], nColor);
1564 
1565  
1566 
1567                    //4方向
1568 
1569                    INT nDown = StartPos.y + 1;
1570 
1571                    while(nDown < CHESS_LINE_NUM && g_ChessTable[StartPos.x][nDown].status == nColor)
1572 
1573                    {       
1574 
1575                             JudgeLine[2].length++;
1576 
1577                             nDown++;//向下查找
1578 
1579                    }
1580 
1581                    //保存對應的點
1582 
1583                    JudgeLine[2].endpos.x = StartPos.x;
1584 
1585                    JudgeLine[2].endpos.y = nDown - 1;
1586 
1587                    //上下兩個方向的查找
1588 
1589                    //上 -
1590 
1591                    if(JudgeLine[2].startpos.y - 1 >= 0)
1592 
1593                    {
1594 
1595                             if(g_ChessTable[JudgeLine[2].startpos.x][JudgeLine[2].startpos.y - 1].status == -1)
1596 
1597                                      JudgeLine[2].EffectLevel ++;
1598 
1599                             else if(g_ChessTable[JudgeLine[2].startpos.x][JudgeLine[2].startpos.y - 1].status == nColor)
1600 
1601                             {
1602 
1603                                      JudgeLine[2].length = 0;
1604 
1605                             }
1606 
1607                    }
1608 
1609                    //下 +
1610 
1611                    if(JudgeLine[2].endpos.y + 1 < CHESS_LINE_NUM)
1612 
1613                    {
1614 
1615                             if(g_ChessTable[JudgeLine[2].endpos.x][JudgeLine[2].endpos.y + 1].status == -1)
1616 
1617                                      JudgeLine[2].EffectLevel ++;
1618 
1619                             else if(g_ChessTable[JudgeLine[2].endpos.x][JudgeLine[2].endpos.y + 1].status == nColor)
1620 
1621                             {
1622 
1623                                      JudgeLine[2].length = 0;
1624 
1625                             }
1626 
1627                    }
1628 
1629                    if(JudgeLine[2].EffectLevel != 0)
1630 
1631                             AddIntoBuf(&JudgeLine[2], nColor);
1632 
1633  
1634 
1635                    //5方向
1636 
1637                    INT nLeftDownOffset = 1;//左下方向偏移地址,x -;y +
1638 
1639                    while(StartPos.x - nLeftDownOffset >= 0 && \
1640 
1641                               StartPos.y + nLeftDownOffset < CHESS_LINE_NUM && \
1642 
1643                               g_ChessTable[StartPos.x - nLeftDownOffset][StartPos.y + nLeftDownOffset].status == nColor)
1644 
1645                    {
1646 
1647                             JudgeLine[3].length++;
1648 
1649                             nLeftDownOffset++;
1650 
1651                    }
1652 
1653                    JudgeLine[3].endpos.x = StartPos.x - (nLeftDownOffset - 1);//為了邏輯清楚,就先這么寫了
1654 
1655                    JudgeLine[3].endpos.y = StartPos.y + nLeftDownOffset - 1;
1656 
1657                    //左下右上方向
1658 
1659                    //右上
1660 
1661                    if(JudgeLine[3].startpos.y - 1 >= 0 && JudgeLine[3].startpos.x + 1 < CHESS_LINE_NUM)
1662 
1663                    {
1664 
1665                             if(g_ChessTable[JudgeLine[3].startpos.x + 1][JudgeLine[3].startpos.y - 1].status == -1)
1666 
1667                                      JudgeLine[3].EffectLevel ++;
1668 
1669                             else if(g_ChessTable[JudgeLine[3].startpos.x + 1][JudgeLine[3].startpos.y - 1].status == nColor)
1670 
1671                             {
1672 
1673                                      JudgeLine[3].length = 0;
1674 
1675                             }
1676 
1677                    }
1678 
1679                    //左下
1680 
1681                    if(JudgeLine[3].endpos.y + 1 < CHESS_LINE_NUM && JudgeLine[3].endpos.x - 1 >= 0)
1682 
1683                    {
1684 
1685                             if(g_ChessTable[JudgeLine[3].endpos.x - 1][JudgeLine[3].endpos.y + 1].status == -1)
1686 
1687                                      JudgeLine[3].EffectLevel ++;
1688 
1689                             else if(g_ChessTable[JudgeLine[3].endpos.x - 1][JudgeLine[3].endpos.y + 1].status == nColor)
1690 
1691                             {
1692 
1693                                      JudgeLine[3].length = 0;
1694 
1695                             }
1696 
1697                    }
1698 
1699                    if(JudgeLine[3].EffectLevel != 0)
1700 
1701                             AddIntoBuf(&JudgeLine[3], nColor);
1702 
1703                    //這段代碼算是暫時廢棄的
1704 
1705                    if(IfRtnVal)
1706 
1707                    {
1708 
1709                             ChessLine * pFinalLine = new ChessLine;
1710 
1711                             if(pFinalLine == NULL)
1712 
1713                                      return NULL;
1714 
1715                             INT MaxLengthAddr = GetMaxValCLAddr(JudgeLine, CheckNum);
1716 
1717                             if(MaxLengthAddr == -1)
1718 
1719                             {
1720 
1721                                      delete pFinalLine;
1722 
1723                                      return NULL;
1724 
1725                             }
1726 
1727                             *pFinalLine = JudgeLine[MaxLengthAddr];
1728 
1729                             return pFinalLine;
1730 
1731                    }
1732 
1733                    return NULL;
1734 
1735 }
1736 
1737 void AddIntoBuf(ChessLine * pcl,INT w_or_b)
1738 
1739 {
1740 
1741          switch(w_or_b)
1742 
1743          {
1744 
1745          case 0://白色
1746 
1747                    if(pcl->length > 1)
1748 
1749                             w_ChessLineBuffer.push_back(*pcl);
1750 
1751                    break;
1752 
1753          case 1:
1754 
1755                    if(pcl->length > 1)
1756 
1757                             b_ChessLineBuffer.push_back(*pcl);
1758 
1759                    break;
1760 
1761          }
1762 
1763 }
1764 
1765 void ChessLineInitial(ChessLine * pcl, POINT * pstartpos, INT n, INT nColor)
1766 
1767 {
1768 
1769          if(pcl == NULL || pstartpos == NULL)
1770 
1771                    return;
1772 
1773          for(int i = 0;i < n;++ i)
1774 
1775          {
1776 
1777                    pcl[i].length = 1;
1778 
1779                    pcl[i].startpos = *pstartpos;
1780 
1781                    pcl[i].ChessType = nColor;
1782 
1783                    pcl[i].EffectLevel = 0;//最低級
1784 
1785          }
1786 
1787 }
1788 
1789 void DeleteCL(ChessLine * pcl)
1790 
1791 {
1792 
1793          delete pcl;
1794 
1795 }
1796 
1797  
1798 
1799 void DrawVecInfo(HDC hdc, std::vector<ChessLine> * pvcl)
1800 
1801 {
1802 
1803          TCHAR wcBuf[100];
1804 
1805          TCHAR tmpbuf[100];
1806 
1807          POINT tmppoint = GetAIPoint();
1808 
1809          INT num_w = pvcl->size();
1810 
1811          assert(num_w >= 0);
1812 
1813          INT num_b = (pvcl + 1)->size();
1814 
1815          assert(num_b >= 0);
1816 
1817          wsprintf(tmpbuf, _T("SP x:%d y:%d;EP x:%d y:%d;Len:%d;EL:%d;HL:%d"),
1818 
1819                    BestLine.startpos.x,
1820 
1821                    BestLine.startpos.y,
1822 
1823                    BestLine.endpos.x,
1824 
1825                    BestLine.endpos.y,
1826 
1827                    BestLine.length,
1828 
1829                    BestLine.EffectLevel,
1830 
1831                    BestLine.length + BestLine.EffectLevel
1832 
1833                    );
1834 
1835          TextOut(hdc, 0, 0, tmpbuf, wcslen(tmpbuf));
1836 
1837          wsprintf(tmpbuf, _T("AI x:%d y:%d"), tmppoint.x, tmppoint.y);
1838 
1839          TextOut(hdc, 900, 0, tmpbuf, wcslen(tmpbuf));
1840 
1841          for(int i = 0;i < num_w;++ i)
1842 
1843          {
1844 
1845                    wsprintf(wcBuf, _T("SP x:%d y:%d;EP x:%d y:%d;Len:%d;EL:%d;HL:%d"),
1846 
1847                             pvcl[0][i].startpos.x, pvcl[0][i].startpos.y,
1848 
1849                             pvcl[0][i].endpos.x, pvcl[0][i].endpos.y,
1850 
1851                             pvcl[0][i].length,
1852 
1853                             pvcl[0][i].EffectLevel,
1854 
1855                             pvcl[0][i].length + pvcl[0][i].EffectLevel);
1856 
1857                    TextOut(hdc, 0, (i+1) * 15, wcBuf, wcslen(wcBuf));
1858 
1859          }
1860 
1861          for(int i = 0;i < num_b;++ i)
1862 
1863          {
1864 
1865                    wsprintf(wcBuf, _T("SP x:%d y:%d;EP x:%d y:%d;Len:%d;EL:%d;HL:%d"),
1866 
1867                             pvcl[1][i].startpos.x, pvcl[1][i].startpos.y,
1868 
1869                             pvcl[1][i].endpos.x, pvcl[1][i].endpos.y,
1870 
1871                             pvcl[1][i].length,
1872 
1873                             pvcl[1][i].EffectLevel,
1874 
1875                             pvcl[1][i].length + pvcl[1][i].EffectLevel);
1876 
1877                    TextOut(hdc, 900, (i+1) * 15, wcBuf, wcslen(wcBuf));
1878 
1879          }
1880 
1881 }
1882 
1883  
1884 
1885 ChessLine * GetBestLine(INT nColor)
1886 
1887 {
1888 
1889          ChessLine * pcl = new ChessLine;
1890 
1891          if(pcl == NULL)
1892 
1893                    return NULL;
1894 
1895          std::vector<ChessLine> * pvcl;
1896 
1897          if(nColor == 0)
1898 
1899                    pvcl = &w_ChessLineBuffer;
1900 
1901          else
1902 
1903                    pvcl = &b_ChessLineBuffer;
1904 
1905          INT nsize = pvcl->size();
1906 
1907          if(nsize == 0)
1908 
1909                    return NULL;
1910 
1911          //刪除沒用的線
1912 
1913          //線還是先不刪了,擅自修改vector的大小會引發大量的越界問題
1914 
1915          /*
1916 
1917          std::vector<ChessLine>::iterator pvcl_itstart = pvcl->begin();
1918 
1919          std::vector<ChessLine>::iterator pvcl_itend = pvcl->end();
1920 
1921          for(int i = 0;i < nsize;)
1922 
1923          {
1924 
1925                    if(pvcl_itstart[i].EffectLevel == 0)
1926 
1927                    {
1928 
1929                             pvcl->erase(pvcl_itstart + i);
1930 
1931                             nsize --;
1932 
1933                             continue;
1934 
1935                    }
1936 
1937                    i++;
1938 
1939          }*/
1940 
1941  
1942 
1943          //然后使用優先級判斷公式 length + EffectLevel
1944 
1945          //先獲取最大值
1946 
1947          INT num_cl = pvcl->size();
1948 
1949          if(num_cl == 0)
1950 
1951                    return NULL;
1952 
1953          INT nMax = 1;
1954 
1955          INT nMaxAddr = 0;
1956 
1957          for(int i = 0;i < num_cl;++ i)
1958 
1959          {
1960 
1961                    if((*pvcl)[i].EffectLevel + (*pvcl)[i].length > nMax && (*pvcl)[i].EffectLevel != 0)
1962 
1963                    {
1964 
1965                             nMax = (*pvcl)[i].EffectLevel + (*pvcl)[i].length;
1966 
1967                             nMaxAddr = i;
1968 
1969                    }
1970 
1971          }
1972 
1973  
1974 
1975          *pcl = (*pvcl)[nMaxAddr];
1976 
1977          return pcl;
1978 
1979 }
1980 
1981  
1982 
1983 POINT GetAIPoint()//根據GetBestLine返回的黑白兩棋子線情況來判斷棋子的位置
1984 
1985 {
1986 
1987          //先獲取全部的線。
1988 
1989          GetALLLine(0);
1990 
1991          GetALLLine(1);
1992 
1993          //這里曾造成內存泄露,原因是返回路徑會切斷刪除函數的調用
1994 
1995          ChessLine * pw_cl = GetBestLine(0);//白子 人方
1996 
1997          ChessLine * pb_cl = GetBestLine(1);//黑子 AI
1998 
1999          ChessLine * pfinal_cl;
2000 
2001          POINT rtnpos = {-1, -1};
2002 
2003          if(pw_cl != NULL && pb_cl != NULL)
2004 
2005          {
2006 
2007                    //防守優先
2008 
2009                    if(pw_cl->EffectLevel + pw_cl->length >= pb_cl->EffectLevel + pb_cl->length)
2010 
2011                             pfinal_cl = pw_cl;
2012 
2013                    else
2014 
2015                             pfinal_cl = pb_cl;
2016 
2017          }
2018 
2019          else if(pw_cl == NULL && pb_cl != NULL)
2020 
2021                    pfinal_cl = pb_cl;
2022 
2023          else if(pb_cl == NULL && pw_cl != NULL)
2024 
2025                    pfinal_cl = pw_cl;
2026 
2027          else //在上面的兩個ChessLine都獲取不到的時候,需要做的是,嘗試去獲取一個單獨的點。
2028 
2029          {
2030 
2031                    POINT SingleFinalPoint = GetSinglePoint();
2032 
2033                    return SingleFinalPoint;
2034 
2035          }
2036 
2037          //這個是測試用數據,全局變量
2038 
2039          BestLine = *pfinal_cl;
2040 
2041          switch(GetValidSEDirection(pfinal_cl->startpos, pfinal_cl->endpos))
2042 
2043                    {
2044 
2045                    case 0://2-6
2046 
2047                             //2
2048 
2049                             if(g_ChessTable[pfinal_cl->startpos.x - 1][pfinal_cl->startpos.y].status == -1
2050 
2051                                      && pfinal_cl->startpos.x - 1 >= 0)
2052 
2053                             {
2054 
2055                                      rtnpos.x = pfinal_cl->startpos.x - 1;
2056 
2057                                      rtnpos.y = pfinal_cl->startpos.y;
2058 
2059                             }
2060 
2061                             else if(pfinal_cl->endpos.x + 1 < CHESS_LINE_NUM)
2062 
2063                             {
2064 
2065                                      rtnpos.x = pfinal_cl->endpos.x + 1;
2066 
2067                                      rtnpos.y = pfinal_cl->endpos.y;
2068 
2069                             }
2070 
2071                             break;
2072 
2073                    case 1://3-7
2074 
2075                             if(g_ChessTable[pfinal_cl->startpos.x - 1][pfinal_cl->startpos.y - 1].status == -1)
2076 
2077                             {
2078 
2079                                      rtnpos.x = pfinal_cl->startpos.x - 1;
2080 
2081                                      rtnpos.y = pfinal_cl->startpos.y - 1;
2082 
2083                             }
2084 
2085                             else
2086 
2087                             {
2088 
2089                                      rtnpos.x = pfinal_cl->endpos.x + 1;
2090 
2091                                      rtnpos.y = pfinal_cl->endpos.y + 1;
2092 
2093                             }
2094 
2095                             //return rtnpos;
2096 
2097                             break;
2098 
2099                    case 2://4-0
2100 
2101                             if(g_ChessTable[pfinal_cl->startpos.x][pfinal_cl->startpos.y - 1].status == -1
2102 
2103                                      && pfinal_cl->startpos.y - 1>= 0
2104 
2105                                      && pfinal_cl->endpos.y + 1 < CHESS_LINE_NUM)
2106 
2107                             {
2108 
2109                                      rtnpos.x = pfinal_cl->startpos.x;
2110 
2111                                      rtnpos.y = pfinal_cl->startpos.y - 1;
2112 
2113                             }
2114 
2115                             else
2116 
2117                             {
2118 
2119                                      rtnpos.x = pfinal_cl->endpos.x;
2120 
2121                                      rtnpos.y = pfinal_cl->endpos.y + 1;
2122 
2123                             }
2124 
2125                             //return rtnpos;
2126 
2127                             break;
2128 
2129                    case 3://5-1
2130 
2131                             if(g_ChessTable[pfinal_cl->startpos.x + 1][pfinal_cl->startpos.y - 1].status == -1
2132 
2133                                      && pfinal_cl->startpos.x + 1 < CHESS_LINE_NUM
2134 
2135                                      && pfinal_cl->startpos.y - 1 >= 0)
2136 
2137                             {
2138 
2139                                      rtnpos.x = pfinal_cl->startpos.x + 1;
2140 
2141                                      rtnpos.y = pfinal_cl->startpos.y - 1;
2142 
2143                             }
2144 
2145                             else
2146 
2147                             {
2148 
2149                                      rtnpos.x = pfinal_cl->endpos.x - 1;
2150 
2151                                      rtnpos.y = pfinal_cl->endpos.y + 1;
2152 
2153                             }
2154 
2155                             //return rtnpos;
2156 
2157                             break;
2158 
2159                    }
2160 
2161          DeleteCL(pw_cl);
2162 
2163          DeleteCL(pb_cl);
2164 
2165          return rtnpos;
2166 
2167 }
2168 
2169  
2170 
2171 INT GetValidSEDirection(POINT SP, POINT EP)//獲取有效的方向,返回值 0,1,2,3分別對應2-6, 3-7, 4-0,5-1, 
2172 
2173 {
2174 
2175          //終點減去起始點
2176 
2177          INT ndirx = EP.x - SP.x;
2178 
2179          INT ndiry = EP.y - SP.y;
2180 
2181          /*
2182 
2183          7(-1,1)         0(0,1) 1(1,1)
2184 
2185          6(-1,0)                           2(1,0)
2186 
2187          5(-1,-1)4(0,-1)       3(1,-1)
2188 
2189          */
2190 
2191          if(ndirx > 0)
2192 
2193          {
2194 
2195                    if(ndiry == 0)//2(1,0)
2196 
2197                             return 0;
2198 
2199                    else//3(1,-1)
2200 
2201                             return 1;
2202 
2203          }
2204 
2205          else if(ndirx == 0)
2206 
2207                    return 2;
2208 
2209          else
2210 
2211                    return 3;
2212 
2213 }
2214 
2215 POINT GetSinglePoint()
2216 
2217 {
2218 
2219          //所謂singlepoint,就是8個相鄰點中沒有任何一點是同色點。
2220 
2221          //函數返回值為從0-7中的有效點中的一個隨機點
2222 
2223          INT npos;
2224 
2225          POINT rtnpoint = {-1, -1};
2226 
2227          for(int i = 0;i < CHESS_LINE_NUM;++ i)
2228 
2229          {
2230 
2231                    for(int j = 0;j < CHESS_LINE_NUM;++ j)
2232 
2233                    {
2234 
2235                             if(g_ChessTable[i][j].status != -1)
2236 
2237                             {
2238 
2239                                      npos = IsValidSinglePoint(i, j);
2240 
2241                                      if(npos == -1)
2242 
2243                                                continue;
2244 
2245                                      switch(npos)
2246 
2247                                      {
2248 
2249                                      //這里的代碼直接return,就不用再break了
2250 
2251                                      case 0:
2252 
2253                                                rtnpoint.x = i - 1;
2254 
2255                                                rtnpoint.y = j - 1;
2256 
2257                                                break;
2258 
2259                                      case 1:
2260 
2261                                                rtnpoint.x = i;
2262 
2263                                                rtnpoint.y = j - 1;
2264 
2265                                                break;
2266 
2267                                      case 2:
2268 
2269                                                rtnpoint.x = i + 1;
2270 
2271                                                rtnpoint.y = j - 1;
2272 
2273                                                break;
2274 
2275                                      case 3:
2276 
2277                                                rtnpoint.x = i - 1;
2278 
2279                                                rtnpoint.y = j;
2280 
2281                                                break;
2282 
2283                                      case 4:
2284 
2285                                                rtnpoint.x = i + 1;
2286 
2287                                                rtnpoint.y = j;
2288 
2289                                                break;
2290 
2291                                      case 5:
2292 
2293                                                rtnpoint.x = i - 1;
2294 
2295                                                rtnpoint.y = j + 1;
2296 
2297                                                break;
2298 
2299                                      case 6:
2300 
2301                                                rtnpoint.x = i;
2302 
2303                                                rtnpoint.y = j + 1;
2304 
2305                                               break;
2306 
2307                                      case 7:
2308 
2309                                                rtnpoint.x = i + 1;
2310 
2311                                                rtnpoint.y = j + 1;
2312 
2313                                                break;
2314 
2315                                      }
2316 
2317                                      return rtnpoint;
2318 
2319                             }
2320 
2321                    }
2322 
2323          }
2324 
2325          return rtnpoint;
2326 
2327 }
2328 
2329  
2330 
2331 INT IsValidSinglePoint(int x, int y)
2332 
2333 {
2334 
2335          assert(x >= 0 && y >=0 && x < CHESS_LINE_NUM && y < CHESS_LINE_NUM);
2336 
2337          char checkflag[8] = {0};//純標記位
2338 
2339          if(x - 1 >= 0)//一次查三個點
2340 
2341          {
2342 
2343                    if(y - 1 >= 0)
2344 
2345                    {
2346 
2347                             if(g_ChessTable[x - 1][y - 1].status == -1)
2348 
2349                                      checkflag[0] = 1;
2350 
2351                    }
2352 
2353                    if(g_ChessTable[x - 1][y].status == -1)
2354 
2355                             checkflag[3] = 1;
2356 
2357                    if(y + 1 < CHESS_LINE_NUM)
2358 
2359                    {
2360 
2361                             if(g_ChessTable[x - 1][y + 1].status == -1)
2362 
2363                                      checkflag[5] = 1;
2364 
2365                    }
2366 
2367          }
2368 
2369          if(y - 1 >= 0 && g_ChessTable[x][y - 1].status == -1)
2370 
2371                             checkflag[1] = 1;
2372 
2373          if(y + 1 < CHESS_LINE_NUM && g_ChessTable[x][y + 1].status == -1)
2374 
2375  
2376 
2377          {
2378 
2379                             checkflag[6] = 1;
2380 
2381          }
2382 
2383          if(x + 1 < CHESS_LINE_NUM)
2384 
2385          {
2386 
2387                    if(g_ChessTable[x + 1][y].status == -1)
2388 
2389                             checkflag[4] = 1;
2390 
2391                    if(y + 1 < CHESS_LINE_NUM)
2392 
2393                    {
2394 
2395                             if(g_ChessTable[x + 1][y + 1].status == -1)
2396 
2397                                      checkflag[7] = 1;
2398 
2399                    }
2400 
2401                    if(y - 1 >= 0)
2402 
2403                    {
2404 
2405                             if(g_ChessTable[x + 1][y - 1].status == -1)
2406 
2407                                      checkflag[2] = 1;
2408 
2409                    }
2410 
2411          }
2412 
2413          /*調試部分
2414 
2415          INT nrtn = 0;
2416 
2417     for(int i = 0;i < 8;++ i)
2418 
2419     {
2420 
2421         if(checkflag[i] == 1)
2422 
2423         {
2424 
2425             nrtn |= 1 << (i * 4);
2426 
2427         }
2428 
2429     }*/
2430 
2431          INT nCounterofValidPoint = 0;
2432 
2433          for(int i = 0;i < 8;++ i)
2434 
2435          {
2436 
2437                    if(checkflag[i] == 1)
2438 
2439                             nCounterofValidPoint ++;
2440 
2441          }
2442 
2443          if(!nCounterofValidPoint)
2444 
2445                    return -1;
2446 
2447          srand(time(0));
2448 
2449          INT nUpSection = rand() % 8;//在這倒是正好能用作地址上限
2450 
2451          INT UpSearch =  nUpSection;
2452 
2453          INT DownSearch = nUpSection;
2454 
2455          for(;UpSearch < 8 || DownSearch >= 0;)
2456 
2457          {
2458 
2459              if(UpSearch < 8)
2460 
2461                    {
2462 
2463                        if(checkflag[UpSearch] == 1)
2464 
2465                 return UpSearch;
2466 
2467             else
2468 
2469                 UpSearch ++;
2470 
2471                    }
2472 
2473                    if(DownSearch >= 0)
2474 
2475                    {
2476 
2477                        if(checkflag[DownSearch] == 1)
2478 
2479                 return DownSearch;
2480 
2481             else
2482 
2483                 DownSearch --;
2484 
2485                    }
2486 
2487          }
2488 
2489 }

 


免責聲明!

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



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