예제 #1
0
def cook_advantage(engine: SimpleEngine, node: GameNode, winner: Color) -> Optional[List[NextMovePair]]:

    is_capture = "x" in node.san() # monkaS
    up_in_material = is_up_in_material(node.board(), winner)

    if node.board().is_repetition(2):
        logger.info("Found repetition, canceling")
        return None

    # if not is_capture and up_in_material and len(node.board().checkers()) == 0:
    #     logger.info("Not a capture and we're up in material, end of the line")
    #     return []

    next = get_next_move(engine, node, winner)

    if not next:
        logger.debug("No next move")
        return []

    if next.best.score.is_mate():
        logger.info("Expected advantage, got mate?!")
        return None

    follow_up = cook_advantage(engine, node.add_main_variation(next.best.move), winner)

    if follow_up is None:
        return None

    return [next] + follow_up
예제 #2
0
def analyze_position(server: Server, engine: SimpleEngine, node: GameNode, prev_score: Score, current_eval: PovScore) -> Union[Puzzle, Score]:

    board = node.board()
    winner = board.turn
    score = current_eval.pov(winner)

    if board.legal_moves.count() < 2:
        return score

    game_url = node.game().headers.get("Site")

    logger.debug("{} {} to {}".format(node.ply(), node.move.uci() if node.move else None, score))

    if prev_score > Cp(400):
        logger.debug("{} Too much of a winning position to start with {} -> {}".format(node.ply(), prev_score, score))
        return score
    if is_up_in_material(board, winner):
        logger.debug("{} already up in material {} {} {}".format(node.ply(), winner, material_count(board, winner), material_count(board, not winner)))
        return score
    elif score >= Mate(1) and not allow_one_mover:
        logger.debug("{} mate in one".format(node.ply()))
        return score
    elif score > mate_soon:
        logger.info("Mate {}#{} Probing...".format(game_url, node.ply()))
        if server.is_seen_pos(node):
            logger.info("Skip duplicate position")
            return score
        mate_solution = cook_mate(engine, copy.deepcopy(node), winner)
        server.set_seen(node.game())
        return Puzzle(node, mate_solution) if mate_solution is not None else score
    elif score >= Cp(0) and win_chances(score) > win_chances(prev_score) + 0.5:
        if score < Cp(400) and material_diff(board, winner) > -1:
            logger.info("Not clearly winning and not from being down in material, aborting")
            return score
        logger.info("Advantage {}#{} {} -> {}. Probing...".format(game_url, node.ply(), prev_score, score))
        if server.is_seen_pos(node):
            logger.info("Skip duplicate position")
            return score
        puzzle_node = copy.deepcopy(node)
        solution : Optional[List[NextMovePair]] = cook_advantage(engine, puzzle_node, winner)
        server.set_seen(node.game())
        if not solution:
            return score
        while len(solution) % 2 == 0 or not solution[-1].second:
            if not solution[-1].second:
                logger.info("Remove final only-move")
            solution = solution[:-1]
        if not solution or (len(solution) == 1 and not allow_one_mover):
            logger.info("Discard one-mover")
            return score
        last = list(puzzle_node.mainline())[len(solution)]
        gain = material_diff(last.board(), winner) - material_diff(board, winner)
        if gain > 1 or (
            len(solution) == 1 and 
            win_chances(solution[0].best.score) > win_chances(solution[0].second.score) + 0.5):
            return Puzzle(node, [p.best.move for p in solution])
        return score
    else:
        return score
예제 #3
0
def is_advanced_pawn_move(node: GameNode) -> bool:
    if node.move.promotion:
        return True
    if moved_piece_type(node) != chess.PAWN:
        return False
    to_rank = square_rank(node.move.to_square)
    return to_rank < 3 if node.turn() else to_rank > 4
