• 五子棋游戏禁手算法的改进


    五子棋游戏禁手算法的改进

    五子棋最新的禁手规则:

    1.黑棋禁手判负、白棋无禁手。黑棋禁手有“三三”(包括“四三三”)、“四四”(包括“四四三”)和“长连”。黑棋只能以“四三”取胜。
    2.黑方五连与禁手同时形成,判黑方胜。
    3.黑方禁手形成时,白方应立即指出。若白方在之后继续落子,则禁手失效,此后指出禁手也不判黑方负。
    注意:本条仅限于“三三”和“四四”禁手点,黑方走出的长连则不同:在对局结束前,白方无论何时指出黑方出现长连,都判白方胜。

    禁手设置算法:
    禁手有三种基本形,即三三禁手和四四禁手,以及长连禁手。
    另外还有三三四,四四三类禁手,以及一些特殊形态的禁手。
    三三禁手形态:00 10100    >   0 x K x 1 x 0
                               00 1100      >   0 x K 1 x  0
                               0 10010      >   0 K x x 1  0
    四四禁手形态:00 11100    >   0 x K 1 1 x  0
                               0 11010      >   0  K 1 x 1  0
                               0 10110      >   0  K x 1  1  0
      K  是遍历搜索点,if (K=1) 黑子时,  x  点位就是三连和四连的禁手点位。
    用遍历搜索算法按15 * 15 棋盘点位pn ( 1 -225 ),
    分四个方向搜索,即左右,上下,右斜,斜左。遇黑子则计算加权分,赋100分值。此方法可减少计算量,无黑子位不计算。三三四四各形态同时搜索查验加权计算,一次遍历加权计分,最后查验加权分情况,超过100分的点位就是禁手点位。如有超过200分的情况就是三三四和四四三禁手。
    长连禁手设置:在判胜负函数detect()时黑棋超过五连即是长连禁手,判黑负,白棋胜。
    有一些特殊的形态如扁担阵暂不设置。此程序是简版,完整版本要全面设置。
    代码中有显示计算的分值点标记,黄色是100分的点位,紫色是超过100分的点位,即禁手点位。这是测试算法时用来查看的,应用时可注释掉。
    此算法是基本的算法,优点是简单和易于理解。

    黑棋如设置禁手要想获胜就只有活四和嵌五,还有就是冲四活三,原先发布的博文有bug,当时较仓促,算法测试不充分,忽略了冲四活三的情况。今就此补充修正代码。

    这里有个巧妙的算法,在原代码上改一下就成了。算法是三三计权分仍然是100而四四的计权分改为110,这样三三禁手分是200,四四禁手分是220,如是三三四就310分,四四三就320分,
             if ( js[i]>110 && js[i]!=210 ) {  ......   }
    这样计分大于110就触发禁手,而若等于210分就是冲四活三必胜点。

    图例:
    黄色点是计分点,紫色点是禁手点

    39becd15fca149bb9764d6e3951930c5.png

    eae195c27fe3462d828fecf2e0a8f7a6.png 

     

    restrict_move ( ) {  //禁手设置
     //黑棋禁手,加权计算查找
       for (i=1;i<=225;i++) {  js[i]=0 ;    }  //init scan
     //三三禁手,01010 此情况中间为可成三三的交点
     //此点记100,若有两个三三,此点就计分200,
     //即禁手点位,黑棋若下子即可判负
      for (i=1;i<=15;i++){    //遍历scan 黑子
      for (j=1;j<=15;j++){    
               k=(i-1)*15+j ;    //pn(n) number
      //三三禁手形 0 0  1  010 0,  k前后 0位计权
     //两边为空中间可成活三,有子是假活三
         if (pn[k]==1){  // scan B (k)
          if (pn[k-1]==0&&pn[k+1]==0&&pn[k+2]==1&&pn[k+3]==0){
            js[k+1]= js[k+1]+100 ;
          if ( pn[k-2]==0)  js[k-1]= js[k-1]+100 ;  
          if (pn[k+4]==0)  js[k+3]= js[k+3]+100 ;   
          if ( pn[k-2]==1&&pn[k+4]==1) { //101(0)101四四的特殊情况
                js[k+1]= js[k+1]+200 ;   }     }//左右
       
         if (pn[k-15]==0&&pn[k+15]==0&&pn[k+30]==1&&pn[k+45]==0){  
             js[k+15]= js[k+15]+100 ;   
          if ( pn[k-30]==0) js[k-15]= js[k-15]+100 ;   
          if (pn[k+60]==0) js[k+45]= js[k+45]+100 ;   
          if ( pn[k-30]==1&&pn[k+60]==1) { //四四
                 js[k+15]= js[k+15]+200 ;   }     }//上下

         if (pn[k-14]==0&&pn[k+14]==0&&pn[k+28]==1&&pn[k+42]==0){  
              js[k+14]= js[k+14]+100 ;  
         if ( pn[k-28]==0) js[k-14]= js[k-14]+100 ;   
          if (pn[k+56]==0) js[k+42]= js[k+42]+100 ;   
          if ( pn[k-28]==1&&pn[k+56]==1) {  //四四
                js[k+14]= js[k+14]+200 ;   }     }//斜左

         if (pn[k-16]==0&&pn[k+16]==0&&pn[k+32]==1&&pn[k+48]==0){  
              js[k+16]= js[k+16]+100 ;   
          if ( pn[k-32]==0) js[k-16]= js[k-16]+100 ;   
          if (pn[k+64]==0) js[k+48]= js[k+48]+100 ;   
          if ( pn[k-32]==1&&pn[k+64]==1) { //四四
                js[k+16]= js[k+16]+200 ;   }     }//右斜

                }  //00 1 0100  四四 1(0)101的特殊情况

    //禁手形 00  1  100 ,  k前后 0位计权
       if (pn[k]==1){  //scan B (k)
       if (pn[k-1]==0&&pn[k+1]==1&&pn[k+2]==0){  
            if (pn[k-2]==0) js[k-2]= js[k-2]+100 ;   
            if (pn[k-2]==0) js[k-1]= js[k-1]+100 ;   
            if (pn[k+3]==0) js[k+2]= js[k+2]+100 ;   
        if (pn[k+3]==0) js[k+3]= js[k+3]+100 ;   }  //左右

        if (pn[k-15]==0&&pn[k+15]==1&&pn[k+30]==0){  
            if (pn[k-30]==0) js[k-30]= js[k-30]+100 ;   
            if (pn[k-30]==0) js[k-15]= js[k-15]+100 ;   
            if (pn[k+45]==0)  js[k+30]= js[k+30]+100 ;   
        if (pn[k+45]==0) js[k+45]= js[k+45]+100 ;   }  //上下
         
        if (pn[k-14]==0&&pn[k+14]==1&&pn[k+28]==0){  
           if (pn[k-28]==0)  js[k-28]= js[k-28]+100 ;   
           if (pn[k-28]==0) js[k-14]= js[k-14]+100 ;   
            if (j<12&&pn[k+42]==0) js[k+28]= js[k+28]+100 ;   
       if (pn[k+42]==0) js[k+42]= js[k+42]+100 ;   }  //斜左

       if (pn[k-16]==0&&pn[k+16]==1&&pn[k+32]==0){  
            if (pn[k-32]==0) js[k-32]= js[k-32]+100 ;   
            if (pn[k-32]==0) js[k-16]= js[k-16]+100 ;   
            if (pn[k+48]==0) js[k+32]= js[k+32]+100 ;   
       if (pn[k+48]==0) js[k+48]= js[k+48]+100 ;   }  //右斜
                  }   //00 1 100

     //禁手形 0 1  0010 ,  中间 0位计权
       if (pn[k]==1){  //scan B (k)
       if (pn[k-1]==0&&pn[k+1]==0&&pn[k+2]==0&&pn[k+3]==1&&pn[k+4]==0){  
            js[k+1]= js[k+1]+100 ;   
            js[k+2]= js[k+2]+100 ;   }  //左右
       if (pn[k-15]==0&&pn[k+15]==0&&pn[k+30]==0&&pn[k+45]==1&&pn[k+60]==0){  
            js[k+15]= js[k+15]+100 ;   
            js[k+30]= js[k+30]+100 ;   }  //上下
       if (pn[k-14]==0&&pn[k+14]==0&&pn[k+28]==0&&pn[k+42]==1&&pn[k+56]==0){  
            js[k+14]= js[k+14]+100 ;   
            js[k+28]= js[k+28]+100 ;   }  //斜左
       if (pn[k-16]==0&&pn[k+16]==0&&pn[k+32]==0&&pn[k+48]==1&&pn[k+64]==0){  
            js[k+16]= js[k+16]+100 ;   
            js[k+32]= js[k+32]+100 ;   }  //右斜
               }  //010010

    //四四禁手算法同三三禁手
    //四四禁手形 00 1 1100 ,  前后 0 位计权
    //更正:冲四加活三不是禁手!! add 110分
    //加:特殊禁手 111 0 x 0 111
      if (pn[k]==1){  //scan B (k)
       if (pn[k+1]==1&&pn[k+2]==1){  
         if (pn[k-2]==0&&pn[k-1]==0){ js[k-1]= js[k-1]+110 ;   js[k-2]= js[k-2]+110 ;  }
         if (pn[k+3]==0&&pn[k+4]==0){
              js[k+3]= js[k+3]+110 ; js[k+4]= js[k+4]+110 ;  }     }  //左右

       if (pn[k+15]==1&&pn[k+30]==1){  
          if (pn[k-30]==0&&pn[k-15]==0){ js[k-15]= js[k-15]+110 ;   js[k-30]= js[k-30]+110 ;  }
           if (pn[k+45]==0&&pn[k+60]==0){
               js[k+45]= js[k+45]+110 ;    js[k+60]= js[k+60]+110 ;  }    }  //上下

         if (pn[k+14]==1&&pn[k+28]==1){  
          if (pn[k-28]==0&&pn[k-14]==0){ js[k-14]= js[k-14]+110 ;   js[k-28]= js[k-28]+110 ;  }
           if (pn[k+42]==0&&pn[k+56]==0){
               js[k+42]= js[k+42]+110 ;    js[k+56]= js[k+56]+110 ;  }    }  //斜左

       if (pn[k+16]==1&&pn[k+32]==1){  
           if (pn[k-32]==0&&pn[k-16]==0){ js[k-16]= js[k-16]+110 ;   js[k-32]= js[k-32]+110 ;  }
           if (pn[k+48]==0&&pn[k+64]==0){ 
               js[k+48]= js[k+48]+110 ;    js[k+64]= js[k+64]+110 ;  }    }  //右斜
              }  //0 1 110   ( 111 0x0 111 )

    //四四禁手形 0 1 1010 ,  0 1 0110 前后 0 位计权
       if (pn[k]==1){  //scan B (k)
       if (pn[k-1]==0&&pn[k+1]==0&&pn[k+3]==1&&pn[k+4]==0){  
        if (pn[k+1]==0&&pn[k+2]==1)js[k+1]= js[k+1]+110 ;   
        if (pn[k+1]==1&&pn[k+2]==0)js[k+2]= js[k+2]+110 ;  
                   }  //左右

        if (pn[k-15]==0&&pn[k+15]==0&&pn[k+45]==1&&pn[k+60]==0){  
        if (pn[k+15]==0&&pn[k+30]==1)js[k+15]= js[k+15]+110 ;   
        if (pn[k+15]==1&&pn[k+30]==0)js[k+30]= js[k+30]+110 ;  
                   }  //上下

        if (pn[k-14]==0&&pn[k+14]==0&&pn[k+42]==1&&pn[k+56]==0){  
        if (pn[k+14]==0&&pn[k+28]==1)js[k+14]= js[k+14]+110 ;   
        if (pn[k+14]==1&&pn[k+28]==0)js[k+28]= js[k+28]+110 ;  
                   }  //斜左

         if (pn[k-16]==0&&pn[k+16]==0&&pn[k+48]==1&&pn[k+64]==0){  
        if (pn[k+16]==0&&pn[k+32]==1)js[k+16]= js[k+16]+110 ;   
        if (pn[k+16]==1&&pn[k+32]==0)js[k+32]= js[k+32]+110 ;  
                   }  //右斜
                 } //0 1 1010  0 1 0110
    //********
               }    }   //scan i , j   整个搜索过程

    //禁手的基本设置就这样
     //长连禁手设置:在判胜负函数 detect ( ) 时
     // 黑棋超过五连即判黑负,白棋胜。

     //********** 
        cs.SetTextSize (16);
         cs.SetTextStyle (0);
         cs.SetFillMode (1);//0不填色,1填色
        for (i=1;i<=225;i++){    //test restrict_move
               if (js[i] !=0) {      //Mark it 标记禁手点
                 s=intToString ( js[i]) ;
         dx=(i-(i/15*15))*40;
         dy=(i/15)*40+40;
           if (dx==0){ dx=15*40;    dy=dy-40;   }
               cs.SetColor (255,250,250,0) ;
               cs.DrawCircle (dx,dy,3) ;     //draw mark
    //更正:冲四加活三不是禁手!! 四加 110分   
        if (js[i]>110&&js[i]!=210){
               cs.SetColor (255,180,0,180) ;
               cs.DrawCircle (dx,dy,5) ;  } //draw mark
                  }     }
        cs.Update ( ) ;
        
    }//restrict_move ( )

    图例:

    在下拉菜单设置禁手

    b8adc3eb9f404515bf356612f0d7a562.png

    7ea6e7c06c0f426d97e52f45b5b7055a.png  

    上图E 9,  G 10 ,H 6 
    此三点位就是冲四活三必杀点

    全局变量加:
    int js[225];         //禁手设置 遍历棋盘点位
    int jsset ;    //0=on 有禁手,1=off 无禁手,下拉菜单

    在白棋下子后加禁手判断:
    white_do ( ){    //白棋下子
        ............ 
           testAIq (  );     //AI 算法测试 ****
       //scan restricted move and draw mark
       if (jsset==0)  restrict_move ( ) ;  //禁手设置标记
     }//white_do ( )

    在判胜负的function detect ( ) 中加代码,
    如触禁手判白棋胜

    detect ( ) {    
        ......  ......
    //*****************************
     if (jsset==0&&pn[n]==1&&js[n]>110&&js[i]!=210){ 
          //禁手,判白棋胜
             print "禁手 pn=", n ;
             cs.SetColor(255,255,0,0);
             cs.SetTextSize (16);
         cs.DrawText ("( 禁手 )",655,(dn+2)*16 );   
             cs.SetTextSize (40);
         cs.DrawText ("( 禁手 )",460,530 );   
             ss9="白棋胜  !"; 
             goto heqi;    }

           ......  ......
    }//detect ( ) 

    其他功能设置感兴趣的朋友可参阅我本站其他博文,

    需要游戏程序源码可联系我:micelu@126.com

    //**End**

  • 相关阅读:
    1.4+1.5 L1、L2正则化
    EL&JSTL:EL表达式总结
    被忽视的数据中心非业务网络规划
    Camunda 7.x 系列【50】任务服务 TaskService
    电商项目中订单系统到底该怎么设计才好?(至尊典藏版)
    电脑上的歌曲怎么传到苹果手机上面,怎么把电脑上的歌导入苹果手机
    Flutter - APP跳转高德、百度、腾讯、谷歌地图
    领域驱动设计——领域的重构
    权限管理与jwt鉴权
    视频基础知识(一)
  • 原文地址:https://blog.csdn.net/baidu_41801770/article/details/132634871