Move ordering help

Discussion of chess software programming and technical issues.

Moderators: hgm, Rebel, chrisw

Ferdy
Posts: 4834
Joined: Sun Aug 10, 2008 3:15 pm
Location: Philippines

Re: Move ordering help

Post by Ferdy »

Richard Allbert wrote: Which means there is a bug somewhere, but I'm getting very frustrated in wondering where to look.

Any ideas?

Thanks

Richard

PS It's tough at the bottom ;)
How about profiling.
jwes
Posts: 778
Joined: Sat Jul 01, 2006 7:11 am

Re: Move ordering help

Post by jwes »

I would try at small fixed depths until you find a significant difference in nodes, and then dump the tree.
Sven
Posts: 4052
Joined: Thu May 15, 2008 9:57 pm
Location: Berlin, Germany
Full name: Sven Schüle

Re: Move ordering help

Post by Sven »

I'd propose that you let us also have a look at your move ordering and your qsearch, if you don't mind. At least in the part of your alphabeta() function you have posted I have not found any severe problem up to now, apart from code formatting issues :wink:

What is your approximate branching factor?

What about your ordering at the root between iterations, do you really ensure to always search the best move from previous iteration first, and in which order do you process the remaining root moves?

Are you working with aspiration window, and if so, how do you handle a fail low?

Just some ideas, as you requested ...

Sven
User avatar
Spacious_Mind
Posts: 317
Joined: Mon Nov 02, 2009 12:05 am
Location: Alabama

Re: Move ordering help

Post by Spacious_Mind »

Sven Schüle wrote:I'd propose that you let us also have a look at your move ordering and your qsearch, if you don't mind. At least in the part of your alphabeta() function you have posted I have not found any severe problem up to now, apart from code formatting issues :wink:

What is your approximate branching factor?

What about your ordering at the root between iterations, do you really ensure to always search the best move from previous iteration first, and in which order do you process the remaining root moves?

Are you working with aspiration window, and if so, how do you handle a fail low?

Just some ideas, as you requested ...

Sven
Hi Sven,

Quick side topic. Do you still have your chess program available for Atari ST ? If so I wouldn't mind checking it out.

Best regards

Nick
Sven
Posts: 4052
Joined: Thu May 15, 2008 9:57 pm
Location: Berlin, Germany
Full name: Sven Schüle

Re: Move ordering help

Post by Sven »

Spacious_Mind wrote:Quick side topic. Do you still have your chess program available for Atari ST ? If so I wouldn't mind checking it out.
Very good question!