예제 #4
0
def get_next_move_pair(engine: SimpleEngine, node: GameNode, winner: Color, limit: chess.engine.Limit) -> NextMovePair:
    info = engine.analyse(node.board(), multipv = 2, limit = limit)
    global nps
    nps.append(info[0]["nps"] / 1000)
    nps = nps[-10000:]
    # print(info)
    best = EngineMove(info[0]["pv"][0], info[0]["score"].pov(winner))
    second = EngineMove(info[1]["pv"][0], info[1]["score"].pov(winner)) if len(info) > 1 else None
    return NextMovePair(node, winner, best, second)
예제 #5
0
def cook_advantage(engine: SimpleEngine, node: GameNode,
                   winner: Color) -> Optional[List[Move]]:
    """
    Recursively calculate advantage solution
    """

    best_move, second_move = get_two_best_moves(engine, node.board(), winner)

    if node.board().turn == winner:
        logger.debug("Getting only advantage move...")

        if best_move.score < juicy_advantage:
            logger.info(
                "Best move is not a juicy advantage, we're probably not searching deep enough"
            )
            return None

        if second_move is not None and second_move.score > Cp(-300):
            logger.debug("Second best move is not terrible")
            return None

    else:
        logger.debug("Getting defensive move...")

        if best_move.score.is_mate():
            logger.info("Expected advantage, got mate?!")
            return None

        if second_move is not None:
            much_worse = second_move.score < Mate(2)
            if not much_worse:
                logger.info("A second defensive move is not that worse")
                return None

    next_moves = cook_advantage(engine,
                                node.add_main_variation(best_move.move),
                                winner)

    if next_moves is None:
        return None

    return [best_move.move] + next_moves
예제 #6
0
def get_next_move(engine: SimpleEngine, node: GameNode, winner: Color) -> Optional[NextMovePair]:
    board = node.board()
    pair = get_next_move_pair(engine, node, winner, get_move_limit)
    logger.debug("{} {} {}".format("attack" if board.turn == winner else "defense", pair.best, pair.second))
    if board.turn == winner and not is_valid_attack(pair):
        logger.debug("No valid attack {}".format(pair))
        return None
    if board.turn != winner and not is_valid_defense(pair):
        logger.debug("No valid defense {}".format(pair))
        return None
    return pair
예제 #7
0
def cook_mate(engine: SimpleEngine, node: GameNode,
              winner: Color) -> Optional[List[Move]]:
    """
    Recursively calculate mate solution
    """

    if node.board().is_game_over():
        return []

    best_move, second_move = get_two_best_moves(engine, node.board(), winner)

    if node.board().turn == winner:
        logger.debug("Getting only mate move...")

        if best_move.score < mate_soon:
            logger.info(
                "Best move is not a mate, we're probably not searching deep enough"
            )
            return None

        if second_move is not None and second_move.score > Cp(-300):
            logger.debug("Second best move is not terrible")
            return None

    else:
        logger.debug("Getting defensive move...")

        if best_move.score < Mate(1) and second_move is not None:
            much_worse = second_move.score == Mate(
                1) and best_move.score < Mate(3)
            if not much_worse:
                logger.info("A second defensive move is not that worse")
                return None

    next_moves = cook_mate(engine, node.add_main_variation(best_move.move),
                           winner)

    if next_moves is None:
        return None

    return [best_move.move] + next_moves
예제 #8
0
def cook_mate(engine: SimpleEngine, node: GameNode, winner: Color) -> Optional[List[Move]]:

    if node.board().is_game_over():
        return []

    pair = get_next_move(engine, node, winner)

    if not pair:
        return None

    next = pair.best

    if next.score < mate_soon:
        logger.info("Best move is not a mate, we're probably not searching deep enough")
        return None

    follow_up = cook_mate(engine, node.add_main_variation(next.move), winner)

    if follow_up is None:
        return None

    return [next.move] + follow_up
예제 #9
0
def moved_piece_type(node: GameNode) -> chess.PieceType:
    return node.board().piece_type_at(node.move.to_square)
예제 #10
0
def is_pawn_move(node: GameNode) -> bool:
    return node.board().piece_type_at(node.move.to_square) == chess.PAWN