zhelyabuzhsky / stockfish

Integrates the Stockfish chess engine with Python
https://pypi.python.org/pypi/stockfish
MIT License
275 stars 56 forks source link

stockfish derivatives not work when analyze pgn #94

Closed sugizo closed 1 year ago

sugizo commented 2 years ago

stockfish derivatives (like fairy_stockfish and brainfish) not work when analyze pgn

import chess.pgn
import time
from IPython.display import display, HTML, clear_output
from stockfish import Stockfish

def who(player):
    return "White" if player == chess.WHITE else "Black"

def analyse(engine, fen):
    stockfish = Stockfish(path = engine, depth = 1, 
                          parameters = {'Contempt': 0, 'Debug Log File': '', 'Hash': 0, 
                                        'Min Split Depth': 0, 'Minimum Thinking Time': 10, 
                                        'Move Overhead': 0, 'MultiPV': 0, 'Ponder': 'false', 
                                        'Skill Level': 0, 'Slow Mover': 0, 'Threads': 0, 
                                        'UCI_Chess960': 'false', 'UCI_Elo': 0, 
                                        'UCI_LimitStrength': 'false'} )

    stockfish.set_fen_position(fen)

    evaluation = stockfish.get_evaluation()
    best_move = stockfish.get_best_move(wtime = 0, btime = 0)
    best_move_time = stockfish.get_best_move_time(0)
    top_moves = stockfish.get_top_moves(3)

    info = {}
    info["evaluation"] = evaluation
    info["best_move"] = best_move
    info["best_move_time"] = best_move_time
    info["top_moves"] = top_moves
    return info

def brainfish_analyse(fen):
    info = analyse("engine/brainfish", fen)
    return info

def fairy_stockfish_analyse(fen):
    info = analyse("engine/fairy_stockfish", fen)
    return info

def stockfish_analyse(fen):
    info = analyse("engine/stockfish", fen)
    return info

def play_pgn(file_pgn, analyse, sleep = 9.9, visual = "svg"):
    """
    visual: "simple" | "svg" | None
    """
    use_svg = (visual == "svg")
    pgn = open(file_pgn)
    read_game = chess.pgn.read_game(pgn)
    read_game_mainline_moves = read_game.mainline_moves()
    board = read_game.board()
    game = chess.pgn.Game()

    try:
        for uci in read_game_mainline_moves:
            board.push(uci)

            if board.is_checkmate():
                html = """
                <table>
                <tr>
                <th rowspan = "7" style = "width:45%%;text-align: center">%s</th>
                <th style = "text-align: left">Board</th>
                <td style = "text-align: left"><pre>%s</pre></td>
                </tr>
                <tr>
                <th style = "text-align: left">Move</th>
                <td style = "text-align: left">UCI : %s</td>
                </tr>
                </table>
                """ % (board._repr_svg_(), board, uci)
            else:
                info = analyse(fen)
                if info:
                    evaluation_type = info['evaluation']['type'] if info['evaluation'] else ''
                    evaluation_value = info['evaluation']['value'] if info['evaluation'] else ''
                    best_move = info['best_move']
                    best_move_time = info['best_move_time']
                    top_moves = "<br />".join(str(i).replace('{', '').replace('}', '').replace('\'', '') for i in info['top_moves'] )
                else: 
                    evaluation_type = ''
                    evaluation_value = ''
                    best_move = ''
                    best_move_time = ''
                    top_moves = ''
                html = """
                <table>
                <tr>
                <th rowspan = "11" style = "width:45%%;text-align: center">%s</th>
                <th style = "text-align: left">Board</th>
                <td style = "text-align: left"><pre>%s</pre></td>
                </tr>
                <tr>
                <th style = "text-align: left">Move</th>
                <td style = "text-align: left">UCI : %s</td>
                </tr>
                <tr>
                <th style = "text-align: left">Evaluation</th>
                <td style = "text-align: left">Type : %s<br />Value : %s</td>
                </tr>
                <tr>
                <th style = "text-align: left">Best Move</th>
                <td style = "text-align: left">%s</td>
                </tr>
                <tr>
                <th style = "text-align: left">Best Move Time</th>
                <td style = "text-align: left">%s</td>
                </tr>
                <tr>
                <th style = "text-align: left">Top Moves</th>
                <td style = "text-align: left">%s</td>
                </tr>
                </table>
                """ % (board._repr_svg_(), board, uci, evaluation_type, 
                       evaluation_value, best_move, best_move_time, top_moves)

            if visual is not None:
                if visual == "svg":
                    clear_output(wait = True)

                display(HTML(html) )

                if visual == "svg":
                    time.sleep(sleep)

    except KeyboardInterrupt:
        msg = "Game Interrupted!"
        return (msg, board)

    if board.is_checkmate():
        msg = "Checkmate : " + who(not board.turn) + " Wins!"
    else:
        msg = "Draw"

    if visual is not None:
        print(msg)
        print('PGN File :', file_pgn)
        print('SVG File :', file_svg)

