Bitboard CHESS ENGINE in C: YouTube series by Code Monkey King

Discussion of anything and everything relating to chess playing software and machines.

Moderators: hgm, Rebel, chrisw

User avatar
maksimKorzh
Posts: 771
Joined: Sat Sep 08, 2018 5:37 pm
Location: Ukraine
Full name: Maksim Korzh

Re: Bitboard CHESS ENGINE in C: YouTube series by Code Monkey King

Post by maksimKorzh »

mvanthoor wrote: Tue Sep 29, 2020 5:36 pm
maksimKorzh wrote: Tue Sep 29, 2020 4:53 pm Thanks Marcel, this is so kind of you!
Btw the fact that I'm not posting in Rustic progress thread doesn't mean that I don't read that)))
It's not required to post there :) It's just a topic where I put down some stuff regarding the engine's development status. The reason is the fact that I received a few "and where is your engine?!" comments when I posted remarks to some topics. Now people can see what the engine can do, and how it does it, even though it's not finished yet.
And regarding move scoring - obviously meaningful and correct variable names make the code cleaner, but I really doubt that you could encounter some issue assuming that move score is stored within the move structure (struct move {int move, int score}) because assuming that structure you would be referencing inner fields of - move and score - still there would be a difference in referencing like move->eval VS position->eval or global position eval or whatever data structure you'll encapsulate your eval within. In BBC I've dropped the move struct completely and using move integer only for simplicity, clarity and to completely modularize move ordering itself, so it's done just right before the loop over the moves within alpha beta search instead of ordering moves on the fly within that loop like VICE, TSCP and many other monolithic engines do.
It wouldn't be a problem to use "evaluation" instead of "score", but it could be confusing, because the value is not an evaluation. I store "score" in the move integer, by the way. The reason I use a struct, is because I can put member functions into it:

https://github.com/mvanthoor/rustic/blo ... en/defs.rs

So I can do this:

let current_move = (some move here)

current_move.piece()
current_move_from()
current_move.to()

And so on.
I see.
I was thinking about encoding score within a move believe it or not but dropped that idea to make move scoring separate and explicit.

why do you use functions pointers within a struct when it seems to be faster just to do decode directly (using macros if C):

Code: Select all

/*
          binary move bits                               hexidecimal constants
    
    0000 0000 0000 0000 0011 1111    source square       0x3f
    0000 0000 0000 1111 1100 0000    target square       0xfc0
    0000 0000 1111 0000 0000 0000    piece               0xf000
    0000 1111 0000 0000 0000 0000    promoted piece      0xf0000
    0001 0000 0000 0000 0000 0000    capture flag        0x100000
    0010 0000 0000 0000 0000 0000    double push flag    0x200000
    0100 0000 0000 0000 0000 0000    enpassant flag      0x400000
    1000 0000 0000 0000 0000 0000    castling flag       0x800000
*/

// encode move
#define encode_move(source, target, piece, promoted, capture, double, enpassant, castling) \
    (source) |          \
    (target << 6) |     \
    (piece << 12) |     \
    (promoted << 16) |  \
    (capture << 20) |   \
    (double << 21) |    \
    (enpassant << 22) | \
    (castling << 23)    \
    
// extract source square
#define get_move_source(move) (move & 0x3f)

// extract target square
#define get_move_target(move) ((move & 0xfc0) >> 6)

// extract piece
#define get_move_piece(move) ((move & 0xf000) >> 12)

// extract promoted piece
#define get_move_promoted(move) ((move & 0xf0000) >> 16)

// extract capture flag
#define get_move_capture(move) (move & 0x100000)

// extract double pawn push flag
#define get_move_double(move) (move & 0x200000)

// extract enpassant flag
#define get_move_enpassant(move) (move & 0x400000)

// extract castling flag
#define get_move_castling(move) (move & 0x800000)
But I guess most likely this violates your coding principles.
User avatar
mvanthoor
Posts: 1784
Joined: Wed Jul 03, 2019 4:42 pm
Location: Netherlands
Full name: Marcel Vanthoor

Re: Bitboard CHESS ENGINE in C: YouTube series by Code Monkey King

Post by mvanthoor »

maksimKorzh wrote: Tue Sep 29, 2020 6:01 pm why do you use functions pointers within a struct when it seems to be faster just to do decode directly (using macros if C):
...
But I guess most likely this violates your coding principles.
You use C macro's where you wrap the shifts and & operations into something more readable, I use member functions:

Code: Select all

pub fn from(&self) -> Square {
	((self.data >> Shift::FROM_SQ as u64) & 0x3F) as Square
}

pub fn to(&self) -> Square {
	((self.data >> Shift::TO_SQ as u64) & 0x3F) as Square
}
The shift value itself is wrapped in the Shift struct, to also give it a name.

When compiling, the compiler replaces "Shift::To_SQ as u64" with the value that's in the struct, and because the function is very short, it even omits the entire function call and places that one line in the spot where the call was (auto-inlining). In fact, the entire struct doesn't exist in the end. The result is the same as yours. In C, the pre-processor removes the macro and replaces it with the called values, and because you also end up with a one-liner, it's inlined as well.

The Rust way is just a more modern notation to do the same thing.

(Rust also has macro's, but they are not intended for simple text replacements like the C macro's, even though they can be used as such. They're intended to write code that can create other code on the fly. I try to forget that they exist, because that stuff makes my head spin. Rust macro's are NOT easy.)

