五子棋对弈机器人

完成时间:2012年

目的和意义

机器人作为下棋一方和人对弈,机器人先手,按照五子棋规则,双方轮流落子,机器人根据棋局选择最佳落子点,系统能够判断胜负。通过此项目熟悉LEGO NXT编程、电机控制,搭建二维移动机构。基本原则:只使用LEGO8547模块和常见的LEGO零件,不使用LEGO 之外的特殊零件,一是符合LEGO 玩家DIY精神,二是便于其他玩家模仿和实现。

使用零件:LEGO8547马达三个,触碰传感器两个,颜色传感器一个,常用零件若干。

编程平台:ROBOTC for LEGO NXT(使用C语言)

主要模块及设计思路

图 1. 五子棋机器人结构及组成。

棋盘、棋子

功能:模拟五子棋。

从下棋体验角度来说,应该选择真实的五子棋棋盘和棋子,实际并没有这么做。主要因为五子棋并非LEGO配件,五子棋的棋盘和棋子尺寸也没有统一的规格,不利于项目的标准化,也不利于大家的仿制。
我用LEGO 常用的plate零件搭了一个棋盘,棋子用的是比较容易买到的塑料小球,尺寸和LEGO中的足球、篮球零件基本相当, 稍小于两个LEGO单位。棋盘每一格是两个LEGO单位。 由于齿条数量和线缆长度的限制,只做了非标准的10*10格的棋盘(标准棋盘15*15格),棋盘尺寸不影响机功能和算法的实现,如果棋盘规模改变,调整程序中的相应参数即可。

移动模块

功能:机器人的平面移动机构,负责将棋子移动到棋盘的指定位置。

机器人的移动不难设计。 最简单的方案当然是水平和竖直方向上各由一个马达驱动。
关键问题是如何精确定位到棋盘中的每一格。虽然LEGO NXT中的电机自带角度传感器,不过精度不高,而且运行距离比较长和速度比较快的情况下,累计误差较大。
一种代替方案是利用两个颜色传感器识别棋盘格子线条实现定位,经过测试,这种方法精确度和速度都比较好,但8547中只有一个颜色传感器,还要用来识别棋子的颜色,所以不可行。
最终方案用了两个触碰传感器来实现X、Y轴方向上的定位,也就是在棋盘周围放置定位销(图1中标注所示)。这种方法的缺点是额外增加了结构,如果棋盘尺寸增加,定位销也要相应增加,而且不如采用颜色传感器的方法精度高,好在落子位置对精度要求不是很高,只要不影响棋子的落点和对棋子颜色的识别即可。

落子模块

功能:保存一定数量的棋子,每次释放一个棋子。

落子装置不是机器人的核心部分,但这个模块反复设计和修改多次,从装小球的容器到释放小球动作试验了好多方案。

第一个问题是装小球的空间不能太小,因为小球比真实的棋子要厚,如果只是简单地垂直放一列,5个小球摞在一起就达到10个LEGO单位了。我的做法是设计一个漏斗形状的空间,漏斗下端可以并排容纳两个小球,漏斗上部可以并排放下4个小球,从宽端到窄端的连接部分做一个斜面。这样大约可以容纳15个棋子。如果棋子用尽,可以手动添加。另一个设计方案(没有实现,不过应该不难):在起始位置放置一个大容量的小球存储装置,每次归位的时候,可以从中添加小球,如图2所示。

图 2. 自动装填机构示意图

比起容量限制,更棘手的问题是如何保证释放小球的稳定性,每次操作做到必须释放一个小球,常见的问题是挤在一起的小球会卡住,下面的小球已经放出去了,上面的小球因为相互挤在一起掉不下去。我采用的结构是容器的出口放置一个可旋转的十字扇叶机构(图3)。扇叶旋转90度释放一个小球,同时起到搅拌小球防止卡住的作用。

图 3. 小球释放机构

计算模块

功能:根据棋局状态,计算落子点,并判定胜负。
这部分相对简单,只要实现五子棋算法即可,在计算落子点的时候没有采用特别复杂的算法,只用了最简单的回溯算法,毕竟NXT的处理器能力有限,而且本项目的重点不在“棋力”上。

输入模块

功能:识别棋盘状态。

