五子棋,人机对战,C语言实现。

 

C语言设计一个人机对战的五子棋程序
摘要:五子棋是一种受大众广泛喜爱的游戏,这里介绍五子棋程序的数据结构、评分规则、胜负判断方法,重点分析了搜索算法,并在传统的博弈算法在五子棋应用中进行一些改进,从而使剪枝更有效,运算性能更好。改进包括:不使用closed表;改变棋盘搜索顺序;增加记录最大棋盘信息的指针。实验证明,这几点改进对提高效率有很高帮助。

关键词:五子棋;极大极小值;剪枝;算法改进

近来随着计算机的快速发展,各种棋类游戏被纷纷请进了电脑,使得那些喜爱下棋,又常常苦于没有对手的棋迷们能随时过足棋瘾。而且这类软件个个水平颇高,大有与人脑分庭抗礼之势。其中战胜过国际象棋世界冠军-卡斯帕罗夫的“深蓝”便是最具说服力的代表;其它像围棋的“手淡”、象棋的“将族”等也以其优秀的人工智能深受棋迷喜爱;而我们今天将向大家介绍的是五子棋的算法。 
  当我们与电脑对战时,您知道这些软件是怎样象人脑一样进行思考的吗?在这里就以此为例和大家一起探讨探讨。

为了使读者对五子棋搜索复杂度有个形象的认识,举一个中国象棋跟五子棋搜索次数的比较(如图)。可以看出同中国象棋相比,五子棋的分支系数大的多,而且胜负条件判断也复杂一些。在极大的分支系数下,搜索程序的最大搜索深度增加1层,耗费的运算时间都将大量增加。因此设计出一个有效的搜索算法是非常重要的。

 

 

中国象棋

五子棋

棋子种类

14

2

棋盘大小

9×10

15×15

分支系数

40

200

棋子数量

递减

递增

胜负条件

某方将帅丧失

某方五个棋子连成一线

 

文章的组织如下:首先简单介绍用C语言作图的基本方法(Turbo C 2.0环境)以及主循环控制下棋模块,其次介绍设计这个五子棋程序的数据结构,然后介绍了评分算法以及胜负判断,最后重点讨论实现搜索算法。

 

1.         基本的C作图方法及主循环控制模块

 

Turbo C提供了非常丰富的图形函数,所有的图形函数的原型均建立在graphics.h中,在使用图形函数时要确保有显示器图形驱动程序*.BGI,同时将集成开发环境Options/Linker中的Graphics lib选为on,只有这样才能保证正确使用图形函数。

这个程序调用一个EGAVGA显示器下能独立图形运行的函数。所谓独立图形运行程序,就是在编译和连接时将相应的驱动程序(*.BGI)直接装入到执行程序,从而能在独立的计算机上运行,避免需要重新编译连接才能运行(请查阅参考书1以及源码)。Turbo C进行画点、画线、封闭图形填充以及图形下文本输出只需要调用graphics.h中相关的函数。

主循环控制模块:控制下棋顺序,当轮到某方下子时,负责将程序转到相应的模块中去,主要担当一个调度者的角色。这个五子棋程序是用键盘控制下棋,所以要用到Turbo C中的bios.h。在一个循环块中等待键盘信息,判断键盘所输入的信息是否需要响应,调用相关的代码进行下棋(参考源码中的main函数部分)。

 

2.         五子棋基本数据结构

 

为整个棋盘建立一张表格用以记录棋子信息,使用一个15*15的二维数组 chessman[15][15] (15*15是五子棋棋盘的大小),数组的每一个元素对应棋盘上的一个交叉点,用“0”表示空位、“1”代表己方棋子、“2”代表对方棋子。这张表也是今后分析的基础。其次要建立一个结构,主要用于搜索过程中,定义如下:

typedef struct five_chess* point;

struct five_chess{

       int x;

       int y;

       int layer;

       int value;

       int score;

       int chess[LENGTH][LENGTH];

       int record[LENGTH][LENGTH];

};

x,y表示在某个位置上扩展出来的新节点,layer是表示第几层扩展,用于控制扩展深度。value表示该点上极大极小值,score表示叶子节点的得分,用于推算父辈节点的valuechess这个二维数组表示扩展出来的棋盘信息,record记录在xy点上扩展过的节点,如果没有扩展record中对应某个值为0。如果record中没有可以扩展的节点,那么该层扩展结束,返回一个特定值。

数组和一个结构构成了程序的基本数据骨架,今后只要再加入一些辅助变量便可以应付自如了。

 

3.         评分规则以及胜负判断

 

评估一个棋盘的分数,主要通过扫描整个棋盘,对每个点评分。对某个点上评分从四个方向(角度分别为04590135的四个方向)分别统计,进而累积该点总分,最后得到整个棋盘的分数。实际上对当前的局面按照下面的规则的顺序进行比较,如果满足某一条规则的话,就给该局面打分并保存,然后退出规则的匹配。注意这里的规则是根据一般的下棋规律的一个总结,在实际运行的时候,用户可以添加规则和对评分机制加以修正(源码中选用了其中部分规则)。评分规则如下:

l         判断是否能成5, 如果是机器方的话给予30000分,如果是人方的话给予-30000 分;

l         判断是否能成活4或者是双死4或者是死43,如果是机器方的话给予10000分,如果是人方的话给予-10000

l         判断是否已成双活3,如果是机器方的话给予5000分,如果是人方的话给予-5000

l         判断是否成死33,如果是机器方的话给予1000分,如果是人方的话给予-1000

l         判断是否能成死4,如果是机器方的话给予500分,如果是人方的话给予-500

l         判断是否能成单活3,如果是机器方的话给予200分,如果是人方的话给予-200

