Source code for warnOfDanger

from typing import Optional, Callable, Dict 
import chess, chess.pgn

[docs]def warnOfDanger(gameNode : chess.pgn.GameNode, log : Optional[Callable[[str], None]] = None, depth : int = 0) -> Optional[Dict[chess.Square, int]]: ''' Warns, if certain pieces are in danger Basis are the material scores from the attackers point of view, i.e. positive scores show an advantage of the attacker :param gameNode: game node to be analysed :returns: a dictionary of square/score pairs ''' pieceScoreDict = { chess.PAWN : 1, chess.KNIGHT : 3, chess.BISHOP : 3, chess.ROOK : 5, chess.QUEEN : 9, chess.KING : 1000 } originalBoard = gameNode.board() fen = originalBoard.fen() if originalBoard.is_game_over(): return None if log is not None and depth > 0: log(' move: {} (next_isWhite = {}, fen = {} ------------------------------'.format((gameNode.ply()+1)/2, originalBoard.turn, fen)) square2ScoreDict : Dict[chess.Square, int] = dict() if originalBoard.is_check(): square2ScoreDict[originalBoard.king(originalBoard.turn)] = - pieceScoreDict[chess.KING] if log is not None: log(' check detected!') return square2ScoreDict originalBoard.push(chess.Move.null()) for piece in chess.PIECE_TYPES: sList = list(originalBoard.pieces(piece, not originalBoard.turn)) for sSquare in sList: board = originalBoard.copy() scoreList = list() sign = -1 if log is not None and depth > 0: log(' square = {}, piece = {}'.format(chess.square_name(sSquare), chess.piece_name(piece))) # 1. assign material scores to each attack and reply while True: minScore = 10000 minSquare = None for actSquare in board.attackers(board.turn, sSquare): actPiece = board.piece_type_at(actSquare) # 2. promote to queen, if applicable if actPiece == chess.PAWN and \ ((board.turn and chess.square_rank(sSquare) == 7) \ or ((not board.turn) and chess.square_rank(sSquare) == 0)): promotion = chess.QUEEN else: promotion = None if chess.Move(actSquare, sSquare, promotion) in board.legal_moves: if minScore > pieceScoreDict[actPiece]: minScore = pieceScoreDict[actPiece] minSquare = actSquare if log is not None and depth > 0: log(' isAttacker = {}, square = {}, piece = {}'.format(sign == 1, chess.square_name(actSquare), chess.piece_name(board.piece_type_at(actSquare)))) elif log is not None and depth > 0: log(' isAttacker = {}, square = {}, piece = {}, illegal move detected'.format(originalBoard.turn != board.turn, chess.square_name(actSquare), chess.piece_name(board.piece_type_at(actSquare)))) if minScore == 10000: break scoreList.append(sign*minScore) board.push(chess.Move(minSquare, sSquare, promotion)) sign = -sign # 2. no attackers if len(scoreList) >= 1: square2ScoreDict[sSquare] = pieceScoreDict[originalBoard.piece_type_at(sSquare)] if len(scoreList) > 1: square2ScoreDict[sSquare] += sum(scoreList[:-1]) if log is not None: log(' square = {}, totScore = {}'.format(chess.square_name(sSquare), square2ScoreDict[sSquare])) return square2ScoreDict
if __name__ == "__main__": import io from pgnParse import read_game def createGame(file): with open(file, mode = 'r', encoding = 'utf-8') as f: newData = f.read() pgn = io.StringIO(newData) game = read_game(pgn) return game ps = "C:/Users/Reinh/OneDrive/Dokumente/Schach/ps210105.pgn" game = createGame(ps) gameNode = game.next() ply = 2 while gameNode is not None: print(' move {}: {} ------------------------------'.format(ply/2, gameNode.move.uci())) square2ScoreDict = warnOfDanger(gameNode, log = print, depth = 0) gameNode = gameNode.next() if len(square2ScoreDict) > 0: print(' square2ScoreDict: ------------------------------') for square, score in square2ScoreDict.items(): print(' {}: {}'.format(chess.square_name(square), score)) ply += 1 print('completed ------------------------------')