示例#1
0
文件: board.py 项目: wsdd2/PikachuGo
    def is_valid_move(self, pos):

        land_posx, land_posy = util.sgf_to_pos(pos)
        board_copy = copy.deepcopy(self.board)
        # 最先判断落子位置是不是空点,如果是不是空点,那么一定是不可以下的。
        if self.board[land_posx][land_posy] != EMPTY_STONE:
            return False
        # 判断它的周围是不是有空点,如果有空点,那么一定是可以下的。
        for i in range(4):
            if 0 <= land_posx + dx[i] < 19 and 0 <= land_posy + dy[i] < 19:
                if self.board[land_posx + dx[i]][land_posy +
                                                 dy[i]] == EMPTY_STONE:
                    return True
        # 判断吃子的话,先算一下zobrist哈希
        num_pos = util.pos_to_num((land_posx, land_posy))
        hash = self.zob_history[-1]
        if self._current_player() == SIDE_BLACK:
            c_zb_state = zb.STATE_BLACK
            o_zb_state = zb.STATE_WHITE
            current_stone = BLACK_STONE
            opposite_stone = WHITE_STONE
        else:
            c_zb_state = zb.STATE_WHITE
            o_zb_state = zb.STATE_BLACK
            current_stone = WHITE_STONE
            opposite_stone = BLACK_STONE
        # 判断是否吃子了,如果吃子了,那么应该是可以下的,但是要判断打劫,用zb哈希
        has_eat = False
        for key, value in self.group[self._opposite_player()].items():
            if value.count_liberty() == 1:
                if value.has_liberty(util.pos_to_num((land_posx, land_posy))):
                    has_eat = True
                    for s in self.group[self._opposite_player()][key].stone:
                        px, py = util.num_to_pos(s)
                        board_copy[px][py] = EMPTY_STONE
                        # 取消对方子的状态
                        hash = zb.get_new_hash(hash, self.zob_arr, o_zb_state,
                                               s)
                        # 赋予空点的状态
                        hash = zb.get_new_hash(hash, self.zob_arr,
                                               zb.STATE_EMPTY, s)
        board_copy[land_posx][land_posy] = current_stone
        hash = zb.get_new_hash(hash, self.zob_arr, zb.STATE_EMPTY, num_pos)
        hash = zb.get_new_hash(hash, self.zob_arr, c_zb_state, num_pos)

        if has_eat and len(self.zob_history) > 2:
            if self.zob_history[-2] == hash:
                # print 'ko rule!'
                return False
        if has_eat:
            return True

        # 如果没有吃子,那么可能就是往对方里面填子,那么用dfs遍历一下,看看这个棋串周围是否有空点
        # pos_x, pos_y是坐标!
        self.found = False
        self._dfs(board_copy, land_posx, land_posy, current_stone)
        if self.found is False:
            self.found = False
            return False
        return True