And don't forget; Rust uses LLVM as its compiler backend, which is also used by CLANG. If you don't do very strange things, the Rust compiler creates (almost) the same machine code as CLANG would do for an equivalent C-program, so they will be about the same speed. If you read through this thread ( http://talkchess.com/forum3/viewtopic.p ... 7&start=10 ) where Terje assisted me / competed with me to optimize make_move() , unmake_move(), and the move generator, you can see that Rustic is just as fast as Weiss... and Weiss is a VICE derivative, in C :)
Author of Rustic, an engine written in Rust.
Releases | Code | Docs | Progress | CCRL
User avatar
maksimKorzh
Posts: 771
Joined: Sat Sep 08, 2018 5:37 pm
Location: Ukraine
Full name: Maksim Korzh

Re: Bitboard CHESS ENGINE in C: YouTube series by Code Monkey King

Post by maksimKorzh »

BBC 1.1 is released:
https://github.com/maksimKorzh/bbc/releases/tag/1.1

What's new:
- added dynamic hash memory allocation
- added tapered evaluation
- added PST from PeSTO engine by Ronald Friederich
- temporary removed all the eval by PST
- empty PV bug fixed
- timing bugs fixed
- id author/name by fixed

This version's strength is slightly weaker than VICE - while BBC crushes VICE in ultra bullet, still loses in blitz 2+1
Here's the latest match:
https://github.com/maksimKorzh/bbc/tree ... ITZ(2%2B1)
User avatar
maksimKorzh
Posts: 771
Joined: Sat Sep 08, 2018 5:37 pm
Location: Ukraine
Full name: Maksim Korzh

Re: Bitboard CHESS ENGINE in C: YouTube series by Code Monkey King

Post by maksimKorzh »

A new update on BBC engine development: version 1.2b is now available:
https://github.com/maksimKorzh/bbc/blob ... bbc_1.2b.c

what's new:
- fixed forfeit on time with increment bug
- restored evaluation
- slightly stronger than version 1.2a
User avatar
maksimKorzh
Posts: 771
Joined: Sat Sep 08, 2018 5:37 pm
Location: Ukraine
Full name: Maksim Korzh

Re: Bitboard CHESS ENGINE in C: YouTube series by Code Monkey King

Post by maksimKorzh »

SUMMARY ON THE SERIES (https://www.youtube.com/watch?v=QUNP-Uj ... wfiWNI76Cs)
Together we've been through the process of creating a bitboard chess engine with ELO strength of 2000+ (latest version is yet about to be rated, hopefully) and probably involving the simplest for understanding C code in existence with EVERY single line commented. My goal was to highlight the core gist essence parts of a modern engine and implement them in the easiest way possible. At this moment I feel like the goal has been satisfied.

What do you guys think?

Now some thoughts on the future of the project.
It sometimes happens that with one idea in mind you eventually end up in completely different place, e.g. I thought I was teaching beginners but after the move generator has been finished the whole process has turned more likely into my own learning (I was taught by Harald Luessen and Pedro Castro - without their help I would've never reached the current point ). Now when the vast majority of features has been implementing obviously there's LOTS of things to improve but what I've noticed so far is that the deeper you go - the less interesting it becomes to watch meanwhile the matter of improving engine's strength is getting slower and slower as we go further on, e.g. improving ELO strength from 1600 to 2000 is quite exciting to watch because of really lots of interesting features are getting added but at this current moment there's nothing really much to look at, for example the last few days I've been fixing time forfeit bug on 1 sec increment mode and restoring previously commented evaluation features (with some minor adjustments) so it seems to be making more sense by making quick catch up video on the development progress updates. Also like whatever TV series, no matter how good it is, if it doesn't stop when there's nothing more to say it just spoils good stuff that was done before IMO. So what I want to say is while the BBC development progress is going on (not that fast as before but still) I would most likely END the series at current state and probably would only be announcing some updates in regards to it.

And now I would be happy to know your thoughts guys in regards to the future of chess programming channel like what kind of topics are to be covered next. I have some ideas and I'm really tempted to know whether some of them would be interesting to you:
1. Chess analytics (I've already tried some, even though not very successful in terms of gaining engine's strength, still pretty exciting in terms of insights)
2. Automated testing, e.g. writing some light version of cutechess-cli in python with an input as a JSON settings file (I don't like command line arguments for it's hard to remember them while in JSON file you can create 2-3 templates and use them forever)
3. Web based GUI (Python Flask) to wrap all the previous ideas into a single app, something like BBC Studio - play/stats/PGNs/etc.

I was also thinking about things like making an android app to monetize this work in future, but as a freelancer it's incredibly difficult for me to switch from client-oriented to product-oriented approach. Most likely my work is doomed to be monetized only via YouTube ads but even to reach that really lots of things should be done, e.g. I'm thinking of a new video format - CHESS ENGINE OVERVIEW to highlight the most engine's features and interesting implementations. Again like working as freelancer in the client -> job -> income format I don't have that much free time so I'd really like to try to switch to some "product-driven" approach to make income non-linear dependent on the work, e.g. just maintaining the product whilst getting some stable income. Currently I'm testing this model with my initial youtube channel dedicated to web scraping where I just got my very first 1k of subs and enabled monetization.

Also I've noticed one interesting thing - I've realized that if I was doing all this work for myself I wouldn't complete even a half while doing this for community not only makes perfect sense but also gives a great inspiration. So what I want to say is very simple - without your support it's incredibly hard for me to know where and why to proceed.

Any thoughts/suggestion/questions are highly appreciated.

P.S. I'm waiting for a new MIC to arrive, so the sound quality of the videos would hopefully become better.