Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draw detection via Zobrist hashes in Position #11

Open
wants to merge 3 commits into
base: master
Choose a base branch
from

Conversation

nvanderw
Copy link

@nvanderw nvanderw commented Apr 12, 2022

Currently chessIO has no efficient way to detect {three,five}fold repetition draws from a given position, i.e. to tell how many times the position has occurred. The way some modern chess implementations handle this is maintaining a position hash as part of the position data type, and also storing a linked list of hashes of the previous board states (see e.g. Stockfish). Since recomputing the hash of the entire board each move would be expensive, Zobrist hashing is used to quickly update the hashes with only the changed elements of the board.

(Optimization: in order to detect if a given position is a draw by repetition, we only need to know about the prior board states since the last pawn move or capture.)

chessIO already has some Zobrist hashing code in the Polyglot module, but no support for incrementally updating the hashes. So I've moved that hashing logic under Game.Chess.Internal.

After applying a move to a position in unsafeDoPly, I've added a step to XOR the before and after bitboards together -- any 1s indicate changes to the board state. For each detected change to the board state, unsafeDoPly looks up the corresponding hash in the Zobrist tables and XORs it with the position hash.

As of 2022-4-12 I'm still ironing out some bugs in the hashing implementation. And then the next step is to implement the hash history updating/searching. But thought I'd send this out for an early review in the meantime. Also: exposing position hashes to library clients would allow them to use transposition tables.

Test is currently breaking around a dozen plies in -- need to dig in
further.
@@ -167,6 +189,28 @@ instance Hashable Position where
`hashWithSalt` color
`hashWithSalt` flags

epKey :: File -> Word64
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

note: moved from Polyglot

src/Game/Chess/Internal.hs Outdated Show resolved Hide resolved
@nvanderw nvanderw changed the title [WIP/incomplete] Draw detection via Zobrist hashes in Position Draw detection via Zobrist hashes in Position Jun 16, 2022
clockRemaining clock (color pos) >>= \case
Nothing -> pure (snd history, Win . opponent . color $ pos)
Just _ ->
if | draw pos -> pure (snd history, Draw)
| checkmate pos -> pure (snd history, Win . opponent . color $ pos)
| Just (n, _) <- repetitions poss
| n <- repetitions pos
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This seems right given the new signature of repetitions but I'm unsure how to validate this code.

@@ -1,42 +1,7 @@
{-# LANGUAGE PatternSynonyms #-}
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We discussed this a bit over email @mlang but to recap, circular dependencies prevent the Internal/Position logic from depending on Polyglot. Moving it here seemed simpler than duplicating the hashing logic, generating new hashes independent of Polyglot etc.

@nvanderw nvanderw marked this pull request as ready for review July 19, 2022 21:46
@oskarpyk
Copy link

oskarpyk commented Aug 8, 2022

@nvanderw Glad to see you're working on this; ChessIO is a key library in a project I'm working on. Would you say your threefold-repetition detection is production ready?

@mlang
Copy link
Owner

mlang commented Sep 6, 2022

Hi. Sorry for the delay, I didnt spent much time on a computer during summeer time.

I am about to merge this PR, however, during review I found anyBits
and I am a bit confused why you define it.
Either I am not awake yet, or anyBits is equivalent to occupied.
ORing the black pieces to all occupied pieces is a no-op.

@nvanderw
Copy link
Author

Hi. Sorry for the delay, I didnt spent much time on a computer during summeer time.

I am about to merge this PR, however, during review I found anyBits and I am a bit confused why you define it. Either I am not awake yet, or anyBits is equivalent to occupied. ORing the black pieces to all occupied pieces is a no-op.

Hey Mario, thanks for reviewing. Sorry for delay on my end, hope all is well for you. I think your read is right here and perhaps I was not awake yet when I wrote that piece. I think we can just use occupied. Will give that a try and re-run the tests to be sure.

@nvanderw
Copy link
Author

nvanderw commented Oct 17, 2022

@nvanderw Glad to see you're working on this; ChessIO is a key library in a project I'm working on. Would you say your threefold-repetition detection is production ready?

It is designed to be, and I've added test coverage to ensure the hashing works the way I expect, but I'm loath to claim that there are no bugs etc. I'd be curious to hear you try it out and tell me your experience.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants