예제 #1
0
def best_play(board: chess.Board, player, profondeur=5):
    """
    :param profondeur: profondeur de l'algorithme
    :param board: chess.Board
    :param player: boolean
    :return: chess.Move
    """
    results = []
    id = 0
    moves = []
    pool = mp.Pool(processes=16)
    for move in board.legal_moves:
        moves.append(move)
        clone = board.copy(stack=False)
        clone.push(move)
        if player:
            results.append((pool.apply(_min, args=(clone, profondeur)), id))
        else:
            results.append((pool.apply(_max, args=(clone, profondeur)), id))
        id += 1
    if player:
        m = max(results)
    else:
        m = min(results)

    return moves[m[1]]
예제 #2
0
def without_opponent_pieces(board: chess.Board) -> chess.Board:
    """Returns a copy of `board` with the opponent's pieces removed."""
    b = board.copy()
    b.ep_square = None
    for piece_type in chess.PIECE_TYPES:
        for sq in b.pieces(piece_type, not board.turn):
            b.remove_piece_at(sq)
    return b
예제 #3
0
 def __init__(self,
              board: chess.Board = None,
              main_UI=None,
              *args,
              **kwargs):
     super().__init__(*args, **kwargs)
     self.board = board.copy()
     self.main_UI = main_UI
예제 #4
0
def expand_state_chess(state: chess.Board) -> Dict[str, chess.Board]:
    children = {}
    for move in state.legal_moves:
        # TODO: can we push/pop to aviod copies?
        board = state.copy()
        board.push(move)
        children[move.uci()] = board
    return children
예제 #5
0
def get_defending_pieces(board: chess.Board, defending_color: chess.Color, square: chess.Square) -> [chess.PieceType]:

    cloned_board = board.copy()

    defending_pieces = get_attacking_pieces(cloned_board, defending_color, square)

    del cloned_board

    return defending_pieces
예제 #6
0
파일: agentuct.py 프로젝트: andvra/funchess
    def make_move(self, board: chess.Board, is_white: Boolean):
        t_start = time.time()
        self.reset()
        #root = ChessNode(board, 0)
        #print('The current system have {} CPUs of which {} are usable'.format(mp.cpu_count(),len(os.sched_getaffinity(0))))
        max_num_processes = 3
        num_legal_moves = board.legal_moves.count()
        legal_moves_per_process = math.ceil(num_legal_moves /
                                            max_num_processes)
        processes = []
        root_parts: List[ChessNode] = []
        out_queue = mp.Queue()
        print("Starting make_move")
        # When there are few moves left, they might be all distributed among less than all processes.
        #   Eg. with max_num_processes==3 and num_legal_moves==4, we get legal_moves_per_process==2
        #   so we'll only use 2 processes.
        num_processes = math.ceil(num_legal_moves / legal_moves_per_process)
        for i in range(0, num_processes):
            # Only create node if there are legal moves left to use
            if num_legal_moves > legal_moves_per_process * i:
                root_parts.append(
                    ChessNode(board.copy(), 0, i * legal_moves_per_process,
                              legal_moves_per_process))
                processes.append(
                    mp.Process(target=self.run_it,
                               args=(board.copy(), is_white, root_parts[-1],
                                     t_start, i + 1, out_queue)))
                processes[-1].start()
        for p in processes:
            p.join()
        max_val, max_val_move = -math.inf, None
        for i in range(0, num_processes):
            cur_max_move, cur_max_val = out_queue.get()
            print(cur_max_move, cur_max_val)
            if cur_max_val > max_val:
                max_val = cur_max_val
                max_val_move = cur_max_move
        print("Done with all the jobs")

        move = max_val_move
        print(
            'Chose move {} with value/visits={}. Node count: {} Max depth: {}'.
            format(max_val_move, max_val, self.no_nodes, self.max_level))
        return move
예제 #7
0
def quiesce(fen, alpha, beta, flag, options):
    # flag check
    if not flag.is_set():
        return 0, 0

    # heuristic
    if options.heuristic == 'NeuralNetwork':
        stand_pat = heuristic.nn_heuristic(fen, options, options.model)
    elif options.heuristic == 'Random':
        stand_pat = heuristic.random_heuristic()
    else:
        stand_pat = heuristic.heuristic(fen, options)

    nodes = 1

    if (stand_pat >= beta):
        return beta, nodes

    board = Board(fen)
    if len(board.piece_map()) > 8:
        delta = True
    else:
        delta = False

    if delta:
        # full delta pruning
        if (stand_pat < alpha - 1000):
            return alpha, nodes

    if (stand_pat > alpha):
        alpha = stand_pat

    # expansion and search
    legal = board.legal_moves
    for move in legal:
        board.push(move)
        new = board.copy()
        board.pop()

        if board.is_capture(move) or new.is_check():
            # delta pruning
            if delta and board.is_capture(move):
                value = value_captured_piece(
                    board.piece_type_at(move.to_square)) + 200
                if (stand_pat + value < alpha):
                    continue

            score, count = quiesce(new.fen(), -beta, -alpha, flag, options)
            score = -score
            nodes += count

            if (score >= beta):
                return beta, nodes
            if (score > alpha):
                alpha = score
    return alpha, nodes
예제 #8
0
def alphabeta(board: chess.Board, depth: int, alpha: chess.Board, beta: chess.Board, player: chess.Color):
    if depth == 0:
        return eval(board, alpha, beta, player)
    
    if player == chess.WHITE:
        for move in board.generate_legal_moves():
            new_board = board.copy()
            new_board.push(move)
            alpha = alphabeta(new_board, depth-1, alpha, beta, chess.BLACK)
            if model.predict_mlp(utils.bitify(beta.fen()), utils.bitify(alpha.fen()))[0] == 1:
                break
        return alpha
    else:
        for move in board.generate_legal_moves():
            new_board = board.copy()
            new_board.push(move)
            beta = alphabeta(new_board, depth-1, alpha, beta, chess.WHITE)
            if model.predict_mlp(utils.bitify(beta.fen()), utils.bitify(alpha.fen()))[0] == 1:
                break
        return beta
