Delta between two PovScores

Sort:
stevenadema

Hi All, 

I am trying to build a python program that analyses your chess.com games, part of which is the difference between the best possible move and the ones you made.

My issue is that the python-chess library evaluates the position and returns a PovScore object I am unable to cast to an integer for comparison. I also can't directly subtract one PovScore from another.

Just wondering if anyone has tried to do something similar or has more experience with python chess engine.analyse()

chrka

First thing you have to do is decide what exactly you mean by the difference of evaluation between moves, in particular, how are you going to handle mates? In principle, they should count as ±∞, but you probably will want to introduce some form of cutoff.

After that you only have pick a side to evaluate the score from (White, Black, or the current player).

Let's say you want "score" from White's perspective, and you'll cut off a mate at ±1000 cp. Then you can get an integer valued score as: "score.white().score(mate_score=1000)"

See the official docs for more info.

 

MGleason

I didn't use Python, and my tool was primarily intended for amateur cheat detection, determining whether someone was worth reporting, but you might find what I did in PGN Spy to be of interest: https://github.com/MGleason1/PGN-Spy

Dauntless777
MGleason wrote:

I didn't use Python, and my tool was primarily intended for amateur cheat detection, determining whether someone was worth reporting, but you might find what I did in PGN Spy to be of interest: https://github.com/MGleason1/PGN-Spy

Nice 👍 

stephen_33

Although I use Python quite a lot for processing team-matches in a couple of leagues I help run, I haven't got involved with the analysis of games up till now.

But having installed the python-chess package & looking over the documentation, I think I may have the solution you want? Having loaded a given FEN string into 'board', the position analysis is done using..

info = engine.analyse(board, chess.engine.Limit(depth=20))

..that returns a dictionary object in 'info'. For example..

{'multipv': 1, 'depth': 20, 'seldepth': 22, 'score': PovScore(Mate(+4), WHITE), 'time': 0.278, 'nodes': 544195, 'nps': 1957000, 'tbhits': 0, 'hashfull': 4, 'pv': [Move.from_uci('f1e1'), Move.from_uci('b4e1'), Move.from_uci('b2e2'), Move.from_uci('e1f2'), Move.from_uci('g1f2'), Move.from_uci('h6h5'), Move.from_uci('e2e8')], 'lowerbound': True}

The 'score' key returns a (string) value in this case of PovScore(Mate(+4), WHITE). When mate isn't detected, the CP score is given: PovScore(Cp(-4), WHITE)

So one way of obtaining the CP score is simply to detect the sub-string "Cp" & extract the value from within the following parentheses & then converting it to an integer.

Does that help at all?

stevenadema
stephen_33 wrote:

Although I use Python quite a lot for processing team-matches in a couple of leagues I help run, I haven't got involved with the analysis of games up till now.

But having installed the python-chess package & looking over the documentation, I think I may have the solution you want? Having loaded a given FEN string into 'board', the position analysis is done using..

info = engine.analyse(board, chess.engine.Limit(depth=20))

..that returns a dictionary object in 'info'. For example..

{'multipv': 1, 'depth': 20, 'seldepth': 22, 'score': PovScore(Mate(+4), WHITE), 'time': 0.278, 'nodes': 544195, 'nps': 1957000, 'tbhits': 0, 'hashfull': 4, 'pv': [Move.from_uci('f1e1'), Move.from_uci('b4e1'), Move.from_uci('b2e2'), Move.from_uci('e1f2'), Move.from_uci('g1f2'), Move.from_uci('h6h5'), Move.from_uci('e2e8')], 'lowerbound': True}

The 'score' key returns a (string) value in this case of PovScore(Mate(+4), WHITE). When mate isn't detected, the CP score is given: PovScore(Cp(-4), WHITE)

So one way of obtaining the CP score is simply to detect the sub-string "Cp" & extract the value from within the following parentheses & then converting it to an integer.

Does that help at all?

Right, so I've got the 'info' dictionary, but I don't know how to grab the 'score' as a string. If I try to access it as info['score'] it's type is PovScore.  Is there way to convert it to a literal string? Thanks for your help.