# choose : stockfish_analyse
# brainfish_analyse, fairy_stockfish_analyse = looping forever 
play_pgn("pgn/chess-play.pgn", stockfish_analyse, sleep = 0.0)

no error occured but the result is not expected (looping forever)

kieferro commented 2 years ago

I can't reproduce your bug because I don't have the PGN file you used as input. It would be very useful for debugging if you could find a specific position for which Stockfish does not output anything and then post it here. Have you ever tried to analyze the position yourself using the Stockfish-only application in the terminal? If it works fine there, then logically it's a wrapper issue. However, I rather suspect that it's a problem of Stockfish itself, because I've had similar problems as well (Issues regarding this topic: https://github.com/official-stockfish/Stockfish/issues/4023, https://github.com/official-stockfish/Stockfish/issues/3911). Especially Stockfish 15 often gets stuck in endless loops.

sugizo commented 2 years ago

work both single line and pgn but when using python module stockfish is slower compare to chess python module (chess.engine() )

$ ~/Downloads/chess/engine/stockfish -v
Stockfish 11 64 by T. Romstad, M. Costalba, J. Kiiski, G. Linscott

work when analyze single line fen but looping forever when analyze pgn (multi moves)

$ ~/Downloads/chess/engine/brainfish -v
Brainfish 260522 64 by Thomas Zipproth
$ ~/Downloads/chess/engine/fairy_stockfish -v
Fairy-Stockfish 11.2 LB 64 by Fabian Fichter

not work at all (wheter single line fen or pgn (multi moves) )

$ ~/Downloads/chess/engine/asmfishx -v
asmFishX_10_base
$ ~/Downloads/chess/engine/stockfish_mv -v
Stockfish 2018-11-29 64 Multi-Variant by D. Dugovic, F. Fichter et al

here the chess-play.pgn for test above

[Event "chess"]
[Site "localhost"]
[Date "2022-05-31"]
[Round "1"]
[White "first_player"]
[Black "wyldchess_player"]
[Result "0-1"]

1. Nh3 e5 2. Ng5 Qxg5 3. Rg1 Bc5 4. Rh1 Qf4 5. Rg1 Bxf2# 0-1

environment

n.b all engine is work fine using python chess module (chess.engine() )

johndoknjas commented 2 years ago

best_move = stockfish.get_best_move(wtime = 0, btime = 0) best_move_time = stockfish.get_best_move_time(0)

Your code is hanging on these two lines. If you run Stockfish directly and send 0 for movetime (or 0 for wtime/btime), it also doesn't stop searching.

sugizo commented 2 years ago

already tried like on pypi

best_move = stockfish.get_best_move(wtime = 1000, btime = 1000)
best_move_time = stockfish.get_best_move_time(1000)

still got the same result, that's why, try to change parameter hopefully can change the result but the result is same (looping forever)

best_move = stockfish.get_best_move(wtime = 0, btime = 0)
best_move_time = stockfish.get_best_move_time(0)

btw the 0 value run well when using stockfish

$ ~/Downloads/chess/engine/stockfish -v
Stockfish 11 64 by T. Romstad, M. Costalba, J. Kiiski, G. Linscott
johndoknjas commented 2 years ago

Not sure then, since when using 1000 for the time values, it doesn't loop forever for me (using the wrapper for both SF 15 and SF 11).

For running stockfish directly, 0 values also run fine for me on Stockfish 11 (but not on Stockfish 15). Using the wrapper with 0 values has the expected result - fine on SF 11, not on SF 15.

sugizo commented 2 years ago

trying to minimalize in thought that pgn must do recursive job, so perhaps it cause looping forever then here are code just to analyze the fen with the suggestion from other to use default value as parameter and depth around 25

result no error occured, but the time elapse for execution time is take longer than python-chess

image

code

import chess
import time
from IPython.display import display, HTML, clear_output
from stockfish import Stockfish

def analyse(engine, fen):
    st = time.time()
    stockfish = Stockfish(path = engine, depth = 25)

    stockfish.set_fen_position(fen)

    evaluation = stockfish.get_evaluation()
    best_move = stockfish.get_best_move(wtime = 1000, btime = 1000)
    best_move_time = stockfish.get_best_move_time(1000)
    top_moves = stockfish.get_top_moves(3)

    info = {}
    info["evaluation"] = evaluation
    info["best_move"] = best_move
    info["best_move_time"] = best_move_time
    info["top_moves"] = top_moves
    et = time.time()
    elapsed_time = et - st
    return info, elapsed_time

