All time spent in quiescent search - why?

Discussion of chess software programming and technical issues.

Moderators: hgm, Rebel, chrisw

User avatar
Don
Posts: 5106
Joined: Tue Apr 29, 2008 4:27 pm

Re: All time spent in quiescent search - why?

Post by Don »

I suspect he has the stand pat rule wrong somehow.

Essentially you can STOP searching if the score before generating any moves is >= beta, but even if that is not the case you STILL have the option to STOP searching if you don't like any of the captures.

So the FIRST move you should try is the "stand pat" move, in other words NO move and treat this as if it were a searched subtree - a fully searched move that returned the stand pat score and can be accepted if it's "better" than the other real captures. Which also means that it can update alpha if appropriate.

My guess is that this is what you have been missing. You can stand pat if it gives you a beta cutoff, but you can also stand pat even if it doesn't (if that is better than trying other moves.)

Don

hgm wrote:Something seems wrong here:

[d]rnbqk2r/ppp1bpp1/3p1n1p/4p1B1/3PP3/2N2N2/PPP2PPP/R2QKB1R w KQkq - 0 6

Pd4xe5 Nf6xe4 Bg5xe7 Ne4xc3 Be7xd8 Nc3xd1 Bd8xc7 Pd6xe5 Bc7xb8 Nd1xf2 Bb8xa7

Upto d6xe5 everything makes sense, because both sides have been capturing equal value on each move. White took the lead each time, so that black had to recapture and could not afford to stand pat without incurring a loss. After the recapture it was neutral again, so that white had to try something to see if he could do better.

But after Bxb8 Nxf2 black did not equalize, and captured a Pawn after losing a Knight. So white is at +2 here by standing pat, which should be above beta, and thus cause an immediate cuttoff. So it should never get to searching Bxa7.

[d]rBb1k2r/pp3pp1/7p/4p3/8/5N2/PPP2nPP/R3KB1R w KQkq - 0 11

Kxf2 is another move that should have been searched before Bxa7, and should also have produced a beta cutoff, if the stand pat had not done that already.
bob
Posts: 20943
Joined: Mon Feb 27, 2006 7:30 pm
Location: Birmingham, AL

Re: All time spent in quiescent search - why?

Post by bob »

