MVV LVA

Discussion of chess software programming and technical issues.

Moderators: hgm, Rebel, chrisw

User avatar
xr_a_y
Posts: 1871
Joined: Sat Nov 25, 2017 2:28 pm
Location: France

MVV LVA

Post by xr_a_y »

Dear all,

I cannot find an old post talking deeply about mvv/lva implementation. I know there are some, but the search gives very few entries with "mvv lva".
I know some engine do table by hand and other uses formulas like

Code: Select all

mvv*queenValue - lva  
or things like in vice

Code: Select all

MvvLvaScores[Victim][Attacker] = VictimScore[Victim] + 6 - ( VictimScore[Attacker] / 100);
I have found in the forum some specific recommendations to handle special cases.

My question is do we have a more or less optimized MVVLVA table out there, without taking into account any recapture knowledge ?

I was surprise to find a bug in Weini this afternoon affecting MVVLVA table. When I switch ScoreType from float to int a couple of weeks ago a self-test between the two versions told me nothing went wrong. But something was, the MVVLVA table was not good anymore, the MvvLvaScore were more or less egal to Mvv (the fact this had no impact is a little bit scary ...). So I give it a thought today and start using this simple thing (don't worry about the two K entries) :

Code: Select all

const ScoreType MvvLvaScores[15][15] = {
        //      K, K, Q, R, B, N, P, 0, P, N, B, R, Q, K, K
        /*K*/ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
        /*K*/ { 0, 0, 9, 9, 9, 9, 9, 0, 9, 9, 9, 9, 9, 0, 0},
        /*Q*/ { 0, 3, 4, 5, 6, 7, 8, 0, 8, 7, 6, 5, 4, 3, 0},
        /*R*/ { 0, 1,-5, 2, 3, 4, 5, 0, 5, 4, 3, 2,-5, 1, 0},
        /*B*/ { 0, 1,-6,-2, 1, 1, 3, 0, 3, 1, 1,-2,-6, 1, 0},
        /*N*/ { 0, 1,-6,-2, 0, 1, 2, 0, 2, 1, 0,-2,-6, 1, 0},
        /*P*/ { 0, 1,-9,-5,-3,-2, 0, 0, 0,-2,-3,-5,-9, 1, 0},
        /*0*/ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
        /*P*/ { 0, 1,-9,-5,-3,-2, 0, 0, 0,-2,-3,-5,-9, 1, 0},
        /*N*/ { 0, 1,-6,-2, 0, 1, 2, 0, 2, 1, 0,-2,-6, 1, 0},
        /*B*/ { 0, 1,-6,-2, 1, 1, 3, 0, 3, 1, 1,-2,-6, 1, 0},
        /*R*/ { 0, 1,-5, 2, 3, 4, 5, 0, 5, 4, 3, 2,-5, 1, 0},
        /*Q*/ { 0, 3, 4, 5, 6, 7, 8, 0, 8, 7, 6, 5, 4, 3, 0},
        /*K*/ { 0, 0, 9, 9, 9, 9, 9, 0, 9, 9, 9, 9, 9, 0, 0},
        /*K*/ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
                                       };
where capture are always placed first (they all receive a large bonus, less than TT move or promotions but bigger than killers) but they are ordered using the former table. This seems to have a positive impact on the score (tournament still running).
User avatar
hgm
Posts: 27809
Joined: Fri Mar 10, 2006 10:06 am
Location: Amsterdam
Full name: H G Muller

Re: MVV LVA

Post by hgm »

MVV is by far the most important ordering criterion. By capturing the Queen whenever you can, you will put a quick stop to plunder raids, which otherwise lead to search explosion in QS. Everything else is just minor tweeking.

If you have no information on protection of pieces, strict MVV/LVA is typically best. Except that it hardly matters where you order the King LVA-wise. Because when you capture a protected piece with a King it only requires a single node to refute it, and when it was not protected it is immaterial with what you capture. Sorting HxL captures of juicy victims behind captures of lower victims makes you miss cut-moves when the victim was unprotected, and in any case leads to bushy trees, because you leave powerful pieces on the board. Even if capture of a valuable victim doesn't provide a cut-move, because it has SEE<0, at least the search tree is small because the opponent has fewer options without the piece.

A rather simple kludge to avoid wasting tile on bad HxL captures without prior protection info, is to make Search() abort (returning a recognizable error score) when it discovers the previously moved piece is capturable. Just like you would abort with a +INF score when the King is capturable. E.g. you could pass the last to-square to Search as a parameter, and pass an invalid square for HxL and equal captures. If during move generation there is a move to the passed square you then immediately return INF+1. If a returned score is -INF-1 you then postpone the move (lowering its sort key to just before the non-captures), and try again.