def brainfish_analyse(fen):
    engine = "engine/brainfish"
    info, elapsed_time = analyse(engine, fen)
    return info, elapsed_time, engine

def fairy_stockfish_analyse(fen):
    engine = "engine/fairy_stockfish"
    info, elapsed_time = analyse(engine, fen)
    return info, elapsed_time, engine

def stockfish_analyse(fen):
    engine = "engine/stockfish"
    info, elapsed_time = analyse(engine, fen)
    return info, elapsed_time, engine

def view_epd(engine):
    fen = input("FEN : ") or "r1bqkbnr/p1pp1ppp/1pn5/4p3/2B1P3/5Q2/PPPP1PPP/RNB1K1NR w KQkq - 2 4"

    board = chess.Board(fen)

    if engine == 0 or board.is_checkmate():
        html = """
        <table>
        <tr>
        <th rowspan = "9" style = "width:45%%;text-align: center">%s</th>
        <th style = "text-align: left">Board</th>
        <td style = "text-align: left"><pre>%s</pre></td>
        </tr>
        </table>
        """ % (board._repr_svg_(), board)
    else:
        if engine == 1:
            info, elapsed_time, engine = brainfish_analyse(fen)
        elif engine == 2:
            info, elapsed_time, engine = fairy_stockfish_analyse(fen)
        elif engine == 3:
            info, elapsed_time, engine = stockfish_analyse(fen)

        if info:
            evaluation_type = info['evaluation']['type']
            evaluation_value = info['evaluation']['value']
            best_move = info['best_move']
            best_move_time = info['best_move_time']
            top_moves = "<br />".join(str(i).replace('{', '').replace('}', '').replace('\'', '') for i in info['top_moves'] )
        else: 
            evaluation_type = ''
            evaluation_value = ''
            best_move = ''
            best_move_time = ''
            top_moves = ''
        html = """
        <table>
        <tr>
        <th rowspan = "9" style = "width:45%%;text-align: center">%s</th>
        <th style = "text-align: left">Board</th>
        <td style = "text-align: left"><pre>%s</pre></td>
        </tr>
        <tr>
        <th style = "text-align: left">Evaluation</th>
        <td style = "text-align: left">Type : %s<br />Value : %s</td>
        </tr>
        <tr>
        <th style = "text-align: left">Best Move</th>
        <td style = "text-align: left">%s</td>
        </tr>
        <tr>
        <th style = "text-align: left">Best Move Time</th>
        <td style = "text-align: left">%s</td>
        </tr>
        <tr>
        <th style = "text-align: left">Top Moves</th>
        <td style = "text-align: left">%s</td>
        </tr>
        <tr>
        <th style = "text-align: left">Engine</th>
        <td style = "text-align: left">%s</td>
        </tr>
        <tr>
        <th style = "text-align: left">Elapsed Time Execution</th>
        <td style = "text-align: left">%s</td>
        </tr>
        </table>
        """ % (board._repr_svg_(), board, evaluation_type, evaluation_value, 
               best_move, best_move_time, top_moves, engine, elapsed_time)

    clear_output(wait = True)
    display(HTML(html) )

# choose between : brainfish_analyse (1), fairy_stockfish_analyse (2), stockfish_analyse (3)
view_epd(2)

environment

again the first code above on initial issue is work for stockfish but not it's derivatives

sugizo commented 2 years ago

same logic like above but this one using ipywidgets, result more slower than above image

code

import chess
import time
from IPython.display import display, HTML, clear_output
from stockfish import Stockfish
from ipywidgets import Text, Dropdown, interact

def analyse(engine, fen):
    st = time.time()
    stockfish = Stockfish(path = engine, depth = 25)

    stockfish.set_fen_position(fen)

    evaluation = stockfish.get_evaluation()
    best_move = stockfish.get_best_move(wtime = 1000, btime = 1000)
    best_move_time = stockfish.get_best_move_time(1000)
    top_moves = stockfish.get_top_moves(3)

    info = {}
    info["evaluation"] = evaluation
    info["best_move"] = best_move
    info["best_move_time"] = best_move_time
    info["top_moves"] = top_moves
    et = time.time()
    elapsed_time = et - st
    return info, elapsed_time

def brainfish_analyse(fen):
    engine = "engine/brainfish"
    info, elapsed_time = analyse(engine, fen)
    return info, elapsed_time, engine

