My castling code

Discussion of chess software programming and technical issues.

Moderators: hgm, Rebel, chrisw

User avatar
mvanthoor
Posts: 1784
Joined: Wed Jul 03, 2019 4:42 pm
Location: Netherlands
Full name: Marcel Vanthoor

Re: My castling code

Post by mvanthoor »

As soon as it is done, my engine will be open sourced on Github.

You can then draw your own conclusions with regard to its readability. Playing strength and functionality is not a goal for the first version.

And yes, I haven't written a complete chess engine yet, but I have been programming professionally for the last 15 years (the first 5-6 of them in C; and I've had to maintain a lot of almost incomprehensible code where guessing was sometimes the only way to try and find out what it did.

I do think that descriptive variable and function names (if not overdone) and avoiding deep nesting and long functions make code more readable, more maintainable, better testable, and easier to comprehend.

I also refer to your own post in this topic where you state that you are not understanding everything about Michael's idea and code. ps: that's true for me as well, and I'm convinced that I would understand it better with a more descriptive coding style.

It isn't for nothing that in businesses, more descriptive (and safer) languages are now the default even though they are slower. I greatly lament the fact that Rust went for the C-like way of shorthanding everything.

Why fn? Why not just function?
Why impl? Why not implementation?
Why len()? Why not length()?
Why pub and not public?

And so on. Sometimes, it seems as if "system programming languages" are created to look cryptic because C looked cryptic. In the 70's and 80's, less characters was better for various reasons. There is no need for that in 2020 anymore, except for maintaining similarity to C.
Author of Rustic, an engine written in Rust.
Releases | Code | Docs | Progress | CCRL
AlvaroBegue
Posts: 931
Joined: Tue Mar 09, 2010 3:46 pm
Location: New York
Full name: Álvaro Begué (RuyDos)

Re: My castling code

Post by AlvaroBegue »

I also have attention problems, which makes it impossible for me to read code with short identifiers. Mathematicians are generally terrible with this, using single-letter variable names for everything. Once there are more than six of these being used, I have to go back and forth between the text and the definitions, and I end up not understanding much. This is pretty a big part of why I left mathematics.

I've been a professional programmer for 20 years (since I left math) and for the first 10 or so, I used to have to fix bugs in a realtime trading system (that means getting a phone call at 3am because something failed, reading a bunch of code, fixing the problem and going back to bed; every minute the system is down costs the company thousands of dollars). This gave me a deep appreciation for simple, clear code and well chosen names.

Finding good names is a matter of good taste, and there aren't any hard rules, but abbreviations that are obvious to you when you write the code can confuse the reader endlessly.

Getting the computer to do what you want is easy. Good code is written for the benefit of the people that will read it.
mar
Posts: 2554
Joined: Fri Nov 26, 2010 2:00 pm
Location: Czech Republic
Full name: Martin Sedlak

Re: My castling code

Post by mar »

You should take your own advice then, RuyDos is full of single-character variables/function arguments and even function names.
I have no problem with short names as I understand that o is occupancy, N stands for north etc.,
but if you assume a certain position and do the exact opposite yourself, well...

As for C - the only thing that C did wrong was operator precedence: bitwise ops should have had higher priority than relational, so that we could write
if (a & b == 0) ... instead of ((a & b) == 0)
Due to this, I write more brackets than necessary as I don't want to take any chances. Unfortunately basically all languages inherited this flaw
Martin Sedlak
User avatar
mvanthoor
Posts: 1784
Joined: Wed Jul 03, 2019 4:42 pm
Location: Netherlands
Full name: Marcel Vanthoor

Re: My castling code

Post by mvanthoor »