最初的想法是通过电脑连接摄像头识别棋局,优点是可以“看到”棋盘状态,不过最终没有采用。一是因为有人已经实现该方案,再重复没什么意思;二是机器人将变得复杂,和电脑、摄像头耦合在一起,增加了系统复杂度;第三,也是更重要的,利用电脑进行识别运算,NXT的主控模块就成为单纯的执行控制机构,成了一个有头无脑的纯机械了,NXT内置的处理器和存储空间应该可以完成更高级的工作。

NXT中包含一个颜色传感器,在只有一个颜色传感器的情况下,只能一格一格地扫描棋盘来找到新的落子。这种方式相比摄像头图像识别,肯定要慢得多。不过也有一定的优化空间,比如在正常情况下,有些格子不可能是落子点,也就是可以预判对手的落子点,优先扫描这些可能的落子点,一旦发现新棋子就可以结束扫描过程。剩下的问题就是在程序中存储棋局状态,具体实现可以参看代码部分。

NXT端代码

#pragma config(Sensor, S1,     humanPort,               sensorTouch)
#pragma config(Sensor, S2,     colorPort,           sensorCOLORFULL)
#pragma config(Sensor, S3,     touchPortX,          sensorTouch)
#pragma config(Sensor, S4,     touchPortY,          sensorTouch)
#pragma platform(NXT)

///////////////////////////////////////////////////////////////////////////////////////

//---------------------------------------------------------
// system configuration
//
//---------------------------------------------------------
const byte motorX=motorA; // control move in X Axle
const byte motorY=motorB;
const byte motorChess=motorC; // control drop chessman

const byte positiveDirection=1;
const byte negativeDirection=-1;

//-----------------------------------
// system function
void initSystem();

void waitMotorMoveFinish();
void waitDropChessFinish();

//---------------------------------------------------------
// chess configuration and fuction
//
//---------------------------------------------------------

const byte noChessman=0;
const byte PlayerRobot=REDCOLOR;
const byte playerHuman=GREENCOLOR;

const byte chessBoardDimension=10;

const byte withdrawGrids=3;

typedef struct
{
  byte X;
  byte Y;
} Point;

Point latestHumanChessPoint;
Point latestRobotChessPoint;
Point robotBestDropPoint;
Point humanBestDropPoint;

byte motorXPower=-90;
byte motorXCalibrationPower=-30;
byte motorYPower=90;
byte motorYCalibrationPower=30;
byte lastXDireciton=1;
byte lastYDireciton=1;

// the lengh of chess board in X Axle
//int chessBoardYDimension=10;  
// the lengh of chess board in Y Axle

byte colorSensorRelativeX=1;
byte colorSensorRelativeY=0;

byte currentX;
byte currentY;
byte targetX;
byte targetY;

int dropOneChessmanEncoder=350;
byte dropChessmanMotorPower=40;

//---------------------------------

void initChess();
bool confirmChessmanReady();

void dropOneChessman();
void lockChessman();
void dropOneChessmanAt(byte X, byte Y);

byte checkChessmanColor(byte X , byte Y); // check the color of chessman in certain position
//bool findHumanChessmanInRange(byte XStart , byte YStart , byte XEnd, byte YEnd);
bool findChessman(byte x,byte y); // find human's chessman
bool searchHumanChessman();

void moveTo(byte x , byte y);
bool moveOneXGrid(byte direction);
void calibrateXMove(byte direction);
bool moveOneYGrid(byte direction);
void calibrateYMove(byte direction);
void withdraw();

void waitHumanDrop();

//---------------------------------------------------------
// chess algorithm
//---------------------------------------------------------
/*
chessBoard, 0 no chessman, 1 Red color robot, 2 others color human
the index  [0][0]  of this array means out of chessboard.
*/

byte chessBoard[chessBoardDimension+1][chessBoardDimension+1];

byte data1[chessBoardDimension+1][chessBoardDimension+1];
byte data2[chessBoardDimension+1][chessBoardDimension+1];
byte data3[chessBoardDimension+1][chessBoardDimension+1];
byte data4[chessBoardDimension+1][chessBoardDimension+1];
byte data5[chessBoardDimension+1][chessBoardDimension+1];
byte data6[chessBoardDimension+1][chessBoardDimension+1];
byte data7[chessBoardDimension+1][chessBoardDimension+1];
byte data8[chessBoardDimension+1][chessBoardDimension+1];

byte topBorder=1;
byte bottomBorder=chessBoardDimension;
byte leftBorder=chessBoardDimension;
byte rightBorder=1;

