TicTacToe(井字棋)的算法——不比人的智商低的AI

      何为TicTacToe?

TicTacToe也俗称井字棋或三连棋(两人轮流在一有九格方盘上划加字或圆圈, 谁先把三个同一记号排成横线、直线、斜线, 即是胜者),男女老少皆宜的入门级棋类游戏。

      

       关于游戏的实现方式,比较简单,在这就不多说了。直接谈一谈如何写出在TicTacToe游戏中不输给人类棋手的电脑。

       想要写出游戏AI,第一步是自己要会玩游戏,有自己的策略。第二部才是告诉我们的AI如何实现这样的策略。我们在玩的时候发现能尽可能占据有利的位置会帮助我们快速赢下比赛,玩游戏还能发现的一点就是一定要防住对面即将连成一条线的一路,还有很多,很多。这些特征可以说明,游戏中不同情况下,盘面的每一个位置似乎有着不同的重要性,我们完全可以利用这一点来指导我们的AI去驰骋沙场。(我一开始打算仔细判断每一种情况,比如双二单二什么的,结果不但代码量大而且效果和这样的思路没什么区别)

       也就是说,给与AI一个非常重要的变量(一个3*3的数组),表示每一个位置的权值,也就是该位置的重要性的体现。比赛中第一重要的当然是自己连成一条线,这可以赋予最高的权值。而且,这个权值应该大到什么地步呢?当然应该大到无论其他什么情况发生都以此为第一要素的地步。所以我们可以在AI检查出自己能成一条线的位置时,给这个位置赋值10000,代表这个位置至高无上的地位。

       第二重要的则是防止对手成一条线,此举的重要性,可谓一人之下万人之上,就在有这种情况的时候赋予这个位置1000的值吧。

       其他需要赋值的三种情况是:1.某条线仅仅有己方棋子一枚;2.某条线仅仅有敌方棋子一枚;3.某条线上空空如也。如果是我们的AI先手,我认为第一条的权重大于第二条大于第三条。而AI后手则2>3>1。

       因为玩过Tictactoe的朋友一定知道,这是一个先手赢不了,后手输不了的游戏。但是作为后手一旦失误就会输棋,先手若能抓住后手的失误则能赢棋。数年前中国五子棋第一人那威曾经说过“先手要攻,后手要守”。虽然是说五子棋,不过Tictactoe实际上就是三字棋,与五子棋的区别仅仅是少了很多很多的变化而已。所以我们需要给我们的AI贯彻这样的思想:先手要积极进攻,后手要小心放手。好了,AI的思路基本上就是这样了,下面贴上核心部分的代码(java)

 

