Moderator: Andres Valverde
....
else if (i < 4) {
if (DEFEND(nsq,bishop))
defender[de1++]=bishop;
else if (DEFEND(nsq,queen))
defender[de1++]=queen;
else if (ATTACK(nsq,bishop))
attacker[at1++]=bishop;
else if (ATTACK(nsq,queen))
attacker[at1++]=queen;
else break;
} else if (i >= 4) {
....
H.G.Muller wrote:Thanks for the useful reactions!
I agree with L?szl? that it is nice that (for the purpose of move ordering) there is a safe side to which one can err.
The terminology 'x-ray' is new to me. Does this mean the inclusion of pieces in the exchange that are behind other pieces that move in the same direction (such as doubled Rooks, or a Queen backed by a Bishop)?
Stuart's routine seems a bit inaccurate: if I got it correctly it searches through any piece on the ray it is tracing upto the very edge of the board, even if the piece it encounters is not moving in the direction of the ray. (This is probably a bug, I suppose the last break in the sliders loop was meant to take care of that, but it is unreacheable: either i<4 or i>=4, so the first two ifs catch everything. If the i-loop is continued should really depend on one of the if(DEFEND/ATTACK) catches, these should have formed in if() else if()... chain, so that if none of them catches, control falls through to the break that is now commented out. This is more efficient as well, since once a Bishop is found as defender, there will certainly not be a defender-Queen on the same square, neither can there be an attacker.)....
else if (i < 4) {
if (DEFEND(nsq,bishop))
defender[de1++]=bishop;
else if (DEFEND(nsq,queen))
defender[de1++]=queen;
else if (ATTACK(nsq,bishop))
attacker[at1++]=bishop;
else if (ATTACK(nsq,queen))
attacker[at1++]=queen;
else break;
} else if (i >= 4) {
....
In addition the qsort() seems to sort irrespective of wheter a piece was only seen by X-rays, as if it were allowed to hop over the leading piece: A Pawn defended by a Bisshop behind a Queen and attacked by 2 Knights will be considered a bad capture because the recapture with the Bishop is sorted in front, while in fact it is a winning capture, gaining a Pawn.
Such forced sub-optimal capture order makes the sorting problem not obvious, although I am inclined to think that it never pays to involve a higher piece that is backed by a lower one earlier in the sequence than you would involve it when it is not backed, because if two unequal pieces move along the same ray, the higher one must be a Queen. So the leading piece must be normally sorted, but the trailing piece must not be allowed to overtake it, even if it is lower. Bubble-sort seems more easily adapted to do this than quick-sort, and is efficient enough since the attacker or defender array will hardly ever contain more than 3 pieces...
Pawns are also not backed by Bishops or Queens, but I suppose you are aware of that and that that is the reason why you say X-rays are only partially supported.
David Weller wrote:Just to chime in ...
I use sSEE [simple SEE] in my engine for ordering captures
if victim is protected by anything < attacker, relegate capture to back of list
smcracraft wrote:Yes - there are many problems with the code and even given that the result for the practical program was great. Absolutely great.
H.G.Muller wrote:How far can you go making a smart SEE without making the algorithm as expensive as a (quiescence-like) search? Are there smart algorithms around for this? I would be very happy with a SEE that would recognize pinnings (also on other pieces than the King!), (de)blocking threats (such as doubled Rooks, but also things like a Bishop stepping away from a Rook line) and overloaded defenders.
Tord Romstad wrote:There is an alternative approach which is almost as accurate, but much easier to implement: Write a simple and fast SEE, and a function which takes a position as input and decides whether the simple SEE can be trusted for this position.
H.G.Muller wrote:I am still very much in doubt if having a SEE is worth it. Mind you, I am not contesting the idea that move sorting is a crucial factor in determining the search efficiency. I just wonder if it is possible to achieve similar quality move sorting without the SEE.
Even a very basic SEE, e.g. the one posted above by Stuart, is comparatively expensive: it requires generation of a complete list of captures for both sides. In usual forward move generation this would produce all non-captures as a side effect, since the sliding pieces must scan though all normal moves to find the capture. (This might be different with bitboards?) Inverse move generation (to see what comes into a square, in stead of what goes out of it) is even more expensive, since you have to look in all 16 directions rather than just in the directions that the piece on that square moves. So the most efficient way to generate the attack/defense information that SEE needs as input might be to run your regular move generator, (perhaps telling it that it should use X-rays and report pseudo-captures), and then quick-sort the moves by target-square. At least if you want to apply SEE globally to the board.
A SEE that does only recaptures (no pins or overloads) is equivalent to a QS that only considers recaptures. In QS (and every position starts with a QS because of internal iterative deepening) I use a special move generator, that starts by selectively generating the captures of the piece that last moved, similar to Stuarts example. I do this by (after identifying the pawn captures, which can come fromonly 2 squares) running through the piece list, low to high, and testing if that pice can capture the target suare from a delta-vector table. This automatically produces the moves in the desired order. You might argue that playing out the exchange in search, you would have to redo the move generation for the same side many times (i.e. repeat it every 2 ply), while SEE does it only once and continues to use the generated list for the full depth of the exchange. Moves to the same square never interfere, so you can do that. This is where your gain must come from, SEE has no other advantages over QS.
On the other hand QS starts generating the captures to the target square as you would do for SEE, and as soon as it finds one, it can try it. The moves were selectively generated in the required order, so no need to generate them all and sort first. If that first capture tried turns out bad (below (lazy) eval, which in QS is a fail low), and the refutation move was a recapture by the opponent, it does not continue generating and trying recaptures with more valuable pieces. Just like SEE would also only compute the single capture order expected to be most favorable. (Only with other refutations, which can only occur in QS that also search other things than recapture, it tries other captures. This under the assumption that the refutation might have nothing to do with the captured piece being suffiiently defended, but could be tied to problems with using this particular capturer, i.e. it might have been pinned or overloaded.)
In many cases there might never be a second time that the same side moves. The opponent might not even get a chance, if there were no attacks to the square. (SEE might have wasted time making the defenders list anyway...) If the exchange is so deep that the same side does have to move again, it would go through the piece list again from the beginning, in stead of continuing at the piece it used two plies earlier. This is indeed a duplication of effort. But not entirely in vain, because it implements the x-rays: a lower-valued piece earlier in the list might now have access to the square under attack. Otherwise it was very easy to prevent, by telling qsearch() where it should start scanning the piece list. (E.g. pass this as an argument to the recursive call when you were already in the process of selective recapture generation. Even with x-rays, you can tell it when it does'nt have to bother with pawns again, because pawns never have x-ray vision. )
So it seems to me that a very simple QS that selectively generates recaptures can on the average even beat a SEE in speed. In addition it might be very easy to make it smarter than just recaptures, by continuing the search after the recaptures have been refuted and you don't consider it worth trying more of them. For instance, searching capture moves that go over the square abandoned on the previous ply (which can also be generated selectively) you would detect and take account of all pinnings.
Return to Programming and Technical Discussions
Users browsing this forum: No registered users and 37 guests