lucasart wrote:ZirconiumX wrote:In My Stupid Opinion, maybe you could try to generate your own magics, or at least encapsulate the magic code in a class.
Matthew:out
I don't think class encapsulation is relevant in this particular case. Magic bitboards have a functional reality, not an object reality. Basically you have precomputed arrays, and 2 functions (bishop_attack and rook_attack). Encapsulating into a class is conceptually wrong, because an instance of that class doesn't make any sense. Why would I ever want to instantiate several Magic objects ? IMO the right way to encapsulate such things is to use a
namespace, not a class.
This misconception (that everything should be encapsulated into a class) typically emanates from Java programmer.
As for generating my own magics, I really don't want to spend my time reinventing the wheel. I have enough things to do before I get something decent (comparable to Ilari's
brillant cutechess-cli).
But yes, you have a fair point about encapsulation in general (class or namespace). I need to think about createing some (well chosen) namespaces.
A class having only static members serves roughly the same purpose as a namespace in this case. (You can find huge discussions about this, of course ...) It is clearly debatable which of the two concepts would be more appropriate, but both would be correct for your case. Personally I dislike namespaces for several reasons:
- they allow, and therefore invite to, distributing declarations of the same scope (namespace) over several (header) files which is impossible when using a class;
- they do not allow access specifiers ("public", "protected", "private"), everything is public so there is no encapsulation, no separation of interface and implementation;
- unlike class declarations, a namespace declaration does not end with a semicolon, a detail which I really hate
There may be other arguments against classes with only static members, of course, and it is a matter of taste and - perhaps - discipline. If you choose namespaces for the purpose of encapsulating "magic bitboards" functionality then it is perfectly fine if you can accept the limitations I mentioned above.
Whenever something has a state that can change during the lifetime of a program I choose a class, even if it has only one instance. In single-CPU chess engines this might be "the board", "the game" or "the searcher" (where the latter is something like a machine that does some processing on demand and has an internal state while working). Also "the evaluator" can have an internal state. "The move generator" usually hasn't (but might have) so it may be a candidate for a namespace or a "static members only" class (sometimes also called a "module").
As to your question about naming, for me the most important points are:
1) be as consistent as possible, but without exaggerating heavily;
2) always imagine that someone else reads your code.
Everything else either derives directly from that, or is less important.
I can tell you my own naming rules, maybe you find 10% of these useful for you.
- Classes, types, namespaces and constants start with an uppercase letter;
- as an exception, typedef's to standard built-in types, like "typedef unsigned int uint;", start with a lowercase letter since they resemble built-in types;
- member functions, member variables, local variables start with a lowercase letter (not counting the prefix yet, see below);
- there are no "global" variables in own code, except for temporary debugging purposes;
- instance member variables have a prefix "m_" that helps to distinguish them easily from formal method parameters (some people use a suffix like "_" either for instance members or for parameters which is also fine, although I think that "m_" increases readability a bit);
- static member variables ("class members") have a prefix "c_";
- regarding use of mixed case vs. use of underlines, I mostly use mixed case but I don't consider that a religion;
- use longer names for almost everything with non-local visibility, like class names or member names;
- short or very short names can be appropriate for parameters and local variables denoting frequently occurring concepts; examples: "i", "j" for loop counters, "s" for square ids (square numbers), "b" for the board (reference to a board instance), "m" for a move, etc.;
- pointer variables start with a "p", in case of instance members with "m_p", in case of static members with "c_p";
- unlike other coding guidelines, mine do not contain a rule that defines something like "zero-terminated string variables start with 'sz', integer variables start with 'i', bools start with 'b'"; sometimes I use something like that but I feel it interferes with my understanding of readability;
- all classes and other types describing something that has a state and can be instantiated are named by a noun that describes one such instance (no plural);
- namespaces (if I use them at all, see above) and those classes having only static members usually are named by a noun that describes the domain (although I still tend to use the name "MoveGenerator" instead of "MoveGeneration");
- for member functions returning boolean attributes use a form like "isXX", "hasXX";
- for member functions returning other attributes (whether they are internally stored as member variables or indirectly derived from them) use the name of the attribute itself (some people use "getXX" which is also o.k. of course but it sounds a bit procedural for me);
- for member functions that modify something and/or do some processing, use a verb describing that activity (this may actually be the most difficult part when choosing good identifiers);
- for member variables and local variables use a name describing the value itself; in case of arrays use either the singular describing one element or a name describing the whole array, but no plural form since that might create irritation at the place of using array elements.
There may be more rules that I don't remember at the moment, but the ones given above are probably the most important ones for me.
Sven