예제 #9
0
 def get_move(self, board: chess.Board) -> chess.Move:
     # TODO: Implement Minimax to explore more than a single move ahead.
     possible_moves = []
     board = board.copy()
     for move in board.legal_moves:
         board.push(move)
         possible_moves.append((self.evaluate(board), move))
         board.pop()
     sorted_moves = sorted(possible_moves, key=lambda x: x[0], reverse=board.turn)
     for move in sorted_moves[:3]:
         print(f'Move: {move[1]}, evaluated at: {move[0]}')
     return sorted_moves[0][1]
예제 #10
0
def is_defended(board: Board, piece: Piece, square: Square) -> bool:
    if board.attackers(piece.color, square):
        return True
    # ray defense https://lichess.org/editor/6k1/3q1pbp/2b1p1p1/1BPp4/rp1PnP2/4PRNP/4Q1P1/4B1K1_w_-_-_0_1
    for attacker in board.attackers(not piece.color, square):
        if board.piece_at(attacker).piece_type in ray_piece_types:
            bc = board.copy(stack = False)
            bc.remove_piece_at(attacker)
            if bc.attackers(piece.color, square):
                return True

    return False
예제 #11
0
    def generate_ply_info_list_for_game(game):
        "Returns a dict which contains the list of tasks to be run"
        board = Board()
        ply_no = 0
        game_tasks = []
        for ply in game.mainline_moves():
            board.push(ply)
            ply_info = {"ply_no": ply_no, "board": board.copy()}
            game_tasks.append(ply_info)
            ply_no = ply_no + 1

        return {"game_tasks": game_tasks, "ply_count": ply_no}
def rollout_move(board: Board,
                 move: str,
                 simulations: int = 10) -> Tuple[str, Number[Any]]:
    rollouts_scores = list()
    for _ in range(simulations):
        child_node = board.copy()
        child_node.push(move)
        while not child_node.is_game_over():
            rand_move = get_random_move(child_node)
            child_node.push_san(rand_move)
        result = parse_result(child_node.result())
        rollouts_scores.append(result)
    return (move, np.mean(rollouts_scores))
예제 #13
0
 def create_image(self, board: chess.Board):
     fen = board.copy().fen().split(' ')[0].split('/')
     obervations = np.empty(shape=(8, 8), dtype=str)  # make empty container
     for i, row in enumerate(fen):
         j = 0
         for square in row:
             if square.isnumeric():
                 for _ in range(j, int(square) + j):
                     obervations[i][j] = '.'
                     j += 1
                 continue
             obervations[i][j] = square
             j += 1
     return obervations.tolist()
예제 #14
0
def erase_random_piece(board: chess.Board):
    removable_piece_squares = []
    for square, piece in board.piece_map().items():
        if piece.piece_type != chess.KING:
            removable_piece_squares.append(square)

    for _ in range(0, 10):
        square = random.choice(removable_piece_squares)
        new_board = board.copy()
        new_board.remove_piece_at(square)
        if new_board.is_valid():
            return new_board

    return None
예제 #15
0
def mcts_player(board: chess.Board, evaluation: EvaluationFunction, iterations: int, maxdepth: int) -> chess.Move:
    for move_choice in board.legal_moves:
        copy = board.copy()
        copy.push(move_choice)
        if copy.is_game_over():
            board.push(move_choice)
            return move_choice

    root = MCTSNode(
        ChessState(board, board.turn, maxdepth),
        evaluation,
        move_to_get_here=None,
    )
    move = root.best_action(iterations)
    # move = UCT(board, itermax, depthmax, evaluation)
    board.push(move)
    return move
예제 #16
0
    def get_syzygy(self, board: chess.Board) -> tuple[int, chess.Move]:
        """
        Get a move from Syzygy tablebases.

        :param chess.Board board: Board to get best move and evaluation.
        :return: The evaluation from Syzygy tablebases and the best move.
        :rtype: tuple[int, chess.Move]
        """
        # Generate DTZ list
        wdl: dict[int, dict[int, chess.Move]] = {
            2: {},
            1: {},
            0: {},
            -1: {},
            -2: {}
        }
        for move in board.legal_moves:
            test_board: chess.Board = board.copy()
            test_board.push(move)
            wdl[self.syzygy_tb.probe_wdl(test_board)][self.syzygy_tb.probe_dtz(
                test_board)] = move
        # Get best WDL
        best_wdl: int = -2
        while wdl[best_wdl] == {}:
            best_wdl += 1
        # Get best move
        best_dtz: int = 0
        best_dtz = max(wdl[best_wdl].items(), key=lambda key: key[0])[0]
        # Evaluation
        print(wdl)
        syzygy_evaluation: int = 0
        if best_wdl < 0:
            if board.turn:
                syzygy_evaluation = 10000
            else:
                syzygy_evaluation = -10000
        elif best_wdl == 0:
            syzygy_evaluation = 0
        else:
            if board.turn:
                syzygy_evaluation = -10000
            else:
                syzygy_evaluation = 10000
        # Return
        return syzygy_evaluation, wdl[best_wdl][best_dtz]
예제 #17
0
def _evaluate_move(turn: chess.Color, board: chess.Board,
                   move: chess.Move) -> float:
    # sum up the total weighted pieces for each player and return a score

    board = board.copy()
    board.push(move)

    total = 0

    if board.is_game_over():
        return -1000 if board.turn == turn else 1000

    for p in (chess.PAWN, chess.KNIGHT, chess.BISHOP, chess.ROOK, chess.QUEEN,
              chess.KING):
        for c in board.pieces(p, turn):
            total += piece[p] + pst[p][c]

    return total
예제 #18
0
def run(col, depth):
    refer = get_ref()

    if col == True:
        old_bo = online_minmax(col, Board(), refer, depth)
    else:
        old_bo = Board()

    while True:
        see_bo = read2(ref=refer, flip=col)
        print(old_bo, '\n_______________')
        print(see_bo, '\n_______________')

        if str(old_bo) != str(see_bo):
            new_bo = old_bo.copy()
            new_bo.push(find_move(old_bo, see_bo))
            move_bo = online_minmax(col, new_bo, refer, depth)

            read_bo = read2(ref=refer, flip=col)
            if str(move_bo) == str(read_bo):
                old_bo = move_bo
