I've pulled the lastest master for Weiss after your last 1.5% update. In KiwiPete6, Weiss is now down to the higher 200.xx and lower 201.xx range.
I also cleaned up Rustic a bit more (basically reversing the "last correct option + everything" in match), removing some unnecessary type casts that aren't necessary anymore because of earlier changes, some consistency... It shouldn't have made any difference, but sometimes, Rustic now indeed achieves a 199.xx run for KiwiPete6.
Weiss: 200.98 seconds
Rustic: 199.33 seconds
Starpos7 are still the same for both (actually, Weiss lost about 0.2 seconds):
Weiss: 76.01 seconds
Rustic: 78.10 seoncds
After running tests with other positions, it seems Weiss and Rustic are always within +/- 2 seconds of one another; sometimes Weiss is a bit faster, sometimes it's Rustic, depending on the position.
I think that, for all intents and purposes, we can call the speed to be equal.
=== About engine and code construction; skip if not interested... ===
I've also tried a rearrangements of the code; basically, I moved gamestate updates such as castling permissions and side to move AFTER the legality check. (If the move isn't legal, those updates aren't necessary as unmake() will undo them.) Both with an early return from make(), and when using an if-statement, the performance drops by 6%.
It seems Rust (or the CPU...) is very sensitive to nested if-statements and upsetting the flow of straightforward code. Sometimes, trying to prevent doing something with if-statements is more expensive than just doing the work and then either throw it away or reverse it if it turned out to be unnecessary. (I saw the same thing in add_move() with the promotions.)
I do have a big refactor ahead, because I wrote the move generator as a module that does not change the board, but when I want to test the pinning stuff with the knight, I must change the board. As it is now, I'm in the weird position that some functions in the move generator must change the board (i.e. the bitboards) and not change the board (i.e., the move generator itself that is incorporated in the board), at the same time, and that's obviously not possible.
I did it this way so I don't have to pass the zobrist module, move generator, evaluator, and so on into each function.
In many chess engines, things such as the move generator are just a bunch of global variables and functions that you can change and call from anywhere, but that's not allowed in Rust. (Because, in that case, Rust can't guarantee thread saftey.)
I'm going to package the non-changing modules (move generator, zobrist-handler, evaluation, maybe others such as search...) into a single immutable core, which is initialized and created at the program start, and then passed into each function that also accepts a board.
That way, it resolves the mutability conflict, and I have to only pass one pointer to a global "core" struct; a struct that will never change. The only changing object in the engine will be the board.
So, how am I going to call that struct? "Core" is lame. I'm partial to "Cerebrus" or just "Brain" :p
===== output from perft =====
Weiss, No PEXT:
Code: Select all
Marcel@WORKSTATION MINGW64 /c/code/weiss/src
$ ./weiss-dev.exe
perft 6 r3k2r/p1ppqpb1/bn2pnp1/3PN3/1p2P3/2N2Q1p/PPPBBPPP/R3K2R w KQkq - 0 1
Starting perft to depth 6
move 1 : g2h3 : 158328615
...
move 48 : e1f1 : 139601450
Perft complete:
Time : 200975ms
Leaves: 8031647685
LPS : 39963416
Code: Select all
Benchmarking perft 1-6:
8 r . . . k . . r
7 i . i i q i b .
6 b n . . i n i .
5 . . . I N . . .
4 . i . . I . . .
3 . . N . . Q . i
2 I I I B B I I I
1 R . . . K . . R
A B C D E F G H
Zobrist key: fb86acbc2034870e
Active Color: White
Castling: KQkq
En Passant: -
Half-move clock: 0
Full-move number: 1
Perft 1: 48 (0 ms, inf leaves/sec)
Perft 2: 2039 (0 ms, inf leaves/sec)
Perft 3: 97862 (2 ms, 48931000 leaves/sec)
Perft 4: 4085603 (101 ms, 40451514 leaves/sec)
Perft 5: 193690690 (4741 ms, 40854395 leaves/sec)
Perft 6: 8031647685 (199331 ms, 40293018 leaves/sec)
Total time spent: 204175 ms
Execution speed: 40306227 leaves/second