示例#2
0
    def place_stone_num(self, pos):
        land_posx, land_posy = util.num_to_pos(pos)
        current_player = self._current_player()
        opposite_player = self._opposite_player()
        num_pos = util.pos_to_num((land_posx, land_posy))
        current_stone, opposite_stone = None, None
        # 如果是停一手,则做如下操作
        """
            mistake
            !!!!
            停一手忘记更新棋盘了!
        """
        if land_posx < 0:
            self.history[current_player].append(pos)
            self.zob_history.append(self.zob_history[-1])
            """
            2019年1月7日01:54:54修补该bug
            """
            """
            
            if current_player == SIDE_BLACK:
                current_stone = BLACK_STONE
                opposite_stone = WHITE_STONE
            else:
                current_stone = WHITE_STONE
                opposite_stone = BLACK_STONE
            """
            self.round += 1

            return
        # 先判断是否合法

        if self.is_valid_move(util.pos_to_sgf((land_posx, land_posy))) is False:
            # print 'Invalid!'
            return False
        # 如果合法,则做这些操作:
        """
        1. 减去对方棋串的气
        2. 将新的子加入本方的棋串中
            2.1 遍历本方所有棋串的气,如果有重合,记录备用
            2.2 将这些有重合的都加入这个新的棋串中去
            2.3 删除那些重合加入的棋串
            2.4 重新计算新串的气
        3. 对对面所有没气了的棋串:如果对面没有没气了的棋串,并且本方的气是0,说明自尽,那么对本方这块棋的周围,
            3.1 对于每个没气了的棋串的每个子,看上下左右(注意边界)
            3.2 上下左右如果有本方的棋子,那么加入一个列表备用
            3.3 删除这个棋串
        4. 重新生成棋盘
        5. 记录的这些黑子,判断属于哪些棋块,对这些棋块重新算气
        """
        # 合法了的话,棋盘更新
        if current_player == SIDE_BLACK:
            self.board[land_posx][land_posy] = BLACK_STONE
            current_stone = BLACK_STONE
            opposite_stone = WHITE_STONE
        else:
            self.board[land_posx][land_posy] = WHITE_STONE
            current_stone = WHITE_STONE
            opposite_stone = BLACK_STONE
        # 1. 减去对方棋串的气,并用dead列表记录哪些死透了的棋子
        dead = []
        for key, value in self.group[opposite_player].items():
            assert isinstance(value, Group)
            value.remove_liberty(num_pos)
            if value.count_liberty() == 0:
                dead.append(key)

        # 遍历本方所有棋串的气,如果有重合,记录备用在merge里面
        merge = []
        for key, value in self.group[current_player].items():
            assert isinstance(value, Group)
            if value.has_liberty(num_pos):
                merge.append(key)
        # 2.2 新建一个棋串,将这些有重合的都加入这个新的棋串中去
        self.group[current_player][self.round] = Group(self.round)
        self.group[current_player][self.round].add_stone(num_pos)
        # 2.3 删除那些重合加入的棋串
        for ids in merge:
            self.group[current_player][self.round].merge_stone(self.group[current_player][ids])
            self.group[current_player].pop(ids)
        # 2.4 重新计算新串的气(有一个不必要的参数side)
        self.group[current_player][self.round].recount_liberty(current_player, self.board)

        # 3 对面所有没气了的棋串,在dead里面,删掉这些子,但是记录一下他们
        record_dead_stone = set()
        for d in dead:
            for st in self.group[opposite_player][d].stone:
                record_dead_stone.add(st)
                posx, posy = util.num_to_pos(st)
                # 在这里清除了对方的死子
                self.board[posx][posy] = EMPTY_STONE
        # 由于记录了死子的位置,所以看一下它的上下左右,有没有本方的子
        # 有的话,记录本方这些子的棋串号
        recount_liberty_group = set()
        for dead_pos in record_dead_stone:
            posx, posy = util.num_to_pos(dead_pos)
            for i in range(4):
                if 0 <= posx + dx[i] < 19 and 0 <= posy + dy[i] < 19:
                    if self.board[posx + dx[i]][posy + dy[i]] == current_stone:
                        for key, value in self.group[current_player].items():
                            if value.has_stone(util.pos_to_num((posx + dx[i], posy + dy[i]))):
                                recount_liberty_group.add(key)
        # 删掉这些棋串
        for del_ids in dead:
            self.group[opposite_player].pop(del_ids)
        # 对需要重新算气的本方棋串重新算气
        for rec in recount_liberty_group:
            self.group[current_player][rec].recount_liberty(current_player, self.board)

        if current_player == SIDE_BLACK:
            c_zb_state = zb.STATE_BLACK
            o_zb_state = zb.STATE_WHITE
        else:
            c_zb_state = zb.STATE_WHITE
            o_zb_state = zb.STATE_BLACK

        hash = self.zob_history[-1]
        # 取消本方落子的那个空点
        hash = zb.get_new_hash(hash, self.zob_arr, zb.STATE_EMPTY, util.pos_to_num((land_posx, land_posy)))
        # 本方落子在那个空点
        hash = zb.get_new_hash(hash, self.zob_arr, c_zb_state, util.pos_to_num((land_posx, land_posy)))

        for ds in record_dead_stone:
            hash = zb.get_new_hash(hash, self.zob_arr, o_zb_state, ds)
            hash = zb.get_new_hash(hash, self.zob_arr, zb.STATE_EMPTY, ds)

        self.zob_history.append(hash)
        self.history[current_player].append(pos)
        # self.print_board()
        self.round += 1
        return True