예제 #19
0
    def predict_best_move(self, curr_board: chess.Board) -> list:
        turn = curr_board.turn
        eval_moves = []
        mate_moves = []
        opponent_mates = []

        for move in curr_board.legal_moves:
            new_board = curr_board.copy()
            new_board.push(move)
            # analyze position and remember the best one, analyze is not yet implemented
            prediction = self.analyze(new_board)
            evaluation = prediction[0].item()
            moves_til_mate = prediction[1].item()
            is_mate = prediction[2].item()

            if turn:
                if is_mate > 0.5 and moves_til_mate >= 0:
                    mate_moves.append((curr_board.san(move), moves_til_mate))
                elif is_mate > 0.5:
                    opponent_mates.append((curr_board.san(move), moves_til_mate))
                else:
                    eval_moves.append((curr_board.san(move), evaluation))
            else:
                if is_mate > 0.5 and moves_til_mate <= 0:
                    mate_moves.append((curr_board.san(move), moves_til_mate))
                elif is_mate > 0.5:
                    opponent_mates.append((curr_board.san(move), moves_til_mate))
                else:
                    eval_moves.append((curr_board.san(move), evaluation))

        if turn:
            eval_moves.sort(key=lambda x: x[1], reverse=True)
            mate_moves.sort(key=lambda x: x[1])
            opponent_mates.sort(key=lambda x: x[1])
        else:
            eval_moves.sort(key=lambda x: x[1])
            mate_moves.sort(key=lambda x: x[1], reverse=True)
            opponent_mates.sort(key=lambda x: x[1], reverse=True)

        return eval_moves, mate_moves, opponent_mates
예제 #20
0
파일: kidpawn.py 프로젝트: leopd/kidpawn
def lookahead1_move(original: Board, verbose: bool = False) -> Move:
    """Looks ahead one move
    """
    best_score = None
    best_moves = None

    def is_better(reference_score, new_score) -> bool:
        if reference_score is None:
            return True
        if original.turn == chess.WHITE:
            return new_score > reference_score
        else:
            return reference_score > new_score

    my_moves = list(original.legal_moves)
    for my_move in my_moves:
        b = original.copy()
        b.push(my_move)
        if b.is_game_over():
            score = score_board(b)
        else:
            their_move = pick_move(b)
            b.push(their_move)
            score = score_board(b)
        if verbose:
            print(f"{my_move} then {their_move} gives {score}.")
        if is_better(best_score, score):
            best_score = score
            best_moves = [my_move]
        elif score == best_score:
            best_moves.append(my_move)

    if verbose:
        print(f"\nBest moves are {best_moves} scoring {best_score}")
    if best_moves:
        return random.choice(best_moves)
    else:
        print("No valid moves to pick from")
        return None
예제 #21
0
    def getGameEnded(self, board: chess.Board, player):
        """
        Input:
            board: current board
            player: current player (1 or -1)

        Returns:
            r: 0 if game has not ended. 1 if player won, -1 if player lost,
               small non-zero value for draw.

        """
        new_board = board.copy()

        if new_board.is_game_over(claim_draw=True):
            if new_board.result() == "1-0":
                return 1
            elif new_board.result() == "1/2-1/2":
                return -0.5
            else:
                return -1

        return 0
예제 #22
0
파일: mctsv2.py 프로젝트: Meigs2/MChess
    def search(self, board: chess.Board):
        """
        Recursevly search a tree using UCT and the NNet to intellegently select the proper tree to search.
        """
        if board.is_game_over():
            result = board.result()
            return -end_states[result]

        board_hash = polyglot.zobrist_hash(board)

        if board_hash not in self.Policy_vectors:
            # Get nnet prediction of the current board for use in the
            # Determine which branch to explore next.
            # If black to play, mirror the board, unmirror at end. NNet always sees current player as white.
            # Make a copy of the board as to preserve our move stack.
            temp = None
            if not board.turn:
                # If black to play, flip and mirror board.
                temp = board.transform(chess.flip_vertical)
                temp.apply_mirror()
            else:
                temp = board.copy()

            cannonical = adapter.get_cannonical(temp)
            policy_vector, nnet_value = self.network.predict(cannonical)

            # Mask out invalid moves
            valids = adapter.moves_to_policy_mask(list(board.legal_moves))
            policy_vector *= valids

            # Normalize vector, add valid moves if needed.
            if np.sum(policy_vector > 0):
                policy_vector /= np.linalg.norm(policy_vector)
                self.Policy_vectors[board_hash] = policy_vector
            else:
                print(
                    "All valid moves were masked. Adding valids. Warning if lots of these messages."
                )
                policy_vector += valids
                policy_vector /= np.linalg.norm(policy_vector)

            self.N_vals[board_hash] = 0
            del temp

            # Return the esimate until we actually reach the end.
            return -nnet_value

        # Iterate over legal moves and get the probability of making that move, according to the nnet.
        action_heuristic_dict = {}
        curr_move_policy = self.Policy_vectors[board_hash]
        for move in list(board.legal_moves):
            move_prob = adapter.get_move_prob(curr_move_policy, move)
            action_heuristic_dict[move] = self.get_ucb(board_hash, move,
                                                       move_prob * 2.0)

        # Pick move with max value, make it bigger
        move = max(action_heuristic_dict, key=action_heuristic_dict.get)

        # action_heuristic_dict[max_move] *= 50.0
        # # Normalize
        # values = np.array(list(action_heuristic_dict.values()))
        # values += np.abs(values.min())
        # values /= np.linalg.norm(values)
        # p = self.fix_p(values)

        # move = np.random.choice(list(action_heuristic_dict.keys()), p=p)

        board.push(move)
        value = self.search(board)
        board.pop()

        # We've done our search, now we're back-propigating values for the next search.
        self.update_values(board_hash, move, value)
        self.N_vals[board_hash] += 1
        return -value
예제 #23
0
 def make_child(self, board: chess.Board, move: str) -> chess.Board:
     child = board.copy()
     child.push(move)
     return child
def get_mobility(board: Board, mobility_coeff: float = 1) -> float:
    player_legal_moves = len(list(board.legal_moves))
    _board = board.copy()
    _board.turn = not board.turn
    oponent_legal_moves = len(list(_board.legal_moves))
    return mobility_coeff * (player_legal_moves - oponent_legal_moves)
