bob wrote:I disagree. The way you use bitboards is _completely_ different than the way you use mailbox representation. With bitboards, you think "sets of squares" whereas with mailbox, you think "looping over a group of squares". The way you think about things is totally different. From generating checks (generate all moves and then cull non-checks to finding sets of squares that can give check and see if you can find a correct piece with a set of attack squares that intersects with the set of targets.
Generating checks would be different, yes. It is a good example of the little low-level components of a chess program which is likely to depend heavily on the board representation. Regardless of the board representation, however, a generator for checking moves is easy to write, and easy to replace.
Different philosophies. I don't go for speed "up front". But I try to never design with blinders on so that I make decisions that will make it very difficult to go fast later. That is an easy mistake to make, and you end up rewriting things that could have been designed differently up front.
That's precisely why I prefer to always optimize for flexibility. I don't care if all my functions are as fast as technically possible; but I do care that if any part of my program turns out to be too slow, I can easily replace it without massive changes throughout the program.
For chess-related stuff, yes. But for things like evaluating mobility, or generating just checks, or generating just legal check evasions, it is a different thing.
I evaluate mobility in precisely the same way both with and without bitboards: I count the number of attacked squares not occupied by friendly pieces. Of course, exactly how the counting is done depends on the board representation, but it is hardly an interesting or important part of the program, and is easily hidden in a little low-level function. In the main evaluation function, I think
Code: Select all
score += RookMobilityWeight * board.rook_mobility(square);
is both more readable and more flexible than
Code: Select all
score += RookMobilityWeight * count_1s(board.rook_attacks(square) & ~friendlyPieces);
Generating checks and check evasions, as mentioned before, are just semi-trivial low-level which are easy to write and easy to replace, regardless of the board representation.
Ditto for evaluation.
I concede that there is currently a considerable amount of direct bitboard manipulation in my evaluation function, but it doesn't have to be that way.
I consider this to be one of the main weaknesses of my program.
You can ask multiple questions with a single AND if you know how to do it and plan on doing that from the beginning.
That's the kind of thing I never want to do, at least not if it ties me to one specific board representation. I have a general dislike for clever-looking code, unless it can be hidden behind some convenient abstraction.
Without doubt, this difference in philosophy is one of the main reasons why your program is faster than mine, but it is also the reason my program isn't that much weaker, despite being developed with a tiny fraction of the development and testing time, and by a vastly inferior programmer. Writing code in such a way that the board representation becomes unimportant and invisible makes it possible even for mediocre programmers to write a decent chess program.
Nobody can argue against your successes, so your approach surely works for you. However, I am sure it wouldn't work for the average programmer. Very few programmers could have written Crafty. Almost anyone could have written Glaurung.
Tord