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 ------------------------------')