예제 #25
0
 def successors(board: chess.Board) -> Iterator[chess.Board]:
     for move in board.legal_moves:
         board_after = board.copy(stack=False)
         board_after.push(move)
         yield board_after
예제 #26
0
    def search(self, board: chess.Board, depth, side, alpha=-2, beta=2):
        """
        Performs a minimax search with alpha-beta pruning.
        Alpha, beta are set to -2, 2 respectively; because the suppport of the CNN is [0,1].

        Args:
            `board`:    Chess board state at current node. 
            `depth`:    Search depth.
            `side`:     White (True), Black (False). White is the maximising player.
            `alpha`:    Current minimum score White is assured of.
            `beta`:     Current maximum score Black is assured of.

        Returns:
            (evaluation, uci-encoded move that `side` should play)
        """

        if depth == 0:
            input = szr.Serializer(board).serialize()
            return (self.model.predict(np.expand_dims(input, 0)), board.pop())

        if board.is_game_over():
            res = board.result()
            if res == '0-1':
                return 0
            elif res == '1-0':
                return 1
            else:
                return 0.5

        if side:
            max_score = -2
            best_move = ''
            for move in board.legal_moves:
                new_board = board.copy()
                new_board.push(move)
                score = self.search(new_board, depth - 1, alpha, beta,
                                    False)[0]
                if score > max_score:
                    max_score = score
                    best_move = move
                alpha = max(alpha, score)
                if alpha >= beta:
                    # black has a better option available earlier on in the tree.
                    break
            return (max_score, best_move)
        else:
            min_score = 2
            best_move = ''
            for move in board.legal_moves:
                new_board = board.copy()
                new_board.push(move)
                score = self.search(new_board, depth - 1, alpha, beta, True)[0]
                min_score = min(score, min_score)
                if score < min_score:
                    min_score = score
                    best_move = move
                beta = min(beta, score)
                if beta <= alpha:
                    # white has a better option avilable earlier on in the tree.
                    break
            return (min_score, best_move)