注:游戏的地图保存在一个叫net的数组里,每一个位置值为0表示没有棋,1表示为X棋,2表示为O棋

       int[][] net=new int[4][4];//0 means null;1 means X;2 means O;
       int level[][]=new int[4][4];//越大优先级越高 
       IOHelper h=new IOHelper();
       int o2=10000;//一个己方活二权值
       int x2=1000;//一个对方活二权值
       int x=10;//一个对方活一权值
       int o=6;//一个己方活一权值
       int nothing=4;//一个空行权值

     for(int i=0;i<3;i++){
      for(int j=0;j<3;j++){
       if(net[i][j]!=0)level[i][j]=0;
       else{
       //横竖两条,每人都有
       //自己活二,,220
       if(((net[0][j]+net[1][j]+net[2][j])==4)&&(net[0][j]*net[1][j]*net[2][j])==0&&((net[0][j]-1)*(net[1][j]-1)*(net[2][j]-1))==-1)
        level[i][j]=level[i][j]+o2;
       if(((net[i][0]+net[i][1]+net[i][2])==4)&&(net[i][0]*net[i][1]*net[i][2])==0&&((net[i][0]-1)*(net[i][1]-1)*(net[i][2]-1))==-1)
        level[i][j]=level[i][j]+o2;
       //对方活二,110
       if(((net[0][j]+net[1][j]+net[2][j])==2)&&(net[0][j]*net[1][j]*net[2][j])==0&&((net[0][j]-1)*(net[1][j]-1)*(net[2][j]-1))==0)
        level[i][j]=level[i][j]+x2;
       if(((net[i][0]+net[i][1]+net[i][2])==2)&&(net[i][0]*net[i][1]*net[i][2])==0&&((net[i][0]-1)*(net[i][1]-1)*(net[i][2]-1))==0)
        level[i][j]=level[i][j]+x2;
       //单个X,100
       if(((net[0][j]+net[1][j]+net[2][j])==1)&&(net[0][j]*net[1][j]*net[2][j])==0&&((net[0][j]-1)*(net[1][j]-1)*(net[2][j]-1))==0)
        level[i][j]=level[i][j]+x;
       if(((net[i][0]+net[i][1]+net[i][2])==1)&&(net[i][0]*net[i][1]*net[i][2])==0&&((net[i][0]-1)*(net[i][1]-1)*(net[i][2]-1))==0)
        level[i][j]=level[i][j]+x;
       //单个O,200
       if(((net[0][j]+net[1][j]+net[2][j])==2)&&(net[0][j]*net[1][j]*net[2][j])==0&&((net[0][j]-1)*(net[1][j]-1)*(net[2][j]-1))==1)
        level[i][j]=level[i][j]+o;
       if(((net[i][0]+net[i][1]+net[i][2])==2)&&(net[i][0]*net[i][1]*net[i][2])==0&&((net[i][0]-1)*(net[i][1]-1)*(net[i][2]-1))==1)
        level[i][j]=level[i][j]+o;
       //空行,000
       if(((net[0][j]+net[1][j]+net[2][j])==0)&&(net[0][j]*net[1][j]*net[2][j])==0&&((net[0][j]-1)*(net[1][j]-1)*(net[2][j]-1))==-1)
        level[i][j]=level[i][j]+nothing;
       if(((net[i][0]+net[i][1]+net[i][2])==0)&&(net[i][0]*net[i][1]*net[i][2])==0&&((net[i][0]-1)*(net[i][1]-1)*(net[i][2]-1))==-1)
        level[i][j]=level[i][j]+nothing;
       
       //分情况
       //主对角线
       if((i==0&&j==0)||(i==2&&j==2)||(i==1&&j==1)){
        //己方活二
        if(((net[0][0]+net[1][1]+net[2][2])==4)&&(net[0][0]*net[1][1]*net[2][2])==0&&
        ((net[0][0]-1)*(net[1][1]-1)*(net[2][2]-1))==-1)
        level[i][j]=level[i][j]+o2;
        //对方活二
        if(((net[0][0]+net[1][1]+net[2][2])==2)&&(net[0][0]*net[1][1]*net[2][2])==0&&
          ((net[0][0]-1)*(net[1][1]-1)*(net[2][2]-1))==0)
          level[i][j]=level[i][j]+x2;
        //单个X
        if(((net[0][0]+net[1][1]+net[2][2])==1)&&(net[0][0]*net[1][1]*net[2][2])==0&&
          ((net[0][0]-1)*(net[1][1]-1)*(net[2][2]-1))==0)
          level[i][j]=level[i][j]+x;
        //单个O
        if(((net[0][0]+net[1][1]+net[2][2])==2)&&(net[0][0]*net[1][1]*net[2][2])==0&&
          ((net[0][0]-1)*(net[1][1]-1)*(net[2][2]-1))==1)
          level[i][j]=level[i][j]+o;
        //空行,000
        if(((net[0][0]+net[1][1]+net[2][2])==0)&&(net[0][0]*net[1][1]*net[2][2])==0&&
          ((net[0][0]-1)*(net[1][1]-1)*(net[2][2]-1))==-1)
         level[i][j]=level[i][j]+nothing;
       }
       
       //副对角线
       if((i==0&&j==2)||(i==2&&j==0)||(i==1&&j==1)){
        //己方活二
        if(((net[0][2]+net[1][1]+net[2][0])==4)&&(net[0][2]*net[1][1]*net[2][0])==0&&
        ((net[0][2]-1)*(net[1][1]-1)*(net[2][0]-1))==-1)
        level[i][j]=level[i][j]+o2;
        //对方活二
        if(((net[0][2]+net[1][1]+net[2][0])==2)&&(net[0][2]*net[1][1]*net[2][0])==0&&
          ((net[0][2]-1)*(net[1][1]-1)*(net[2][0]-1))==0)
          level[i][j]=level[i][j]+x2;
        //单个X
        if(((net[0][2]+net[1][1]+net[2][0])==1)&&(net[0][2]*net[1][1]*net[2][0])==0&&
          ((net[0][2]-1)*(net[1][1]-1)*(net[2][0]-1))==0)
          level[i][j]=level[i][j]+x;
        //单个O
        if(((net[0][2]+net[1][1]+net[2][0])==2)&&(net[0][2]*net[1][1]*net[2][0])==0&&
          ((net[0][2]-1)*(net[1][1]-1)*(net[2][0]-1))==1)
          level[i][j]=level[i][j]+o;
        //空行,000
        if(((net[0][2]+net[1][1]+net[2][0])==0)&&(net[0][2]*net[1][1]*net[2][0])==0&&
          ((net[0][2]-1)*(net[1][1]-1)*(net[2][0]-1))==-1)
         level[i][j]=level[i][j]+nothing;
       }//the end of for if
       }//the end of if
      }//the end of for j
     }//the end of for j


