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.
My castling code
Moderators: hgm, Rebel, chrisw
-
- Posts: 931
- Joined: Tue Mar 09, 2010 3:46 pm
- Location: New York
- Full name: Álvaro Begué (RuyDos)
Re: My castling code
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.
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.
-
- Posts: 2559
- Joined: Fri Nov 26, 2010 2:00 pm
- Location: Czech Republic
- Full name: Martin Sedlak
Re: My castling code
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
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
-
- Posts: 1784
- Joined: Wed Jul 03, 2019 4:42 pm
- Location: Netherlands
- Full name: Marcel Vanthoor
Re: My castling code
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.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.
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.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.
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.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.
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.)
-
- Posts: 1784
- Joined: Wed Jul 03, 2019 4:42 pm
- Location: Netherlands
- Full name: Marcel Vanthoor
Re: My castling code
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 = ...
}
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...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
-
- Posts: 176
- Joined: Sun Dec 08, 2019 8:16 pm
- Full name: Dmitry Shechtman
Re: My castling code
I must say I use single-letter names for local parameters and variables almost exclusively, so my castling code looks like this:
(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:
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?
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;
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;
-
- Posts: 347
- Joined: Tue Nov 19, 2019 4:34 am
- Location: https://github.com/TerjeKir/weiss
- Full name: Terje Kirstihagen
-
- Posts: 176
- Joined: Sun Dec 08, 2019 8:16 pm
- Full name: Dmitry Shechtman
Re: My castling 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
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 ). 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;
-
- Posts: 347
- Joined: Tue Nov 19, 2019 4:34 am
- Location: https://github.com/TerjeKir/weiss
- Full name: Terje Kirstihagen
Re: My castling code
"running out of alphabet"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 ). 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;
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"
-
- Posts: 176
- Joined: Sun Dec 08, 2019 8:16 pm
- Full name: Dmitry Shechtman
Re: My castling code
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.
I should definitely do that at some point. Right now it's mostly down to trial and error.You could try compiler explorer to see what the machine code generated for each version is like.
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...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"
Thanks!