byte getChessAtBoard(byte x,byte y);

bool checkChessResult(byte player) ;

void CalGameSatus(byte player);
void GetBestDropPoint();
void getChessmanBorder();

void test();

//-----------------------------------------------------------
//sub task for robot move
//-----------------------------------------------------------

/*
move robot in X axle.
*/
task moveX( ){

  byte dist;

  while(true){
    NXTDisplayTextLine(3,"cur x: %d", currentX);//debug
    dist=targetX-currentX;

    if(dist!=0){// should move
        byte d=dist/abs(dist); //direction

      for(byte i=0;i<abs(dist);){

        if( moveOneXGrid(positiveDirection*d) ){

          if( SensorValue(touchPortX)==0 ){
            PlaySound(soundBeepBeep);//debug
            //calibrateXMove(positiveDirection*d);
          }

          currentX=currentX+d;
          i++;

          PlaySound(soundBlip); //debug
          }else{
        }

      }
    }

    //wait1Msec(50);
  }

}

/*
move robot in Y axle.
*/
task moveY(){

  while(true){

    byte dist=targetY-currentY;

    if(dist!=0){
      byte d=dist/abs(dist); //direction

      for(byte i=0;i<abs(dist);){

        if ( moveOneYGrid(positiveDirection*d) ){

          if( SensorValue(touchPortY)==0 ){
            PlaySound(soundBeepBeep);
            //calibrateYMove(positiveDirection*d);
          }

          currentY=currentY+d;
          i++;
          PlaySound(soundBlip); //debug
          }else{

        }

      }
    }
    //wait1Msec(50);

  }//while
}

//---------------------------------------------------------
// main task
//---------------------------------------------------------