예제 #27
0
class Viridithas():
    def __init__(
        self,
        human: bool = False,
        fen: str = 'rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1',
        pgn: str = '',
        time_limit: float = 15,
        fun: bool = False,
        contempt: int = 3000,
        book: bool = True,
        advancedTC: list = [],
    ):
        if pgn == '':
            self.node = Board(fen)
        else:
            self.node = Board()
            for move in pgn.split():
                try:
                    self.node.push_san(move)
                except Exception:
                    continue
        self.time_limit = time_limit
        if advancedTC:
            self.endpoint = time.time()+advancedTC[0]*60
            self.increment = advancedTC[1]
        else:
            self.endpoint = 0
            self.increment = 0
        self.fun = fun
        self.contempt = contempt
        self.human = human
        self.nodes = 0
        self.advancedTC = advancedTC
        self.hashtable: dict[Hashable, TTEntry] = dict()
        self.inbook = book

    def set_position(self, fen):
        self.node = Board(fen)

    def __repr__(self) -> str:
        return str(self.node) + '\n' + self.__class__.__name__+"-engine at position " + str(self.node.fen())

    def __str__(self) -> str:
        return self.__class__.__name__

    def user_setup(self):
        if input("Do you want to configure the bot? (Y/N) ").upper() != 'Y':
            return

        myname = self.__class__.__name__.upper()

        print(f"BEGINNING USER CONFIGURATION OF {myname}-BOT")

        datadict = get_engine_parameters()

        self.__init__(
            human=datadict["human"],
            fen=datadict["fen"],
            pgn=datadict["pgn"],
            time_limit=datadict["time_limit"],
            fun=datadict["fun"],
            contempt=datadict["contempt"],
            book=datadict["book"],
            advancedTC=datadict["advancedTC"]
        )

    def gameover_check_info(self):
        checkmate = self.node.is_checkmate()
        draw = self.node.is_stalemate() or \
            self.node.is_insufficient_material( ) or \
            self.node.is_repetition(2) or self.node.is_seventyfive_moves() or not any(self.node.generate_legal_moves())
        return checkmate or draw, checkmate, draw

    # @profile
    def evaluate(self, depth: float, checkmate: bool, draw: bool) -> float:
        self.nodes += 1

        if checkmate:
            return MATE_VALUE * int(max(depth+1, 1)) * (1 if self.node.turn else -1)
        if draw:
            return -self.contempt * (1 if self.node.turn else -1)

        rating: float = 0

        rating += pst_eval(self.node)
        # rating += see_eval(self.node)

        # rating += mobility(self.node) * MOBILITY_FACTOR
        
        # rating += piece_attack_counts(self.node) * ATTACK_FACTOR

        # rating += king_safety(self.node) * KING_SAFETY_FACTOR

        # rating += space(self.node) * SPACE_FACTOR

        return rating

    def single_hash_iterator(self, best):
        yield best

    def captures_piece(self, p):  # concentrate on MVV, then LVA
        return itertools.chain(
            self.node.generate_pseudo_legal_moves(self.node.pawns, p),
            self.node.generate_pseudo_legal_moves(self.node.knights, p),
            self.node.generate_pseudo_legal_moves(self.node.bishops, p),
            self.node.generate_pseudo_legal_moves(self.node.rooks, p),
            self.node.generate_pseudo_legal_moves(self.node.queens, p),
            self.node.generate_pseudo_legal_moves(self.node.kings, p),
        )

    #@profile
    def captures(self):  # (MVV/LVA)
        return (m for m in itertools.chain(
            self.captures_piece(
                self.node.occupied_co[not self.node.turn] & self.node.queens),
            self.captures_piece(
                self.node.occupied_co[not self.node.turn] & self.node.rooks),
            self.captures_piece(
                self.node.occupied_co[not self.node.turn] & self.node.bishops),
            self.captures_piece(
                self.node.occupied_co[not self.node.turn] & self.node.knights),
            self.captures_piece(
                self.node.occupied_co[not self.node.turn] & self.node.pawns),
        ) if self.node.is_legal(m))

    def winning_captures(self):  # (MVV/LVA)
        target_all = self.node.occupied_co[not self.node.turn]
        target_3 = target_all & ~self.node.pawns
        target_5 = target_3 & (~self.node.bishops | ~self.node.knights)
        target_9 = target_5 & ~self.node.rooks
        return itertools.chain(
            self.node.generate_pseudo_legal_moves(self.node.pawns, target_all),
            self.node.generate_pseudo_legal_moves(self.node.knights, target_3),
            self.node.generate_pseudo_legal_moves(self.node.bishops, target_3),
            self.node.generate_pseudo_legal_moves(self.node.rooks, target_5),
            self.node.generate_pseudo_legal_moves(self.node.queens, target_9),
            self.node.generate_pseudo_legal_moves(self.node.kings, target_9),
        )

    def losing_captures(self):  # (MVV/LVA)
        target_pawns = self.node.pawns
        target_pnb = target_pawns | self.node.bishops | self.node.knights
        target_pnbr = target_pnb | self.node.rooks
        return itertools.chain(
            self.node.generate_pseudo_legal_moves(self.node.knights, target_pawns),
            self.node.generate_pseudo_legal_moves(self.node.bishops, target_pawns),
            self.node.generate_pseudo_legal_moves(self.node.rooks, target_pnb),
            self.node.generate_pseudo_legal_moves(self.node.queens, target_pnbr),
            self.node.generate_pseudo_legal_moves(self.node.kings, target_pnbr),
        )

    def ordered_moves(self):
        return (m for m in itertools.chain(
            # self.winning_captures(),
            self.captures_piece(
                self.node.occupied_co[not self.node.turn] & self.node.queens),
            self.captures_piece(
                self.node.occupied_co[not self.node.turn] & self.node.rooks),
            self.captures_piece(
                self.node.occupied_co[not self.node.turn] & self.node.bishops),
            self.captures_piece(
                self.node.occupied_co[not self.node.turn] & self.node.knights),
            self.captures_piece(
                self.node.occupied_co[not self.node.turn] & self.node.pawns),
            self.node.generate_pseudo_legal_moves(
                0xffff_ffff_ffff_ffff, ~self.node.occupied_co[not self.node.turn]),
            # self.losing_captures()
        ) if self.node.is_legal(m))

    def pass_turn(self) -> None:
        self.node.push(Move.from_uci("0000"))

    #@profile
    def qsearch(self, alpha: float, beta: float, depth: float, colour: int, gameover: bool, checkmate: bool, draw: bool) -> float:

        val = self.evaluate(1, checkmate, draw) * colour
        if gameover:
            return val
        if val >= beta:
            return beta
        if (val < alpha - QUEEN_VALUE):
            return alpha
        
        alpha = max(val, alpha)

        for capture in self.captures():
            self.node.push(capture)
            gameover, checkmate, draw = self.gameover_check_info()
            score = -self.qsearch(-beta, -alpha, depth - 1, -colour, gameover, checkmate, draw)
            self.node.pop()
            if score >= beta:
                return score
            alpha = max(score, alpha)

        return alpha

    def tt_lookup(self, board: Board) -> "TTEntry":
        key = board._transposition_key()
        return self.hashtable.get(key, TTEntry.default())

    def tt_store(self, board: Board, entry: TTEntry):
        key = board._transposition_key()    
        self.hashtable[key] = entry

    #@profile
    def negamax(self, depth: float, colour: int, alpha: float, beta: float) -> float:
        initial_alpha = alpha

        # (* Transposition Table Lookup; self.node is the lookup key for ttEntry *)
        tt_entry = self.tt_lookup(self.node)
        if not tt_entry.is_null() and tt_entry.depth >= depth:
            if tt_entry.type == EXACT:
                return tt_entry.value
            elif tt_entry.type == LOWERBOUND:
                alpha = max(alpha, tt_entry.value)
            elif tt_entry.type == UPPERBOUND:
                beta = min(beta, tt_entry.value)

            if alpha >= beta:
                return tt_entry.value
            
            if self.node.is_legal(tt_entry.best):
                moves = itertools.chain([tt_entry.best], filter(lambda x: x != tt_entry.best, self.ordered_moves()))
            else:
                moves = self.ordered_moves()
        else:
            moves = self.ordered_moves()

        gameover, checkmate, draw = self.gameover_check_info()

        if gameover:
            return colour * self.evaluate(depth, checkmate, draw)

        if depth < 1:
            return self.qsearch(alpha, beta, depth, colour, gameover, checkmate, draw)

        current_pos_is_check = self.node.is_check()
        if not current_pos_is_check and depth >= 3 and abs(alpha) < MATE_VALUE and abs(beta) < MATE_VALUE:
            # MAKE A NULL MOVE
            self.node.push(Move.null())  
            # PERFORM A LIMITED SEARCH
            value = - self.negamax(depth - 3, -colour, -beta, -beta + 1)
            # UNMAKE NULL MOVE
            self.node.pop()

            if value >= beta:
                return beta

        do_prune = self.pruning(depth, colour, alpha, beta, current_pos_is_check)

        best_move = Move.null()
        search_pv = True
        value = float("-inf")
        for move_idx, move in enumerate(moves):
            if move_idx == 0:
                best_move = move
            gives_check = self.node.gives_check(move)
            is_capture = self.node.is_capture(move)
            is_promo = bool(move.promotion)
            depth_reduction = search_reduction_factor(
                move_idx, current_pos_is_check, gives_check, is_capture, is_promo, depth)
                
            if do_prune:
                if not gives_check and not is_capture: 
                    continue

            self.node.push(move)
            
            if search_pv:
                r = -self.negamax(depth - depth_reduction, -colour, -beta, -alpha)
                value = max(value, r)
            else:
                r = -self.negamax(depth - depth_reduction, -colour, -alpha-1, -alpha)
                if (r > alpha): # // in fail-soft ... & & value < beta) is common
                    r = -self.negamax(depth - depth_reduction, -colour, -beta, -alpha) #// re-search
                value = max(value, r)

            self.node.pop()

            if value > alpha:
                alpha = value
                best_move = move
            alpha = max(alpha, value)
            if alpha >= beta:
                break
            search_pv = False

        # (* Transposition Table Store; self.node is the lookup key for ttEntry *)
        # ttEntry = TTEntry()
        tt_entry.value = value
        if value <= initial_alpha:
            tt_entry.type = UPPERBOUND
        elif value >= beta:
            tt_entry.type = LOWERBOUND
        else:
            tt_entry.type = EXACT
        tt_entry.depth = depth
        tt_entry.best = best_move
        tt_entry.null = False
        self.tt_store(self.node, tt_entry)

        return value

    def pruning(self, depth, colour, alpha, beta, current_pos_is_check):
        if not (not current_pos_is_check and abs(
                alpha) < MATE_VALUE / 2 and abs(beta) < MATE_VALUE / 2) or depth > 2:
            return False

        see = see_eval(self.node) * colour

        DO_D1_PRUNING = depth <= 1 and see + FUTILITY_MARGIN < alpha

        DO_D2_PRUNING = depth <= 2 and see + FUTILITY_MARGIN_2 < alpha

        return DO_D1_PRUNING or DO_D2_PRUNING

    def move_sort(self, moves: list, ratings: list):
        pairs = zip(*sorted(zip(moves, ratings), key=operator.itemgetter(1)))
        moves, ratings = [list(pair) for pair in pairs]
        return moves, ratings

    def pv_string(self):
        count = 0
        moves = []
        while True:
            e = self.tt_lookup(self.node)
            if e.is_null() or not self.node.is_legal(e.best):
                break

            # print(self.node.__str__())
            # print(self.node.__repr__())
            # print(e.best)
            # print(self.node.san(e.best))

            moves.append(self.node.san(e.best))
            self.node.push(e.best)
            count += 1
        
        for _ in moves:
            self.node.pop()

        if count == 0: return ""
        return " ".join(moves)

    def turnmod(self) -> int:
        return -1 if self.node.turn else 1

    def show_iteration_data(self, moves: list, values: list, depth: float, start: float) -> tuple:
        t = round(time.time()-start, 2)
        print(f"{self.node.san(moves[0])} | {-round((self.turnmod()*values[0])/1000, 3)} | {str(t)}s at depth {str(depth + 1)}, {str(self.nodes)} nodes processed, at {str(int(self.nodes / (t+0.00001)))}NPS.\n", f"PV: {self.pv_string()}\n", end="")
        return (self.node.san(moves[0]), self.turnmod()*values[0], self.nodes, depth+1, t)

    def search(self, ponder: bool = False, readout: bool = True):
        val = float("-inf")
        start_time = time.time()
        self.nodes = 0
        moves = [next(self.ordered_moves())]
        saved_position = deepcopy(self.node)

        alpha, beta = float("-inf"), float("inf")
        valWINDOW = PAWN_VALUE / 4

        WINDOW_FAILED = False

        try:
            depth = 1
            while depth < 40:
                best = self.tt_lookup(self.node).best
                time_elapsed = time.time() - start_time
                # check if we aren't going to finish the next search in time
                if time_elapsed > 0.5 * self.time_limit and not ponder and not WINDOW_FAILED:
                    return best, val

                val = self.negamax(
                    depth, self.turnmod(), alpha=alpha, beta=beta)
                # print(val)
                if ((val <= alpha) or (val >= beta)):
                    # We fell outside the window, so try again with a
                    # full-width window (and the same depth).
                    alpha = float("-inf")
                    beta = float("inf")
                    WINDOW_FAILED = True
                    continue
                WINDOW_FAILED = False

                best = self.tt_lookup(self.node).best
                # check if we've run out of time
                if time_elapsed > self.time_limit and not ponder:
                    return best, val

                moves = [self.tt_lookup(self.node).best]
                values = [self.tt_lookup(self.node).value]

                if readout:
                    self.show_iteration_data(moves, values, depth, start_time)

                alpha = val - valWINDOW # Set up the window for the next iteration.
                beta = val + valWINDOW
                depth += 1
        except KeyboardInterrupt:
            self.node = saved_position
            pass
        return moves[0], val

    def ponder(self) -> None:
        self.origin = self.node.copy()
        self.search(ponder=True)

    def get_book_move(self):
        # book = chess.polyglot.open_reader(
        #     r"ProDeo292/ProDeo292/books/elo2500.bin")
        book = chess.polyglot.open_reader(
            r"books/elo2500.bin")
        main_entry = book.find(self.node)
        choice = book.weighted_choice(self.node)
        book.close()
        return main_entry.move, choice.move

    def engine_move(self) -> Move:
        # add flag_func for egtb mode
        if self.advancedTC:
            self.time_limit = (self.endpoint-time.time())/20
        print("Time for move: " + str(round(self.time_limit, 2)) + "s")
        if self.inbook:
            try:
                best, choice = self.get_book_move()
                if self.fun:
                    self.node.push(choice)
                    return choice
                else:
                    self.node.push(best)
                print(chess.pgn.Game.from_board(self.node)[-1])
            except IndexError:
                self.time_limit = self.time_limit*2
                best, _ = self.search()
                self.node.push(best)
                print(chess.pgn.Game.from_board(self.node)[-1])
                self.inbook = False
                self.time_limit = self.time_limit/2
        else:
            best, _ = self.search()
            self.node.push(best)
            print(chess.pgn.Game.from_board(self.node)[-1])
        # self.record_stack()
        self.endpoint += self.increment
        return best

    def user_move(self) -> str:
        move = input("enter move: ")
        while True:
            try:
                self.node.push_san(move)
                break
            except Exception:
                move = input("enter move: ")
        return move

    def display_ending(self) -> None:
        if self.node.is_stalemate():
            print('END BY STALEMATE')
        elif self.node.is_insufficient_material():
            print('END BY INSUFFICIENT MATERIAL')
        elif self.node.is_fivefold_repetition():
            print('END BY FIVEFOLD REPETITION')
        elif self.node.is_checkmate:
            print("BLACK" if self.node.turn == BLACK else "WHITE", 'WINS ON TURN',
                  self.node.fullmove_number)
        else:
            print('END BY UNKNOWN REASON')

    def run_game(self, indefinite=True) -> str:
        while not self.node.is_game_over():
            print(self.__repr__())
            if self.human and self.node.turn:
                try:
                    self.ponder()
                except KeyboardInterrupt:
                    self.node = self.origin
                    self.user_move()

            else:  # SWAP THESE ASAP

                self.engine_move()
            if not indefinite:
                break
        self.display_ending()
        try:
            return str(chess.pgn.Game.from_board(self.node)[-1])
        except Exception:
            return "PGN ERROR"

    def play_viri(self, fen=None):
        player_colour = input(
            "Enter the human player's colour in the form b/w\n--> ")
        while player_colour not in ['b', 'w']:
            player_colour = input(
                "Enter the human player's colour in the form b/w\n--> ")
        player_colour = WHITE if player_colour == 'w' else BLACK
        timeControl = int(
            input("how many seconds should viri get per move?\n--> "))
        self.__init__(human=True, time_limit=timeControl, fen=fen, book=True, fun=False)
        self.fun = False
        while not self.node.is_game_over():
            print(self.__repr__())
            if player_colour == self.node.turn:
                # try:
                #     self.ponder()
                # except KeyboardInterrupt:
                #     self.node = self.origin
                #     self.user_move()
                self.user_move()
            else:
                self.engine_move()
        self.display_ending()

    def perftx(self, n):
        if n == 0:
            self.nodes += 1
        else:
            for move in self.ordered_moves():
                self.node.push(move)
                self.perftx(n - 1)
                self.node.pop()

    def perft(self, n):
        self.nodes = 0
        self.perftx(n)
        print(self.nodes)

    def uci(self):
        start = input()
        while start != "uci":
            start = input()
        print("id", end="")
        while True:
            command = input()
            if command == "ucinewgame":
                board = Board()
            elif command.split()[0] == "position":
                fen = command[command.index(
                    "fen") + 3: command.index("moves") - 1]
                moves = command[command.index("moves"):].split()
                if fen == "startpos":
                    fen = 'rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1'
                board = Board(fen)
                for move in moves:
                    board.push(Move.from_uci(move))
    def say_last_move(game: chess.Board):
        """Take a chess.BitBoard instance and speaks the last move from it."""
        move_parts = {
            'K': 'king.ogg',
            'B': 'bishop.ogg',
            'N': 'knight.ogg',
            'R': 'rook.ogg',
            'Q': 'queen.ogg',
            'P': 'pawn.ogg',
            '+': '',
            '#': '',
            'x': 'takes.ogg',
            '=': 'promote.ogg',
            'a': 'a.ogg',
            'b': 'b.ogg',
            'c': 'c.ogg',
            'd': 'd.ogg',
            'e': 'e.ogg',
            'f': 'f.ogg',
            'g': 'g.ogg',
            'h': 'h.ogg',
            '1': '1.ogg',
            '2': '2.ogg',
            '3': '3.ogg',
            '4': '4.ogg',
            '5': '5.ogg',
            '6': '6.ogg',
            '7': '7.ogg',
            '8': '8.ogg'
        }

        bit_board = game.copy()
        move = bit_board.pop()
        san_move = bit_board.san(move)
        voice_parts = []

        if san_move.startswith('O-O-O'):
            voice_parts += ['castlequeenside.ogg']
        elif san_move.startswith('O-O'):
            voice_parts += ['castlekingside.ogg']
        else:
            for part in san_move:
                try:
                    sound_file = move_parts[part]
                except KeyError:
                    logging.warning('unknown char found in san: [%s : %s]', san_move, part)
                    sound_file = ''
                if sound_file:
                    voice_parts += [sound_file]

        if game.is_game_over():
            if game.is_checkmate():
                wins = 'whitewins.ogg' if game.turn == chess.BLACK else 'blackwins.ogg'
                voice_parts += ['checkmate.ogg', wins]
            elif game.is_stalemate():
                voice_parts += ['stalemate.ogg']
            else:
                if game.is_seventyfive_moves():
                    voice_parts += ['75moves.ogg', 'draw.ogg']
                elif game.is_insufficient_material():
                    voice_parts += ['material.ogg', 'draw.ogg']
                elif game.is_fivefold_repetition():
                    voice_parts += ['repetition.ogg', 'draw.ogg']
                else:
                    voice_parts += ['draw.ogg']
        elif game.is_check():
            voice_parts += ['check.ogg']

        if bit_board.is_en_passant(move):
            voice_parts += ['enpassant.ogg']

        return voice_parts