def fairy_stockfish_analyse(fen):
    engine = "engine/fairy_stockfish"
    info, elapsed_time = analyse(engine, fen)
    return info, elapsed_time, engine

def stockfish_analyse(fen):
    engine = "engine/stockfish"
    info, elapsed_time = analyse(engine, fen)
    return info, elapsed_time, engine

text_fen = Text(description = 'FEN', 
                value = 'r1bqkbnr/p1pp1ppp/1pn5/4p3/2B1P3/5Q2/PPPP1PPP/RNB1K1NR w KQkq - 2 4', 
                disabled = False, )

dropdown_engine = Dropdown(options = [('Nothing', 0), ('brainfish', 1), 
                                      ('fairy_stockfish', 2), ('stockfish', 3), 
                                     ], 
                           description = 'Engine', disabled = False, )

@interact(fen = text_fen, engine = dropdown_engine)
def view_epd(fen, engine):
    board = chess.Board(fen)

    if engine == 0 or board.is_checkmate():
        html = """
        <table>
        <tr>
        <th rowspan = "9" style = "width:45%%;text-align: center">%s</th>
        <th style = "text-align: left">Board</th>
        <td style = "text-align: left"><pre>%s</pre></td>
        </tr>
        </table>
        """ % (board._repr_svg_(), board)
    else:
        if engine == 1:
            info, elapsed_time, engine = brainfish_analyse(fen)
        elif engine == 2:
            info, elapsed_time, engine = fairy_stockfish_analyse(fen)
        elif engine == 3:
            info, elapsed_time, engine = stockfish_analyse(fen)

        if info:
            evaluation_type = info['evaluation']['type']
            evaluation_value = info['evaluation']['value']
            best_move = info['best_move']
            best_move_time = info['best_move_time']
            top_moves = "<br />".join(str(i).replace('{', '').replace('}', '').replace('\'', '') for i in info['top_moves'] )
        else: 
            evaluation_type = ''
            evaluation_value = ''
            best_move = ''
            best_move_time = ''
            top_moves = ''
        html = """
        <table>
        <tr>
        <th rowspan = "9" style = "width:45%%;text-align: center">%s</th>
        <th style = "text-align: left">Board</th>
        <td style = "text-align: left"><pre>%s</pre></td>
        </tr>
        <tr>
        <th style = "text-align: left">Evaluation</th>
        <td style = "text-align: left">Type : %s<br />Value : %s</td>
        </tr>
        <tr>
        <th style = "text-align: left">Best Move</th>
        <td style = "text-align: left">%s</td>
        </tr>
        <tr>
        <th style = "text-align: left">Best Move Time</th>
        <td style = "text-align: left">%s</td>
        </tr>
        <tr>
        <th style = "text-align: left">Top Moves</th>
        <td style = "text-align: left">%s</td>
        </tr>
        <tr>
        <th style = "text-align: left">Engine</th>
        <td style = "text-align: left">%s</td>
        </tr>
        <tr>
        <th style = "text-align: left">Elapsed Time Execution</th>
        <td style = "text-align: left">%s</td>
        </tr>
        </table>
        """ % (board._repr_svg_(), board, evaluation_type, evaluation_value, 
               best_move, best_move_time, top_moves, engine, elapsed_time)

    clear_output(wait = True)
    display(HTML(html) )

quick deduction (pls correct if wrong) during analyze *.pgn, it take move by move, so it's seems like not work, or looping forever while the truth is seems like the process is slower compare to python-chess

sugizo commented 2 years ago

same logic like above but this using python chess with default parameters (same engine version, without ipywidget) result image

code

import chess
import chess.engine
import time
from IPython.display import display, HTML, clear_output

def analyse(engine, board):
    st = time.time()
    with chess.engine.SimpleEngine.popen_uci(engine) as engine:
        info = engine.analyse(board, chess.engine.Limit() )
    clear_output(wait = True)
    et = time.time()
    elapsed_time = et - st
    return info, elapsed_time

def brainfish_analyse(board):
    engine = "engine/brainfish"
    info, elapsed_time = analyse(engine, board)
    return info, elapsed_time, engine

def fairy_stockfish_analyse(board):
    engine = "engine/fairy_stockfish"
    info, elapsed_time = analyse(engine, board)
    return info, elapsed_time, engine

def stockfish_analyse(board):
    engine = "engine/stockfish"
    info, elapsed_time = analyse(engine, board)
    return info, elapsed_time, engine