mike_bike_kite wrote:I have quite a simple chess program. It does an ID search with quiescence with moves done in MVV - LVA order. Everything seems OK but I'm now seeing that the depth in the ID isn't going very deep at all (to save embarrassment I won't say how deep) but the quiescence search (the bit that does all the takes) just goes on and on, sometimes to a depth of 30.

In a given situation it will try the most likely takes first ie pawn x queen before queen x pawn but the problem is it will still attempt these other takes and this is leading to some very deep (and pointless) searches.

I also do a stand pat score with alpha beta cut off before attempting any jumps.

Any ideas?

(I'd appreciate it if you could make your explanations as simple as possible)
Couple of things that I use.

(1) use SEE and do not search captures with SEE < 0, as those are generally bad.

(2) use MVV/LVA ordering, so that you get rid of the queens and rooks first, since they tend to go on feeding forays and drive the search deeper as the queens eat away on opposite sides of the board.

(3) qsearch is always going to be a major part of the tree, because of the exponential shape the tree has. You ALWAYS get to the qsearch at the end of every normal branch you search, and then you add on captures. It only takes a couple to make the q-search examine 90% of the total nodes searched.
User avatar
lucasart
Posts: 3232
Joined: Mon May 31, 2010 1:29 pm
Full name: lucasart

Re: All time spent in quiescent search - why?

Post by lucasart »

mike_bike_kite wrote:I have quite a simple chess program. It does an ID search with quiescence with moves done in MVV - LVA order. Everything seems OK but I'm now seeing that the depth in the ID isn't going very deep at all (to save embarrassment I won't say how deep) but the quiescence search (the bit that does all the takes) just goes on and on, sometimes to a depth of 30.

In a given situation it will try the most likely takes first ie pawn x queen before queen x pawn but the problem is it will still attempt these other takes and this is leading to some very deep (and pointless) searches.

I also do a stand pat score with alpha beta cut off before attempting any jumps.

Any ideas?

(I'd appreciate it if you could make your explanations as simple as possible)
could you show us your quiescent search function ? it's hard to say what you're doing wrong otherwise. the slightest thing can be dramatic... i have experience these problems before, so i know the story
User avatar
lucasart
Posts: 3232
Joined: Mon May 31, 2010 1:29 pm
Full name: lucasart

Re: All time spent in quiescent search - why?

Post by lucasart »

bob wrote:(2) use MVV/LVA ordering, so that you get rid of the queens and rooks first, since they tend to go on feeding forays and drive the search deeper as the queens eat away on opposite sides of the board.
That's interesting ! I naively used SEE ordering for captures and promotions, whether in the search or in the qsearch. Does it really make a big difference on the qsearch ?
bob
Posts: 20943
Joined: Mon Feb 27, 2006 7:30 pm
Location: Birmingham, AL

Re: All time spent in quiescent search - why?

Post by bob »

mike_bike_kite wrote:Captures yes - I used to play a lot of checkers and the terminology has seeped across :oops:

Test position = e5 d4 d6 Nf3 Nf6 Bg5 Be7 Nc3 h6 which leaves white to move with a lot of possible exchanges.

I have a single routine which returns the moves in the order they should be tried (MVV - LVA plus a few other small hints). The move order for white in the above position is the following:

Code: Select all

Move scores at depth 2
   Bg5xf6 &#40;10157&#41; = 170 &#40;in 16ms&#41;
   Pd4xe5 &#40;10077&#41; = 141 &#40;in 62ms&#41;
   Bg5xh6 &#40;9841&#41; = -57 &#40;in 63ms&#41;
   Nf3xe5 &#40;9597&#41; = -154 &#40;in 78ms&#41;
   Bg5-e3 &#40;273&#41; = 57 &#40;in 859ms&#41;
   Bg5-d2 &#40;267&#41; = 24 &#40;in 641ms&#41;
   Bg5-h4 &#40;253&#41; = 23 &#40;in 2547ms&#41;
   Bg5-c1 &#40;251&#41; = -500000 &#40;in 719ms&#41;
Best move Bg5xf6
The MVV-LVA score is shown in brackets while the score returned by the search is shown after the "=" sign. The move Bg5xf6 gets that score because all captures are given a 10000 score to place them in front of normal moves, the victim - attacker score is even in this case, a bonus is given because the attacker is moving from a square that was attacked by a pawn. The score -500000 is a time out score but please note I'm getting the time out when the ID is set at level 2 - something tells me that I'm not gong to be challenging Kasparov just yet.

Here is one continuation with a lot of jumps:
Pd4xe5 Nf6xe4 Bg5xe7 Ne4xc3 Be7xd8 Nc3xd1 Bd8xc7 Pd6xe5 Bc7xb8 Nd1xf2 Bb8xa7 Nf2xh1 Nf3xe5 Ra8xa7 Ne5xf7 Ra7xa2 Nf7xh8 => -693

I haven't worked out how to tell if the stand pat score but I'm working on that now. It sounds like that could be the culprit. Any other ideas welcome of course :)

Thanks for the quick reply!
How do you compute the scores? for MVV/LVA, I use a very simple formulation:

score = (captured << 7) - moving_piece;

I use the << 7 because a pawn = 100. That makes the captured piece dominate the value.

For RxQ I would get score = 900 * 128 - 500 = 114700
for PxQ I would get score = 900 * 128 - 100 = 115100
for QxP I would get score = 100 * 128 - 900 = 11900

sorted into descending order, I get PxQ, RxQ, QxP. The captured piece dominates, the value of the capturing piece inversely affects the score...

Next, are you doing "stand pat" correctly? First thing you do in quiesce() is compute the static eval. If it is >= beta, or <= alpha, you return instantly and don't look at any captures. Otherwise, you set alpha = the stand-pat score you just computed, and search captures that appear reasonable. Then you return the best score you saw, which will often be the stand-pat score if there are no good captures.
bob
Posts: 20943
Joined: Mon Feb 27, 2006 7:30 pm
Location: Birmingham, AL

Re: All time spent in quiescent search - why?

Post by bob »

lucasart wrote:
bob wrote:(2) use MVV/LVA ordering, so that you get rid of the queens and rooks first, since they tend to go on feeding forays and drive the search deeper as the queens eat away on opposite sides of the board.
That's interesting ! I naively used SEE ordering for captures and promotions, whether in the search or in the qsearch. Does it really make a big difference on the qsearch ?
It makes a difference. Sometimes large, sometimes not, but it is significant...
User avatar
Don
Posts: 5106
Joined: Tue Apr 29, 2008 4:27 pm

Re: All time spent in quiescent search - why?

Post by Don »

lucasart wrote:
bob wrote:(2) use MVV/LVA ordering, so that you get rid of the queens and rooks first, since they tend to go on feeding forays and drive the search deeper as the queens eat away on opposite sides of the board.
That's interesting ! I naively used SEE ordering for captures and promotions, whether in the search or in the qsearch. Does it really make a big difference on the qsearch ?
Use see() for move ordering is perfectly legit, but if your tree is exploding I would suspect that it is wrong.

I think most program uses see only for pruning bad moves and it's understood that MVVLVA is slightly superior to see() for move ordering, not by much but by a little. This is definitely counter-intuitive. But it's a huge win to pruning losing captures in quies.
User avatar
sje
Posts: 4675
Joined: Mon Mar 13, 2006 7:43 pm

Re: All time spent in quiescent search - why?

Post by sje »

bob wrote:First thing you do in quiesce() is compute the static eval. If it is >= beta, or <= alpha, you return instantly and don't look at any captures.
If the stand pat score is less than or equal to alpha, it might still be improved by some gainer (capture/promotion) move.

A better test at the start of a quiescence node would be:

Code: Select all

if (&#40;stand_pat_score + best_possible_gain&#40;position&#41;) <= alpha&#41; then return alpha;
And for each quiescence node move:

Code: Select all

if (&#40;stand_pat_score + best_possible_gain&#40;move&#41;) <= alpha&#41; then skip_move;
mike_bike_kite
Posts: 98
Joined: Tue Jul 26, 2011 12:18 am
Location: London

Re: All time spent in quiescent search - why?

Post by mike_bike_kite »

bob wrote:How do you compute the scores? for MVV/LVA, I use a very simple formulation:

score = (captured << 7) - moving_piece;

I use the << 7 because a pawn = 100. That makes the captured piece dominate the value.

For RxQ I would get score = 900 * 128 - 500 = 114700
for PxQ I would get score = 900 * 128 - 100 = 115100
for QxP I would get score = 100 * 128 - 900 = 11900

sorted into descending order, I get PxQ, RxQ, QxP. The captured piece dominates, the value of the capturing piece inversely affects the score...
Each move at a given level is assigned a MVV/LVA score. The scoring method is now similar to your own ie
  • Jumps -> 10000 + victim * 1000 - attacker * 100
    if from square attacked by pawn -> +20
    if to square attacked by pawn -> -20
    prefer centre squares over edge -> range 0-30
    castling -> +40
    checking the opponent -> +70
    moving pawns near to promotion -> range 40-90
    killer mover -> +500
    minor pieces to centre -> range 0-30
This MVV/LVA score is shown in brackets after the moves for the given position. So for the first move below the MVV/LVA score is calculated as 10000 + 3*1000 - 3*100 +/- other things for centre and squares being defended:

Code: Select all

   Move &#40;MVV/LVA order&#41; = actual score &#40;time&#41;
   Bg5xf6 &#40;12557&#41; = 297 &#40;in 16ms&#41;
   Pd4xe5 &#40;10877&#41; = 316 &#40;in 203ms&#41;
   Bg5xh6 &#40;10561&#41; = -5 &#40;in 15ms&#41;
   Nf3xe5 &#40;10317&#41; = -59 &#40;in 16ms&#41;
   Bg5-e3 &#40;273&#41; = 120 &#40;in 78ms&#41;
   Bg5-d2 &#40;267&#41; = 122 &#40;in 110ms&#41;
   Bg5-h4 &#40;253&#41; = 321 &#40;in 171ms&#41;
   Bg5-c1 &#40;251&#41; = 119 &#40;in 94ms&#41;
   Bg5-f4 &#40;153&#41; = -295 &#40;in 0ms&#41;
   Pd4-d5 &#40;117&#41; = -250 &#40;in 16ms&#41;
   Bf1-b5 &#40;97&#41; = 592 &#40;in 62ms&#41;
   Nc3-d5 &#40;37&#41; = 118 &#40;in 172ms&#41;
   Bf1-d3 &#40;33&#41; = -98 &#40;in 16ms&#41;
   Bf1-c4 &#40;33&#41; = -59 &#40;in 15ms&#41;
   Bf1-e2 &#40;27&#41; = -98 &#40;in 0ms&#41;
   Pg2-g4 &#40;27&#41; = -209 &#40;in 63ms&#41;
   Pg2-g3 &#40;27&#41; = -162 &#40;in 16ms&#41;
   Pb2-b4 &#40;27&#41; = -195 &#40;in 0ms&#41;
   Pb2-b3 &#40;27&#41; = -177 &#40;in 15ms&#41;
   Nf3-d2 &#40;27&#41; = -318 &#40;in 0ms&#41;
   Nc3-e2 &#40;27&#41; = -203 &#40;in 31ms&#41;
   Nc3-b5 &#40;27&#41; = -159 &#40;in 16ms&#41;
   Ph2-h3 &#40;21&#41; = -140 &#40;in 16ms&#41;
   Pa2-a3 &#40;21&#41; = -151 &#40;in 15ms&#41;
   Ph2-h4 &#40;13&#41; = -186 &#40;in 32ms&#41;
   Pa2-a4 &#40;13&#41; = -176 &#40;in 15ms&#41;
   Nf3-h4 &#40;13&#41; = -254 &#40;in 16ms&#41;
   Nc3-a4 &#40;13&#41; = -208 &#40;in 15ms&#41;
   Nf3-g1 &#40;5&#41; = -312 &#40;in 16ms&#41;
   Nc3-b1 &#40;5&#41; = -265 &#40;in 31ms&#41;
   Rh1-g1 &#40;0&#41; = -179 &#40;in 16ms&#41;
   Ke1-e2 &#40;0&#41; = -186 &#40;in 0ms&#41;
   Ke1-d2 &#40;0&#41; = -267 &#40;in 0ms&#41;
   Qd1-c1 &#40;0&#41; = -202 &#40;in 16ms&#41;
   Qd1-b1 &#40;0&#41; = -212 &#40;in 0ms&#41;
   Qd1-d2 &#40;0&#41; = -167 &#40;in 15ms&#41;
   Qd1-d3 &#40;0&#41; = -204 &#40;in 16ms&#41;
   Qd1-e2 &#40;0&#41; = -176 &#40;in 0ms&#41;
   Ra1-b1 &#40;0&#41; = -161 &#40;in 0ms&#41;
   Ra1-c1 &#40;0&#41; = -161 &#40;in 15ms&#41;
   Bf1-a6 (-109&#41; = -100 &#40;in 32ms&#41;
bob wrote:Next, are you doing "stand pat" correctly? First thing you do in quiesce() is compute the static eval. If it is >= beta, or <= alpha, you return instantly and don't look at any captures. Otherwise, you set alpha = the stand-pat score you just computed, and search captures that appear reasonable. Then you return the best score you saw, which will often be the stand-pat score if there are no good captures.
I doubt it. I don't really have alpha and beta values stored separately. I just store best scores for each level, setting it to a minimum value before starting a search and then storing the best scores as they come in. I do do alpha beta cut offs but I use the scoring mentioned. With the stand pat scores I do this before trying any moves and then treat the score returned as a normal move - which means it can produce an alpha cut off.

My results are better now in that I'm getting past a depth 2 search but sadly I'm only getting a depth 3 search now. The quiescent search used to go to depths of 30 but now is reaching around 20.

I've been attempting to understand these posts, change my code, debug and post back but I've been a little overwhelmed! I've decided to try and respond to each question individually where I can.
Aleks Peshkov
Posts: 892
Joined: Sun Nov 19, 2006 9:16 pm
Location: Russia

Re: All time spent in quiescent search - why?

Post by Aleks Peshkov »

mike_bike_kite wrote:I don't really have alpha and beta values stored separately. I just store best scores for each level, setting it to a minimum value before starting a search and then storing the best scores as they come in. I do do alpha beta cut offs but I use the scoring mentioned. With the stand pat scores I do this before trying any moves and then treat the score returned as a normal move - which means it can produce an alpha cut off.
I doubt you treat alpha-beta (even minimax correctness is questionable) properly.