AlvaroBegue wrote: Mon Apr 20, 2020 3:48 am I also have attention problems, which makes it impossible for me to read code with short identifiers. Mathematicians are generally terrible with this, using single-letter variable names for everything. Once there are more than six of these being used, I have to go back and forth between the text and the definitions, and I end up not understanding much. This is pretty a big part of why I left mathematics.
Exactly the same here. I've been known to give single-letter or symbol variables a longer descriptive name, use that throughout the calculation, and then change it to the "official" one.
I've been a professional programmer for 20 years (since I left math) and for the first 10 or so, I used to have to fix bugs in a realtime trading system (that means getting a phone call at 3am because something failed, reading a bunch of code, fixing the problem and going back to bed; every minute the system is down costs the company thousands of dollars). This gave me a deep appreciation for simple, clear code and well chosen names.
Seems you did a similar job to mine; fixing code on the fly. The difference is that you worked with a trading system, where I worked with machinery and embedded systems running 24/7.
Finding good names is a matter of good taste, and there aren't any hard rules, but abbreviations that are obvious to you when you write the code can confuse the reader endlessly.
A lot of code I read is like trying to read mathematical formulas wrapped in if, for and while, because of the short variable and function names. As Michael said in a different topic where we also touched on this: to each their own, but my variable names are longer than what I usually see.

Mar: With regard to operator precedence I never take any changes by the way, especially not if there are assignments within the comparisons. Same for macro's in C; basically everything is in parenthesis. I always write (x == 5) && ( (a == 1) || (a == b) ) even though some of the parenthesis I write are actually not necessary. (Rust has built-in linting that starts complaining if you use parenthesis that are obviously unnecessary, and I get whacked over the head for this at regular intervals.)
Author of Rustic, an engine written in Rust.
Releases | Code | Docs | Progress | CCRL
User avatar
mvanthoor
Posts: 1784
Joined: Wed Jul 03, 2019 4:42 pm
Location: Netherlands
Full name: Marcel Vanthoor

Re: My castling code

Post by mvanthoor »

mar wrote: Mon Apr 20, 2020 6:40 am I have no problem with short names as I understand that o is occupancy, N stands for north etc.,
but if you assume a certain position and do the exact opposite yourself, well...
You know this, because some things are "unspoken rules" in the chess engine world, like N being North, "occ" being occupancy, "pos" being position, "bb" are bitboards, and so on. For people new to chess programming it can be confusing though, especially if an engine doesn't have many comments and explanations in the code.

I use some of the well-known 'default' shorthands as well in my engine. Sometimes, I also use shorthands that are defined in the header. For example, I have some where I pass the move generator struct into the function, and it has a header such as this:

Code: Select all

pub fn initialize(mg: MoveGenerator) {
	mg.some_variable = ...
}
As for C - the only thing that C did wrong was operator precedence: bitwise ops should have had higher priority than relational, so that we could write
if (a & b == 0) ... instead of ((a & b) == 0)
Due to this, I write more brackets than necessary as I don't want to take any chances. Unfortunately basically all languages inherited this flaw
Other things that C doesn't really do too well is capability of reading and writing across array bounds, implicit variable casting, etc... . Compared to modern languages (and even some old languages... ADA comes to mind), C's typing is weak. It CAN be very handy if you know exactly what you're doing, but if you make a mistake somewhere, your program either crashes or misbehaves. Even though I'm the first to state that having to type "some_thing as usize" in Rust. "some_thing" is a u8. I'm wanting to use it in a context where a usize is needed, such as array indexing. A u8 WILL fit into a usize (= 'largest unsigned integer on the system/OS), so why make me state the conversion? It is consistent, though...
Author of Rustic, an engine written in Rust.
Releases | Code | Docs | Progress | CCRL
User avatar
leanchess
Posts: 176
Joined: Sun Dec 08, 2019 8:16 pm
Full name: Dmitry Shechtman

Re: My castling code

Post by leanchess »

I must say I use single-letter names for local parameters and variables almost exclusively, so my castling code looks like this:

Code: Select all

	board[k] = ANY;
	board[i] = ANY;
	board[j] = KING;
	board[l] = ROOK;

	uint64_t n = BITS(j);
	uint64_t mn = m | n;
	uint64_t op = BITS(k) | BITS(l);
	ps->bits[ANY]  = s ^ (mn | op);
	ps->bits[KING] = n;
	ps->bits[ROOK] = r ^ op;

	pnext->dirty = d | mn;
	pnext->check = c;