l         判断是否已成双活2,如果是机器方的话给予100分,如果是人方的话给予-100

l         判断是否能成死3,如果是机器方的话给予50分,如果是人方的话给予-50

l         判断是否能成双活2,如果是机器方的话给予10分,如果是人方的话给予-10

l         判断是否能成活2,如果是机器方的话给予5分,如果是人方的话给予-5

l         判断是否能成死2,如果是机器方的话给予3分,如果是人方的话给予-3

   

胜负判断实际上是据当前最后一个落子的情况来判断胜负的。实际上需要从四个位置判断,以该子为出发点的水平,竖直和两条分别为 45度角和135度角的线,目的是看在这四个方向是否最后落子的一方构成连续五个的棋子,如果是的话,就表示该盘棋局已经分出胜负。

 

4.         搜索算法的实现

 

α-β剪枝是在极大极小搜索算法基础上发展起来的,因此先来分析下经典的极大极小搜索过程,如下:


T:=(sMAX),OPEN:=(s),CLOSED:=( );开始时树由初始节点构成,OPEN表只含有s
LOOP1:IF OPEN=( THEN GO LOOP2
n:FIRSTOPEN),REMOVEnOPEN),ADDnCLOSED);
IF n可直接判定为赢、输或平局
THEN f
n:∨-0GO LOOP1
ELSE EXPAND
n→{ni}ADD{ }T
IF d
ni)<k THEN ADD{ }OPEN),GO LOOP1
ELSE
计算f ),GO LOOP1nI达到深度k,计算各端节点f值。
LOOP2IF CLOSEDNIL THEN GO LOOP3
ELSE :
FIRSTCLOSED);
IF MAX)∧(f MIN)有值)
THEN f
:max{f }REMOVE CLOSED);若MAX所有子节点均有值,则该MAX取其极大值。
IF
MIN)∧(f MAX)有值)
THEN f
:min{f }REMOVE CLOSED);若MIN所有子节点均有值,则该MIN取其极小值。
GO LOOP2
LOOP3IF fs≠NIL THEN EXITENDMMoveT));s有值,则结束或标记走步。

MINIMAX过程是把搜索树的生成和格局估值这两个过程分开来进行,即先生成全部搜索树,然后再进行端节点静态估值和倒推值计算,这显然会导致低效率。为了使生成和估值过程紧密结合,采用有界深度优先策略进行搜索,这样当生成达到规定深度的节点时,就立即计算其静态估值函数,而一旦某个非端节点有条件确定其倒推值时就立即计算赋值。这就是所谓的α-β过程。α-β算法归纳如下:

α剪枝:若任一极小值层节点的β值小于或等于它任一先辈极大值居节点的α值,即α(先辈层)≥β(后继层),则可中止该极小值层中这个MIN节点以下的搜索过程。这个MIN节点最终的倒推值就确定为这个β

β剪枝:若任一极大值层节点的α值大于或等于它任一先辈极小值层节点的β值,即α(后继层)≥β(先辈层),则可以中止该极大值层中这个MAX节点以下的搜索过程。这个MAX节点的最终倒推值就确定为这个α值。

 

5.         算法的改进

 

这里的算法改进主要是集中于五子棋程序运用上的极大极小、α-β剪枝的改进。这个五子棋的算法流程图如下(以扩展两层为例):

