thomasahle / sunfish

Sunfish: a Python Chess Engine in 111 lines of code
https://www.chessprogramming.org/Sunfish
Other
2.95k stars 543 forks source link

Mirror the board instead of rotating when transitioning to black. #118

Open Dolphus2 opened 3 months ago

Dolphus2 commented 3 months ago

The engine uses piece square tables to evaluate positions. These piece square tables are not symmetric, which means that rotating the board and viewing black as "white" effectively changes the incentives of black, since black has its king to the left of the queen rather than the right. As such, where the white king starts on the king pst space with value 6, the black king starts on -14 and is incentivized to move one space left or right, because the king pst models it as having already castled. This cannot be right.

Another example is that if you evaluate the value of the starting position of black and white, it comes out to a difference of (6-13) - (-14-31) = 38 in favour of white because of the reversed position of the king and queen according to the piece square tables. This is true even if black starts and is just an artifact of the non-symmetry of the piece square tables. But chess is equivalent if black starts, so it does not make sense that white should have an advantage in this case. These are two quite simple, illustrative examples, but this influences the engine's positional play all the way down the search tree.

The starting positions of white and black are identical when mirrored horizontally. Thus if the board is instead mirrored horizontally, then piece square tables will be correctly applied to both black and white. This is the only thing this pull request changes. I have implemented two static methods in the Position object to make the change very clear, but you are welcome to change it as you see fit. Thank you for this nice chess engine. board_original board_rotated

theonicisan commented 1 month ago

Wow! What an awesome insight. Thanks for that. I have actually progressed the code a bit and will update soon. Reading your post fixed a problem that the back of my mind was bothered about ;)

thomasahle commented 1 month ago

It's true that the rotate() method doesn't flip the perspective in the traditional sense, but instead rotates the board, in a way that doesn't preserve evaluations. At least when the PST is not symmetric.

However, I'm not convinced it actually makes a big difference in ELO terms. Since the PR adds 13 extra lines, it needs to result in a pretty significant improvement.

Thank you for the PR though! You can always fork sunfish if you want to make a "larger but more complete" engine!

thomasahle commented 1 month ago

Btw, your mirror_square function can be simplified to 110 - p + 2 * (p % 10) so nearly as simple as 119 - p. However, self.board[::-1] is likely a lot faster than the list comprehension in mirror_board, so I'm afraid this change would actually lose a lot of ELO.