大二的时候一次小学期作业写的五子棋智能算法。这里分析一下算法实现。
数据结构分析:
我们把整个棋盘看成一个二维数组,为了便于扩展,棋盘大小最大是25×25,可以自由选择棋盘大小。在棋盘的数组上,用数值的大小表示棋盘的危急程度,当然对于下棋双方来说,相对一边越而对另一边就越有利。
一个位置用一个结构来表示:
struct Seat
{
int x;//x坐标
int y;//y坐标
int Urgent;
};
这个结构包含棋盘上的坐标位置和这个位置的危急程度。
整个棋盘上,还用到一个叫做“获胜组合”的结构,即每并排的五个子组成一个可能获胜组合,我们用一个结构来表示一个可能获胜组合:
//获胜组合节点,一个节点为一种可能获胜组合
struct WinUnion{
WinUnion(){}
int Type;//标识组合方向--纵向:1;横向:2;反正斜:3;正斜:4;
bool HasDanger;//该组合数是否有效
Seat seat[5];//五个组成一组
};//所有获胜组合数
//每个位置上的可能获胜组合总数
在这个结构中Type表示组合的方向,由于有四个可能方向它们是:横向;反正斜;正斜,分别用1、2、3、4来表示。这样只要下了子的位置,这个位置的危急程度就为0,因为没有人可以通过往这个地方下子可以取胜。
最后把下棋的角色封装成一个类,用于保存下棋过程中角色的棋盘情况,定义如下:
class PlayerStr
{
public:
PlayerStr(){}
public:
//标识是己方还是对方-1:对方;1:己方
int flag;
//标识是否手禁手约束
bool Inhibiting;
//桌面布子情况:-1:对方;1:己方
int Table[25][25];
//棋盘危急程度
int Urgent[25][25];
//每个位置指向所有相关联的可能获胜组合,动态创建可能获胜组合,并使相关联五个AllUnion指向它
UnionNode AllUnion[25][25];
};
其中inhibiting表示是否禁手约束,先下子者为禁手,如果inhibiting为true则使用禁手规则,否则不使用。Table数组表示桌面部子情况,0为没有子的情况,-1为对方的棋子,1为己方的棋子。Urgent数组为整个桌面的危急程度,这个危急程度跟Seat坐标的危急程度有分别,Seat坐标结构的危急程度指的是与这个坐标所组成的所有可能获胜组合对这个位置造成的危急程度,与这个位置组成的可能获胜组合中,越多对方的子在同一条直线上危急程度就越大。而Urgent数组则表示这个位置可能获胜组合越多就越危急。即Urgent数组是某个位置上所有可能获胜组合的可能获胜组合造成的危急总和。
AllUnion存储所有位置上的所有可能获胜组合链表组,从一个坐标查找链表可以找到所有与这个坐标组成的可能获胜组合。我们定义链表节点为:
struct UnionNode{
WinUnion *UnionData;//指向一个组合
UnionNode *Next;//下一个可能获胜组合
};
AllUnion存储了所有链表的链表头。
一个位置上的AllUnion[j+k][i]包含所有和这个位置相关的可能获胜组合WinUnion。
例如AllUnion[0][0]可以通过Next访问到所有WinUnion组合的Seat值为:
|
Seat.x |
Seat.y |
一个WinUnion可能获胜组合 |
0 |
0 |
1 |
0 |
|
2 |
0 |
|
3 |
0 |
|
4 |
0 |
|
一个WinUnion可能获胜组合 |
0 |
0 |
0 |
1 |
|
0 |
2 |
|
0 |
3 |
|
0 |
4 |
|
一个WinUnion可能获胜组合 |
0 |
0 |
1 |
1 |
|
2 |
2 |
|
3 |
3 |
|
4 |
4 |
表1 (0,0)位置的可能获胜组合左边
(int Urgent[25][25]和UnionNode AllUnion[25][25]的关系:
AllUnion[25][25]的每个元素是一个链表,链表的每个元素记录一个可能获胜组合,所有元素都是五个位置的组合,组合都包含当前位置的坐标。一个位置下所有组合的Urgent;之和组成Urgent[25][25],Urgent[25][25]是从整个棋盘角度判断哪个位置更危急。)
关于获胜组合,补充一个图片,便于理解:
图1 获胜组合
结构定义StructInfo.h:
//--------------------------------智能五子棋数据结构--------------------------------
#include<stdio.h>
#include<stdlib.h>
#include<malloc.h>
#define ERROR 0
#define FALSE 0
#define TRUE 1
#define OVERFLOW 0
struct Seat
{
int x;//x坐标
int y;//y坐标
int Urgent;
};
//获胜组合节点,一个节点为一种可能获胜组合
struct WinUnion{
WinUnion(){}
int Type;//标识组合方向--纵向:;横向:;反正斜:;正斜:;
bool HasDanger;//该组合数是否有效
Seat seat[5];//五个组成一组
};//所有获胜组合数
//每个位置上的可能获胜组合总数
struct UnionNode{
WinUnion *UnionData;//指向一个组合
UnionNode *Next;//下一个可能获胜组合
};
//***************************************************************
//PlayerStr对弈者数据类,标记是否先手(先下子)、棋盘状况,
//危急程度、可能获胜组合、棋盘上的每一空位指向它的可能获胜组合
//***************************************************************
class PlayerStr
{
public:
PlayerStr(){}
public:
//标识是己方还是对方-:对方;:己方
int flag;
//标识是否手禁手约束
bool Inhibiting;
//桌面布子情况:-:对方;:己方
int Table[25][25];
//棋盘危急程度
int Urgent[25][25];
//所有可能获胜组合