예제 #29
0
def main():
    """
    https://stackoverflow.com/a/19655992
    """

    # pylint: disable=too-many-branches

    input_queue = queue.Queue()
    input_thread = Thread(target=add_input, args=(input_queue,))
    input_thread.daemon = True
    input_thread.start()

    board = chess.Board(INITIAL)
    time_manager = TimeManager()
    LOGGER.debug("------ NEW SESSION ------")

    while True:
        if not input_queue.empty():
            smove = input_queue.get()
            LOGGER.warning("Command captured: %s", smove)
        else:
            if time_manager.done:
                move_to_go = time_manager.decision
                assert move_to_go is not None
                print('bestmove', move_to_go)
            continue

        if smove == 'quit':
            LOGGER.debug("UCI Quit called, Engine dismissed")
            break

        elif smove == "isready":
            print("readyok")

        elif smove == 'uci':
            print("id name Studienarbeitsengine")
            print("id author Daniel und Stefan")

        elif smove == "ucinewgame":
            board = Board()

        elif smove == "position startpos":
            board = Board()

        elif smove.startswith("position startpos moves"):
            moves = [Move.from_uci(uci=uci) for uci in smove.split(" ")[3:]]
            LOGGER.debug("Setting moves: %s", str(moves))
            board = Board()
            for move in moves:
                board.push(move)

        elif smove.startswith("go"):
            parts = smove.split(" ")[1:]
            LOGGER.debug("Parts of go: %s", parts)
            information = process_uci_time_information(*parts)\
                if len(parts) % 2 == 0 else {"infinite": True}
            time_manager.info_from_uci(**information)
            time_manager.perform_search(board.copy())

        elif smove.startswith("stop"):
            pass

        else:
            print("Error (unkown command):", smove)
