五子棋工作文档

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 }

转载于:https://www.cnblogs.com/matrix-r/p/3249068.html

完全自制的五子棋人机对战游戏(VC++实现)相关推荐

  1. 基于VC++的五子棋人机对战游戏设计

    资源下载地址:https://download.csdn.net/download/sheziqiong/85706416 资源下载地址:https://download.csdn.net/downl ...

  2. java五子棋人机对战_实现简单的人机对战五子棋(实践)

    五子棋人机对战实践项目 总的任务和目标 完成一个人机对战的五子棋项目,基本效果如下: 第一部分 Java绘图原理 1.   基本概念 像素,坐标 2.   组件自定义绘图原理 3.   Graphic ...

  3. C#实现五子棋人机对战的思路与源码分享

    前言 业余闲暇时,基于.netframework使用C#语言开发了一个五子棋人机对战的游戏,在此做些整理和分享,先上图. winform版 UI虽然很简洁,却花了些功夫,图形元素全动态生成,毕竟不是美 ...

  4. 五子棋人机对战_10.带人机对战的五子棋程序

    今天我们带来一个带人机对战功能的五子棋程序.程序基于前面文章中的框架搭建,新增人机对战的策略.程序基于规则进行决策,不考虑禁手,玩家执黑子先行.棋盘规模采用15乘15,棋盘规模容易扩展,程序棋力中等, ...

  5. C语言 AI智能,五子棋 人机对战,人人对战

    C语言五子棋,人机对战,人人对战 chunli@Linux:~$ cat main.c # include<stdio.h> # include<string.h> # inc ...

  6. JAVA1V1人机对战游戏

    GameTest public class GameTest {public static void main(String[] args) {//初始化Game youxi = new Game() ...

  7. 人工智能 - 五子棋人机对战

    人工智能 - 五子棋人机对战 作者: jig    阅读人次: 6635    文章来源:本站原创    发布时间:2007-7-12     网友评论(8)条 原帖及讨论:http://bbs.bc ...

  8. 五子棋人机对战_原生JS+Canvas实现五子棋游戏

    原生JS+Canvas实现五子棋游戏 效果图 主要功能模块为: 1.人机对战功能 2.悔棋功能 3.撤销悔棋功能 二.代码详解 2.1 人机对战功能实现 从效果图可以看到,棋盘的横竖可以放的位置为15 ...

  9. python五子棋人机对战_Python:游戏:五子棋之人机对战

    原标题:Python:游戏:五子棋之人机对战 开端 画棋盘 首先肯定是要画出棋盘来,用 pygame 画出一个 19 × 19 或 15 × 15 的棋盘并不是什么难事,这在之前的文章中已经多次用到, ...

最新文章

  1. RabbitMQ安装与初始配置【转载】
  2. Linux显示某文件中有关某字符串的信息
  3. CodeForces - 1215C Swap Letters(暴力+思维+模拟)
  4. Spark _16 _SparkUIMaster HA
  5. Selenium自动化测试-3.元素定位(2)
  6. 阶段1 语言基础+高级_1-3-Java语言高级_06-File类与IO流_04 IO字节流_3_字节输出流_OutputStream类FileOutputStream...
  7. gog 中 git提交push到远程时出现error: RPC failed; HTTP 413 curl 22
  8. cad帧数测试软件,怎样让cad运行速度更快_cad如何设置运行更流畅
  9. java中this和super的用法区别
  10. verlay虚拟化技术_Overlay解决虚拟化难题 - 节点动态 - 海南师范大学信息网络与数据中心| Network Center HaiNan Normal University...
  11. 使用c++ winhttp实现post请求
  12. php替换掉,php替换
  13. 下载excel打开后弹出警告提示,文件类型和文件内容不符问题处理
  14. Go语言aes加密解密处理
  15. java 数字补零_java数字位数不足在前后补0
  16. 【算法概论】分治算法:k路归并
  17. 在vue中二级页面返回一级页面
  18. 初识 JavaScript(揭开javascript的神秘面纱)
  19. windows下配置IIS以及优化配置
  20. Opencv去除高光的算法--Highlights Removal

热门文章

  1. 火箭军下连学计算机专业好吗,火箭军招收定向培养士官院校增至16所 涵盖18个专业...
  2. erdas查看灰度直方图_实习1:ERDAS软件视窗基本操作
  3. Source Insight 4.0 代码自动排版 2019
  4. My97DatePicker设置,包括隐藏 清空,设置最大日期等
  5. 微信定向流量_我和小伙伴都玩微信定向流量了
  6. 深圳python培训比较好的机构-深圳python培训哪家好
  7. 电传输之POE供电的介绍
  8. GDKOI2014「壕壕的寒假作业」
  9. Mybatis在Maven中的应用技巧
  10. 淄博市计算机培训动画制作,网页制作与电脑动画制作培训