task
main ()
{
  initSystem();

  initChess();

  StartTask(moveX);
  StartTask(moveY);

  //test();
  int k=1;
  while(false){

    for(;k<6;k++){
      moveTo(k,0);
      wait1Msec(200);
    }
    wait1Msec(100);
    if(k==6) k=1;
    wait1Msec(100);
  }

  dropOneChessmanAt(5,5);
  withdraw();

  while(true){

    NXTDisplayTextLine(3,"x: %d", currentX);
    NXTDisplayTextLine(4,"Y: %d", currentY);

    wait1Msec(500);

    PlaySound(soundShortBlip);

    waitHumanDrop();
    if( searchHumanChessman()){
    }else{

    };

    if( checkChessResult(playerHuman)==true ){ // human win?
        PlaySound(soundDownwardTones);
      moveTo(1,0);
      StopAllTasks();
    }

    GetBestDropPoint(); // compute drop point for robot
    dropOneChessmanAt(robotBestDropPoint.X,robotBestDropPoint.Y);
    if( checkChessResult(playerRobot)==true ){ // robot win?
        PlaySound(soundDownwardTones);
      moveTo(1,0);
      StopAllTasks();

   }

      withdraw();

    }

  }

  void test(){

    chessBoard[5][3]=PlayerRobot;
    chessBoard[5][2]=playerHuman;

    searchHumanChessman();
  }

  //---------------------------------------------------------
  // system function
  //---------------------------------------------------------

  void initSystem(){

    nNXTButtonTask  = -2;  // Grab control of the buttons.
    nNXTExitClicks  = 3; // Triple clicking EXIT button will terminate program

    nMotorPIDSpeedCtrl[motorA] = mtrSpeedReg;
    nMotorPIDSpeedCtrl[motorB] = mtrSpeedReg;
    // nMotorPIDSpeedCtrl[motorC] = mtrSpeedReg;

    bFloatDuringInactiveMotorPWM = false;  // brake when no power
  }

  void waitDropChessFinish( ){

    while( nMotorRunState[motorChess] == runStateRunning ){
      //wait1Msec(50);
      //playsound(soundBlip); // debug
    }
    return;
  }

  //---------------------------------------------------------
  // chess function
  //---------------------------------------------------------

  void initChess(){
    // init chess board, no chessman on chessboard.
    for(int m=1;m<chessBoardDimension;m++){
      for(int n=1;n<chessBoardDimension;n++){
        chessBoard[m][n]=noChessman;
      }
    }

    //start
    currentX=1;
    targetX=1;
    currentY=0;
    targetY=0;

    //check x y touch in border.
    while(sensorValue(touchPortX)!=1 ){
      PlaySound(soundException);
      wait1Msec(3000);
    }
    //wait1Msec(1000);
    while(sensorValue(touchPortY)!=1){
      PlaySound(soundLowBuzz);
      wait1Msec(3000);
    }

    lockChessman(); // set in lock status.
    // confirmChessmanReady(); // put chessman in loader.

  }

  void moveTo(byte x , byte y){

    targetX=x;
    targetY=y;

    while(currentX!=targetX || currentY!=targetY){
      wait1Msec(600);
      //wait
    }
    return;
    //waitMotorMoveFinish();
  }

  /*
  move motor in X Axle. direction is based on the chessboard.
  the direction can be reserve via setting motorX and motorY power.
  Note!!!!, this function is blocked. it should be called in a task() in unblocked circumstance.
  */
  bool moveOneXGrid(byte direction){

    bool inGrid=false;

    if(SensorValue(touchPortX) == 1 ){ // on grid border flag.

      while(true){

        motor[motorX]=motorXPower*direction;

        if(!inGrid && SensorValue(touchPortX)==0){ // in grid
            inGrid=true;
        }

        if(inGrid && SensorValue(touchPortX) == 1 ){ // reach the next border flag.
            motor[motorX]=0;
          inGrid=false;
          lastXDireciton=direction;
          break;
        }

      }////while();

      return true;

      // wait1Msec(50);

      // check whether on border flag, if not, calibration, avoiding move too fast to pass border

      }else{

      calibrateXMove(lastXDireciton);

      return false;
    }

  }///////////////////////////////////////

  /*
  refer moveOneXGrid(int direction)
  */
  bool moveOneYGrid(byte direction){

    bool inGrid=false;

    if(SensorValue(touchPortY) == 1 ){ // on grid border flag.

      while(true){

        motor[motorY]=motorYPower*direction;

        if(!inGrid && SensorValue(touchPortY)==0){ // in grid
            inGrid=true;
        }

        if(inGrid && SensorValue(touchPortY) == 1 ){ // reach the next border flag.
            inGrid=false;
          lastYDireciton=direction;
          motor[motorY]=0; // stop
          break;
        }
        //wait1Msec(100);
      }////while();

      return true;

      }else{

      calibrateYMove(lastYDireciton);
      return false;
    }

  }///////////////////////////////////////

  void calibrateXMove(byte direction){

    PlaySound(soundException); // debug

    motor[motorX]=-motorXCalibrationPower*direction;   // go back

    while(SensorValue(touchPortX) == 0){ // until go back to grid flag

    }

    motor[motorX]=0;
  }

  void calibrateYMove(byte direction){

    PlaySound(soundException); // debug

    motor[motorY]=-motorYCalibrationPower*direction;   // go back

    while(SensorValue(touchPortY) == 0){ // until go back to grid flag

    }

    motor[motorY]=0;
  }

  /*
  After dropping chess, robot goes back. it is not definitely necessay to go to start point,
  just leaveing enough space for human's dropping.
  */
  void withdraw()
  {
    int withdrawX=0;
    byte withdrawY=0;

    byte findX;
    byte findY;

    getChessmanBorder();

    if( bottomBorder- withdrawGrids<0){
      withdrawY=0;
      }else{
      withdrawY=bottomBorder-withdrawGrids;
    }

    if( bottomBorder- 2<1){
      findY=1;
      }else{
      findY=bottomBorder-2;
    }

    if( findY%2==1){
      if(leftBorder-2<1){
        withdrawX=1;
        }else{
        withdrawX=leftBorder-2;
      }
      }else{

      if(rightBorder+2>chessBoardDimension){
        withdrawX=chessBoardDimension;
        }else{
        withdrawX=rightBorder+2;
      }

    }

    moveTo(withdrawX+colorSensorRelativeX,withdrawY+colorSensorRelativeY);

  }//function end

  /*
  alarm user put chessman in loader.
  */
  bool confirmChessmanReady(){

    bool status=false;

    while(nNXTButtonPressed!=kEnterButton){
      NXTDisplayCenteredTextLine(1, "press Enter if ");
      NXTDisplayCenteredTextLine(3, "chessman loaded");

      PlaySound(soundBeepBeep);
      wait1Msec(1000);
    }

    status=true;
    return status;
  }

  /*
  Drop one chessman. the chessman position depends on current position.
  */
  void dropOneChessman(){

    nMotorEncoderTarget(motorChess)=dropOneChessmanEncoder;
    motor[motorChess]=dropChessmanMotorPower; //drop one chessman

    wait1Msec(300);
    waitDropChessFinish( );

    lockChessman();

    wait1Msec(300);

    waitDropChessFinish( );

    //update board status
    chessBoard[currentX][currentY]=PlayerRobot;
    latestRobotChessPoint.X=currentX;
    latestRobotChessPoint.Y=currentY;

  }

  void dropOneChessmanAt(byte X, byte Y){
    moveTo(X,Y);
    dropOneChessman();
  }

  /*
  close loader,avoiding chessman unexpected dropping
  */
  void lockChessman(){

    nMotorEncoderTarget(motorChess)=-dropOneChessmanEncoder; // lock loader
    motor[motorChess]=-dropChessmanMotorPower;

    waitDropChessFinish( );

  }

  /*
  check chessman color at (x,y)position.
  Beucase color sensor is aside dropped chessman, the axel has to be revised.
  In other words, snesor positive is relative to drop position.
  */
  byte checkChessmanColor(byte X , byte Y){

    moveTo(X+colorsensorRelativeX,Y+colorsensorRelativeY);
    return (byte)SensorValue[colorPort]  ;

  }

  /*
  scan color in certain range.
  */
  bool findHumanChessmanInRange(byte XStart , byte YStart , byte XEnd, byte YEnd){

    for(byte m=YStart;m<=YEnd;m++){

      if(m%2==1){ // in odd row

        for(byte n=XStart;n<=XEnd;n++){
          byte color = checkChessmanColor(n,m);
          if ( color==playerHuman && chessBoard[n][m]==noChessman){ // find new human chessman
              PlaySound(soundBeepBeep);//debug
            chessBoard[n][m]=playerHuman; //update chessboard status
            latestHumanChessPoint.X=n;
            latestHumanChessPoint.Y=m;
            return true;
          }
        }
      }

      if(m%2==0){ // in even row
          for(byte n=XEnd;n>=XStart;n--){
          byte color = checkChessmanColor(n,m);
          if ( color==playerHuman  && chessBoard[n][m]==noChessman ){ // find new human chessman
              PlaySound(soundBeepBeep);//debug
            chessBoard[n][m]=playerHuman; //update chessboard status
            latestHumanChessPoint.X=n;
            latestHumanChessPoint.Y=m;
            return true;
          }
        }
      }

    }// each row
    return false;
  }

  void getChessmanBorder(){

    for(int y=1;y<=chessBoardDimension;y++){
      for(int x=1;x<=chessBoardDimension;x++){
        if(chessBoard[x][y]!=noChessman){

          if(leftBorder>x){
            leftBorder=x;
          }
          if(rightBorder<x){
            rightBorder=x;
          }

          if(topBorder<y){
            topBorder=y;
          }
          if(bottomBorder>y){
            bottomBorder=y;
          }

        }
      }
    }

  }

  bool findChessman(byte x, byte y){
    byte color = checkChessmanColor(x,y);
    if ( color==playerHuman && chessBoard[x][y]==noChessman){ // find new human chessman
        PlaySound(soundBeepBeep);//debug
      chessBoard[x][y]=playerHuman; //update chessboard status
      latestHumanChessPoint.X=x;
      latestHumanChessPoint.Y=y;
      return true;
    }

    return false;
  }

  /*
  Find the latest chessman dropped by human. Just search the possible position where human maybe
  drop.
  */
  bool searchHumanChessman( ){

    for(int y=1;y<=chessBoardDimension;y++){

      if(y%2==1){

        for(int x=1;x<=chessBoardDimension;x++){
          if(chessBoard[x][y]==noChessman){

            for(byte d=-2;d<=2;d++){

              if(x+d>chessBoardDimension || x+d<1){

                }else{

              }

              if(y+d>chessBoardDimension || y+d<1){

                }else{

              }

              if(getChessAtBoard(x+d,y)!=noChessman ||
                getChessAtBoard(x,y+d)!=noChessman ||
              getChessAtBoard(x+d,y+d)!=noChessman ||
              getChessAtBoard(x-d,y+d)!=noChessman){
                if(findChessman(x,y)){
                  return true;
                }

              }

            }///d
          }// no chessman
        }// each x
      }//////////////
      if(y%2==0){
        for(int x=chessBoardDimension;x>=1;x--){

          if(chessBoard[x][y]==noChessman){

            for(byte d=-2;d<=2;d++){

              if(x+d>chessBoardDimension || x+d<1){

                }else{

              }

              if(y+d>chessBoardDimension || y+d<1){

                }else{

              }

              if(getChessAtBoard(x+d,y)!=noChessman ||
                getChessAtBoard(x,y+d)!=noChessman ||
              getChessAtBoard(x+d,y+d)!=noChessman ||
              getChessAtBoard(x-d,y+d)!=noChessman){
                if(findChessman(x,y)){
                  return true;
                }

              }

            }///d

          }// no chessman

        }

      }

    }

    return false;
  }

  bool findHumanChessman2( ){

    byte top=0;
    byte bottom=0;
    byte right=0;
    byte left=0;

    getChessmanBorder();

    if(topBorder+2>chessBoardDimension){
      top=chessBoardDimension;
      }else{
      top=topBorder+2;
    }

    if(rightBorder+2>chessBoardDimension){
      right=chessBoardDimension;
      }else{
      right=rightBorder+2;
    }

    if(leftBorder-2<1){
      left=1;
      }else{
      left=leftBorder-2;
    }

    if(bottomBorder-2<1){
      bottom=1 ;
      }else{
      bottom=bottomBorder-2;
    }

    return findHumanChessmanInRange(left,bottom,right,top);

  }

  //---------------------------------------------------------
  // chess algorithm
  //---------------------------------------------------------
  /*
  retrieve chessman at chessboard, avoid index out at array.
  */
  byte getChessAtBoard(byte x,byte y){
    byte iX=x,iY=y;
    if(x>chessBoardDimension){
      iX=chessBoardDimension;
    }
    if(y>chessBoardDimension){
      iY=chessBoardDimension;
    }
    if(x<1){
      iX=1;
    }
    if(y<1){
      iY=1;
    }
    return chessBoard[iX][iY];
  }

  /*
  check whether the player is winner.
  */
  bool checkChessResult(byte player)
  {
    int x, y;
    // hori
    for ( y = 1; y <=chessBoardDimension; y++ )
    {
      for ( x = 1; x <= chessBoardDimension-4; x++ )
      {
        if ( player ==chessBoard[x][y]  &&
          player ==chessBoard[x+1][y] &&
        player ==chessBoard[x+2][y]  &&
        player ==chessBoard[x+3][y]  &&
        player ==chessBoard[x+4][y]  )

        return true;
      }
    }

    // vertical direction
    for ( y = 1; y <= chessBoardDimension-4; y++ )
    {
      for ( x = 1; x <= chessBoardDimension; x++ )
      {
        if ( player ==chessBoard[x][y] &&
          player ==chessBoard[x][y+1] &&
        player ==chessBoard[x][y+2] &&
        player ==chessBoard[x][y+3] &&
        player ==chessBoard[x][y+4] )

        return true;
      }
    }
    //  "\" direction
    for ( y = 1; y <= chessBoardDimension-4; y++ )
    {
      for ( x = 1; x <= chessBoardDimension-4; x++ )
      {
        if ( player ==chessBoard[x][y]&&
          player ==chessBoard[x+1][y+1] &&
        player ==chessBoard[x+2][y+2]&&
        player ==chessBoard[x+3][y+4]&&
        player ==chessBoard[x+4][y+4] )

        return true;

      }
    }
    // "/" direction
    for ( y = 1; y <= chessBoardDimension-4; y++ )
    {
      for ( x = 4; x <= chessBoardDimension; x++ )
      {
        if ( player ==chessBoard[x][y] &&
          player ==chessBoard[x-1][y+1] &&
        player ==chessBoard[x-2][y+2] &&
        player ==chessBoard[x-3][y+3] &&
        player ==chessBoard[x-4][y+4] )

        return true;
      }
    }

    return false;
  }

  /*
  calculate status for one player according to dropped chessman.
  */

  void CalGameSatus(byte player)
  {
    byte a,b,c,d;

    for(a=1;a<=chessBoardDimension;a++)
    {
      for(b=1;b<=chessBoardDimension;b++)
      {
        if(chessBoard[a][b]==noChessman)
        {
          d=0;
          for(c=1;c<5;c++)
          {
            if(a-c<=0) // left border
              break;
            if(chessBoard[a-c][b]!=player)//left
              break;
            else
              d++;
          }

          for(c=1;c<5;c++)
          {
            if( a+c>chessBoardDimension)
              break;
            if(chessBoard[a+c][b]!=player)  //right
              break;
            else
              d++;
          }

          data1[a][b]=d%4;   // save in horizon data
          data2[a][b]=d/4;

          d=0;
          for(c=1;c<5;c++)
          {
            if(b-c<=0)
              break;
            if(chessBoard[a][b-c]!=player)  //top
              break;
            else
              d++;
          }
          for(c=1;c<5;c++)
          {
            if(b+c>chessBoardDimension)
              break;
            if(chessBoard[a][b+c]!=player)
              break;
            else
              d++;
          }

          data3[a][b]=d%4;   // vertical
          data4[a][b]=d/4;

          d=0;
          for(c=1;c<5;c++)
          {
            if( b-c==0||a-c==0)
              break;
          if(chessBoard[a-c][b-c] !=player)  //left top
            break;
          else
            d++;
        }
        for(c=1;c<5;c++)
        {
          if(b+c>chessBoardDimension||a+c>chessBoardDimension)
            break;
          if( chessBoard [a+c][b+c]!=player )
            break;
          else
            d++;
        }

        data5[a][b]=d%4;   //  "\"
        data6[a][b]=d/4;
        d=0;
        for(c=1;c<5;c++)
        {
          if( a-c<=0 || b+c>chessBoardDimension)
            break;
          if(chessBoard[a-c][b+c]!=player )  //left bottom
            break;
          else
            d++;
        }
        for(c=1;c<5;c++)
        {
          if( a+c>chessBoardDimension || b-c<=0)
            break;
          if(chessBoard[a+c][b-c]!=player)  //right bottom
            break;
          else
            d++;
        }

        data7[a][b]=d%4; // "/"
        data8[a][b]=d/4;

      }
    }
  }
}