예제 #30
0
def score(bord: chess.Board, move):
    scr = 0
    board = bord.copy()
    current_turn = board.turn

    #Material Value
    if board.is_capture(move):
        captured_piece = board.piece_type_at(move.to_square)

        for c in range(1, 7):
            if captured_piece == c:
                if current_turn == False:
                    #print("black caps white")
                    scr += COSTS[c - 1]
                    break
                else:
                    scr -= COSTS[c - 1]
                    #print("white caps black")
                    break

    #Pre-move misc
    # if board.is_check():
    #     if current_turn == 0:
    #         scr += 5
    #     else:
    #         scr -= 5

    #Freedom of board
    # board.push(move) #New board with testing move
    # possible_moves = board.legal_moves

    # if current_turn == 0:
    #     scr += len(list(possible_moves)) * 0.1
    # else:
    #     scr -= len(list(possible_moves)) * 0.1

    #Centre control
    # for sq in TRUE_CENTER:
    #     if board.piece_at(sq):
    #         pc = board.piece_at(sq)
    #         if pc.color == chess.WHITE:
    #             scr += 0.5
    #         else:
    #             scr -= 0.5
    #
    # for sq in EXTENDED_CENTER:
    #     if board.piece_at(sq):
    #         pc = board.piece_at(sq)
    #         if pc.color == chess.WHITE:
    #             scr += 0.3
    #         else:
    #             scr -= 0.3

    #Attacks and Attackers:
    # for square in chess.SQUARES:
    #
    #     if board.piece_at(square):
    #
    #         #TODO Pins
    #
    #         piece = board.piece_at(square)
    #         attacks = board.attacks(square)
    #         attackers = board.attackers(not piece.color, square)
    #
    #         for atk_sqr in attacks:
    #
    #             attacked_piece = board.piece_at(atk_sqr)
    #
    #             for d in range(1, 7):
    #                 try:
    #                     if attacked_piece.piece_type == d:
    #
    #                         if current_turn == 0:
    #                             scr += COSTS[d - 1] / 2
    #                             break
    #                         else:
    #                             scr -= COSTS[d - 1] / 2
    #                             break
    #
    #                 except AttributeError:
    #                     pass

    return scr
