Sven Schüle wrote:bob wrote:Sven Schüle wrote:[...]The part about "ideas should not be protected" is exactly the point that it is all about. Regarding the evaluation function, what Vas obviously did, like almost all other chess programmers, was that he defined which evaluation criteria (Mark Watkins called it "features" in his paper) he wanted to use for R1beta, and found the criteria from Fruit (which are not especially unique) very useful. For each single criterion you define something like a formula or "evaluation logic" that describes how exactly you apply that criterion. Then you implement it, usually in the most straightforward, efficient and "strong" way that suits your needs for your engine.
Now evaluation criteria are clearly "ideas" or "concepts", and the formulas describing their application are on the level of "algorithms". Both "ideas" and "algorithms" are accepted, also by you, as not protectable.
When it comes to implementing these formulas that you have defined so far, you are now entering the level of "code". But in case of the evaluation function there is not much to do anymore for you, since in the vast majority of cases you have no choices left for your implementation, you immediately derive it from your logical formulas. [...]
Quite the contrary, there are OTHER approaches. We actually consider the square itself, that is, it is more important to have mobility that passes thru center squares and mobility that hits a square on your own edge/corner of the board. In Cray Blitz we computed the value of each square as we did the evaluation, so that our mobility calculation included a bonus for each square that was a dynamic term computed during an evaluation cycle. Then we could produce a bigger bonus for attacking squares near the opponent's king, or for attacking squares in front of an opponent's passed pawn, or for attacking squares on the opponent's side of the board that are weak so that we could safely post a piece on them, etc.
There is not just ONE way to do mobility. Some exclude squares attacked by enemy pawns, some do not.
the list goes on.
Hi Bob,
my point was not that there were only one way to do mobility. It was all about "concept" vs. "implementation".
If you would take your time to actually read and try to understand what people write then replies like the one I am writing now would be redundant