1. All what I wrote for Atari ST was very weak, buggy, and did not conform to any standard protocol like WB (I didn't even know about WB at that time). There was no way for automatic engine-engine play implemented, only a "proprietary" console interface.

2. I'm afraid I don't have access to these old programs anymore. They are probably on the HD of my old Atari ST which does still exist but only in a packing case from where it did not escape during the last, say, 15 years :cry: And very likely the bad news is that the HD might have died in the meantime, I simply don't know - R.I.P.

3. The last things I did on my Atari were early versions of "Surprise". It is possible that I have a copy of some old version of it on some floppy disk but even if that copy were still intact, I don't know where I have it ... There are "a lot" of floppies in some packing cases 8-)

If I happen to find any of these buggy dinos by accident some day then maybe I'll remember your request ...

Sven
User avatar
Roman Hartmann
Posts: 295
Joined: Wed Mar 08, 2006 8:29 pm

Re: Move ordering help

Post by Roman Hartmann »

Richard Allbert wrote:Hi Roman,

Thanks for the reply.

How are you describing "fail high on first move" ? Do you update a counter for every position searched, or only in the case of a fail high?
...
Sorry, I just noted that I calculated my fail-high percentage wrong. My fail-high rate at the first move is not 48% but rather 92%. So my move ordering is quite good actually.

Btw, do you order your moves at the root?

Roman
User avatar
hgm
Posts: 27837
Joined: Fri Mar 10, 2006 10:06 am
Location: Amsterdam
Full name: H G Muller

Re: Move ordering help

Post by hgm »

I was already wondering what move ordering you could have that only managed 48%. :lol: Simply playing the MVV/LVA capture first should already do much better than that.

Just to make sure: all these reported numbers do not consider null-move prunings as a first-move cutoff, right? If you say 92% it is 92% of the real moves tried first that gae the cutoff.

From my statistics, at d=2 I have 446k first-move cutoffs (of which 263k by hash move), out of 563k cutoffs by a real move. But I have 2.9M null-move prunings.

Note that at d=1 I hardly have any hash-move cutoffs. I guess at d=1 there ususally is no hash move.
User avatar
Spacious_Mind
Posts: 317
Joined: Mon Nov 02, 2009 12:05 am
Location: Alabama

Re: Move ordering help

Post by Spacious_Mind »

Sven Schüle wrote:
Spacious_Mind wrote:Quick side topic. Do you still have your chess program available for Atari ST ? If so I wouldn't mind checking it out.
Very good question!

1. All what I wrote for Atari ST was very weak, buggy, and did not conform to any standard protocol like WB (I didn't even know about WB at that time). There was no way for automatic engine-engine play implemented, only a "proprietary" console interface.

2. I'm afraid I don't have access to these old programs anymore. They are probably on the HD of my old Atari ST which does still exist but only in a packing case from where it did not escape during the last, say, 15 years :cry: And very likely the bad news is that the HD might have died in the meantime, I simply don't know - R.I.P.

3. The last things I did on my Atari were early versions of "Surprise". It is possible that I have a copy of some old version of it on some floppy disk but even if that copy were still intact, I don't know where I have it ... There are "a lot" of floppies in some packing cases 8-)

If I happen to find any of these buggy dinos by accident some day then maybe I'll remember your request ...

Sven
Thanks Sven,

Best regards

Nick
Richard Allbert
Posts: 792
Joined: Wed Jul 19, 2006 9:58 am

Re: Move ordering help

Post by Richard Allbert »

Hi Sven,

It'll be rather long.... because I do this in the movelist class... (which is used by the move generator).

Code: Select all


const uint HISMAX = 50000;

#define	HASH	2600000
#define	M_KILLER	2500000
#define	WIN_CAPT	2400000
#define	Q_PROM_CAPT	2300000
#define	Q_PROM	2200000
#define	GCAP_QQ	2100000
#define	GCAP_RR	2000000
#define	GCAP_NN	1900000
#define	GCAP_BB	1800000
#define	GCAP_PP	1700000
#define	SEECAP	1600000
#define	KILLER1	1500000
#define	KILLER1_PLY	1400000
#define	KILLER2	1300000
#define	KILLER2_PLY	1200000
#define	OO	1100000
#define	OOO	1000000
#define	MINORPROM	900000


static const int equalcap[13] =
{0,GCAP_PP,GCAP_PP,GCAP_NN,GCAP_NN,
   GCAP_BB,GCAP_BB,GCAP_RR,GCAP_RR,
   GCAP_QQ,GCAP_QQ,0,0};

/*moveorderingpsqt*/
static const int orderpawn[64] = {
0	,	0	,	0	,	0	,	0	,	0	,	0	,	0	,
10	,	10	,	10	,	10	,	10	,	10	,	10	,	10	,
20	,	20	,	20	,	20	,	20	,	20	,	20	,	20	,
30	,	30	,	30	,	30	,	30	,	30	,	30	,	30	,
40	,	40	,	40	,	40	,	40	,	40	,	40	,	40	,
60	,	60	,	60	,	60	,	60	,	60	,	60	,	60	,
80	,	80	,	80	,	80	,	80	,	80	,	80	,	80	,
100	,	100	,	100	,	100	,	100	,	100	,	100	,	100
};

static const int orderknight[64] = {
0	,	10	,	10	,	10	,	10	,	10	,	10	,	0	,
10	,	15	,	30	,	30	,	30	,	30	,	15	,	10	,
10	,	20	,	50	,	50	,	50	,	50	,	20	,	10	,
10	,	20	,	50	,	80	,	80	,	50	,	20	,	10	,
10	,	20	,	50	,	80	,	80	,	50	,	20	,	10	,
10	,	20	,	50	,	50	,	50	,	50	,	20	,	10	,
10	,	15	,	30	,	30	,	30	,	30	,	15	,	10	,
0	,	10	,	10	,	10	,	10	,	10	,	10	,	0
};

static const int orderbishop[64] = {
80	,	60	,	50	,	30	,	30	,	50	,	60	,	80	,
60	,	80	,	60	,	50	,	50	,	60	,	80	,	60	,
50	,	60	,	80	,	60	,	60	,	80	,	60	,	50	,
30	,	50	,	60	,	80	,	80	,	60	,	50	,	30	,
30	,	50	,	60	,	80	,	80	,	60	,	50	,	30	,
50	,	60	,	80	,	60	,	60	,	80	,	60	,	50	,
60	,	80	,	60	,	50	,	50	,	60	,	80	,	60	,
80	,	60	,	50	,	30	,	30	,	50	,	60	,	80
};

static const int orderkingmid[64] = {
80	,	80	,	80	,	40	,	40	,	70	,	80	,	40	,
20	,	20	,	20	,	10	,	10	,	20	,	20	,	20	,
0	,	0	,	0	,	0	,	0	,	0	,	0	,	0	,
0	,	0	,	0	,	0	,	0	,	0	,	0	,	0	,
0	,	0	,	0	,	0	,	0	,	0	,	0	,	0	,
0	,	0	,	0	,	0	,	0	,	0	,	0	,	0	,
0	,	0	,	0	,	0	,	0	,	0	,	0	,	0	,
0	,	0	,	0	,	0	,	0	,	0	,	0	,	0
};

static const int orderkingend[64] = {
0	,	10	,	20	,	40	,	40	,	20	,	10	,	0	,
10	,	20	,	30	,	50	,	50	,	30	,	20	,	10	,
20	,	30	,	40	,	60	,	60	,	40	,	30	,	20	,
40	,	50	,	60	,	80	,	80	,	60	,	50	,	40	,
40	,	50	,	60	,	80	,	80	,	60	,	50	,	40	,
20	,	30	,	40	,	60	,	60	,	40	,	30	,	20	,
10	,	20	,	30	,	50	,	50	,	30	,	20	,	10	,
0	,	10	,	20	,	40	,	40	,	20	,	10	,	0
};

static const int orderrook[64] = {
10	,	10	,	10	,	20	,	20	,	10	,	10	,	10	,
0	,	0	,	0	,	0	,	0	,	0	,	0	,	0	,
0	,	0	,	0	,	0	,	0	,	0	,	0	,	0	,
0	,	0	,	0	,	0	,	0	,	0	,	0	,	0	,
0	,	0	,	0	,	0	,	0	,	0	,	0	,	0	,
20	,	20	,	20	,	20	,	20	,	20	,	20	,	20	,
80	,	80	,	80	,	80	,	80	,	80	,	80	,	80	,
40	,	40	,	40	,	40	,	40	,	40	,	40	,	40
};


static const int *midordertab[] = {
    0,
    orderpawn, orderpawn,
    orderknight, orderknight,
    orderbishop, orderbishop,
    orderrook, orderrook,
    orderrook, orderrook,
    orderkingmid, orderkingmid
};

static const int *endordertab[] = {
    0,
    orderpawn, orderpawn,
    orderknight, orderknight,
    orderbishop, orderbishop,
    orderrook, orderrook,
    orderrook, orderrook,
    orderkingend, orderkingend
};

void cMovelist::init_opt()
{
    opt->histab = true;
    opt->mvvlva = true;
    opt->killer = true;
    opt->hashmove = true;
    opt->matekiller = true;
}

void cMovelist::resethistab()
{
    uint pce,sq;
    for&#40;pce = pE; pce <= pbK; ++pce&#41;
    &#123;
        for&#40;sq = 0; sq < BRDSQ; ++sq&#41;
        histab.value&#91;pce&#93;&#91;sq&#93; = 1;
    &#125;
    histab.max = 1;
&#125;

void cMovelist&#58;&#58;resetstats&#40;)
&#123;
    stats.killer1=stats.killer2=stats.matekiller=0;
    stats.histab=stats.capture=stats.badcapture=stats.hashmove=0;
    stats.qprom=stats.minorprom=0;
    stats.epcapture=stats.castle=0;
&#125;

void cMovelist&#58;&#58;resetkillers&#40;)
&#123;
    for&#40;uint i = 0; i < maxply; ++i&#41;
    &#123;
        killers.k1&#91;i&#93;=killers.k2&#91;i&#93;=killers.mk&#91;i&#93;=NULLMOVE;
    &#125;
&#125;

void cMovelist&#58;&#58;scoremove&#40;uint move, uint depth, uint pce&#41;
&#123;
    uint to = TO&#40;move&#41;;

    //histab.value&#91;pce&#93;&#91;to&#93; += depth*depth;
    histab.value&#91;pce&#93;&#91;to&#93; += depth;
    if&#40;histab.value&#91;pce&#93;&#91;to&#93;  > histab.max&#41;
    histab.max = histab.value&#91;pce&#93;&#91;to&#93;;

&#125;


void cMovelist&#58;&#58;failowmove&#40;uint move, uint depth, uint pce&#41;
&#123;
    uint to = TO&#40;move&#41;;

    if&#40;histab.value&#91;pce&#93;&#91;to&#93;>depth*depth&#41;
    &#123;
    histab.value&#91;pce&#93;&#91;to&#93; -= depth*depth;
    if&#40;histab.value&#91;pce&#93;&#91;to&#93;  < 1&#41;
    histab.value&#91;pce&#93;&#91;to&#93; = 1;
    &#125;

&#125;


void cMovelist&#58;&#58;score_killer&#40;uint move, int score, uint ply&#41;
&#123;
  if&#40;score > matescore-200&#41;
  &#123;
   killers.mk&#91;ply&#93; = move;
   return;
  &#125;

  if&#40; !&#40;FlagCAPEP&move&#41; )
  &#123;
     if&#40;move != killers.k1&#91;ply&#93;)
     &#123;
       killers.k2&#91;ply&#93; = killers.k1&#91;ply&#93;;
       killers.k1&#91;ply&#93; = move;
     &#125;
     else
     &#123;
      killers.k2&#91;ply&#93; = move;
     &#125;
  &#125;
&#125;


cMovelist&#58;&#58;cMovelist&#40;)
&#123;
    uint pce,sq;
    for&#40;pce = pE; pce <= pbK; ++pce&#41;
    &#123;
        for&#40;sq = 0; sq < BRDSQ; ++sq&#41;
        histab.value&#91;pce&#93;&#91;sq&#93; = 1;
    &#125;
    histab.max = 1;
    init_opt&#40;);
&#125;



int cMovelist&#58;&#58;ordercapture&#40;uint move, cBoard &pboard, cMaterial &material&#41;
&#123;

  int from,to,val;
  from = FROM&#40;move&#41;;
  to = TO&#40;move&#41;;
  uint side;
  uint pcefrom = pboard.p2board&#40;)&#91;from&#93;;
  uint pceto = pboard.p2board&#40;)&#91;to&#93;;

  if&#40;PROM&#40;move&#41;)//promotion
  &#123;
      if&#40;PROM&#40;move&#41;==pbQ||PROM&#40;move&#41;==pwQ&#41;//queen
      &#123;
          stats.capture++;
          return Q_PROM_CAPT;
      &#125;
      else
      &#123;
          stats.badcapture++;
          return MINORPROM;
      &#125;
  &#125;
  else//not a prom
  &#123;
	val = piecevals&#91;pceto&#93; - piecevals&#91;pcefrom&#93;;

	if&#40;val>0&#41;
	&#123;
	 stats.capture++;
	 return WIN_CAPT+val;
	&#125;
    else if&#40;val == 0&#41;
    &#123;     
     stats.capture++;
     return equalcap&#91;pcefrom&#93;;
    &#125;
    else
    &#123;
        side = pboard.getside&#40;);
        if&#40;isattack&#40;pboard, material, side^1, to&#41;)
        &#123;
           stats.badcapture++;
           return 1;
        &#125;
        else
        &#123;
           stats.capture++;
           return SEECAP;
        &#125;
    &#125;
  &#125;
&#125;

void cMovelist&#58;&#58;ordermove&#40;uint move, uint &cap, const uint &flag, const uint &prom, uint &ply, uint pce, cBoard &pboard, int *score, cMaterial &material&#41;
&#123;
     *score = -200;
     if&#40;move==pvmove && opt->hashmove&#41;
     &#123;
        *score = HASH;
         stats.hashmove++;
     &#125;
     else if&#40;opt->matekiller && move==killers.mk&#91;ply&#93;)
     &#123;
         *score = M_KILLER;
         stats.matekiller++;
     &#125;
     else if&#40; cap && opt->mvvlva )
     &#123;
       *score = ordercapture&#40;move, pboard, material&#41;;
     &#125;
     else if&#40;move & FlagEP&#41;
     &#123;
         *score = GCAP_PP;
         stats.epcapture++;
     &#125;
     else if&#40;prom!=pE&#41;
     &#123;
         if&#40;prom==pwQ||prom==pbQ&#41;
         &#123;
             *score=Q_PROM;
             stats.qprom++;
         &#125;
         else
         &#123;
             *score=MINORPROM;
             stats.minorprom++;
         &#125;
     &#125;
     else if&#40;move==killers.k1&#91;ply&#93; && opt->killer&#41;
     &#123;
         *score = KILLER1;
         stats.killer1++;
     &#125;
     else if&#40;move==killers.k2&#91;ply&#93; && opt->killer&#41;
     &#123;
         *score = KILLER2;
         stats.killer2++;
     &#125;
     else if &#40;move & FlagCA&#41;
     &#123;
         stats.castle++;
         if&#40;files&#91;TO&#40;move&#41;&#93;==FILEC&#41;
         &#123;
             *score = OOO;
         &#125;
         else
         &#123;
             *score = OO;
         &#125;
     &#125;
     else
     &#123;
        
           if&#40;&#40;material.getmaterial&#40;cW&#41; + material.getmaterial&#40;cB&#41;) > ENDMAT&#41;
           &#123;
               tab = midordertab&#91;pce&#93;;
           &#125;
           else
           &#123;
               tab = endordertab&#91;pce&#93;;
           &#125;

           *score=tab&#91;sqto64&#40;TO&#40;move&#41;)&#93;-tab&#91;sqto64&#40;FROM&#40;move&#41;)&#93;;
           stats.histab++;
	 &#125;
&#125;

void cMovelist&#58;&#58;addpawnmove&#40;uint &from, uint &to, uint &cap, uint side, uint &ply, uint pce, cBoard &pboard, cMaterial &material&#41;
&#123;    
     if&#40;side==cW&#41;
     &#123;
         if&#40;ranks&#91;from&#93;==RANK7&#41;
         &#123;             
             this->addmove&#40;from,to,cap,FlagE,pwQ,ply,pce,pboard,material&#41;;
             this->addmove&#40;from,to,cap,FlagE,pwN,ply,pce,pboard,material&#41;;
             this->addmove&#40;from,to,cap,FlagE,pwR,ply,pce,pboard,material&#41;;
             this->addmove&#40;from,to,cap,FlagE,pwB,ply,pce,pboard,material&#41;;
         &#125;
         else
         &#123;
             this->addmove&#40;from,to,cap,FlagE,FlagE,ply,pce,pboard,material&#41;;
         &#125;

     &#125;
     else
     &#123;         
         if&#40;ranks&#91;from&#93;==RANK2&#41;
         &#123;
             ASS&#40;ranks&#91;to&#93;==RANK1&#41;;
             this->addmove&#40;from,to,cap,FlagE,pbQ,ply,pce,pboard,material&#41;;
             this->addmove&#40;from,to,cap,FlagE,pbN,ply,pce,pboard,material&#41;;
             this->addmove&#40;from,to,cap,FlagE,pbR,ply,pce,pboard,material&#41;;
             this->addmove&#40;from,to,cap,FlagE,pbB,ply,pce,pboard,material&#41;;
         &#125;
         else
         &#123;
             this->addmove&#40;from,to,cap,FlagE,FlagE,ply,pce,pboard,material&#41;;
         &#125;
     &#125;
&#125;

void cMovelist&#58;&#58;addmove&#40;uint &from, uint &to, uint &cap, const uint &flag, const uint &prom, uint &ply, uint pce, cBoard &pboard, cMaterial &material&#41;
&#123;
          uint count = movecount&#91;ply&#93;;
     uint move = &#40;from | &#40;to<<7&#41; | &#40;cap<<16&#41; | &#40;prom<<20&#41; | flag&#41;;
     movelist&#91;ply&#93;&#91;count&#93; = move;

     movescores&#91;ply&#93;&#91;count&#93; = -200;
     ordermove&#40;move, cap, flag, prom, ply, pce, pboard, &movescores&#91;ply&#93;&#91;count&#93;, material&#41;;
     movecount&#91;ply&#93;++;
&#125;


and for completeness...


class cMovelist &#123;

private&#58;

        uint movelist&#91;maxply&#93;&#91;maxmoves&#93;;
        uint movecount&#91;maxply&#93;;
        int movescores&#91;maxply&#93;&#91;maxmoves&#93;;
        sHistab histab;
        sKillers killers;
        uint pvmove;
        sOrderstats stats;

        const int *tab;


        cMovelist&#40; const cMovelist & );
        cMovelist &operator = ( const cMovelist & cMovelist&#41;;

        void cMovelist&#58;&#58;ordermove&#40;uint move, uint &cap, const uint &flag, const uint &prom, uint &ply, uint pce, cBoard &pboard, int *score, cMaterial &material&#41;;
        int ordercapture&#40;uint move, cBoard &pboard, cMaterial &material&#41;;

public&#58;

        cMovelist&#40;);

        sScoreOpt opt&#91;1&#93;;
        void init_opt&#40;);

       uint *p2list&#40;uint &ply&#41; &#123;return movelist&#91;ply&#93;;&#125;
       int *p2scores&#40;uint &ply&#41; &#123;return movescores&#91;ply&#93;;&#125;
       uint &getcount&#40;uint &ply&#41; &#123; return movecount&#91;ply&#93;; &#125;//shows for current ply
       uint &getpvmove&#40;) &#123; return pvmove; &#125;
       void setpvmove &#40;uint pvm&#41; &#123; pvmove = pvm; &#125;

       void printmovelist&#40;uint &ply&#41;;//prints movelist for the current ply
       void printatplymovelist&#40;uint ply&#41;;//prints movelist for the given ply
       void resetcount&#40;uint &ply&#41; &#123; movecount&#91;ply&#93;=0;&#125;
       void incrmovecount&#40;uint &ply&#41; &#123; this->movecount&#91;ply&#93;++; &#125;
       void addmove&#40;uint &from, uint &to, uint &cap, const uint &flag, const uint &prom, uint &ply, uint pce, cBoard &pboard, cMaterial &material&#41;;
       void addpawnmove&#40;uint &from, uint &to, uint &cap, uint side, uint &ply, uint pce, cBoard &pboard, cMaterial &material&#41;;
       void scoremove&#40;uint move, uint depth, uint pce&#41;;
       void failowmove&#40;uint move, uint depth, uint pce&#41;;
       void score_killer&#40;uint move, int score, uint ply&#41;;
       void resethistab&#40;);
       void resetkillers&#40;);
       void resetstats&#40;);

       void says_stats&#40;);

&#125;;
That's all a mess isn't it?
:D :D

@ HG - Thanks for the tip about those stats. I thought I was printing detailed enough info... obviously not!

@Roman - this is without root sorting. Your 92% seems a little better!! :D

Also, if I add PVS

Code: Select all


    int played=0;

    for&#40;uint i = 0; i < mlist.getcount&#40;game.getply&#40;)); ++i&#41;
    &#123;
        picknext&#40;i,mlist&#41;;

     if &#40;makemove&#40;game,mat,his,list&#91;i&#93;))
     &#123;
       takemove&#40;game,mat,his&#41;;
       continue;
     &#125;

     check = incheck&#40;game,mat,game.getside&#40;));
     ext = 0;
     if&#40;check&#41; &#123; stats->checkext++; ext = 1; &#125;
     
     if &#40;played==0 && opt->pvs&#41;
     &#123;
        stats->pvssearch++;
        val = -alphabeta&#40;-alpha-1, -alpha, depth-1+ext, true, check&#41;;
        if (&#40;val > alpha&#41; && &#40;val < beta&#41;)
        &#123;
            stats->pvsresearch++;
            val = -alphabeta&#40; -beta, -alpha, depth-1+ext, true, check&#41;;
        &#125;
     &#125;

     takemove&#40;game,mat,his&#41;;
     if&#40;status->stopped == STOPPED&#41; &#123; return 0;&#125;

     played++;

     from = FROM&#40;list&#91;i&#93;);

     if &#40;val > alpha&#41;
     &#123;

..etc
    
Then the node count increases, rather than an expected decrease.

I don't have time tonight, but I will look at this tomorrow evening after work, starting with some stats by depth as in the table above. :D

Thanks for the replies...

Although, one thing I did notice. I left the other engines with PVS on. When I turned this off, they weren't much different than Jabba.

So the problem could well be the PVS implementation... sorry. :?
Sven
Posts: 4052
Joined: Thu May 15, 2008 9:57 pm
Location: Berlin, Germany
Full name: Sven Schüle

Re: Move ordering help

Post by Sven »

Richard Allbert wrote:It'll be rather long.... because I do this in the movelist class... (which is used by the move generator).
No problem, I can read and understand most of your code quite well!
Richard Allbert wrote:

Code: Select all

// snipped &#40;EDIT&#58; and improved indentation&#41;

int cMovelist&#58;&#58;ordercapture&#40;uint move, cBoard &pboard, cMaterial &material&#41;
&#123;
  // ...
  if&#40;PROM&#40;move&#41;)//promotion
  &#123;
  // ...
  &#125;
  else//not a prom
  &#123;
    val = piecevals&#91;pceto&#93; - piecevals&#91;pcefrom&#93;;

    if&#40;val>0&#41;
    &#123;
      stats.capture++;
      return WIN_CAPT+val;
    &#125;
    else if&#40;val == 0&#41;
    &#123;
      stats.capture++;
      return equalcap&#91;pcefrom&#93;;
    &#125;
    else
    &#123;
      side = pboard.getside&#40;);
      if&#40;isattack&#40;pboard, material, side^1, to&#41;)
      &#123;
        stats.badcapture++;
        return 1;
      &#125;
      else
      &#123;
        stats.capture++;
        return SEECAP;
      &#125;
    &#125;
  &#125;
&#125;
That's all a mess isn't it?
:D :D
Not quite. But there some issues.

1) You don't really do MVV/LVA for the winning and equal captures. Instead you score winning captures by the difference of piece values, and you score all equal captures lower than winning captures. This results in a different order compared to original MVV/LVA for some cases. It may turn out that your method is sufficient, though, but many people do it differently. For instance, usually QxQ is always tried before PxN since QxQ removes a queen from the board which reduces the tree quite well.

2) For potentially losing captures (capturing a minor piece), you make the score depend on whether the captured piece is defended, by calling isattack(). If it is defended then you regard the move as bad capture, otherwise you assign a constant score SEECAP which is lower than all scores for equal captures. Both is not quite what I would do.

a) "Defended": Consider captures like NxP, RxP, QxP. Even if the pawn is defended it is not clear whether you have a bad capture since it will depend on the value of the "least defender", and maybe as well on the presence of more attackers and defenders. As long as you do not take that into account you cannot conclude having a bad capture. Doing so may wrongly put many captures to the end of the move list. Knowing that the least defender is a pawn would already be better, although this would restrict the set of bad captures you can recognize.

b) "Not defended": Here you have more information than you are using, in fact you have identified a winning capture, so why not scoring it accordingly? IMO it is better to score QxR (undefended) higher than PxN, for instance, although there may be other opinions. Also capturing an undefended queen should go before capturing an undefended pawn, for instance, but you assign the same score for both.

I think that using SEE for potentially losing captures is much better than your method of calling isAttack(), and I also believe that isAttack() is too expensive when considering the very small amount of information you get returned and can use in your case. If you don't want to implement SEE right now then you will have to fix your "compromise" solution at least, or fully stick with plain MVV/LVA instead without even trying to detect bad captures.

I'm currently having the same problem with my new engine: I do not have a fully satisfying SEE implementation yet but I wish to detect at least some subset of bad captures. So what I currently do is to find out, for potentially losing captures, whether the least defender is a pawn or, in some cases, a minor piece. It is a compromise that seems to work, but of course I want to replace this by SEE asap.

Sven