CSDN博客

img ggg82

五子棋人机交互

发表于2003/9/10 11:31:00  1764人阅读

五子棋人机交互

程序分两部分: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>MAX_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>MAX_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>MAX_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>MAX_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[i][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=&PList; else list=&CList; 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)即可。
0 0

相关博文

我的热门文章

img
取 消
img