完全自制的五子棋人机对战游戏(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 }

 

posted @ 2013-08-09 21:52  Matrix_R  阅读(15672)  评论(1编辑  收藏  举报