예제 #31
0
    def say_last_move(game: chess.Board):
        """Take a chess.BitBoard instance and speaks the last move from it."""

        PicoTalkerDisplay.c_taken = False  #molli
        PicoTalkerDisplay.c_castle = False  #molli
        PicoTalkerDisplay.c_knight = False  #molli
        PicoTalkerDisplay.c_rook = False  #molli
        PicoTalkerDisplay.c_king = False  #molli
        PicoTalkerDisplay.c_bishop = False  #molli
        PicoTalkerDisplay.c_pawn = False  #molli
        PicoTalkerDisplay.c_queen = False  #molli
        PicoTalkerDisplay.c_check = False  #molli
        PicoTalkerDisplay.c_mate = False  #molli
        PicoTalkerDisplay.c_stalemate = False  #molli
        PicoTalkerDisplay.c_draw = False  #molli

        move_parts = {
            'K': 'king.ogg',
            'B': 'bishop.ogg',
            'N': 'knight.ogg',
            'R': 'rook.ogg',
            'Q': 'queen.ogg',
            'P': 'pawn.ogg',
            '+': '',
            '#': '',
            'x': 'takes.ogg',
            '=': 'promote.ogg',
            'a': 'a.ogg',
            'b': 'b.ogg',
            'c': 'c.ogg',
            'd': 'd.ogg',
            'e': 'e.ogg',
            'f': 'f.ogg',
            'g': 'g.ogg',
            'h': 'h.ogg',
            '1': '1.ogg',
            '2': '2.ogg',
            '3': '3.ogg',
            '4': '4.ogg',
            '5': '5.ogg',
            '6': '6.ogg',
            '7': '7.ogg',
            '8': '8.ogg'
        }

        bit_board = game.copy()
        move = bit_board.pop()
        san_move = bit_board.san(move)
        voice_parts = []

        if san_move.startswith('O-O-O'):
            voice_parts += ['castlequeenside.ogg']
            PicoTalkerDisplay.c_castle = True
        elif san_move.startswith('O-O'):
            voice_parts += ['castlekingside.ogg']
            PicoTalkerDisplay.c_castle = True
        else:
            for part in san_move:
                try:
                    sound_file = move_parts[part]
                except KeyError:
                    logging.warning('unknown char found in san: [%s : %s]',
                                    san_move, part)
                    sound_file = ''
                if sound_file:
                    voice_parts += [sound_file]
                    if sound_file == 'takes.ogg':
                        PicoTalkerDisplay.c_taken = True  #molli
                    elif sound_file == 'knight.ogg':
                        PicoTalkerDisplay.c_knight = True  #molli
                    elif sound_file == 'king.ogg':
                        PicoTalkerDisplay.c_king = True  #molli
                    elif sound_file == 'rook.ogg':
                        PicoTalkerDisplay.c_rook = True  #molli
                    elif sound_file == 'pawn.ogg':
                        PicoTalkerDisplay.c_pawn = True  #molli
                    elif sound_file == 'bishop.ogg':
                        PicoTalkerDisplay.c_bishop = True  #molli
                    elif sound_file == 'queen.ogg':
                        PicoTalkerDisplay.c_queen = True  #molli

        if game.is_game_over():
            if game.is_checkmate():
                wins = 'whitewins.ogg' if game.turn == chess.BLACK else 'blackwins.ogg'
                voice_parts += ['checkmate.ogg', wins]
                PicoTalkerDisplay.c_mate = True
            elif game.is_stalemate():
                voice_parts += ['stalemate.ogg']
                PicoTalkerDisplay.c_stalemate = True
            else:
                PicoTalkerDisplay.c_draw = True
                if game.is_seventyfive_moves():
                    voice_parts += ['75moves.ogg', 'draw.ogg']
                elif game.is_insufficient_material():
                    voice_parts += ['material.ogg', 'draw.ogg']
                elif game.is_fivefold_repetition():
                    voice_parts += ['repetition.ogg', 'draw.ogg']
                else:
                    voice_parts += ['draw.ogg']
        elif game.is_check():
            voice_parts += ['check.ogg']
            PicoTalkerDisplay.c_check = True

        if bit_board.is_en_passant(move):
            voice_parts += ['enpassant.ogg']

        return voice_parts