def view_fen(engine):
    fen = input("FEN : ") or "r1bqkbnr/p1pp1ppp/1pn5/4p3/2B1P3/5Q2/PPPP1PPP/RNB1K1NR w KQkq - 2 4"

    board = chess.Board(fen)

    if engine == 0 or board.is_checkmate():
        html = """
        <table>
        <tr>
        <th rowspan = "9" style = "width:45%%;text-align: center">%s</th>
        <th style = "text-align: left">Board</th>
        <td style = "text-align: left"><pre>%s</pre></td>
        </tr>
        </table>
        """ % (board._repr_svg_(), board)
    else:
        if engine == 1:
            info, elapsed_time, engine = brainfish_analyse(board)
        elif engine == 2:
            info, elapsed_time, engine = fairy_stockfish_analyse(board)
        elif engine == 3:
            info, elapsed_time, engine = stockfish_analyse(board)

        if info:
            score = info['score']
            info_pv_uci = [info_pv.uci() for info_pv in info['pv'] ]
            pv = ", ".join(info_pv_uci)
            nodes = info['nodes']
            depth = info['depth']
            info_time = info['time']
        else: 
            score = ''
            pv = ''
            nodes = ''
            depth = ''
            info_time = ''
        html = """
        <table>
        <tr>
        <th rowspan = "9" style = "width:45%%;text-align: center">%s</th>
        <th style = "text-align: left">Board</th>
        <td style = "text-align: left"><pre>%s</pre></td>
        </tr>
        <tr>
        <th style = "text-align: left">Score</th>
        <td style = "text-align: left">%s</td>
        </tr>
        <tr>
        <th style = "text-align: left">PV</th>
        <td style = "text-align: left">%s</td>
        </tr>
        <tr>
        <th style = "text-align: left">Depth</th>
        <td style = "text-align: left">%s</td>
        </tr>
        <tr>
        <th style = "text-align: left">Nodes</th>
        <td style = "text-align: left">%s</td>
        </tr>
        <tr>
        <th style = "text-align: left">Time</th>
        <td style = "text-align: left">%s</td>
        </tr>
        <tr>
        <th style = "text-align: left">Engine</th>
        <td style = "text-align: left">%s</td>
        </tr>
        <tr>
        <th style = "text-align: left">Elapsed Time Execution</th>
        <td style = "text-align: left">%s</td>
        </tr>
        </table>
        """ % (board._repr_svg_(), board, score, pv, depth, 
               nodes, info_time, engine, elapsed_time)

    clear_output(wait = True)
    display(HTML(html) )

# choose between : brainfish_analyse (1), fairy_stockfish_analyse (2), stockfish_analyse (3)
view_fen(2)

different elapsed time execution from picture aboves (without ipywidget)

stockfish module = 100.6
chess module = 0.1

that's why put the thinkable parameters that can make stockfish module faster by set it to 0, except for depth = 1 and 'Minimum Thinking Time': 10

sugizo commented 2 years ago

this one python chess with ipywidget, slower than without ipywidgets but faster compare to stockfish module result image

code

import chess
import chess.engine
import time
from IPython.display import display, HTML, clear_output
from ipywidgets import Text, Dropdown, interact

def analyse(engine, board):
    st = time.time()
    with chess.engine.SimpleEngine.popen_uci(engine) as engine:
        info = engine.analyse(board, chess.engine.Limit() )
    clear_output(wait = True)
    et = time.time()
    elapsed_time = et - st
    return info, elapsed_time

def brainfish_analyse(board):
    engine = "engine/brainfish"
    info, elapsed_time = analyse(engine, board)
    return info, elapsed_time, engine

def fairy_stockfish_analyse(board):
    engine = "engine/fairy_stockfish"
    info, elapsed_time = analyse(engine, board)
    return info, elapsed_time, engine

def stockfish_analyse(board):
    engine = "engine/stockfish"
    info, elapsed_time = analyse(engine, board)
    return info, elapsed_time, engine

text_fen = Text(description = 'FEN', 
                value = 'r1bqkbnr/p1pp1ppp/1pn5/4p3/2B1P3/5Q2/PPPP1PPP/RNB1K1NR w KQkq - 2 4', 
                disabled = False, )

dropdown_engine = Dropdown(options = [('Nothing', 0), ('brainfish', 1), 
                                      ('fairy_stockfish', 2), ('stockfish', 3), 
                                     ], 
                           description = 'Engine', disabled = False, )

