2

I am trying to use stockfish to evaluate a chess position using FEN notation all in Python. I am mainly using two libraries (pgnToFen I found on github here: https://github.com/SindreSvendby/pgnToFen and Stockfish the MIT licensed one here: https://github.com/zhelyabuzhsky/stockfish). After many bugs I have reached problem after problem. Stockfish not only can't analyse this FEN position (3b2k1/1p3pp1/8/3pP1P1/pP3P2/P2pB3/6K1/8 b f3 -) but it infinitely loops! "No worries!" and thought changing the source code would be accomplishable. Changed to _put(), but basically I am unable to put dummy values in because stdin.flush() won't execute once I give it those values! Meaning I don't even think I can skip to the next row in my dataframe. :( The code I changed is below.

def _put(self, command: str, tmp_time) -> None:
        if not self.stockfish.stdin:
            raise BrokenPipeError()
        self.stockfish.stdin.write(f"{command}\n")

        try:
            self.stockfish.stdin.flush()

        except:
            if command != "quit":
                self.stockfish.stdin.write('isready\n')  
                try:   
                    time.sleep(tmp_time)
                    self.stockfish.stdin.flush()
                except:
                    #print ('Imma head out', file=sys.stderr)
                    raise ValueError('Imma head out...')
                    #sys.stderr.close()

def get_evaluation(self) -> dict:
        """Evaluates current position

        Returns:
            A dictionary of the current advantage with "type" as "cp" (centipawns) or "mate" (checkmate in)
        """

        evaluation = dict()
        fen_position = self.get_fen_position()
        if "w" in fen_position:  # w can only be in FEN if it is whites move
            compare = 1
        else:  # stockfish shows advantage relative to current player, convention is to do white positive
            compare = -1
        self._put(f"position {fen_position}", 5)
        self._go()
        x=0
        while True:
            x=x+1
            text = self._read_line()
            #print(text)
            splitted_text = text.split(" ")
            if splitted_text[0] == "info":
                for n in range(len(splitted_text)):
                    if splitted_text[n] == "score":
                        evaluation = {
                            "type": splitted_text[n + 1],
                            "value": int(splitted_text[n + 2]) * compare,
                        }
            elif splitted_text[0] == "bestmove":
                return evaluation

            elif x == 500:
                evaluation = {
                    "type": 'cp',
                    "value": 10000,
                }
                return evaluation

and last but not least change to the init_ contructor below:

self._stockfish_major_version: float = float(self._read_line().split(" ")[1])

And the code where I am importing this code to is below, this is where errors pop up.

import pandas as pd
import re
import nltk
import numpy as np
from stockfish import Stockfish
import os
import sys

sys.path.insert(0, r'C:\Users\path\to\pgntofen')

import pgntofen

#nltk.download('punkt')

#Changed models.py for major version line 39 in stockfish from int to float

stockfish = Stockfish(r"C:\Users\path\to\Stockfish.exe")

file = r'C:\Users\path\to\selenium-pandas output.csv'

chunksize = 10 ** 6
for chunk in pd.read_csv(file, chunksize=chunksize):
    for index, row in chunk.iterrows():
        FullMovesStr = str(row['FullMoves']) 
        FullMovesStr = FullMovesStr.replace('+', '')
        if "e.p" in FullMovesStr:
            row.to_csv(r'C:\Users\MyName\Logger.csv', header=None, index=False, mode='a')
            
            print('Enpassant')
            continue

        tokens = nltk.word_tokenize(FullMovesStr)
        
        movelist = []
        for tokenit in range(len(tokens)):
            
            if "." in str(tokens[tokenit]):
                try:
                    tokenstripped = re.sub(r"[0-9]+\.", "", tokens[tokenit])
                    token = [tokenstripped, tokens[tokenit+1]]
                    movelist.append(token)
                except:
                    continue
            else:
                continue
        DFMoves = pd.DataFrame(movelist, columns=[['WhiteMove', 'BlackMove']])
        DFMoves['index'] = row['index']
        DFMoves['Date'] = row['Date']
        DFMoves['White'] = row['White']
        DFMoves['Black'] = row['Black']
        DFMoves['W ELO'] = row['W ELO']
        DFMoves['B ELO'] = row['B ELO']
        DFMoves['Av ELO'] = row['Av ELO']
        DFMoves['Event'] = row['Event']
        DFMoves['Site'] = row['Site']
        DFMoves['ECO'] = row['ECO']
        DFMoves['Opening'] = row['Opening']
        pd.set_option('display.max_rows', DFMoves.shape[0]+1)
        print(DFMoves[['WhiteMove', 'BlackMove']])
        seqmoves = []
        #seqmovesBlack = []
        evalmove = []

        pgnConverter = pgntofen.PgnToFen()
        #stockfish.set_fen_position("rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1")
        #rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1
        for index, row in DFMoves.iterrows():
            try:
                stockfish.set_fen_position("rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1")
            except:
                evalmove.append("?")
                continue
            #stockfish.set_fen_position("rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1")
            pgnConverter.resetBoard()
            WhiteMove = str(row['WhiteMove'])
            BlackMove = str(row['BlackMove'])

            if index == 0:
                PGNMoves1 = [WhiteMove]
                seqmoves.append(WhiteMove)
                #seqmoves.append(BlackMove)
            else:
                seqmoves.append(WhiteMove)
                #seqmoves.append(BlackMove)
                PGNMoves1 = seqmoves.copy()

            #print(seqmoves)
            try:
                pgnConverter.pgnToFen(PGNMoves1)
                fen = pgnConverter.getFullFen()
            except:
                break

            try:
          
                stockfish.set_fen_position(fen)
                print(stockfish.get_board_visual())
                evalpos = stockfish.get_evaluation()
                evalmove.append(evalpos)
              
            except:
                pass
          
            try:
                stockfish.set_fen_position("rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1")
            except:
                evalmove.append("?")
                continue

            pgnConverter.resetBoard()

            if index == 0:
                PGNMoves2 = [WhiteMove, BlackMove]
                seqmoves.append(BlackMove)
            else:
                seqmoves.append(BlackMove)
                PGNMoves2 = seqmoves.copy()

            try:
                pgnConverter.pgnToFen(PGNMoves2)
                fen = pgnConverter.getFullFen()
            except:
                break

            try:
                stockfish.set_fen_position(fen)
                print(stockfish.get_board_visual())
                evalpos = stockfish.get_evaluation()
                print(evalpos)
                evalmove.append(evalpos)
            except:
                pass

        #DFMoves['EvalWhite'] = evalwhite
        #DFMoves['EvalBlack'] = evalblack
        print(evalmove)

So the detailed question is getting stockfish.get_evalution() to just skip, or better yet fix the problem, for this ( 3b2k1/1p3pp1/8/3pP1P1/pP3P2/P2pB3/6K1/8 b f3 - ) FEN position. I have been working on this problem for quite a while so any insight into this would be very much appreciated.

My specs are Windows 10, Python 3.9, Processor:Intel(R) Core(TM) i9-10980XE CPU @ 3.00GHz 3.00 GHz and RAM is 64.0 GB.

Thanks :)

  • @LittleCoder is correct that your FEN is invalid. I tried to use the FEN with Stockfish 15 directly via the command line, and it also crashed (so the problem isn't with the python stockfish library). FENs must have six fields, but your FEN only has 4 fields. For more info on this see: https://en.wikipedia.org/wiki/Forsyth%E2%80%93Edwards_Notation – Inertial Ignorance May 22 '22 at 07:54

1 Answers1

4

Ok. It seems your fen is invalid (3b2k1/1p3pp1/8/3pP1P1/pP3P2/P2pB3/6K1/8 b f3 -). So check that. And python-chess (https://python-chess.readthedocs.io/en/latest/index.html) library allows you to use FEN AND chess engines. So, pretty cool no ? Here is an example of theses two fantastics tools :

import chess
import chess.engine
import chess.pgn

pgn = open("your_pgn_file.pgn")
game = chess.pgn.read_game(pgn)

engine = chess.engine.SimpleEngine.popen_uci("your_stockfish_path.exe")

 # Iterate through all moves, play them on a board and analyse them.
board = game.board()
for move in game.mainline_moves():
    board.push(move)
    print(engine.analyse(board, chess.engine.Limit(time=0.1))["score"])
LittleCoder
  • 391
  • 1
  • 13