maksimKorzh wrote: ↑Sat Sep 12, 2020 9:49 am
Sorry for late reply.
I've been re-reading this post many times already. It's very inspiring. Thank you so much for being so heart-opened.
Thanks
No problem; I have quite some experience in programming (studied computer science and I'm a software engineer by profession), but with regard to actual chess programming, my experience is limited to about a year.
Yup, that was me for years but after starting chess programming youtube channel something has changed. I mean when starting sharing your experience with others it opens a highway for your own growth e.g. I've just discovered what everybody does - stealing... ahh, sorry, getting inspired by the ideas from the open source engines and this is completely a new level for me because for years all of my attempts were doomed to no more 1500 ELO points while now I'm starting to understand how things work (at least regarding search).
We (at least, most of us, I think) don't "steal" code; but we do learn from open source engines and implement idea's from other engines. You can't have missed how fast the NNUE networks have spread from the first tentative implementation in a Shogi engine, to chess engines, and now even to Stockfish.
So if I can teach people to UNDERSTAND ideas from other engines and embedd them into their own this would already be good. And quite a fun thing that with this new "skill" I now feel that I can change/improve literally any part of my current engine. I was starting new engine evry time just because couldn't maintain the previous, couldn't change it much and also when I just started I was more addicted to various board representations and movegeneration techniques rather than search & eval.
Starting new engines is good in the beginning, to get a feel for what you want to do, and you learn from the mistakes you make. At some point however, you'll have to decide on an approach (mailbox, bitboard, etc...) and stick with it, because otherwise, you'll be doing work over and over and over again. There's a point where you're experienced enough to implement a good move generator, so all your implementations will be good. Making one after the other becomes a waste of time from then on. It gets more productive to keep improving the engine you have.
(I actually decided to not go forward with the VICE-like mailbox engine I started with, but turn it into a magic bitboard engine from the get-go. So I started writing the bitboard move generator, and at some point, the engine was actually half mailbox and half magic bitboard...)
Perft results:
Wukong runs perft depth 6 with optimization flags for about 21 second
BBC(my current, covered on youtube): runs perft 6 with optimization flags for about 7 seconds.
Rustic runs perft 6 on the starting position in 2.96 seconds, on an Intel i7-6700K, single-threaded, with no hash and no perft-only move generator tricks such as bulk-counting. (But there are many engines that are still faster...)
Both use exactly the same make move function, the only difference is that BBC uses pre-calculated attack tables and magic bitboards for sliding pieces, so pure lookup, no calculations. But anywat there're LOTS of things to improve which I would probably be covering on youtube after the initial release so that people could see not only the process of initial implementation of an engine but also the development progress.
I look forward to that
After the first version of my engine is finished, I'll have book to write...
I would still keep it as didactic as possible, with global variables everywhere it's possible and modularity (so you can likely take away a single part, say move ordering and replace it with a more effective one WITHOUT touching move generator or search).
You're going to get into problems if you keep using global variables, and very long functions with many indentations. My recommendation would be to keep variables as local as possible, and make functions as small as possible.
In fact, I have just feature-gated some functionality in my engine. (It means that the feature can be disabled or enabled at compile time.) The engine contains a module called "wizardry", which has a function that can generate magic numbers; so people can see where those numbers in the move generator come from. It also has a testing-module, that runs a huge PERFT-test on 172 well-known and known-good positions.
Those features don't have to be in the final engine; they only need to be compiled if I'd change something in the move generator somewhere and I either need new magic numbers, or need to test the move generator change. See, building without and with "extra" features:
Code: Select all
$ cargo build --release
Compiling rustic v0.1.0 (C:\Code\rustic)
Finished release [optimized] target(s) in 9.26s
Marcel@WORKSTATION MINGW64 /c/Code/rustic
$ ./target/release/rustic.exe -w
error: Found argument '-w' which wasn't expected, or isn't valid in this context
USAGE:
rustic.exe [OPTIONS]
For more information try --help
Marcel@WORKSTATION MINGW64 /c/Code/rustic
$ cargo build --release --features extra
Compiling rustic v0.1.0 (C:\Code\rustic)
Finished release [optimized] target(s) in 9.10s
Marcel@WORKSTATION MINGW64 /c/Code/rustic
$ ./target/release/rustic.exe -w
Engine: Rustic Alpha 1
Author: Marcel Vanthoor <mail@marcelvanthoor.nl>
Description: UCI Chess Engine, written in Rust
Finding magics for: Rook
a1: 324259726147719720u64 (offset: 0, end: 4095, attempts: 11340)
b1: 1170944767933030402u64 (offset: 4096, end: 6143, attempts: 56287)
c1: 468427139954245960u64 (offset: 6144, end: 8191, attempts: 44740)
d1: 1224988994322960384u64 (offset: 8192, end: 10239, attempts: 100879)
... and so on.
I can easily extend this to the "comm" modules: uci, xboard, and console. Then users can actually decide if they want to compile the engine with all the communication protocols available and then select them with "rustic -c uci" or "rustic -c xboard", or they can decide to compile only the "uci" module if they don't intend to use anything else.
This can be achieved because most stuff is local to the module that uses it. The only things that are global to all modules is the stuff that needs to be global, such as piece types, square types, precalculated bitwise operations (BB_SQUARES, BB_RANKS, BB_FILES in my case); stuff that's used EVERYWHERE.
After I've started researching open source chess engine's code (my new cool skill!) I've realized that even though there're LOTS of engines ther're only a few PATTERNS the are following and within a single patters there might be only a couple of unique ideas. So as far as new engines are doomed to clone those patterns I would at least like follow some GUIDELINES to make it TRULY DIDACTIC and MODULAR. At least I have not yet seen an engine doing implementing the approach I'm promoting, I'll be happy to find one like that some day)
Yes, alpha/beta-engines have been researched for about 60 (!) years now. There have been many refinements, approaches, and implementations during that time, but at some point, the best ones obviously stick around. (There have also been many different implementations of bitboards, of which "fancy magic bitboards" are the fastest at this point. Or maybe PEXT bitboards, but they only work well on newer Intel CPU's.) It's no surprise you're finding those patterns and idea's in most engines.
With regard to modularity... my engine is already on the verge of being able to choose communication protocols while starting, and one could even decide to compile them into the engine or not. If I'd extend this approach to the board representation, move generator, and evaluation, the engine could actually choose among them when starting. (And in the case of evaluations, even switch on the fly.) I don't know if I will ever do that. But it can be done.
One of the engines that did something like this in the past (containing several bitboard implementations in a single engine) is Elephant:
https://www.chessprogramming.org/Elephant