2006
01-12

程序分两部分:1。核心程序  2。windows图形界面程序
此处只介绍核心程序部分。下面,我们边看边侃。
//========定义文件部分=============================
#define MAX_LENGTH  19  //棋盘格数
#define COMPUTER  1    //计算机棋子
#define PLAYER    2    //玩家棋子
int qp[MAX_LENGTH][MAX_LENGTH];    //定义棋盘
int iswin;    //输赢标记,1=计算机胜,2=玩家胜,0=未分胜负;
#define RandInt(n)  (rand()%n)    //随机数宏
struct LIST
{
  int id;
  struct LIST *next;
}*PList=NULL,*CList=NULL;   //定义链表,Plist为玩家棋局链表指针,用于阻击,Clist为计算机棋局链表指针,用于攻击
//=========核心程序代码部分=========================
//本函数将数据add添加到链表中,并将数据按从大到小排序
//add低位为棋盘位置,高位代表该位置在当前棋局中的优先级
static int AddList(struct LIST **List,int add)
{
  struct LIST *tmp=*List;
  struct LIST **temp=List;
  int atemp=add>>16;
  int id;
  struct LIST *newlist=malloc(sizeof(*newlist));
  if(!newlist)
    return 1;
  while(tmp)
  {
    id=(tmp->id)>>16;
    if(id<=atemp)
      break;
    if(id>atemp)
    {
      temp=&(tmp->next) ;
      tmp=tmp->next;
    }
  }
  newlist->id=add;
  newlist->next=tmp;
  *temp=newlist;
  return 0;
}
//函数获得指定链中最大优先级的值
static int GetMax(struct LIST *List)
{
  if(List)
    return (List->id>>16);
  return 0;
}
//函数获得指定链中的链首数据
static int GetLast(struct LIST **List)
{
  if(*List)
  {
    int ret;
    struct LIST *temp;
    ret=((*List)->id&0xffff);//取低字节棋盘位置数据
    temp=*List;
    *List=(*List)->next;
    free(temp);
    return ret;
  }
  return 0;
}
#define DestroyList(l)  while(GetLast(&l))  //宏——销毁链表
//函数探测参数tmp指定的棋盘位置的优先级并加入相应的链表中
//凡进入该函数扫描的点,皆为棋盘上的空位置。我们将在此处分析该位置在棋局中的地位。
//tmp=y*MAX_LENGTH+x
static int AddTo_List(int tmp)
{
  int temp;
  int py,px;
  py=tmp/MAX_LENGTH;
  px=tmp%MAX_LENGTH;
  //探测计算机在此位置的优先级
  qp[py][px]=COMPUTER;
  temp=scan(px,py,0);//最后一个参数必须为0,否则将进入死循环。
  AddList(&CList,(temp<<16)+tmp);
  //探测玩家在此位置的优先级
  qp[py][px]=PLAYER;
  temp=scan(px,py,0);//同上。
  AddList(&PList,(temp<<16)+tmp);

  qp[py][px]=0;//恢复空子状态。

  return 0;
}
//函数对指定的棋盘位置进行格式化全方位扫描,并返回该位置在棋局中的优先级
//进行格式化扫描,可以减少扫描的冗余度,提高扫描效率。这对于大棋盘,如本例的19*19
尤为适用。
//mode控制扫描方式,0=试验性扫描,不将扫描结果加入链表。1=实战性扫描,将扫描结
果加入链表。程序可利用链表中的数据,进行下一步棋的决策。
//px,py在循环中反复使用,我们为其加上const标记,防止不必要的麻烦。
static int scan(const int px,const int py,int mode)
{
  register int i;
  int play=qp[py][px];//获得该位置棋子
  int ret=0;rtemp=0;//返回值
  int temp[4];
  int base;
  base=RandInt(8);//生成随机数,使用不同的起点,使棋局具有一定的随机性
  //对该棋子八个方向进行扫描
  for(i=0;i<8;i++)
  {
    int x=px;
    int y=py;
    int side=0;//边界标记
    register j;
    switch((base+i)%8)
    {
    case 0://x–
      {
        //格式化,首先定位到第一个不与play相同的位置。
        //这样,在此方向上,我们便可以只考虑四格棋盘位置,大大减少了不必要的开销。
        do
        {
           x–;
        }while(x> =0&&qp[y][x]==play);
        //该方向前面没有充足的位置,置标记位为1。这样,我们可以略去无效探测。
        if(x+5>MA X_LENGTH)
        {
           side=1;
        }
        else
        {
           x+=2;//沿该方向向前,跳过第一个与play相等的位置,对前方四个位置进行纪录
           for(j=0;j<4;j++)
           {
             temp[j]=(MAX_LENGTH*y+x);//将棋盘位置合成整型数据保存
             x++;
           }
        }
        break;
      }
    case 1://x–;y–;
      {
        do
        {
           x–;
           y–;
        }while(x> =0&&y>=0&&qp [y][x]==play);
        if(x+5>MA X_LENGTH||y+5>MAX_LENGTH)
        {
           side=1;
        }
        else
        {
           x+=2;
           y+=2;
           for(j=0;j<4;j++)
           {
             temp[j]=(MAX_LENGTH*y+x);
             x++;
             y++;
           }
        }
        break;
      }
    case 2://y–;
      {
        do
        {
           y–;
        }while(y> =0&&qp[y][x]==play);
        if(y+5>MA X_LENGTH)
        {
           side=1;
        }
        else
        {
           y+=2;
           for(j=0;j<4;j++)
           {
             temp[j]=(MAX_LENGTH*y+x);
             y++;
           }
        }
        break;
      }
    case 3://x–;y++;
      {
        do
        {
           x–;
           y++;
        }while(x> =0&&y<MAX_LENGTH&&qp [y][x]==play);
        if(x+5>MA X_LENGTH||y-5<0)
        {
           side=1;
        }
        else
        {
           x+=2;
           y-=2;
           for(j=0;j<4;j++)
           {
             temp[j]=(MAX_LENGTH*y+x);
             x++;
              y–;
           }
        }
        break;
      }
    case 4://x++;
      {
        do
        {
           x++;
        }while(x< MAX_LENGTH&&qp[y][x]==play);
        if(x-5<0)
        {
           side=1;
        }
        else
        {
           x-=2;
           for(j=0;j<4;j++)
           {
             temp[j]=(MAX_LENGTH*y+x);
              x–;
           }
        }
        break;
      }
    case 5://x++;y–;
      {
        do
        {
           x++;
           y–;
        }while(x< MAX_LENGTH&&y>=0&&qp [y][x]==play);
        if(x-5<0| |y+5>MAX_LENGTH)
        {
           side=1;
        }
        else
        {
           x-=2;
           y+=2;
           for(j=0;j<4;j++)
           {
             temp[j]=(MAX_LENGTH*y+x);
              x–;
             y++;
           }
        }
        break;
      }
    case 6://x++;y++;
      {
        do
        {
           x++;
           y++;
        }while(x< MAX_LENGTH&&y<MAX_LENGTH&&qp [y][x]==play);
        if(x-5<0| |y-5<0)
        {
           side=1;
        }
        else
        {
           x-=2;
           y-=2;
           for(j=0;j<4;j++)
           {
             temp[j]=(MAX_LENGTH*y+x);
              x–;
              y–;
           }
        }
        break;
      }
    case 7://y++;
      {
        do
        {
           y++;
        }while(y< MAX_LENGTH&&qp[y][x]==play);
        if(y-5<0)
        {
           side=1;
        }
        else
        {
           y-=2;
           for(j=0;j<4;j++)
           {
             temp[j]=(MAX_LENGTH*y+x);
              y–;
           }
        }
        break;
      }
    }
    if(!side)
    {
      //该部分是决定优先级的关键部分,本例只使用了简单的优先级决定方案,可以通过修改该部分使程序具有更高的智能。
      int t=0;
      int k[4]={8,4,2,1};
      int kt[16]={1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16};
      //对纪录的棋盘位置进行检测
      for(j=0;j<4;j++)
      {
        int jx=temp[j]%MAX_LENGTH;
        int jy=temp[j]/MAX_LENGTH;
        int p=qp[jy][jx];
        if(p==play)
           t+=k[j];
        else if(p==0)//可利用位置
        {
           if(mode)
             AddTo_List(temp[j]);//对可利用位置进行探测纪录
        }
        else//存在阻隔
        {
          t–;//此处有可能使t=-1,因为t用于数组下标,所以我们必须将其排除。
           if(t<0)
             t=0;
           break;
        }
      }
      if(t==0xf&&mode)//若五子连成一线,返回零
        return 0;
      if(ret<kt[t] )      {      ret=kt[t];      if(ret>rtemp)      {      int t=ret;      ret=rtemp;      rtemp=t;       }       }    }
} return (ret+rtemp);//当前局势}
//初始化棋盘
void initqp(void)
{
  register int i;
  register int j;
  for(i=0;i<MAX_LENGTH;i++)
    for(j=0;j<MAX_LENGTH;j++)
      qp[j]=0;
  iswin=0;
  srand(time(NULL));//初始化随机生成器
}
//主函数,检测玩家所走位置,返回计算机所走位置
//px,py代表玩家棋子位置。
int five(int px,int py)
{

  struct LIST **list;
  int tmp;
  if(qp[py][px]!=0)//该位置已存在棋子
  {
    return -1;
  }
  //对玩家所走棋子进行扫描
  qp[py][px]=PLAYER;
  if(!scan(px,py,1))
  {  
    //玩家胜
    DestroyList(PList);
    DestroyList(CList);
    iswin=PLAYER;
    return 0;
  }
  if(GetMax(PList)>GetMax(CList))//确定攻击性
    list=&PList;//防御
  else
    list=&CList;//攻击
  while(tmp=GetLast(list))//获取最大优先级的棋盘位置
  {
    px=tmp%MAX_LENGTH;
    py=tmp/MAX_LENGTH;
    if(qp[py][px]!=0)//该位置不为空则继续循环
    {
      if(GetMax(PList)>GetMax(CList))//重新确定攻击性
        list=&PL ist;
      else
        list=&CL ist;
      continue;
    }
    break;
  }
  if(!tmp) //在链表中未找到数据则生成随机数据
  {
    do
    {
      px=RandInt(MAX_LENGTH);
      py=RandInt(MAX_LENGTH);
    }while(qp[py][px]!=0);
  }
  //计算机走子
  qp[py][px]=COMPUTER;
  if(!scan(px,py,1))
  {
    //计算机胜
    DestroyList(PList);
    DestroyList(CList);
    iswin=COMPUTER;
  }
  return ((py*MAX_LENGTH)+px);//返回走子位置
}
核心程序部分就这么多,图形界面部分只需调用函数initqp()初始化棋盘,然后不断调用函数five(int px,int py)即可。


留下一个回复