根据棋盘信息chessman[15][15]建立根结点s0(数据结构:five_chesman,并把s0压入栈中

扩展s1=top();判断s1->layer是否等于-1

s1->layer不等于-1push(s1),扩展s2=top(),查看s2->layer是否-1

s2->layer!=-1,计算此时棋盘得分score,并判断是否要更改上一层的极小值

s2->layer==-1,pop():判断是否更改极大值,max_chess指向得分最高的棋盘

如果s1->layer=-1,表示搜索结束,返回最大棋盘信息max_chess

 

采用C语言写的代码段如下:

s0=malloc(sizeof(struct five_chess));

for(i=0;i<3000;i++)

close[i]=NULL;

for(i=0;i<LENGTH;i++)

for(j=0;j<LENGTH;j++){

       s0->chess[i][j]=chessman[i][j];

       s0->record[i][j]=chessman[i][j];

}

s0->layer=0;

s0->value=-30000;

s0->score=-30000;

push(s0);

while(is_empty()!=0){

       s0=top();

       s1=expand(s0);

       if(s1->layer==-1){

              pop();

              continue;

       }

       close[num++]=s1;

       s1->value=30000;

       push(s1);

       while(1){

              s2=expand(s1);

              if(s2->layer==-1){

                     s1=top();

                     pop();

                     if(s1->value>top()->value){

                            top()->value=s1->value;

                            max_chess=s1;

                     }

                     break;

              }

              s2->score=score(s2->chess);

              temps=top();

              if(s2->score<temps->value)

                     temps->value=s2->score;

 

这个程序主要特点有:

 

l         没有使用closed表,而改用一个指针指向得分最大的棋盘信息,并且使用一个记录表登记已经扩展过的结点。这样就不需要对closed表进行大量的访问,很大程度上提高了搜索性能。

l         在扩展结点的时候,把棋盘分成三个部分:中间层(坐标5<=x<10,5<=y<10)、第二层(2<=x<12,2<=y<12并且除去中间层的那些点),第三层(0<=x<14,0<=y<14除去中间层和第二层的结点)。把棋盘分成这样三个部分扩展的依据是:越靠近中间位置的结点得分越高,这样先从得分高的结点开始计算,那么剪枝的次数就更多,从而很大程度上提高运算效率。实际上,扩展的最佳算法是以中间结点为中心,采用螺旋式搜索,这样最大程度上提高效率。

l         使用一个记录得分最高的棋盘信息的指针typedef struct five_chess *point,这点改进能节省大量空间。因为扩展过程的结点非常多,如果采用这个改进,那么就可以扩展后删除那些占用空间。此外在返回棋盘信息时,查找某个位置下棋也非常方便。

 

参考文献:

 

[1]. 马少平,朱小燕 .人工智能. 北京:清华大学出版社,2004

[2]. 汪晓平.C语言高级实例解析.北京:清华大学出版社,2002


/*
 * 使用键盘的上下左右键移动棋盘,空格键表示下棋,ESC键退出程序
 */
#include <stdio.h>
#include <stdlib.h>
#include <bios.h>
#include <graphics.h>
#include<malloc.h>
/*
 * 对应键盘键的十六进制数字
 */
#define ESC 0x11b
#define UP 0x4800
#define DOWN 0x5000
#define LEFT 0x4b00
#define RIGHT 0x4d00
#define BLANK 0x3920

#define PLAYER1 1
#define PLAYER2 2
#define COMPUTER 2
#define LENGTH 15
#define SEARCH_DEEP 2
/*
 * 用来在函数can_expand()查找可以扩展的节点步长
 */
#define STEP 1

/************全局变量定义***************/
int             x1 = 240,
                y1 = 240,
                oldx = 240,
                oldy = 240;
int             key_mode;
int             key_net;
int             step_sum = 0;
int             chessman[LENGTH][LENGTH];
int             depth = 2; /* 搜索的深度 */
int             a = 0,
                b = 0;
int             flag_run;
int             win_flag = 0;


typedef struct five_chess *point;
struct five_chess {
    int             x;
    int             y;
    int             layer;
    double          value;
    double          score;
    int             chess[LENGTH][LENGTH];
    int             record[LENGTH][LENGTH];
} A;

int             stack_deep0 = 0;
point           stack_c[10];
point           close[600];

void
push(point s0)
{
    if (stack_deep0 < 10)
 stack_c[stack_deep0++] = s0;
}

point
top()
{
    if (stack_deep0 > 0)
 return stack_c[stack_deep0 - 1];
    /*else return 一个什么样的东西?*/
}

void
pop()
{
    if (stack_deep0 > 0)
 stack_deep0--;
}

int
is_empty()
{
    if (stack_deep0 != 0)
 return 1;
    else
 return 0;
}

 

/************函数的声明**************/
void            five();
void            show();
int             win_or_not(int x0, int y0, int who,
      int chessman[LENGTH][LENGTH], int a);
void            set_chessman();
void            print_result();
/************评价函数部分************/
double          score_row(int i, int j, int chessman[LENGTH][LENGTH]);
double          score_col(int i, int j, int chessman[LENGTH][LENGTH]);
double          score_diag_45(int i, int j, int chessman[LENGTH][LENGTH]);
double          score_diag_135(int i, int j, int chessman[LENGTH][LENGTH]);
double          total_score(int who_running, int chessman[LENGTH][LENGTH]);
double          score(int chessman[LENGTH][LENGTH]);
int             rowdt(int i, int j, int chessman[LENGTH][LENGTH]);
int             coldt(int i, int j, int chessman[LENGTH][LENGTH]);
int             diadt(int i, int j, int chessman[LENGTH][LENGTH]);
int             vdiadt(int i, int j, int chessman[LENGTH][LENGTH]);
int             can_expand(int i, int j, int chessman[LENGTH][LENGTH]);
void          copy(point s1, point s0);

int
POW(int s, int t)
{
    int             sum = s,
                    i;
    if (t <= 0)
 return 1;
    for (i = 0; i < t; i++)
 sum *= sum;
    return sum;

}


/*
 * 定义computer先手
 */
point
expand(point s0)
{
    int             flag;
    int             i,
                    j;
    point           new_chess = (point) malloc(sizeof(struct five_chess));
/*************************这里出错***********************************/
    for (i = 0; i < LENGTH; i++)
 for (j = 0; j < LENGTH; j++)
     new_chess->chess[i][j] = s0->chess[i][j];

    for (i = 0; i < LENGTH; i++)
 for (j = 0; j < LENGTH; j++)
     new_chess->record[i][j] = s0->chess[i][j];

/*************************这里出错***********************************/
    if (s0->layer % 2 == 0)
 flag = COMPUTER;
    else
 flag = PLAYER1;


    for (i = 0; i < LENGTH; i++)
 for (j = 0; j < LENGTH; j++) {

     if (s0->record[i][j])                          /*如果有棋子*/
  continue;
     if (can_expand(i, j, s0->chess) == 0)    /*如果有棋子,而且沿水平,树直,左上—右下,右上—左下,四个方向可以扩展*/
  continue;
     s0->record[i][j] = flag;
     new_chess->chess[i][j] = flag;
     new_chess->layer = s0->layer + 1;
     new_chess->x = i;
     new_chess->y = j;
     new_chess->record[i][j] = flag;
     return new_chess;
 }
    /*
     * for(i=5;i<10;i++) for(j=5;j<10;j++){ if(s0->record[i][j]) continue;
     * if(can_expand(i,j,s0->chess)==0) continue; s0->record[i][j]=flag;
     * new_chess->x=i; new_chess->y=j; new_chess->record[i][j]=flag;
     * new_chess->layer=s0->layer+1; new_chess->chess[i][j]=flag ; return
     * new_chess; } for(i=2;i<12;i++) for(j=2;j<12;j++){
     * if(i>4&&i<10&&j>4&&j<10) continue; if(s0->record[i][j]) continue;
     * if(can_expand(i,j,s0->chess)==0) continue; s0->record[i][j]=flag;
     * new_chess->x=i; new_chess->y=j; new_chess->record[i][j]=flag;
     * new_chess->layer=s0->layer+1; new_chess->chess[i][j]=flag; return
     * new_chess;
     *
     * } for(i=0;i<LENGTH;i++) for(j=0;j<LENGTH;j++){
     * if(i>1&&i<12&&j>1&&j<12) continue; if(s0->record[i][j]) continue;
     * if(can_expand(i,j,s0->chess)==0) continue; s0->record[i][j]=flag;
     * new_chess->chess[i][j]=flag; new_chess->layer=s0->layer+1;
     * new_chess->x=i; new_chess->y=j; new_chess->record[i][j]=flag; return
     * new_chess; }
     */
    new_chess->layer = -1;
    return new_chess;
}

 

void
computer()
{
    int             i,
                    j,
                    k,
                    num = 0;
    int             break_now = 0;
    int             break_then = 0;
    int             go_on = 0;
    point         s0 = NULL,
                    s1 = NULL,
                    s2 = NULL,
                    max_chess = NULL;
    point          temps = NULL,
                    s01;
    /*
     * 堆栈的初始化
     */
    stack_deep0 = 0;
    s0 = malloc(sizeof(struct five_chess));
    for (i = 0; i < 600; i++)                           /*为什么是600*/
 close[i] = NULL;                                  /*close是一个point 数组*/
    close[num++] = s0;
    for (i = 0; i < LENGTH; i++)
 for (j = 0; j < LENGTH; j++) {
     s0->chess[i][j] = chessman[i][j];
     s0->record[i][j] = chessman[i][j];
 }
    s0->layer = 0;
    s0->value = -3000000;
    s0->score = -3000000;
    push(s0);
    while (is_empty() != 0) {                /*看是栈否为空*/
 s01 = top();                                   /*如果不是空*/
 s1 = expand(s01);                         /*从栈顶开始展开*/
 close[num++] = s1;
 if (s1->layer == -1) {
     pop();
     continue;
 }
 go_on =
     win_or_not((s1->x + 1) * 30, (s1->y + 1) * 30, 2, s1->chess,
         1);
 if (go_on == 2) {
     a = (s1->x + 1) * 30;
     b = (s1->y + 1) * 30;
     break_then = 1;
     break;
 }
 go_on =
     win_or_not((s1->x + 1) * 30, (s1->y + 1) * 30, 1, s1->chess,
         1);
 if (go_on == 1) {
     a = (s1->x + 1) * 30;
     b = (s1->y + 1) * 30;
     break_then = 1;
     break;
 }
 s1->value = 30000;
 push(s1);
 while (1) {
     s1 = top();
     s2 = expand(s1);
     if (s2->layer == -1) {
  pop();
  if (s1->value > top()->value) {
      top()->value = s1->value;
      max_chess = s1;
  }
  free(s2);
  break;
     }//end of if
     s2->score = score(s2->chess);
     temps = top();
     if (s2->score < temps->value)
  temps->value = s2->score;
     free(s2);
 }//end of whiile(1)
    }
    if (break_then == 0) {
 for (i = 0; i < LENGTH; i++) {
     for (j = 0; j < LENGTH; j++)
  if (max_chess->chess[i][j] != chessman[i][j]) {
      a = i * 30 + 30;
      b = j * 30 + 30;
      break_now = 1;
      break;
  }
     if (break_now == 1)
  break;
 }
    }
    for (i = 0; i < 600; i++) {
 if (close[i] == NULL)
     continue;
 free(close[i]);
    }

}

/**********************************************************/
void
main()
{
    int             key;
    int             play_with_who = 1;

    printf("1.Play with human/n2.Play with computer/nPlease choice: ");
    scanf("%d", &play_with_who);
 
    five();                           /*显示棋盘*/
    show();

    if (play_with_who == 1) {      /*人与人玩*/
 while (1) {    /*设置人与人玩的界面*/
     settextstyle(0, 0, 2);
     if ((step_sum + 1) % 2) {
  setcolor(1);
  outtextxy(500, 180, "Player2");
  setcolor(4);
  outtextxy(500, 180, "Player1");
     } else {
  setcolor(1);
  outtextxy(500, 180, "Player1");
  setcolor(10);
  outtextxy(500, 180, "Player2");
     }


     if (bioskey(1))
  /*
   * 按了一次键盘那么就true,执行下面代码,这是bios。h
   */
     {
  key = bioskey(0);
  /*
   * 返回一个键盘值,如没有按键,则一直等待
   */
  switch (key) {
  case ESC:
      exit(0);
  case LEFT:
      if (x1 > 30) {
   x1 -= 30;
   show();      /*显示方框*/
      }
      break;
  case UP:
      if (y1 > 30) {
   y1 -= 30;
   show();
      }
      break;
  case RIGHT:
      if (x1 < 450) {
   x1 += 30;
   show();
      }
      break;
  case DOWN:
      if (y1 < 450) {
   y1 += 30;
   show();
      }
      break;
  case BLANK:                  /*按下空格键后放置棋子*/
      {
   if (chessman[x1 / 30][y1 / 30])
       break;              /*如果当前位置有棋子,不能放置,退出*/
   step_sum++;       /*如果没有棋子,下一步谁走加1*/
   /*
    * P1 设置棋子
    */
   if (step_sum % 2) {
       setcolor(15);    /*画棋子*/
       setfillstyle(SOLID_FILL, 15); /* 封闭图形,进行实体填充*/
       circle(x1, y1, 10);           /*画圆*/
       floodfill(x1, y1, 15);       /*填充圆*/
       chessman[x1 / 30][y1 / 30] = PLAYER1;               /*设置棋盘状态*/
       win_flag = win_or_not(x1, y1, 1, chessman, 0);      /*分析游戏是否结束,谁胜谁败*/
       if (win_flag == 1)
    outtextxy(480, 240, "P1 Win");
       else if (win_flag == 3)
    outtextxy(480, 240, "DOGFALL");
       if (win_flag != 0) {                     /*如果没人胜,游戏继续*/
    while (bioskey(1) == 0);
    closegraph();                       /*what this mean?*/
       }
   } else { /* P2 设置棋子 */

       setcolor(12);                       
       setfillstyle(SOLID_FILL, 12);
       circle(x1, y1, 10);
       floodfill(x1, y1, 12);
       chessman[x1 / 30][y1 / 30] = PLAYER2;
       win_flag = win_or_not(x1, y1, 2, chessman, 0);
       if (win_flag == 2)
    outtextxy(480, 240, "P2 Win");
       else if (win_flag == 3)
    outtextxy(480, 240, "DOGFALL");
       if (win_flag != 0) {
    while (bioskey(1) == 0);
    closegraph();
       }
   }

 

      }
      break;
  }
     }
 }
    } else {
 chessman[7][7] = COMPUTER;             /*人和电脑玩,电脑先走一步*/          
 setcolor(12);
 setfillstyle(SOLID_FILL, 12);
 circle(240, 240, 10);
 floodfill(240, 240, 12);
 flag_run = 0;                    /*有什么用?*/
 step_sum++;                   /*下一步谁走?*/
 while (1) {
     while (1) {
  if (flag_run == 1)
      break;
  if (bioskey(1)) {
      key = bioskey(0);
      /*
       * 返回一个键盘值,如没有按键,则一直等待
       */
      switch (key) {

      case ESC:
   exit(0);
      case LEFT:
   if (x1 > 30) {
       x1 -= 30;
       show();
   }
   break;
      case UP:
   if (y1 > 30) {
       y1 -= 30;
       show();
   }
   break;
      case RIGHT:
   if (x1 < 450) {
       x1 += 30;
       show();
   }
   break;
      case DOWN:
   if (y1 < 450) {
       y1 += 30;
       show();
   }
   break;
      case BLANK:
   {
       if (chessman[x1 / 30 - 1][y1 / 30 - 1])
    break;                                                          /*有棋子了不走*/

       setcolor(15);
       setfillstyle(SOLID_FILL, 15); /* 封闭图形,进行实体填充
         */
       circle(x1, y1, 10);
       floodfill(x1, y1, 15);                              /*画棋子*/
       chessman[x1 / 30 - 1][y1 / 30 - 1] = PLAYER1;

       flag_run = 1;                                  /*有什么用?*/
       step_sum++;                                 /*下一步谁走*/
       win_flag = win_or_not(x1, y1, 1, chessman, 0);   /*谁胜谁负*/
       if (win_flag == 1)
    outtextxy(480, 240, "P1 Win");
       else if (win_flag == 3)
    outtextxy(480, 240, "DOGFALL");
       if (win_flag != 0) {
    while (bioskey(1) == 0);                                 /*没有人胜则继续等待下棋*/
    closegraph();
       }

   }
      }  /* switch */

  }

     }
     computer();                                            /*调用智能体*/
     /*
      * a,b存放的是现在电脑准备下的位置
      * 返回一个a,b的结构体不是更好,用全局变量不爽啊
      * struct {
      *          int a;
      *         int b;
      *  }
      */

     setcolor(12);
     setfillstyle(SOLID_FILL, 12);
     circle(a, b, 10);
     floodfill(a, b, 12);
     chessman[a / 30 - 1][b / 30 - 1] = COMPUTER;
     flag_run = 0;
     step_sum++;
     win_flag = win_or_not(a, b, 2, chessman, 0);
     if (win_flag == 2)
  outtextxy(480, 240, "ComputerWin");
     else if (win_flag == 3)
  outtextxy(480, 240, "DOGFALL");
     if (win_flag != 0) {
  while (bioskey(1) == 0);
  closegraph();
     }

 

 

 }
    }

}
void
five()
{
    int             i,
                    j;
    /*
     * 画棋盘的过程
     */
    int             gdriver = DETECT,
                    gmode;
    registerbgidriver(EGAVGA_driver);
    initgraph(&gdriver, &gmode, " ");
    /*
     * 对显示适配器进行配置
     */
    setbkcolor(1);

    for (i = 0; i < 30; i++) {
 setcolor((i >= 2) ? 9 : i);
 rectangle(i, i, 479 - i, 479 - i); /* 画矩形边框 */
    }
    /*
     * 画棋盘
     */
    for (i = 1; i < 14; i++)
 for (j = 1; j < 14; j++) {
     setcolor(14);
     line(30 + 30 * i, 30, 30 + 30 * i, 449);
     line(30, 30 + 30 * i, 449, 30 + 30 * i);
 }
    /*
     * 画整个图的边框
     */
    for (i = 0; i < 15; i++) {
 setcolor(i);
 rectangle(i, i, 640 - i, 480 - i);
 line(480 - i, 15, 480 - i, 465);
    }
    /*
     * 输出屏幕右侧的信息
     */
    setcolor(4);
    settextstyle(0, 0, 2);
    outtextxy(500, 45, "GOBANG");
    setcolor(10);
    settextstyle(0, 0, 1);
    outtextxy(500, 90, "Designed By");
    outtextxy(514, 118, "Ye Binbin");
    outtextxy(480, 140, "from class A of CS");


}

/*
 * 移动光标
 */
void
show()
{
    setcolor(1);

    if (oldx < 450) {
 if (oldy > 30)
     line(oldx + 7, oldy - 15, oldx + 15, oldy - 15);
 if (oldy > 30)
     line(oldx + 15, oldy - 15, oldx + 15, oldy - 7);
 if (oldy < 450)
     line(oldx + 15, oldy + 7, oldx + 15, oldy + 15);
 if (oldy < 450)
     line(oldx + 15, oldy + 15, oldx + 7, oldy + 15);
    }
    if (oldx > 30) {
 if (oldy < 450)
     line(oldx - 7, oldy + 15, oldx - 15, oldy + 15);
 if (oldy < 450)
     line(oldx - 15, oldy + 15, oldx - 15, oldy + 7);
 if (oldy > 30)
     line(oldx - 15, oldy - 7, oldx - 15, oldy - 15);
 if (oldy > 30)
     line(oldx - 15, oldy - 15, oldx - 7, oldy - 15);
    }
    setcolor(12);
    if (x1 < 450) {
 if (y1 > 30)
     line(x1 + 7, y1 - 15, x1 + 15, y1 - 15);
 if (y1 > 30)
     line(x1 + 15, y1 - 15, x1 + 15, y1 - 7);
 if (y1 < 450)
     line(x1 + 15, y1 + 7, x1 + 15, y1 + 15);
 if (y1 < 450)
     line(x1 + 15, y1 + 15, x1 + 7, y1 + 15);
    }

    if (x1 > 30) {
 if (y1 < 450)
     line(x1 - 7, y1 + 15, x1 - 15, y1 + 15);
 if (y1 < 450)
     line(x1 - 15, y1 + 15, x1 - 15, y1 + 7);
 if (y1 > 30)
     line(x1 - 15, y1 - 7, x1 - 15, y1 - 15);
 if (y1 > 30)
     line(x1 - 15, y1 - 15, x1 - 7, y1 - 15);
    }
    oldx = x1;
    oldy = y1;

}

 

void
set_chessman()
{
    /*
     * 棋子有三种状态,0是未初始状态,1是控制方棋子,2是对方棋子
     */
    int             i,
                    j;
    for (i = 0; i < 15; i++)
 for (j = 0; j < 15; j++)
     chessman[i][j] = 0;
}


/*
 * 0表示没有赢,1表示p1胜利,2表示p2胜利,3表示平局
 */
int
win_or_not(int x0, int y0, int who, int chessman[LENGTH][LENGTH], int a)
{
    int             i = x0 / 30 - 1,
                    j = y0 / 30 - 1;
    int             who_run = who;
    int             line_sum = -1;
    int             tmp_i = i,
                    tmp_j = j;
    int             c;
    if (a == 1) {
 /*
  * 测试第一层扩展是否满足赢的条件
  */
 c = chessman[i][j];
 chessman[i][j] = who_run;
    }


    while (1) {   /* 查找共行的棋子是否连接了五个 */
 while (tmp_i >= 0 && line_sum != 4) {
     if (chessman[tmp_i--][j] == who_run)
  line_sum++;
     else
  break;
 }
 if (line_sum == 4)
     line_sum++;
 tmp_i = i;
 while (tmp_i <= 15 && line_sum != 5) {
     if (chessman[tmp_i++][j] == who_run)
  line_sum++;
     else
  break;
 }
 if (line_sum == 5) {
     if (a == 1)
  chessman[i][j] = c;
     return who_run;
 }
 line_sum = -1;
 tmp_i = i;
 break;

    }
    while (1) {   /* 查找共列的棋子是否连接了五个 */
 while (tmp_j >= 0 && line_sum != 4) {
     if (chessman[i][tmp_j--] == who_run)
  line_sum++;
     else
  break;
 }
 if (line_sum == 4)
     line_sum++;
 tmp_j = j;
 while (tmp_j <= 15 && line_sum != 5) {
     if (chessman[i][tmp_j++] == who_run)
  line_sum++;
     else
  break;
 }
 if (line_sum == 5) {
     if (a == 1)
  chessman[i][j] = c;
     return who_run;
 }
 line_sum = -1;
 tmp_j = j;
 break;

    }
    while (1) {   /* 查找上对角线上是否连接了五个 */
 while (line_sum != 4 && tmp_i <= 15 && tmp_j >= 0) {
     if (chessman[tmp_i++][tmp_j--] == who_run)
  line_sum++;
     else
  break;
 }
 tmp_i = i;
 tmp_j = j;
 if (line_sum == 4)
     line_sum++;
 while (line_sum != 5 && tmp_i >= 0 && tmp_j <= 15) {
     if (chessman[tmp_i--][tmp_j++] == who_run)
  line_sum++;
     else
  break;
 }
 if (line_sum == 5) {
     if (a == 1)
  chessman[i][j] = c;
     return who_run;
 }
 tmp_i = i;
 tmp_j = j;
 line_sum = -1;
 break;
    }
    while (1) {   /* 查找下对角线上是否连接了五个 */
 while (line_sum != 4 && tmp_i >= 0 && tmp_j >= 0) {
     if (chessman[tmp_i--][tmp_j--] == who_run)
  line_sum++;
     else
  break;
 }
 tmp_i = i;
 tmp_j = j;
 if (line_sum == 4)
     line_sum++;
 while (line_sum != 5 && tmp_i <= 15 && tmp_j <= 15) {
     if (chessman[tmp_i++][tmp_j++] == who_run)
  line_sum++;
     else
  break;
 }
 if (line_sum == 5) {
     if (a == 1)
  chessman[i][j] = c;
     return who_run;
 }
 break;
    }
    if (step_sum == 225) {
 if (a == 1)
     chessman[i][j] = c;
 return 3;
    }
    if (a == 1)
 chessman[i][j] = c;
    return 0;

}

 

double
score_row(int i, int j, int chessman[LENGTH][LENGTH])
{
    int             sum_chessmen = 0;
    double          score = 0;
    int             mid_j;
    int             who_running = chessman[i][j];
    if (j == LENGTH) {
 while (chessman[i][j] == who_running) {
     j--;
     sum_chessmen++;
 }
 if (sum_chessmen >= 5)
     score = 200000;
 else {
     if (chessman[i][j] == 0) /* 没有下子,活的情况 */
  score = 2000 / POW(10, 4 - sum_chessmen);
     else
  score = 0; /* 死的情况 */
 }
    } else {
 while (chessman[i][j] == who_running && j != LENGTH) {
     j++;
     sum_chessmen++;
 }
 mid_j = j;
 j = j - sum_chessmen - 1;
 while (chessman[i][j] == who_running && j != -1) {
     j--;
     sum_chessmen++;
 }
 if (j >= 0 && mid_j < LENGTH) {
     if (chessman[i][j] == 0 && chessman[i][mid_j] == 0)
  score = 18000 / POW(50, 4 - sum_chessmen);
     else if ((chessman[i][j] != 0 && chessman[i][mid_j] == 0)
       || (chessman[i][j] == 0 && chessman[i][mid_j] != 0))
  score = 2000 / POW(10, 4 - sum_chessmen);
     else
  score = 0;
 }
 if (j < 0 && mid_j < LENGTH) {
     if (chessman[i][mid_j] == 0)
  score = 2000 / POW(10, 4 - sum_chessmen);
     else
  score = 0;
 }
 if (j >= 0 && mid_j >= LENGTH) {
     if (chessman[i][j] == 0)
  score = 2000 / POW(10, 4 - sum_chessmen);
     else
  score = 0;
 }
 if (j < 0 && mid_j >= LENGTH)
     score = 0;
    }
    return score;
}

double
score_col(int i, int j, int chessman[LENGTH][LENGTH])
{
    int             sum_chessmen = 0,
                    mid_i;
    double          score = 0;
    int             who_running = chessman[i][j];
    if (i == LENGTH) {
 while (chessman[i][j] == who_running) {
     i--;
     sum_chessmen++;
 }
 if (sum_chessmen >= 5)
     score = 200000;
 if (chessman[i][j] == 0)
     score = 2000 / POW(10, 4 - sum_chessmen);
 else
     score = 0;
    } else {
 while (chessman[i][j] == who_running) {
     i++;
     sum_chessmen++;
 }
 mid_i = i;
 if (i == LENGTH || chessman[i][j] != who_running) {
     i = i - sum_chessmen;
     while (chessman[i - 1][j] == who_running) {
  i--;
  sum_chessmen++;
     }
     if (i >= 0) {
  if (chessman[i][j] == 0 && chessman[mid_i][j] == 0)
      score = 18000 / POW(50, 4 - sum_chessmen);
  else if ((chessman[i][j] != 0 && chessman[mid_i][j]) == 0
    || (chessman[i][j] == 0
        && chessman[mid_i][j] != 0))
      score = 2000 / POW(10, 4 - sum_chessmen);
  else
      score = 0;
     }
     if (i < 0 && mid_i < LENGTH) {
  if (chessman[mid_i][j] == 0)
      score = 2000 / POW(10, 4 - sum_chessmen);
  else
      score = 0;
     }
     if (i < 0 && mid_i < LENGTH) {
  if (chessman[mid_i][j] == 0)
      score = 2000 / POW(10, 4 - sum_chessmen);
  else
      score = 0;
     }
     if (i >= 0 && mid_i >= LENGTH) {
  if (chessman[i][j] == 0)
      score = 2000 / POW(10, 4 - sum_chessmen);
  else
      score = 0;
     }
 }
    }
    return score;
}

double
score_diag_45(int i, int j, int chessman[LENGTH][LENGTH])
{
    int             sum_chessmen = 0;
    double          score = 0;
    int             mid_i,
                    mid_j;
    int             who_running = chessman[i][j];
    if (i == LENGTH || j == LENGTH) {
 while (chessman[i][j] == who_running && i > 1 && j > 1) {
     i--;
     j--;
     sum_chessmen++;
 }
 if (sum_chessmen >= 5)
     score = 200000;
 else {
     if (chessman[i][j] == 0)
  score = 2000 / POW(10, 4 - sum_chessmen);
     else
  score = 0;
 }
    } else {
 while (chessman[i][j] == who_running && i <= LENGTH && j <= LENGTH) {
     i++;
     j++;
     sum_chessmen++;
 }
 mid_i = i;
 mid_j = j;
 i = i - sum_chessmen;
 j = j - sum_chessmen;
 while (chessman[i - 1][j - 1] == who_running) {
     i--;
     j--;
     sum_chessmen++;
 }
 if (sum_chessmen >= 5)
     score = 200000;
 if (i >= 0 && j >= 0 && mid_i < LENGTH && mid_j < LENGTH) {
     if (chessman[mid_i][mid_j] == 0 && chessman[i][j] == 0)
  score = 18000 / POW(50, 4 - sum_chessmen);
     else if ((chessman[mid_i][mid_j] == 0 && chessman[i][j] != 0)
       || (chessman[mid_i][mid_j] != 0
    && chessman[i][j] == 0))
  score = 2000 / POW(10, 4 - sum_chessmen);
     else
  score = 0;
 } else {
     if (i >= 0 && j >= 0) {
  if (chessman[i][j] == 0)
      score = 2000 / POW(10, 4 - sum_chessmen);
  else
      score = 0;
     } else if (mid_i < LENGTH && mid_j < LENGTH) {
  if (chessman[mid_i][mid_j] == 0)
      score = 2000 / POW(10, 4 - sum_chessmen);
  else
      score = 0;
     } else
  score = 0;
 }
    }
    return score;
}

double
score_diag_135(int i, int j, int chessman[LENGTH][LENGTH])
{
    int             sum_chessmen = 0;
    double          score = 0;
    int             mid_i,
                    mid_j;
    int             who_running = chessman[i][j];
    while (chessman[i][j] == who_running && j != -1 && i < LENGTH) {
 i++;
 j--;
 sum_chessmen++;
    }
    mid_i = i;
    mid_j = j;
    j += sum_chessmen;
    i -= sum_chessmen;
    j++;
    i--;
    while (chessman[i][j] == who_running && j != LENGTH) {
 i--;
 j++;
 sum_chessmen++;
    }
    if (sum_chessmen >= 5)
 score = 200000;
    else {
 if (i >= 0 && j < LENGTH && mid_j >= 0 && mid_i < LENGTH) {
     if (chessman[i][j] == 0 && chessman[mid_i][mid_j] == 0)
  score = 18000 / POW(50, 4 - sum_chessmen);
     else {
  if ((chessman[i][j] == 0 && chessman[mid_i][mid_j] != 0)
      || (chessman[i][j] != 0
   && chessman[mid_i][mid_j] == 0))
      score = 2000 / POW(10, 4 - sum_chessmen);
  else
      score = 0;
     }
 } else {
     if (i >= 0 && j < LENGTH) {
  if (chessman[i][j] == 0)
      score = 2000 / POW(10, 4 - sum_chessmen);
  else
      score = 0;
     }
     if (mid_j >= 0 && mid_i < LENGTH) {
  if (chessman[mid_i][mid_j] == 0)
      score = 2000 / POW(10, 4 - sum_chessmen);
  else
      score = 0;
     }
 }
    }
    return score;
}

double
total_score(int who_running, int chessman[LENGTH][LENGTH])
{
    /*
     * 统计出在该点上的得分,who_running=1表示人的棋子,2为电脑的棋子
     */
    int             i,
                    j;
    double          score = 0;
    for (i = 0; i < LENGTH; i++)
 for (j = 0; j < LENGTH; j++) {
     if (chessman[i][j] == who_running) {
  score += score_row(i, j, chessman);
  score += score_col(i, j, chessman);
  score += score_diag_45(i, j, chessman);
  score += score_diag_135(i, j, chessman);
     }
 }
    return score;
}

double
score(int chessman[LENGTH][LENGTH])
{
    /*
     * 计算最终的得分数,分别考虑了在这个位置放对方棋子跟自己棋子的综合
     */
    double          sum1,
                       sum2;
    sum1 = total_score(COMPUTER, chessman);
    sum2 = total_score(PLAYER1, chessman);
    return sum1 - sum2;
}

/*
 * 扩展-----剪枝过程
 */

int
rowdt(int i, int j, int chessman[LENGTH][LENGTH])    /*在树直方向*/
{
    int             k;
    int             midjl = j - STEP,                 /*当前棋子的上方*/
                    midjr = j + STEP + 1;         /*当前棋子的下方棋子的下方??????*/
    if (midjl < 0)                                     
 midjl = 0;                  
    if (midjr > LENGTH)
 midjr = LENGTH;
    for (k = midjl; k < midjr; k++)             /**/
 if (chessman[i][k] != 0)                 /*如果有棋子*/
     return 1;
    return 0;
}

int
coldt(int i, int j, int chessman[LENGTH][LENGTH])          /*水平方向*/
{
    int             k;
    int             midil = i + STEP + 1,                  /*当前的右边棋子的右一个*/
                    midiu = i - STEP;                       /*当前棋子的左一个*/
    if (midiu < 0)
 midiu = 0;
    if (midil > LENGTH)
 midil = LENGTH;
    for (k = midiu; k < midil; k++)
 if (chessman[k][j] != 0)
     return 1;
    return 0;
}

int
diadt(int i, int j, int chessman[LENGTH][LENGTH])      /*右上到左下方向*/
{
    int             k,
                    midi,
                    midj;
    midi = i;
    midj = j;
    for (k = 0; k < STEP; k++) {
 midi++;
 midj--;
 if (midj < 0 || midi >= LENGTH)
     break;
 if (chessman[midi][midj] != 0)
     return 1;
    }
    for (k = 0; k < STEP; k++) {
 i--;
 j++;
 if (i < 0 || j >= LENGTH)
     break;
 if (chessman[i][j] != 0)
     return 1;
    }
    return 0;
}

int
vdiadt(int i, int j, int chessman[LENGTH][LENGTH])    /*左上到右下方向*/
{
    int             k,
                    midi,
                    midj;
    midi = i;
    midj = j;
    for (k = 0; k < STEP; k++) {
 midi--;
 midj--;
 if (midi < 0 || midj < 0)
     break;
 if (chessman[midi][midj] != 0)
     return 1;
    }
    for (k = 0; k < STEP; k++) {
 i++;
 j++;
 if (j >= LENGTH || i >= LENGTH)
     break;
 if (chessman[i][j] != 0)
     return 1;
    }
    return 0;
}


int
can_expand(int i, int j, int chessman[LENGTH][LENGTH])
{
    if (rowdt(i, j, chessman))
 return 1;
    if (coldt(i, j, chessman))
 return 1;
    if (diadt(i, j, chessman))
 return 1;
    if (vdiadt(i, j, chessman))
 return 1;
    /*
     * 如果不能扩展,返回0
     */
    return 0;
}

/************************************************************/

  • 5
    点赞
  • 31
    收藏
    觉得还不错? 一键收藏
  • 8
    评论
评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值