niklasf / python-chess

A chess library for Python, with move generation and validation, PGN parsing and writing, Polyglot opening book reading, Gaviota tablebase probing, Syzygy tablebase probing, and UCI/XBoard engine communication
https://python-chess.readthedocs.io/en/latest/
GNU General Public License v3.0
2.4k stars 521 forks source link

Adding a new variant #451

Closed bkitej closed 4 years ago

bkitej commented 4 years ago

Hello, I'm looking to write my own variant. Simply put, I'd like to adapt the ruleset to allow each side to take its own pieces. I'm doing this in the hopes of later adapting a MCTS algorithm to learn to play this variant.

Having gone through the codebase, it seems like it should be simple enough in concept to adapt generate_pseudo_legal_moves to this purpose, but I'm getting caught up in the implementation details.

What should I look for to modify to get this variant to work? I've been to the docs, are there any other resources I should look at?

niklasf commented 4 years ago

There is no documentation for the internals. You could try to check out how some of the variants in chess/variant.py are implemented.

bkitej commented 4 years ago

I wasn't able to find anything I felt I could use as far as rewriting the friendly piece capture rules. It's possible I missed something useful, after all I'm new to bitboard notation.

Again, in theory I had hoped to be able to remove friendly piece checks, e.g. to maybe replace masks like Board.occupied_co[self.turn] with Board.occupied. My idea was to push an illegal SAN (e.g. 'Nb1xd2' on move 1) and use pdb to mine the trace for where it throws the "illegal move" error. I've traced it as deep as attacks_mask() in generate_pseudo_legal_moves, which uses scan_reversed (which I'm afraid I don't understand at all, this is the first time I've ever seen bit operators)... but I still haven't been able to remove type checks completely to generate (pseudo)-legal moves satisfactorily.

Can you advise? Is there one place where I might remove these type checks, or are a multitude spread across multiple methods?

niklasf commented 4 years ago

Ok, wow, I like the systematic approach. Unfortunately I believe the assumption that only enemy pieces can be captured might indeed be spread across multiple methods. Move generation should be the most important one.

To explain scan_reversed(): You probably already know that bitboards represent sets of squares. scan_reversed simply iterates over all squares in the set, in reverse order. (In many cases the order is not important, but due to the selection of low level methods that Python provides, reverse iteration is faster than forward iteration.)

To get you started, here is the first relevant line: https://github.com/niklasf/python-chess/blob/8620c0c55b61d8a9749ce65a615cffbc9b4531c4/chess/__init__.py#L1466

& ~our_pieces removes all pieces of the side to move from the set of target squares. It is an intersection with the complement, i.e. a set subtraction.

bkitej commented 4 years ago

A note on the line in question:

https://github.com/niklasf/python-chess/blob/8620c0c55b61d8a9749ce65a615cffbc9b4531c4/chess/__init__.py#L1466

Additionally, to_mask itself is dependent on occupied spaces. In the case of the "illegal" move Ng1xe2, to_mask will evaluate to 0.