LMR advanced

Create a piece-type | square history table.


static int H [ 12 ] [ 64 ];              // declare history table H [piece] [destination square]


Table is cleared at the start of the search.


During search the history table H is updated for each move as follows:


        if (ply <= 8 && move_is_no_capture)

         { if (move produces a better score) H[piece][square] += depth*depth;

            else H[piece][square] -= depth;

           if (abs(H[piece][square]) >= 2000) H[piece][square] /= 2; }


And so the values will always be in the range of -2000 | +2000.


Then LMR becomes really simple.


void lmr()

 

{       if (depth < 3) return;             // no LMR in the last 3 plies before the horizon


        if (move_count <= 3) return;  // no LMR for the first 3 moves


        if (TT move) return;               // no LMR when TT move


        if (capture) return;


        if (queen promotion) return;


        if (king_in_check) return;


        if (move_gives_check) return;


        if (white_pawn_moves_to_sixth_row_or_beyond) return;

        if (black_pawn_moves_to_third_row_or_beyond) return;


        reduction();                                            // reduce the search by one ply.


        if (we_are_at_the_root) return;                 // we reduce root moves only once.


        conditions for double and triple LMR

       

        value = H [piece] [square];


        if (value < -500) reduction();                     // reduce double


        if (value < -1500) reduction();                    // reduce triple

}


Note: This simple approach should be good for some 30-50 elo.


As one can see from the code the history table is only updated for the first 8 plies because research from the past (somewhere in the 90's) learned me that using higher values the level of noise rapidly contaminates the values in the table.


__________________________________________________________________________________________________



LMR

advanced


There a practically a zillion of possibilities to improve on the above simple system and I will describe those additions to the above I am currently working on and things rapidly become complicated in the sense of finding the right combinations as most of the below instructions (marked with a *) are controlled by an on or off option.


void lmr()

 

{       if (depth < 3) return;             // no LMR in the last 3 plies before the horizon


//      Root moves:

//      we maintain a list with the best moves during search. From iteration 8 and on moves that have been best

//      are never reduced.

 

        if (TT move) return;               // no LMR when TT move

        if (king_in_check) return;

        if (queen promotion) return;


        if (white_pawn_moves_to_sixth_row_or_beyond) return;

        if (black_pawn_moves_to_third_row_or_beyond) return;


        if (good capture) return;                               // SEE >=0

*     if (bad capture) { reduce one ply; return; }     // SEE <0

 

        if (move_gives_check && good) return;

*      if (move_gives_check && bad) { reduce one ply; return; }


//      Evaluate R (the number of reductions)


//      Step-1: get the initial R from a table [depth] [movecount], view, copy&paste the table from here.


        a=depth; b=movecount; if (b>63) b=63;

        R=lmr_tab[a][b];


//      Step-2: get the value of the history table and based on the value modify R


        value = H [piece] [square];

*      if (value > 0) R--;                              // reduce less if the history move is positive

*      if (value > 500) R--;                           // reduce less

*      if (value > 1000) R--;                         // reduce less

*      if (value > 1500) R--;                         // reduce less


*      if (value < 0) R++;                            // reduce more if the history move is bad

*      if (value < -500) R++;                        // reduce more

*      if (value < -1000) R++;                      // reduce more

*      if (value < -1500) R++;                      // reduce more


//     Step-3: reduce less if the evaluation score of the position - a (small) margin is (still) better than ALPHA.


*      if (evaluation score > ALPHA+margin) R--;


//     Step-4: reduce less if the evaluation score - the board material is greater than a margin, currently set to 0.50.

//                The idea is that if the color to move has a positional advantage of half a pawn then maybe it's not a

//                good idea to reduce the move too much despite the material on the board.


*      if (evaluation score - board_material > margin) R--;


//      Step-5: reduce less if the current move itself creates a new threat such as h3 attacking the black knight on

//                 G4 with the white pawn.  Attacking pawns don't count, only major pieces (N,B,R,Q). The move itself

//                 should not be bad (SEE<0)


*      if (new_attacking_move) R--;


//      Step-6: reduce less if the move escapes from a threat such as the example in step-5. Subtracting moves

//                  like Nf6 or moves that simply eliminate the threat on G4 should not be reduced too much. Pawns

//                  don't count, only major pieces (N,B,R,Q). The move itself should not be bad (SEE<0)


*        if (hanging piece resolved) R--;


//       Step-7: reduce less if the passed pawn evaluation has increased with a considerable margin in comparison

//                  to the previous ply, currently set to 0.25.


*        if (passed_pawn_eval [current ply] - passed_pawn_eval [previous ply] >= margin) R--;


//       Step-8: Do the same for King Safety, reduce less if the KS evaluation has increased with a considerable margin

//                  in comparison to the previous ply, currently set to 0.25.


*        if (KS_eval [current ply] - KS_eval [previous ply] >= margin) R--;


//      Step-9: reduce less if the overall evaluation (thus evaluation score - board material) has increased with a

//                  considerable margin in comparison to the previous ply, currently set to 0.50. Note the correlation

//                  with step 7 and 8.


*        if (evaluation score [current ply] - evauation scorel [previous ply] >= margin) R--;


//       Step-10: reduce more if the overall evaluation has decreased with a considerable margin in comparison to

//                    the previous ply, currently set to 0.50. Note the reverse correlation with step-9.


*        if (evaluation score [previos ply] - evauation scorel [current ply] >= margin) R++;


//       Step-11: reduce less if the current move is a killer move.


*        if (move = killer_one || killer_two)  R--;


//       Step-12: reduce less if move has an excellent PST score.


*        if (PST [piece_type] [to] - PST [piece_type] [from] >= margin) R--;


//       We finally are there for the last step to calculate the final number of reductions for this move. We apply a

//       percentage (P) for R currently set to 50% which makes R flexible.


P=50;

 

        if (R>0) { printf("Reduce this move with %d plies", (R*P)/100);


That's it.


And a lot to test.


Notes:

The system can be improved by applying priorities for each of the 12 ingredients (steps). Instead of using R-- or R++ one can use R+=10 or R-=6 based on a multiply factor of 10 and then in the final calculation use (R*P)/1000);


The if (R>0) is actually needed else if R would end up negative the move will be extended instead of reduced. It would be fun to try that anyway.


__________________________________________________________________________________________________


LMR

expert


Mail me your LMR pseudo code and I will create a separate page for it.


or use the contact form to contact me.


LMR for starters

pseudo code