//寻找最大权值的位置
    int maxi = 0,maxj = 0,temp = 0;
           for(int i=0;i<3;i++){
                 for(int j=0;j<3;j++){
                      if(level[i][j]>temp)
                      {

                       temp=level[i][j];
                      maxi=i;
                      maxj=j;
                      }
                 System.out.println("net[i][j]="+net[i][j]);
                 System.out.println("level[i][j]="+level[i][j]);
                 }
           }

 

  觉得作者不容易的话请顶一个(*^__^*)  谢谢

很抱歉,根据提供的引用内容中没有提到HTML井字棋算法代码。但是,可以为您提供一个JavaScript实现井字棋游戏的代码范例,您可以将其嵌入到HTML中以实现井字棋游戏。 ```javascript // 创建一个井字棋游戏对象 var TicTacToe = function() { // 游戏状态 this.board = [ [null, null, null], [null, null, null], [null, null, null] ]; // 当前玩家 this.currentPlayer = 'X'; }; // 判断游戏是否结束 TicTacToe.prototype.isGameOver = function() { // 判断行 for (var i = 0; i < 3; i++) { if (this.board[i][0] !== null && this.board[i][0] === this.board[i][1] && this.board[i][1] === this.board[i][2]) { return true; } } // 判断列 for (var j = 0; j < 3; j++) { if (this.board[0][j] !== null && this.board[0][j] === this.board[1][j] && this.board[1][j] === this.board[2][j]) { return true; } } // 判断对角线 if (this.board[0][0] !== null && this.board[0][0] === this.board[1][1] && this.board[1][1] === this.board[2][2]) { return true; } if (this.board[0][2] !== null && this.board[0][2] === this.board[1][1] && this.board[1][1] === this.board[2][0]) { return true; } // 判断是否平局 for (var k = 0; k < 3; k++) { for (var l = 0; l < 3; l++) { if (this.board[k][l] === null) { return false; } } } return true; }; // 下棋 TicTacToe.prototype.play = function(row, col) { if (this.board[row][col] !== null) { return false; } this.board[row][col] = this.currentPlayer; if (this.isGameOver()) { console.log('Game over!'); } else { this.currentPlayer = this.currentPlayer === 'X' ? 'O' : 'X'; } return true; }; ```
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值