/*
get the best drop point
*/
void  GetBestDropPoint( )
{
  long totalMark,maxMark=0;

  long markTransform[5]={0,100,400,2000,10000};

  byte m,n,p;
  int mark;

  long humanMaxMark=0;
  long robotMaxMark=0;

  CalGameSatus(PlayerRobot);

  for(m=1;m<=chessBoardDimension;m++)
  {
    for(n=1;n<=chessBoardDimension;n++)
    {
      totalMark=0;

      mark = data1[m][n]+4*data2[m][n];
      totalMark+=  markTransform[mark];

      mark = data3[m][n]+4*data4[m][n];
      totalMark+=  markTransform[mark];

      mark = data5[m][n]+4*data6[m][n];
      totalMark+=  markTransform[mark];

      mark = data7[m][n]+4*data8[m][n];
      totalMark+=  markTransform[mark];

      if(totalMark>maxMark)
      {
        robotBestDropPoint.X=m;
        robotBestDropPoint.Y=n;
        maxMark=totalMark;
        robotMaxMark=maxMark;
      }
    }
  }// robot player

  // for humnan player
  CalGameSatus(playerHuman);

  for(m=1;m<=chessBoardDimension;m++)
  {
    for(n=1;n<=chessBoardDimension;n++)
    {
      totalMark=0;

      mark = data1[m][n]+4*data2[m][n];
      totalMark+=  markTransform[mark];

      mark = data3[m][n]+4*data4[m][n];
      totalMark+=  markTransform[mark];

      mark = data5[m][n]+4*data6[m][n];
      totalMark+=  markTransform[mark];

      mark = data7[m][n]+4*data8[m][n];
      totalMark+=  markTransform[mark];

      if(totalMark>maxMark)
      {
        humanBestDropPoint.X=m;
        humanBestDropPoint.Y=n;
        maxMark=totalMark;
        humanMaxMark=maxMark;
      }
    }
  }// human player

  if(humanMaxMark>robotMaxMark){ // defense or attack according to each maxMark
      robotBestDropPoint.X=humanBestDropPoint.X;
    robotBestDropPoint.Y=humanBestDropPoint.Y;

  }

}////////////////////////////////////////////

/*
wait human drop, when has dropped, human should press touch to tell robot.
*/
void waitHumanDrop(){

  while( SensorValue(humanPort) == 0 ){
    wait1Msec(200);
  }

  PlaySound(soundBeepBeep);
  return ;

}