(You may notice that I resorted to having two-letter names for OR results, so it's not a golden rule.)

Now, as I noticed that the board updates are confined to a single 64-bit word, I came up with this brilliant optimisation idea:

Code: Select all

	uint64_t b = attack_bits[index + CASTLING_BOARD_BB];
	*(uint64_t*)&board[i & 0x38] ^= b;
where attack_bits are abused to contain precalculated rows of the board (0th or 7th, depending on colour) in this case. Much to my surprise, perft(7) becomes about 1 second SLOWER (out of roughly 66) with this implementation. Any ideas?
Terje
Posts: 347
Joined: Tue Nov 19, 2019 4:34 am
Location: https://github.com/TerjeKir/weiss
Full name: Terje Kirstihagen

Re: My castling code

Post by Terje »

leanchess wrote: Mon Apr 20, 2020 3:26 pm Any ideas?
Write readable code :)
User avatar
leanchess
Posts: 176
Joined: Sun Dec 08, 2019 8:16 pm
Full name: Dmitry Shechtman

Re: My castling code

Post by leanchess »

Terje wrote: Mon Apr 20, 2020 5:08 pm Write readable code :)
Thanks for the tip, weiss guy! ;) I'm going to name my engine Schwartz, as it will probably be the complete opposite of yours in terms of code readability :P

Back to my question. Maybe I wasn't clear enough, but I was asking about the reason for the optimisation attempt failing. I believe the code in the second snippet is reasonably readable (I even used the very long index variable name as I was running out of alphabet :D). Here's the part of the code from the first snippet that I was trying to replace, rewritten especially for the readability czars:

Code: Select all

	board[rook_from] = ANY;
	board[king_from] = ANY;
	board[king_to] = KING;
	board[rook_to] = ROOK;
Terje
Posts: 347
Joined: Tue Nov 19, 2019 4:34 am
Location: https://github.com/TerjeKir/weiss
Full name: Terje Kirstihagen

Re: My castling code

Post by Terje »

leanchess wrote: Mon Apr 20, 2020 5:40 pm
Terje wrote: Mon Apr 20, 2020 5:08 pm Write readable code :)
Thanks for the tip, weiss guy! ;) I'm going to name my engine Schwartz, as it will probably be the complete opposite of yours in terms of code readability :P
:D
leanchess wrote: Mon Apr 20, 2020 5:40 pm
Back to my question. Maybe I wasn't clear enough, but I was asking about the reason for the optimisation attempt failing. I believe the code in the second snippet is reasonably readable (I even used the very long index variable name as I was running out of alphabet :D). Here's the code from the first snippet I was trying to replace rewritten especially for the readability czars:

Code: Select all

	board[rook_from] = ANY;
	board[king_from] = ANY;
	board[king_to] = KING;
	board[rook_to] = ROOK;
"running out of alphabet" :lol:

If the relevant parts of the attack_bits array isn't always in L1 cache the 4 assignments will be way faster. Even if it is, I'm guessing the assignements probably still win or go equal. (I'm by no means an expert though) You could try compiler explorer to see what the machine code generated for each version is like.

Would like to suggest using EMPTY instead of ANY in the context of empty/occupied squares. Doubt I've heard anyone say a square was "any" ;)
User avatar
leanchess
Posts: 176
Joined: Sun Dec 08, 2019 8:16 pm
Full name: Dmitry Shechtman

Re: My castling code

Post by leanchess »

Terje wrote: Mon Apr 20, 2020 6:07 pm If the relevant parts of the attack_bits array isn't always in L1 cache the 4 assignments will be way faster. Even if it is, I'm guessing the assignements probably still win or go equal. (I'm by no means an expert though)
attack_bits is a 1472-long 64-bit array, so that probably is a cache miss. I'll try to compress attack_bits, but I'm afraid it might produce the opposite results, since right now attacks for a given slider at a given square are all close, whereas reusing rays that originate from the edges could result in multiple misses per piece-square. Now that I'm thinking about it, maybe it's worth doing the opposite, i.e. duplicating the entire array (save for the castling hack) for the queen.

You could try compiler explorer to see what the machine code generated for each version is like.
I should definitely do that at some point. Right now it's mostly down to trial and error.

Would like to suggest using EMPTY instead of ANY in the context of empty/occupied squares. Doubt I've heard anyone say a square was "any" ;)
The reason I'm using that is that ANY (0) is the index of the per-side occupancy bitboard, which gets dummy-updated whenever a destination square is empty; but I suppose you're right, an extra #define wouldn't hurt performance...

Thanks!