@interact(fen = text_fen, engine = dropdown_engine)
def view_fen(fen, engine):
    board = chess.Board(fen)

    if engine == 0 or board.is_checkmate():
        html = """
        <table>
        <tr>
        <th rowspan = "9" style = "width:45%%;text-align: center">%s</th>
        <th style = "text-align: left">Board</th>
        <td style = "text-align: left"><pre>%s</pre></td>
        </tr>
        </table>
        """ % (board._repr_svg_(), board)
    else:
        if engine == 1:
            info, elapsed_time, engine = brainfish_analyse(board)
        elif engine == 2:
            info, elapsed_time, engine = fairy_stockfish_analyse(board)
        elif engine == 3:
            info, elapsed_time, engine = stockfish_analyse(board)

        if info:
            score = info['score']
            info_pv_uci = [info_pv.uci() for info_pv in info['pv'] ]
            pv = ", ".join(info_pv_uci)
            nodes = info['nodes']
            depth = info['depth']
            info_time = info['time']
        else: 
            score = ''
            pv = ''
            nodes = ''
            depth = ''
            info_time = ''
        html = """
        <table>
        <tr>
        <th rowspan = "9" style = "width:45%%;text-align: center">%s</th>
        <th style = "text-align: left">Board</th>
        <td style = "text-align: left"><pre>%s</pre></td>
        </tr>
        <tr>
        <th style = "text-align: left">Score</th>
        <td style = "text-align: left">%s</td>
        </tr>
        <tr>
        <th style = "text-align: left">PV</th>
        <td style = "text-align: left">%s</td>
        </tr>
        <tr>
        <th style = "text-align: left">Depth</th>
        <td style = "text-align: left">%s</td>
        </tr>
        <tr>
        <th style = "text-align: left">Nodes</th>
        <td style = "text-align: left">%s</td>
        </tr>
        <tr>
        <th style = "text-align: left">Time</th>
        <td style = "text-align: left">%s</td>
        </tr>
        <tr>
        <th style = "text-align: left">Engine</th>
        <td style = "text-align: left">%s</td>
        </tr>
        <tr>
        <th style = "text-align: left">Elapsed Time Execution</th>
        <td style = "text-align: left">%s</td>
        </tr>
        </table>
        """ % (board._repr_svg_(), board, score, pv, depth, 
               nodes, info_time, engine, elapsed_time)

    clear_output(wait = True)
    display(HTML(html) )

difference chess and stockfish with ipywidget in elapse time execution

stockfish : 121.5
chess : 0.2

question any recomendation to set the parameter on stockfish ?

sugizo commented 2 years ago

error traceback non ipywidget by keyboard interupted

---------------------------------------------------------------------------
KeyboardInterrupt                         Traceback (most recent call last)
<ipython-input-6-f61a2964a72e> in <module>
      2 # brainfish_analyse, fairy_stockfish_analyse = looping forever
      3 
----> 4 play_pgn("pgn/chess-play.pgn", brainfish_analyse, sleep = 0.0)

~/opt/miniconda3/envs/python3_test/lib/python3.7/site-packages/IPython/core/displayhook.py in __call__(self, result)
    260             self.start_displayhook()
    261             self.write_output_prompt()
--> 262             format_dict, md_dict = self.compute_format_data(result)
    263             self.update_user_ns(result)
    264             self.fill_exec_result(result)

