My Zig attempt creating an uci chess engine. The ultimate goal is to put the bot on lichess.org.
Move generation is already pretty fast using the ‘easy’ version of bitboards.
Next step will probably be implementing “pext magic bitboards” to check if that speeds up even more.
The program currently measures only the speeds of 4 positions in the same way as
this one does: https://github.com/Gigantua/Gigantua
The speed of Gigantua is around 3 times higher than my code, which bothers me :). It is party because my code will be a real engine: there is more data involved than only generating moves.
Nevertheless I would be very interested in finding optimizations.
Zig experts: I challenge you
Thanks.
Yes it will monomorphize the functions: create specifics.
I did a little measurement a while ago. I think in general “comptiming” the color to move speeds up 10-15%.
Regarding complex logic:
In general I noticed the compiler is insanely smart. Mostly smarter than I am.
For (for example) the generation of moves I trust it to eliminate the dead comptime code in each generated function.
Also something like: const them: Color = comptime us.opp();
is - I believe - not a real variable in memory.
I don’t know about the Zig compiler’s self-hosted backend, but in a lot of compilers or intermediate representations (and certainly in LLVM IR and SPIR-V for example) even your vars become const due to static-single-assignment form. This:
This makes things like constant folding easier to implement. At a later stage, the compiler will start scheduling registers and will fold operations to re-use values again, reintroducing mutable state. I’m not an expert so I’m not sure how things like loads and stores work in the context of SSA. You can use them in SPIR-V in the same way you’d use a mutable variable (storing multiple times), but in general, mutable variables are lying to you, at least for things that can ultimately be represented as pure expressions.
Thanks.
I start search somewhere in the next 2 weeks, first with Handcrafted Evaluation.
Then I check if he finds the solutions of puzzles on lichess.org.
(Long) after that I will maybe generate my own neural netwerk file, with the help of my smart daughter.
Yes the movegenerator is quite fast. The pinchecks are quite ok and some comptimes also help. Still I am bothered with the fact that Gigantua is much faster.
First I need to complete the UCI protocol.
Oh, I would love to figure out how we could replicate with zig comptime what happens in Gigantua with constexpr and templates. Theoretically this should be possible, but I am unable to get it done.
I have a hard time understanding Your code, so I just ask:
In a perft function people usually do something like this:
The state variable holds color, castling and enpassant options if they are available, this should be comptime.
When You call doMove() You can calculate the next state based on the move: When it is a simple move You just flip the color, but when You move the rook or the king You change the castling options. So You can generate a comptime state{color, castling, enpassant}.
This sounds good, but as soon as You return this value as next in the example zig can not use it as a comptime value. Well, You can use an inline switch to make it comptime again, but that kills the whole point of knowing the return value in advance
True, I was also not able to make a ‘boardstate’ which can be comptime handled.
An inline switch just slows down. So I made a switch which checks:
are there any pins?
are we in check?
And then call gen_moves (comptime check, comptime any_pins).
The update_state of my position slows down as well.
But my main thing now is getting the engine working without any bug.
The first version plays around 2200 ELO strength is my guess, but not bug free.
(mainly repetition drama’s, transpositiontable misery and still a quite naive eval).