I can accept that you are (maybe?) quite excited these days, given that topic that we can also read as the original title of this thread. Actually I changed the title of this post since we are in a slightly different subtopic here which is not about Crafty but about posts of Uri and Julien upon which I replied. And I am also not here to say anything about that "Crafty-Fruit PST" topic, my point was a different one, and I definitely want you to read again my post above.
However, if you additionally need a very brief summary I'll give it to you:
If I define that my engine shall use an evaluation feature E (say "knight mobility") and shall calculate it with the formula F (say "count pseudolegal knight moves and multiply by a bonus") then I have defined a concept on the logical level. This is
not copyrightable. 10000 or more different chess programs on this planet are allowed to do knight mobility evaluation with this concept, using formula F, without touching any copyright or originality issues. But the key point is:
once you have fixed that formula F, its implementation is almost forced, given a concrete board representation and data structures, and with the requirement of being efficient. So the implementation adds practically zero information to the concept, and therefore "code copying" does not apply.
There are really plenty of such examples in the given case, I'll show them in the near future (but everyone can already see them).
Your implementation of (knight) mobility is different as you pointed out, the reason being that you are using a formula "G". Now please try to write down that formula in words (or call it "concept" if you like), and then tell me how many different efficient ways you see to implement "G" in your engine. I would bet you always end up with the same code as soon as "G" is fixed. Maybe someone else would implement it slightly differently in your case but that highly depends on the complexity of your concept. You will surely agree, though, that the concept F is a fairly simple one for which you will not find substantially different bitboard implementations, for instance, most will look like this (assuming the opening-endgame value system):
Code: Select all
int mobCount = POPCNT(allKnightAttacksFrom(sq) & ~board.occupied(color));
opening[color] += KnightMobilityFactorOpening * mobCount;
endgame[color] += KnightMobilityFactorEndgame * mobCount;
I think that is a common property of the domain of evaluation in chess: it is almost all about selecting eval criteria, defining their calculation formula, and finding optimal parameters. The implementation itself is quite trivial once the concepts are settled. (Of course there may be bugs, no doubt.) That does not apply for many other domains of chess programming. It certainly does not apply for things like pruning techniques or hash table implementation.
Sven
I get the point. But what you are saying is occurring way too late. You are saying "OK, I have an idea, I have designed it, I have flow-charted the code, so now there is only one way to write it."
But if you stop above the level of what is commonly called the architectural design (or detailed design) then your statement makes no sense. Because at that level, you have decided on what you are going to do, and what you want to test to pull this off, but you have not gotten to the how. Which means there are many possible choices.
This would apply to most any evaluation term. Because even good chess books generally don't define an evaluation concept at a level low enough to let us convert it to code directly.
I agree, once you have an architectural design, your hands are pretty much tied. But just deciding to evaluate some concept like a weak pawn, or more specifically a backward/isolated pawn, most definitely does _not_ tie your hands as to how it will / can be implemented.
Simple idea: I want to implement "square of the king" where if a passed pawn is inside the square of the enemy king, it can't run in and promote, while if it is outside the square of the king, it can. Well known idea. Now how to implement it? Two different approaches, one for bitboard, one for mailbox. But you are not done, because there are another few wrinkles to deal with. What about the case where both sides have a passed pawn that can't be caught? But one queens with check? A wrinkle. How to implement this detail? Several choices. Another wrinkle, once you get to the detailed design stage. Are you certain that if the pawn is in the square of the king, the king can reach it? Worst case is king is on a diagonal that is the only way to catch the pawn. What if there is a friendly pawn on the diagonal? King is blocked and can't catch it even though it is technically in the square of the king. What if there is an enemy pawn on that diagonal? Ah, you can capture it and continue chasing and catching the pawn. But what if the pawn is defended? Shoot. Blocked again.
That single piece of code has a ton of different implementations. Your hand is not tied until the very end.
Yes, for a simple term, like "is this rook on an open file?" You might just have one way to answer that. With bitboards, an AND with the right mask will do the trick. With mailbox, you can scan the file to see if there are any pawns on it. Or you can do as we did in Cray Blitz and update a set of file flags to identify which files are open/closed, when we Make/Unmake moves, which saves time. So maybe rook on open file is not so "forced"?
And this goes on and on, somethings done in eval, some done in make/unmake, and some have even counted mobility in the move generator. So saying that once you decide on an idea, you are stuck as to how to implement it doesn't make a lot of sense to me... In fact, we often add new ideas in Crafty "quick and dirty" for testing. If the results look promising, I go back and rewrite "fast and clean" which should make the results even better. And it is not uncommon to later find a better way to implement that idea that is even faster. Or more accurate. Or both.
I don't buy this "one way to write something" argument for anything but the simplest of chess-specific ideas...
For knight mobility, since you asked, I have done it in the following ways in Crafty over the years:
(1) I started with slate's attacks-from and attacks-to bitboards. I just extracted the attacks-from for the square the knight was on, which already had all the attack bit set, and popcnt'ed that and multiplied by a constant C.
(2) Later, I added a second table that contained the popcnt() values for all knight attacks on all squares. Now a table lookup and I get the answer without the popcnt() cost (back prior to the hardware popcnt instruction of course.
(3) Later I decided that mobility is not just about how many moves you have, but about which squares you are attacking (we do this today). We have a set of 4 weights. I AND away all bits but the center 4 squares, and multiply by the center square weight. I then AND away all bits but the next 'ring" and multiply the popcnt of that with that ring's weight. Repeated 4 times.
(4) early on, I removed squares from the mobility for pieces if the square was attacked by a piece. That was easy with the "attacks-to" bitboard from Slate's book chapter, where for any square, I can obtain a bitmap that shows all other squares that are attacking that square, both friend and foe. From that I can determine how many attacks by each side, and by which pieces. Lots of choices to play games here. Mobility to a square you attack less than the opponent is not very useful. Mobility to a square attacked by an enemy pawn is certainly not very useful.
Do I need to continue?
This is like saying that once you know you need to sort something, there is only one way to sort it. Not so fast. Bubble sort. Heap sort. Even an insertion/selection sort will work depending on the other details of how it is used...
I simply don't buy this "once you specify the idea, the implementation details become fixed." My experience is completely the opposite. I rarely see just one way to do anything. The trick is to see the "best way". Hopefully the first time you implement it, otherwise the second, or the third.