~/opt/miniconda3/envs/python3_test/lib/python3.7/site-packages/IPython/core/displayhook.py in compute_format_data(self, result)
    149 
    150         """
--> 151         return self.shell.display_formatter.format(result)
    152 
    153     # This can be set to True by the write_output_prompt method in a subclass

~/opt/miniconda3/envs/python3_test/lib/python3.7/site-packages/IPython/core/formatters.py in format(self, obj, include, exclude)
    178             md = None
    179             try:
--> 180                 data = formatter(obj)
    181             except:
    182                 # FIXME: log the exception

<decorator-gen-3> in __call__(self, obj)

~/opt/miniconda3/envs/python3_test/lib/python3.7/site-packages/IPython/core/formatters.py in catch_format_error(method, self, *args, **kwargs)
    222     """show traceback on failed format call"""
    223     try:
--> 224         r = method(self, *args, **kwargs)
    225     except NotImplementedError:
    226         # don't warn on NotImplementedErrors

~/opt/miniconda3/envs/python3_test/lib/python3.7/site-packages/IPython/core/formatters.py in __call__(self, obj)
    700                 type_pprinters=self.type_printers,
    701                 deferred_pprinters=self.deferred_printers)
--> 702             printer.pretty(obj)
    703             printer.flush()
    704             return stream.getvalue()

~/opt/miniconda3/envs/python3_test/lib/python3.7/site-packages/IPython/lib/pretty.py in pretty(self, obj)
    375                 if cls in self.type_pprinters:
    376                     # printer registered in self.type_pprinters
--> 377                     return self.type_pprinters[cls](obj, self, cycle)
    378                 else:
    379                     # deferred printer

~/opt/miniconda3/envs/python3_test/lib/python3.7/site-packages/IPython/lib/pretty.py in inner(obj, p, cycle)
    549         step = len(start)
    550         p.begin_group(step, start)
--> 551         for idx, x in p._enumerate(obj):
    552             if idx:
    553                 p.text(',')

KeyboardInterrupt:

error traceback with ipywidget by keyboard interupted

---------------------------------------------------------------------------
KeyboardInterrupt                         Traceback (most recent call last)
<ipython-input-2-61ccd3833da6> in first_button(self, b)
    274         with self.output_html:
    275             clear_output(wait = True)
--> 276             display(HTML(self.display_html(self.board) ) )
    277 
    278     def previous_button(self, b):

<ipython-input-2-61ccd3833da6> in display_html(self, board)
    192                 info = self.stockfish_analyse(fen)
    193             elif self.engine == 2:
--> 194                 info = self.brainfish_analyse(fen)
    195             elif self.engine == 3:
    196                 info = self.fairy_stockfish_analyse(fen)

<ipython-input-2-61ccd3833da6> in brainfish_analyse(self, fen)
    122     def brainfish_analyse(self, fen):
    123         app_path = "engine/brainfish"
--> 124         info = self.engine_analyse(app_path, fen)
    125         return info
    126 

<ipython-input-2-61ccd3833da6> in engine_analyse(self, app_path, fen)
    109 
    110         evaluation = stockfish.get_evaluation()
--> 111         best_move = stockfish.get_best_move(wtime = 0, btime = 0)
    112         best_move_time = stockfish.get_best_move_time(0)
    113         top_moves = stockfish.get_top_moves(3)

~/opt/miniconda3/envs/python3_test/lib/python3.7/site-packages/stockfish/models.py in get_best_move(self, wtime, btime)
    309         last_text: str = ""
    310         while True:
--> 311             text = self._read_line()
    312             splitted_text = text.split(" ")
    313             if splitted_text[0] == "bestmove":

~/opt/miniconda3/envs/python3_test/lib/python3.7/site-packages/stockfish/models.py in _read_line(self)
    148         if not self._stockfish.stdout:
    149             raise BrokenPipeError()
--> 150         return self._stockfish.stdout.readline().strip()
    151 
    152     def _set_option(

KeyboardInterrupt:

check memory consumption for brainfish (the choosen engine that cause looping forever image

johndoknjas commented 2 years ago

@sugizo A main factor for python stockfish being slower seems to be because of get_top_moves(3). In that fen, White's best move is just a mate is 1. However, since the 3 top moves are requested, the wrapper internally sets MultiPV to 3 (temporarily), in order to tell SF to fully analyze 3 principal variations. It finishes analyzing the Qxf7# variation in an instant, but it takes a bit of time for it to get to depth 25 for the second and third best moves.

Meanwhile for Python chess, I'm assuming the default is for MultiPV to be 1, and I don't think you're calling any functions that request this value to be increased. So the module just has to analyze Qxf7#, and this is done in an instant by SF.

johndoknjas commented 2 years ago

It's still interesting though that it seems to take you around 100 seconds to run your calls to the python stockfish module. I copied some of your source code, and it took under 30 seconds to get to depth 25 (with SF 11). Running SF 11 directly, it seemed slightly faster, but in the same ballpark (roughly 26 seconds maybe).

johndoknjas commented 2 years ago

For the Python Chess vs Python Stockfish comparison, if you figure out a way to set MultiPV = 3 for Python Chess, then I'd assume the modules will take comparable times to get to depth 25.

johndoknjas commented 2 years ago

error traceback non ipywidget by keyboard interupted

---------------------------------------------------------------------------
KeyboardInterrupt                         Traceback (most recent call last)
<ipython-input-6-f61a2964a72e> in <module>
      2 # brainfish_analyse, fairy_stockfish_analyse = looping forever
      3 
----> 4 play_pgn("pgn/chess-play.pgn", brainfish_analyse, sleep = 0.0)

~/opt/miniconda3/envs/python3_test/lib/python3.7/site-packages/IPython/core/displayhook.py in __call__(self, result)
    260             self.start_displayhook()
    261             self.write_output_prompt()
--> 262             format_dict, md_dict = self.compute_format_data(result)
    263             self.update_user_ns(result)
    264             self.fill_exec_result(result)

~/opt/miniconda3/envs/python3_test/lib/python3.7/site-packages/IPython/core/displayhook.py in compute_format_data(self, result)
    149 
    150         """
--> 151         return self.shell.display_formatter.format(result)
    152 
    153     # This can be set to True by the write_output_prompt method in a subclass

