AI围棋学习之路二----游戏类(监听落子前后状态,轮到谁落子,非法落子检测以及判断真眼)

一游戏类(获取当前棋盘状态,当前落子方)

    下面的代码实现了初始化,修改落子后棋盘,以及判断棋局有无结束

#游戏(检测当前棋盘的状态、轮到谁落子,落子后棋盘的状态
class Game():
    def _init_(self,board,current_player,previous_state,move):
        self.board = board
        self.current_player = current_player
        self.previous_state = previous_state
        self.move = move

    #落子后棋盘状态变化
    def apply_move(self,move):
         if move.is_play:
             new_board = copy.deepcopy(self.board) #拷贝
             new_board.place(self.current_player,move.point) #棋盘放置落下的子
         else:
             new_board = self.board
         return Game(new_board,self.current_player.other,self,move)

    @classmethod
    def new_game(cls,board_size):
        if(isinstance(board_size,int)):
            board_size = (board_size,board_size)
            broad = Board(*board_size)
            return Game(board,Player.black,None,None)

    #游戏是否结束
    def is_over(self):

        if self.move is None:
            return False
        if self.move.is_resign:
            return True

        if self.previous_state.move is None:
            return False

        #双方pass,游戏结束
        return self.move.is_pass and self.previous_state.move.is_pass

二 玩家落子非法性检测

   分析:上一篇中下列落子的非法性还没有检测

           1.自填

          2.打劫时不找劫财就提回

          3.在对方不是剩一口气的情况下直接下到别人的真眼中

          其实 1和3可以放在一起来检测,因为如果一方下棋使得对方的棋的气为0的话,对方棋子就要从棋盘上拿掉,那么自己的棋气就不会是0,也就不属于自填了,所以如果满足3的话,那你就没有使对方的气为0,那你直接填入对方眼中,相当于落了一个气为0的子,也就是自填了

    解决:

           1.针对1和3这种情况(判断在使自己气为0的时候有无把对方提掉)

                    由于我们在前面Board类的放置棋子place方法中已经考虑了放置棋子后产生的效果(可能连接了棋子块,可能使对方  棋子块气变少甚至使其气为0从棋盘中拿掉),那么我们可以先把棋子放在棋盘上,得到放置后的新棋子块(此时肯定不存在对方无气的棋子块),再来看这个新棋子块的气,如果还是0,说明就是自填了,如果不是0,说明不是自填

#判断当前落子是不是自填
    #注意如果是把对方棋子吃掉的填子是允许的
    def is_move_killself(self,player,move):
        if not move.is_play:
            return False

        new_board = copy.deepcopy(self.board)
        #先落子,在棋盘类里已经写明如果落下后使对方气没了,那会拿掉对方的棋子,这样保证不是自填
        new_board.place(player,move.point)
        new_block = new_board.getBlock(move.point)

        #如果新的棋子块的气为0,则是自填的
        return new_block.num_liberties()==0

           2.针对2这种情况(下面的效率比较低,后面可以改进)

               当一方提劫后,另一方不找劫财直接去提劫,我们将看到当前游戏的界面与对方未提劫前的界面是一样的,而围棋是禁止同型情况出现的,因此我们避免这种情况出现的方法就是记录游戏的状态,然后每次落子都去判断落子后的状态有无出现过,如果出现过就是非法的

预防打劫不找劫财,即双方落子后的棋盘状态不能和落子前一样

    @property
    def situation(self):
        return (self.current_player,self.board)

    #判断落子后局面有无出现过
    def is_exist_situation(self,player,move):
       if self.move is None:
           return False

       new_board = copy.deepcopy(self.board)
       new_board.place(player,move.point)
       next_situation = (player.other,new_board)
       #之前游戏的状态
       past_state = self.previous_state

       #判断当前的状态有无出现过,出现过是非法的
       while past_state is not None:
           if past_state.situation == next_situation:
               return True
       return False

   总的判断落子是否有效

#总结,判断落子是否有效
    def is_move_valid(self,move):
        if(self.is_over()):#棋局结束
            return False
        if move.is_pass or move.is_resign:#有无pass和投降
            return True

        return (self.board.getColor(move.point) is None and not self.is_move_killself(self.current_player,move) and not self.is_exist_situation(self.current_player,move))

  三 判断有无填真眼(主要为了之后AI不要这样走)

        分析: 在围棋中如果一个未被占据的交叉点上下左右四个方向的相邻点(边是三个方向,角上是两个方向)都是同样颜色的话,那这个交叉点就是一个眼,那眼分为真眼和假眼,假眼就是说你这眼可以被破坏掉,而真眼就是除非这个棋子块的气只剩一口,对方可以吃掉以外,对方无论如何无法落在这个地方,那真眼有三种情况:  

       1.交叉点是在棋盘中央,这个时候如果交叉点的对角方向的四个点都是同色的当然是真眼,其实拿掉一个点,也是一个真眼(对方无法破坏),因此我们只需判断对角上的同色棋子是否达到了3即可

       2.交叉点在棋盘的边上,这时我们会发现对角会有两个方向在棋盘外,同时一个相邻点也在棋盘外,这种情况下,我们发现在棋盘内的对角方向的相邻点一旦有一个点被对方占据,那就是假眼了,因此要保证这两个方向的相邻点是同色的

      3.交叉点在角上,相邻点只有两个在棋盘内,并且只有一个对角方向相邻点在棋盘内,而我们要保证这个对角方向的相邻点是同色的才可以是真眼

     判断2和3这两种情况时,原博主用了一个很巧妙的方法,就是无论是2还是3,只要保证对角方向同色交叉点个数加棋盘外交叉点个数达到4即可,说明对角方向上没有其他颜色的棋子  

 def is_point_in_eye(board,point,color):
        if board.getColor(point) is not None:#当前点已被占
            return False

        #判断该点的邻接点是否为己方棋子,一个眼的邻接点肯定是一样颜色,当这个点在中间,对角线中至少三个是一样颜色,就是真眼,边角区域有效对角线必须全是
        for neighbor in point.neighbor:
            if board.is_on_grid(neighbor):#邻接点在棋盘内
                neighbor_color = board.get(neighbor)
                if(neighbor !=color):
                    return False

        #判断对角线情况,看是否是真眼,真眼不要去填

        #对角线是己方棋子个数
        me_corners = 0
        off_board_corners = 0#在棋盘外的对角线点

        corners = [
            Point(point.row - 1,point.col - 1),
            Point(point.row - 1, point.col + 1),
            Point(point.row + 1, point.col - 1),
            Point(point.row + 1, point.col + 1),
        ]

        for corner in corners:
            if board.is_on_grid(point):
                corner_color = board.getColor(corner)
                if corner_color == color:
                    me_corners += 1
            else:
                off_board_corners += 1
        #有在棋盘外的对角点,那么要保证对角上已方棋子数和棋盘外棋子数加起来为4
        if off_board_corners>0:
            return off_board_corners + me_corners == 4
        #所在点在中央,只要对角点是三个一样颜色的棋子,就是真眼
        else:
            return me_corners >= 3


   自此,搭建AI所需的基础类就差不多了,之后就要去制作一个下围棋的AI了,当然还不是带神经网络的

          

发布了8 篇原创文章 · 获赞 5 · 访问量 1411

猜你喜欢

转载自blog.csdn.net/qq_41957257/article/details/90672896