stevenadema
chrka wrote:

First thing you have to do is decide what exactly you mean by the difference of evaluation between moves, in particular, how are you going to handle mates? In principle, they should count as ±∞, but you probably will want to introduce some form of cutoff.

After that you only have pick a side to evaluate the score from (White, Black, or the current player).

Let's say you want "score" from White's perspective, and you'll cut off a mate at ±1000 cp. Then you can get an integer valued score as: "score.white().score(mate_score=1000)"

See the official docs for more info.

 

Thanks for the suggestion.  This is something I still have to think more about how I will handle.

stevenadema
stevenadema wrote:
stephen_33 wrote:

....

Right, so I've got the 'info' dictionary, but I don't know how to grab the 'score' as a string. If I try to access it as info['score'] it's type is PovScore.  Is there way to convert it to a literal string? Thanks for your help.

Never mind, I found a way. Thanks again grin.png

stephen_33
stevenadema wrote:

Right, so I've got the 'info' dictionary, but I don't know how to grab the 'score' as a string. If I try to access it as info['score'] it's type is PovScore.  Is there way to convert it to a literal string? Thanks for your help.

I misled myself there for a while because printing the info['score'] works perfectly well because the print command has an implied str() operation but trying to manipulate info['score'] as it is keeps generating errors.

The solution is simple - use str(info['score']) & you'll find it returns just what you need. Example:

#+3  # mate in 3 moves
+14  # engine evaluation 14 centipawns in favour of White

..the docs should tell you what other values are returned for conditions such as stalemate etc.

chrka

Why do you want to convert it to a string? The "PovScore" object has the info you want directly. It's "pov(color)" — or the sometimes more convenient "white()" and "black()" — methods give you a Score object, which you can either let handle conversions from mates to a cut-off evaluation, or directly check if it's a mate (and if so, in how many steps), or a "regular" evaluation.

I have some code for evaluating the stability of engine evaluations (in Python using python-chess)  I've been meaning to share in a different thread (the passing of a relative got in the way), let me know if that would be helpful and I'll send you a link when I get around to it.

MGleason
chrka wrote:

I have some code for evaluating the stability of engine evaluations (in Python using python-chess)  I've been meaning to share in a different thread (the passing of a relative got in the way), let me know if that would be helpful and I'll send you a link when I get around to it.

I'd be interested in that!

stephen_33
chrka wrote:

Why do you want to convert it to a string? The "PovScore" object has the info you want directly. It's "pow(color)" — or the sometimes more convenient "white()" and "black()" — methods give you a Score object, which you can either let handle conversions from mates to a cut-off evaluation, or directly check if it's a mate (and if so, in how many steps), or a "regular" evaluation.

I think you mean pov(colour)? There isn't a pow() method. But doesn't that only move the problem of manipulating the score itself back a step? Instead of returning a PovScore object, the pov() method returns an engine.Cp object which can't be used either as a string or numerical without conversion.

chrka

@MGleason Ok, I'll try and make time to write a little README and maybe polish the code a little today or tomorrow. (It's pretty basic stuff though, so I don't know how useful it will be to you.)

@stephen_33 Thanks, fixed! And the result of "pov(color)" (or "white()", "black()") are "chess.engine.Score" objects (regardless if they are "Cp", "Mate", or, "MateGiven") so they can all be converted into a numerical score via the "score(…)" method, or queried for how long until mate using "mate()":

See https://python-chess.readthedocs.io/en/latest/engine.html#chess.engine.Score for details.

stephen_33

chrka, thanks, I can see that now. The score method returns the engine score directly as an integer in centipawns.

info['score'] returns an object that has 4 available attributes or methods:-

white()
black()
is_mate()
pov(<colour>)

In the cases of checkmate or stalemate, an empty dictionary is returned by the engine but once they're filtered out, the info['score'] object provides either mate in 'n' moves or the CP score. So to derive the engine score of a position use:

info['score'].white().score()