~/opt/miniconda3/envs/python3_test/lib/python3.7/site-packages/IPython/core/formatters.py in format(self, obj, include, exclude)
    178             md = None
    179             try:
--> 180                 data = formatter(obj)
    181             except:
    182                 # FIXME: log the exception

<decorator-gen-3> in __call__(self, obj)

~/opt/miniconda3/envs/python3_test/lib/python3.7/site-packages/IPython/core/formatters.py in catch_format_error(method, self, *args, **kwargs)
    222     """show traceback on failed format call"""
    223     try:
--> 224         r = method(self, *args, **kwargs)
    225     except NotImplementedError:
    226         # don't warn on NotImplementedErrors

~/opt/miniconda3/envs/python3_test/lib/python3.7/site-packages/IPython/core/formatters.py in __call__(self, obj)
    700                 type_pprinters=self.type_printers,
    701                 deferred_pprinters=self.deferred_printers)
--> 702             printer.pretty(obj)
    703             printer.flush()
    704             return stream.getvalue()

~/opt/miniconda3/envs/python3_test/lib/python3.7/site-packages/IPython/lib/pretty.py in pretty(self, obj)
    375                 if cls in self.type_pprinters:
    376                     # printer registered in self.type_pprinters
--> 377                     return self.type_pprinters[cls](obj, self, cycle)
    378                 else:
    379                     # deferred printer

~/opt/miniconda3/envs/python3_test/lib/python3.7/site-packages/IPython/lib/pretty.py in inner(obj, p, cycle)
    549         step = len(start)
    550         p.begin_group(step, start)
--> 551         for idx, x in p._enumerate(obj):
    552             if idx:
    553                 p.text(',')

KeyboardInterrupt:

error traceback with ipywidget by keyboard interupted

---------------------------------------------------------------------------
KeyboardInterrupt                         Traceback (most recent call last)
<ipython-input-2-61ccd3833da6> in first_button(self, b)
    274         with self.output_html:
    275             clear_output(wait = True)
--> 276             display(HTML(self.display_html(self.board) ) )
    277 
    278     def previous_button(self, b):

<ipython-input-2-61ccd3833da6> in display_html(self, board)
    192                 info = self.stockfish_analyse(fen)
    193             elif self.engine == 2:
--> 194                 info = self.brainfish_analyse(fen)
    195             elif self.engine == 3:
    196                 info = self.fairy_stockfish_analyse(fen)

<ipython-input-2-61ccd3833da6> in brainfish_analyse(self, fen)
    122     def brainfish_analyse(self, fen):
    123         app_path = "engine/brainfish"
--> 124         info = self.engine_analyse(app_path, fen)
    125         return info
    126 

<ipython-input-2-61ccd3833da6> in engine_analyse(self, app_path, fen)
    109 
    110         evaluation = stockfish.get_evaluation()
--> 111         best_move = stockfish.get_best_move(wtime = 0, btime = 0)
    112         best_move_time = stockfish.get_best_move_time(0)
    113         top_moves = stockfish.get_top_moves(3)

~/opt/miniconda3/envs/python3_test/lib/python3.7/site-packages/stockfish/models.py in get_best_move(self, wtime, btime)
    309         last_text: str = ""
    310         while True:
--> 311             text = self._read_line()
    312             splitted_text = text.split(" ")
    313             if splitted_text[0] == "bestmove":

~/opt/miniconda3/envs/python3_test/lib/python3.7/site-packages/stockfish/models.py in _read_line(self)
    148         if not self._stockfish.stdout:
    149             raise BrokenPipeError()
--> 150         return self._stockfish.stdout.readline().strip()
    151 
    152     def _set_option(

KeyboardInterrupt:

check memory consumption for brainfish (the choosen engine that cause looping forever image

The RAM usage for an engine is essentially just how big its hash table is. The default for Python Stockfish is currently 1024 MB = 1GB, which is why your engines are each using that. If you'd like to use less RAM, set the "Hash" parameter to a lower value (but keep it as some power of 2).

sugizo commented 2 years ago

attached the log of initial problem stockfish derivaties e.g. brainfish and fairy_stockfish not work when analyze pgn result no error occured, seems like looping forever

pgn_stockfish_player.txt

but the same code to analyze pgn work smoothly when using stockfish app engine

what i tried is written above :

quick deduction

kieferro commented 1 year ago

Hello, this project is no longer maintained in this repo but on this fork. (For more information about this please look here).

It is not possible to transfer the entire discussion here. However, when we go through the remaining issues, we will get back to you and either create a proxy issue or find another solution.

Just be aware that the project will no longer be updated here.