Null move

Programming Topics (Computer Chess) and technical aspects as test techniques, book building, program tuning etc

Moderator: Andres Valverde

Null move

Postby Manuel Peña » 11 Feb 2008, 18:37

This is my null move implementation
but i dont get all his potential :(

Code: Select all
//-------------------------------------------------------------------
//NULL MOVE PRURING
//-------------------------------------------------------------------

int R;
int aux100;

#ifdef OPTION_NULL
R=4;
if (!(tipo_before&TIPO_NULL))
if (!(tipo&TIPO_CHECK))
if (eval(alfa,beta)>beta)
{
turn^=TURN;
aux100=mov100;
mov100=0;
ev=-alfabeta(-beta,-alfa,depth-R,ply+1,tipo|TIPO_NULL);
mov100=aux100;
turn^=TURN;
if (setb[Id]&th_stop)
{
arbol.borra_nivel();
return 0;//STOP!!!
}
if (ev>=beta)
{
if (ev>5) return (-alfabeta (-beta,-alfa,depth-5,ply+1,tipo));
if (ev>=APP_MAX_VAL) return APP_MAX_VAL;
return ev;
}
if (ev<APP_MIN_VAL) tipo|=TIPO_NULL_TH;
}
#endif


What do you think about this ...?



Another idea is this

Code: Select all

#ifdef OPTION_NULL2

if (!(tipo_anterior&TIPO_NULL))
if (depth>5)
{
arbol.nuevo_nivel();

if (turno)
{
gencap_b ();
genmov_b ();
genund_b ();
}

else
{
gencap_n ();
genmov_n ();
genund_n ();
}

if (dim()==0)
{
arbol.borra_nivel();
if (turno==BLANCO)
{
if (jaqueb(lsb(rb)))
return MIN_VAL+ply;
return 0;
}
if (turno==NEGRO)
{
if (jaquen(lsb(rn)))
return MIN_VAL+ply;
return 0;
}
}

if ((turno==BLANCO)&&(jaquen(lsb(rb))==0)||(turno==NEGRO )&&(jaquen(lsb(rn))==0))
{

int max_null=MIN_VAL;
for (int i=0;i<dim();i++)
{
R=5;
turno^=BLANCO;
aux100=mov100;                                            //NULL MOVE ES IRREVERSIBLE!?
mov100=0;                                                 //NULL MOVE ES IRREVERSIBLE!?
ev=-alfabeta(-beta,1-beta,depth-R,ply+1,tipo|TIPO_NULL);
mov100=aux100;                                            //NULL MOVE ES IRREVERSIBLE!?
turno^=BLANCO;
if (ev>max_null) max_null=ev;
}

if (max_null<alfa)
{
arbol.borra_nivel();
return max_null;
}

}

arbol.borra_nivel();
}


#endif
Manuel Peña
 
Posts: 15
Joined: 28 Jan 2008, 14:06

Re: Null move

Postby Teemu Pudas » 11 Feb 2008, 19:42

Code: Select all
ev=-alfabeta(-beta,-alfa,depth-R,ply+1,tipo|TIPO_NULL);
Should be -beta,-beta+1. Also,
Code: Select all
if (ev>5) return (-alfabeta (-beta,-alfa,depth-5,ply+1,tipo));
should be
Code: Select all
if (depth>5&&-alphabeta(-beta,-beta+1,depth-5,ply+1,tipo)>=beta) return ev;
Teemu Pudas
 
Posts: 124
Joined: 16 Apr 2007, 14:03

Re: Null move

Postby Manuel Peña » 12 Feb 2008, 12:49

And in the second option i do 2 consecutive moves with the same color
an if the (max_value<alpha) then i prune.

What do yow thin about it?
Manuel Peña
 
Posts: 15
Joined: 28 Jan 2008, 14:06

Re: Null move

Postby Pedro Castro » 12 Feb 2008, 14:30

El movimiento nulo deberías hacerlo mientras el lado que juegue tenga al menos una pieza, no lo hagas en finales de solo peones.

Antes de llamar al alphabeta, debes resetear también el valor de la casilla de al paso.

Hay algunos programas que verifican el movimiento nulo, otros no lo hacen, en cualquier caso si haces una verificación yo lo haría solo en el final, que es cuando realmente puede haber un problema de zugzwang, en mi caso yo hago esta verificación cuando el material es menor al equivalente de un caballo y una dama, funciona bastante bien, la mayoría de los test con zugzwang los resuelve bien y rápido, en la fase inicial de la partida y medio juego no es necesario hacer esta comprobación.

Yo nunca he tenido éxito con extender las amenazas del movimiento nulo, la mayoría de la gente parece que si que le funciona, yo utilizo un mate_killer en su sustitución.

Normalmente el movimiento nulo no es permitido hacerlo varias veces en un turno, como mucho 2 veces, es llamado doble movimiento nulo y parece que funciona de una forma muy parecida al normal. Si utilizas un doble movimiento nulo, entonces no es necesario hacer verificación del movimiento nulo, el doble movimiento nulo elimina el problema de zugzwang.
Best wishes,

Pedro Castro
User avatar
Pedro Castro
 
Posts: 180
Joined: 28 Jan 2005, 01:09
Location: Pays Basque (Spain)

Re: Null move

Postby Manuel Peña » 12 Feb 2008, 16:20

Gracias Pedro.

El segundo trozo de codigo que publique funciona asi:

Si es tu turno y haces dos movimientos consecutivos (en lugar de uno, lo normal) o ninguno (NULL MOVE) y el mejor valor que obtienes es inferior a alfa.

Entoces puedes suponer que la posicion es muy mala y podarla (pruring).

Esto reduce muchisimo el tamaño del arbol ya que se complementa con el "NULL MOVE" normal que poda las posiciones buenas.

Pero no puedo decir mucho acerca de su seguridad.
Manuel Peña
 
Posts: 15
Joined: 28 Jan 2008, 14:06

Re: Null move

Postby Pedro Castro » 12 Feb 2008, 18:53

Con el movimiento nulo nosotros conseguimos sin necesidad de hacer un movimiento, solo cambiando el turno, salir muchas veces del alphabeta. No tenemos que olvidar que durante el pensamiento del programa, gran parte del tiempo el motor está haciendo y deshaciendo jugadas, estas dos funciones son caras, así que si podemos evitarlas mejor.

Hay programas como Fruit, que cuando va a realizar un movimiento, solo con la generación de las jugadas ya sabe si el movimiento va a dar jaque o no, esto es algo fundamental para poder cortar, una vez generado el movimiento sin necesidad de hacerlo, si el movimiento no da jaque y si la evaluación más un margen de seguridad es menor de alpha entonces podemos cortar el movimiento y pasar al siguiente. Esto se llama futility prunnig, este tipo de prunning se suele realizar en profundidades bajas, normalemente hasta 3.

En mi programa, yo tengo que utilizar la función hacer movimiento y luego comprobar si da jaque, luego si aplico futility y corto, todavía tengo que deshacer el movimiento, todo este proceso parece más lento que si no hago futility, así que la futility no me funciona. Yo pienso que quizás a ti con el doble movimiento si luego tienes que deshacerlos te puede ocurrir algo parecido. Si lo utilizas quizás también tengas que pensar en probarlo solo en profundidades bajas y bajo ciertas condiciones.

Si quieres prueba el siguiente código antes del movimiento nulo o después, se trataría de una especie de prunning pero con la ventaja que no hay necesidad de hacer y deshacer el movimiento.

Code: Select all
int nodo_pv = ((beta-alpha)>1);
eval = eval(alpha, beta) + 300;
if (!(tipo&TIPO_CHECK) && !nodo_pv && depth <= 3 && eval < beta) {
     ev = quiesce(alpha, beta);
     if (ev > eval) return ev;
     else return eval;
}
Best wishes,

Pedro Castro
User avatar
Pedro Castro
 
Posts: 180
Joined: 28 Jan 2005, 01:09
Location: Pays Basque (Spain)

Re: Null move

Postby Manuel Peña » 13 Feb 2008, 20:33

Hay programas como Fruit, que cuando va a realizar un movimiento, solo con la generación de las jugadas ya sabe si el movimiento va a dar jaque o no, esto es algo fundamental para poder cortar, una vez generado el movimiento sin necesidad de hacerlo.


Te refieres a esto?

Code: Select all
//-------------------------------------------------------------------
//JAQUE NEGRO
//-------------------------------------------------------------------
//esta el negro en jaque? 1=si 0=no
//pos=posicion rey negro

int posicion::jaquen (int pos)
{
if  ((pb&cap_peon_negro[pos])|(cb&mov_caballo[pos])|(rb&mov_rey[pos])) return 1;
unsigned __int64 a;
unsigned __int64 piezas=pb|cb|ab|tb|db|rb|pn|cn|an|tn|dn|rn;
for (a=(ab|db)&mov_alfil[pos];a;a&=(a-1)) if ((chkad[lsb(a)][pos]&piezas)==0) return 1;
for (a=(tb|db)&mov_torre[pos];a;a&=(a-1)) if ((chktd[lsb(a)][pos]&piezas)==0) return 1;
return 0;
}
Manuel Peña
 
Posts: 15
Joined: 28 Jan 2008, 14:06

Re: Null move

Postby Pedro Castro » 14 Feb 2008, 00:24

El código que me muestras si lo comprendo bien es una función que comprueba si una casilla está siendo atacada, si esta casilla es el rey entonces tienes una función que comprueba si el rey está en jaque.Esta función la tienen que tener todos los programas.

Tu programa utiliza bitboards?

Cuando yo voy a hacer un movimiento, entonces tengo que generar toda la lista de movimientos posibles, cuando generamos la lista hay 2 posibilidades, generar solo movimientos legales o generar también movimientos donde el rey se puede quedar en jaque. Muchos programas utilizan esta segunda opción, si hacemos el movimiento y nos quedamos en jaque simplemente luego deshacemos el movimiento y pasamos al siguiente, se suele hacer así porque como muchos movimientos se cortan entonces no es necesaria hacer esa comprobación y el sistema funciona más rápido que generar solo movimientos legales. Por decir de alguna forma estamos retrasando la comprobación de si el rey se queda en jaque.

En la función de hacer movimiento, yo tengo que comprobar si es posible hacer un enroque, tengo que actualizar las casillas iniciales y finales con las piezas y colores, tengo que actualizar al paso y la regla de los 50 movimientos, la hash y tengo que guardar todo esto, en mi caso aprovecho para actualizar la cuenta de material incrementalmente, son un montón de cosas para hacer. Si un movimiento es ilegal, entonces tengo que deshacer todo esto, lo mismo ocurre si quiero hacer prunning un movimiento, primero tengo que hacerlo, comprobar que no es ilegal, luego tengo que comprobar una serie de cosas como la profundidad y evaluación y si luego lo corto lo tengo que deshacer. Con todas estas cosas no solo no obtengo beneficio del prunning sino que parece el código incluso más lento.

Hay algunos programas, generalmente suelen ser los top, que en su función de generar movimientos, si que saben cuando dan jaque al contrario y esta información la guardan. Estos programas sin necesidad de hacer entonces la función hacer movimiento y deshacer son capaces de cortar el movimiento y pasar al siguiente, si no lo cortan es entonces cuando actualizan todo lo necesario con la función makemove o hacer movimiento. Como lo hacen es algo que no se. Mi opinión personal es que en estos programas la futility prunning tiene que funcionar muy bien ya que pueden cortar un movimiento y pasar al siguiente sin hacer todas esas cosas que yo hago en las funciones hacer movimiento y deshacer.

Un programa con un concepto parecido al mío que tiene que hacer las funciones hacer movimiento y deshacer es Diablo, Diablo hace prunnig en profundidad 1 y 2, yo no estoy seguro que es efectivo aunque si el autor lo deja es porque lo será, la misma idea en mi programa no funciona.

De todos modos no espero mucho ELO del prunning, quizás 20 puntos, parece más efectiva en cuestión de ELO LMR.

El código que te comenté ayer es utilizado creo que por Glaurung y Strelka, se basa en la idea de la futility prunning, pero al estar situado este código por encima o debajo del movimiento nulo, no es necesario estar deshaciendo los movimientos y yo pienso que es más efectivo para programas como danasah o diablo, aunque no lo he probado bien, la ganancia si la hay no es no es mucha y se necesita muchos juegos para poder ver si es efectivo.
Best wishes,

Pedro Castro
User avatar
Pedro Castro
 
Posts: 180
Joined: 28 Jan 2005, 01:09
Location: Pays Basque (Spain)

Re: Null move

Postby Manuel Peña » 15 Feb 2008, 12:15

Hay algunos programas, generalmente suelen ser los top, que en su función de generar movimientos, si que saben cuando dan jaque al contrario y esta información la guardan. Estos programas sin necesidad de hacer entonces la función hacer movimiento y deshacer son capaces de cortar el movimiento y pasar al siguiente, si no lo cortan es entonces cuando actualizan todo lo necesario con la función makemove o hacer movimiento. Como lo hacen es algo que no se. Mi opinión personal es que en estos programas la futility prunning tiene que funcionar muy bien ya que pueden cortar un movimiento y pasar al siguiente sin hacer todas esas cosas que yo hago en las funciones hacer movimiento y deshacer.


Yo genero solo movimientos legales pero no hago y dehago para saber si son legales sino esto...

Code: Select all
//genero movimientos del rey
for (b=(~piezas)&mov_rey[irb];b;b&=(b-1))
{
destino=lsb(b);
if (movlegalb(piezas^rb^setb[destino],pn,cn,an,tn,dn,rn,destino))
arbol.add_mov (irb|(destino<<6)|(WK<<28));
}
}


Code: Select all
int movlegalb (__int64 piezas,__int64 pn,__int64 cn,__int64 an,__int64 tn,__int64 dn,__int64 rn,int pos)
{
__int64 a;
if  ((pn&cap_peon_blanco[pos])|((cn&mov_caballo[pos])|(rn&mov_rey[pos])))     return 0;
for (a=(an|dn)&mov_alfil[pos];a;a&=(a-1)) if ((chkad[lsb(a)][pos]&piezas)==0) return 0;
for (a=(tn|dn)&mov_torre[pos];a;a&=(a-1)) if ((chktd[lsb(a)][pos]&piezas)==0) return 0;
return 1;
}


podria hacer algo similar a la comprobación de legalidad para saber si dejo en jaque al contrario pero no entiendo porque es una ventaja disponer de esa informacion antes de hacer el movimiento?
Manuel Peña
 
Posts: 15
Joined: 28 Jan 2008, 14:06

Re: Null move

Postby Pedro Castro » 15 Feb 2008, 19:45

Manuel Peña wrote:podria hacer algo similar a la comprobación de legalidad para saber si dejo en jaque al contrario pero no entiendo porque es una ventaja disponer de esa informacion antes de hacer el movimiento?


Cuando vamos a reducir la profundidad de los movimientos con LMR o cuando vamos a cortar algunos movimientos con futility prunning tenemos que tener en cuenta algunas consideraciones, por ejemplo en el caso de la LMR no reducir la profundidad de aquellos movimientos que son capturas, aquellos movimientos que tienen extensiones, si estamos previamente en jaque, etc. Otra consideración que podemos considerar es no reducir ni cortar aquellos movimientos que dan jaque al rey contrario, ya que con ello podemos llegar a dar un mate o ganar material y estos movimientos es conveniente que los extendamos, esto es por lo que yo considero que es interesante tener esta información.

Mi programa está basado en el siguiente Alphabeta:
Code: Select all
int AlphaBeta(int depth, int alpha, int beta)

{

    if (depth == 0)

        return Evaluate();

    numero_mov = GenerateMoves(); // legales y no legales

    for (i = 0, i < numero_mov, ++i) {

        if (MakeMove() == ILEGAL) {

             UnmakeMove();

             continue;

        }

        val = -AlphaBeta(depth - 1, -beta, -alpha);

        UnmakeMove();

        if (val >= beta)

            return beta;

        if (val > alpha)

            alpha = val;

    }

    return alpha;

}


La función MakeMove que tiene mi programa es parecida a la de TSCP, un poco más complicada ya que también actualizo el material en ella.

Code: Select all
/* makemove() makes a move. If the move is illegal, it
   undoes whatever it did and returns FALSE. Otherwise, it
   returns TRUE. */

BOOL makemove(move_bytes m)
{
   
   /* test to see if a castle move is legal and move the rook
      (the king is moved with the usual move code later) */
   if (m.bits & 2) {
      int from, to;

      if (in_check(side))
         return FALSE;
      switch (m.to) {
         case 62:
            if (color[F1] != EMPTY || color[G1] != EMPTY ||
                  attack(F1, xside) || attack(G1, xside))
               return FALSE;
            from = H1;
            to = F1;
            break;
         case 58:
            if (color[B1] != EMPTY || color[C1] != EMPTY || color[D1] != EMPTY ||
                  attack(C1, xside) || attack(D1, xside))
               return FALSE;
            from = A1;
            to = D1;
            break;
         case 6:
            if (color[F8] != EMPTY || color[G8] != EMPTY ||
                  attack(F8, xside) || attack(G8, xside))
               return FALSE;
            from = H8;
            to = F8;
            break;
         case 2:
            if (color[B8] != EMPTY || color[C8] != EMPTY || color[D8] != EMPTY ||
                  attack(C8, xside) || attack(D8, xside))
               return FALSE;
            from = A8;
            to = D8;
            break;
         default:  /* shouldn't get here */
            from = -1;
            to = -1;
            break;
      }
      color[to] = color[from];
      piece[to] = piece[from];
      color[from] = EMPTY;
      piece[from] = EMPTY;
   }

   /* back up information so we can take the move back later. */
   hist_dat[hply].m.b = m;
   hist_dat[hply].capture = piece[(int)m.to];
   hist_dat[hply].castle = castle;
   hist_dat[hply].ep = ep;
   hist_dat[hply].fifty = fifty;
   hist_dat[hply].hash = hash;
   ++ply;
   ++hply;

   /* update the castle, en passant, and
      fifty-move-draw variables */
   castle &= castle_mask[(int)m.from] & castle_mask[(int)m.to];
   if (m.bits & 8) {
      if (side == LIGHT)
         ep = m.to + 8;
      else
         ep = m.to - 8;
   }
   else
      ep = -1;
   if (m.bits & 17)
      fifty = 0;
   else
      ++fifty;

   /* move the piece */
   color[(int)m.to] = side;
   if (m.bits & 32)
      piece[(int)m.to] = m.promote;
   else
      piece[(int)m.to] = piece[(int)m.from];
   color[(int)m.from] = EMPTY;
   piece[(int)m.from] = EMPTY;

   /* erase the pawn if this is an en passant move */
   if (m.bits & 4) {
      if (side == LIGHT) {
         color[m.to + 8] = EMPTY;
         piece[m.to + 8] = EMPTY;
      }
      else {
         color[m.to - 8] = EMPTY;
         piece[m.to - 8] = EMPTY;
      }
   }

   /* switch sides and test for legality (if we can capture
      the other guy's king, it's an illegal position and
      we need to take the move back) */
   side ^= 1;
   xside ^= 1;
   if (in_check(xside)) {
      takeback();
      return FALSE;
   }
   set_hash();
   return TRUE;
}


Al final de la función MakeMove se cambia el turno y se comprueba si el rey está en Jaque, si es así la función MakeMove sería ilegal.

Para saber si yo doy jaque al rey contrario tengo que hacer esta función y luego compruebo si está en in_check(side).

La función UnmakeMove es casi igual de costosa que esta.

Ahora vamos a realizar prunning. En el alphabeta yo lo tengo que hacer después de comprobar que la función MakeMove es legal. Luego tengo que comprobar que no doy jaque al rey contrario y si no doy jaque al rey contrario entonces puedo deshacer el movimiento y continuar con otro, pero todo esto me ha salido muy caro y entonces seguramente no funciona la futility prunning.

Code: Select all
int AlphaBeta(int depth, int alpha, int beta)

{

    es_jaque = in_check(side);

    if (depth == 0)

        return Evaluate();

    numero_mov = GenerateMoves(); // legales y no legales

    for (i = 0, i < numero_mov, ++i) {

        if (MakeMove() == ILEGAL) {

             UnmakeMove();

             continue;

        }

        da_jaque = in_check(side);

        // futility prunning

        if (depth <= 2 && !es_jaque && !da_jaque && !nodo_pv) {

             if (eval(alpha, beta) + margen < alpha)

                  UnmakeMove();

                  continue;

       }


        val = -AlphaBeta(depth - 1, -beta, -alpha);

        UnmakeMove();

        if (val >= beta)

            return beta;

        if (val > alpha)

            alpha = val;

    }

    return alpha;

}


Si de alguna forma yo tengo generación de movimientos legales y se que el movimiento da jaque antes de hacer la función makemove, entonces justo después de la instrución "for" lo primero de todo miraría la futility prunning y si me interesa cortar paso al siguiente movimiento sin hacer estas pesadas funciones de hacer movimientos y deshacer, yo creo que Fruit lo hace de esa forma, aunque no estoy seguro.

Talvez tu tengas obtimizado el programa, la generación de movimientos sea legal y más o menos igual de rápida que si hubieras generado también movimientos ilegales, y si puedes comprobar fácilmente si das jaque al rey seguro que esto es una ventaja si quieres utilizar esto para luego hacer prunnig o LMR.
Best wishes,

Pedro Castro
User avatar
Pedro Castro
 
Posts: 180
Joined: 28 Jan 2005, 01:09
Location: Pays Basque (Spain)

Re: Null move

Postby Tord Romstad » 15 Feb 2008, 21:09

Pedro Castro wrote:
Code: Select all
int AlphaBeta(int depth, int alpha, int beta)

{

    es_jaque = in_check(side);

    if (depth == 0)

        return Evaluate();

    numero_mov = GenerateMoves(); // legales y no legales

    for (i = 0, i < numero_mov, ++i) {

        if (MakeMove() == ILEGAL) {

             UnmakeMove();

             continue;

        }

        da_jaque = in_check(side);

        // futility prunning

        if (depth <= 2 && !es_jaque && !da_jaque && !nodo_pv) {

             if (eval(alpha, beta) + margen < alpha)

                  UnmakeMove();

                  continue;

       }


        val = -AlphaBeta(depth - 1, -beta, -alpha);

        UnmakeMove();

        if (val >= beta)

            return beta;

        if (val > alpha)

            alpha = val;

    }

    return alpha;

}


Hi Pedro,

My understanding of Spanish is not good enough to follow your discussion, but the futility pruning in the function above looks buggy: You call eval() after making the move, which gives you a score from the point of view of the wrong player. Something like
Code: Select all
if(-eval(-beta, -alpha) + margin < alpha) ...

should work better. Depending on how your lazy eval works, you might also be able to achieve the same result slightly more cheaply by using
Code: Select all
if(-eval(-(alpha+1), -alpha) + margin < alpha) ...


This is not how futility pruning is usually done, though. Most people do futility pruning based on the static evaluation of the function before the move is made. This is much faster, because you save the work of making and unmaking all the moves, and numerous calls to the evaluation function. Your version might be slightly more accurate, though.

Tord
User avatar
Tord Romstad
 
Posts: 639
Joined: 09 Oct 2004, 12:49
Location: Oslo, Norway

Re: Null move

Postby Pedro Castro » 15 Feb 2008, 22:27

Tord Romstad wrote:This is not how futility pruning is usually done, though. Most people do futility pruning based on the static evaluation of the function before the move is made. This is much faster, because you save the work of making and unmaking all the moves, and numerous calls to the evaluation function. Your version might be slightly more accurate, though.
Tord


Hi Tord,

I tried to explain the same thing to Manuel.

I told Manuel that there are programs as Diablo that when make futility prunning, they need make and unmake all the moves. I understand that the reason that Diablo does this because he needs to know if he gives check to the king contrary and then not cut those moves. (Diablo also has to make the move to see if it is legal or not)

I told Manuel that I thought that the way to prunning in this way is not effective, and I said that programs as TOGA did futility prunnig before making the move, but sure that these programs are capable of knowing if they give a check on the king contrary. Diablo not think that he succeeds if it is not doing the move.

Tord, you believe the futility prunning Diablo works?

Code: Select all
    ev = evaluate();
    incheck = attack_tab[board.xside][KSQ(board.side)];

    ...

    // test if we want to try a futility cut and assign a margin based on depth
    if ((depth < (3*PLY)) && !incheck && !pv_node) {
        try_futility_cut = 1;
   if (know->material < ENDGAME_MAT)
           futility_margin = (depth < (2*PLY)) ? 200 : 400;
   else
           futility_margin = (depth < (2*PLY)) ? 150 : 300;
    }

    order_moves(hash_move);
    while ((move = next_move())) {
        givescheck = isa_check(move);
        make_move(move);
        if (!in_check(board.xside)) {
            moves_searched++;
            ext = extensions(move, givescheck, mate_threat, single_reply);
       (know_stack+ply)->extend = ext;

            // futility cut
            if (try_futility_cut && !ext && !(move & (CAPTURE | PROMOTION))) {
                if (ev < (alpha - futility_margin)) {
                    unmake_move(move);
                    continue;
                }
            }

            if (moves_searched == 1) {
                value = -search(depth-PLY+ext, -beta, -alpha, 1);
            }
            else  {
                value = -search(depth-PLY+ext, -alpha-1, -alpha, 1);
                if (value > alpha && value < beta) {
                    value = -search(depth-PLY+ext, -beta, -alpha, 1);
                }
            }
        }
        unmake_move(move);
Best wishes,

Pedro Castro
User avatar
Pedro Castro
 
Posts: 180
Joined: 28 Jan 2005, 01:09
Location: Pays Basque (Spain)

Re: Null move

Postby Manuel Peña » 18 Feb 2008, 17:43

Eat es mi funcion...

Code: Select all
#include "StdAfx.h"
#include "posicion.h"
#include "definiciones.h"
#include "bitboards.h"
#include "recognicers.h"
#include "evalua.h"
#include "global.h"
#include "mt.h"
#include "hash.h"

using namespace std;
//-------------------------------------------------------------------
//OPCIONES ORDEN
//-------------------------------------------------------------------

#define OPTION_HASH_MOVE
#define OPTION_KILLER1
#define OPTION_KILLER2
#define OPTION_IID

//-------------------------------------------------------------------
//OPCIONES PRURING
//-------------------------------------------------------------------

#define OPTION_HASH
//#define OPTION_PRURING
#define OPTION_NULL
//#define OPTION_NULL2

//-------------------------------------------------------------------
//OPCIONES EXTENSION
//-------------------------------------------------------------------

#define OPTION_CHECK_EXTENSION
#define OPTION_THREAD_EXTENSION

//-------------------------------------------------------------------
//OPCIONES HISTORY
//-------------------------------------------------------------------

#define K_HISTORY 5
#define P_HISTORY 2

//-------------------------------------------------------------------
//ALFABETA
//-------------------------------------------------------------------

int posicion::alfabeta (int alfa,int beta,int depth,int ply,int tipo)
{
_ASSERT(beta==(alfa+1));

int current  =MIN_VAL;
int ev       =MIN_VAL;
int ev2      =MIN_VAL;
int bestmove =0;

int ttmove=0;
int ttflag=UPPER;

int uperbound =MAX_VAL-ply;
int lowerbound=MIN_VAL+ply;
int nmov=0;
int R;
int aux100;

//-------------------------------------------------------------------
//REGLA 50 MOVIMIENTOS
//-------------------------------------------------------------------
//Estos valores no son validos para almacenar en la hash

if (mov100>99)
{
if (movimientos()==0)
{
if (turno==BLANCO)
{
if (jaqueb(lsb(rb)))
return MIN_VAL+ply;
return 0;
}
if (turno==NEGRO)
{
if (jaquen(lsb(rn)))
return MIN_VAL+ply;
return 0;
}
}
else return 0;
}

//-------------------------------------------------------------------
//REPETICIONES
//-------------------------------------------------------------------

if (mov100>7)
if (repeticiones()>2) return 0;

_ASSERT(repeticiones()<=2);

//-------------------------------------------------------------------
//CHECK
//-------------------------------------------------------------------

int tipo_anterior=tipo;

tipo=0;
if ((turno==BLANCO)&&(jaqueb(lsb(rb)))) tipo|=TIPO_JQ;
if ((turno==NEGRO) &&(jaquen(lsb(rn)))) tipo|=TIPO_JQ;

//-------------------------------------------------------------------
//CHECK EXTENSION
//-------------------------------------------------------------------

#ifdef OPTION_CHECK_EXTENSION
if (tipo&TIPO_JQ) depth++;
#endif

//-------------------------------------------------------------------
//QSEARCH
//-------------------------------------------------------------------

if (depth<1) return qsearch(alfa,beta,ply+1);

//-------------------------------------------------------------------
//HASH
//-------------------------------------------------------------------
#ifdef OPTION_HASH

int test_hash=(turno)?hash.consultab(hash_key,depth,&uperbound,&lowerbound,ply):hash.consultan(hash_key,depth,&uperbound,&lowerbound,ply);

if (test_hash==1)
{
if (uperbound <=alfa) return uperbound;
if (lowerbound>=beta) return lowerbound;
//if (uperbound<beta)   beta=uperbound;//ERR?
//if (lowerbound>alfa)  alfa=lowerbound;//ERR?
}
#endif

if (beta==alfa) return alfa;

//-------------------------------------------------------------------
//NULL MOVE PRURING
//-------------------------------------------------------------------

R=4;
//unsigned __int64 x1=hash_key;

#ifdef OPTION_NULL
R=4;
if (!(tipo_anterior&TIPO_NULL))
if (!(tipo&TIPO_JQ))
if (evalua(alfa,beta)>beta)
{
turno^=BLANCO;
aux100=mov100;
mov100=0;
ev=-alfabeta(-beta,-beta+1,depth-R,ply+1,tipo|TIPO_NULL);
mov100=aux100;
turno^=BLANCO;
if (setb[Id]&th_stop)
{
arbol.borra_nivel();
return 0;//STOP!!!
}
if (ev>=beta)
{
//if (ev>5) return (-alfabeta (-beta,-beta+1,depth-5,ply+1,tipo));
if (ev>=APP_MAX_VAL) return APP_MAX_VAL;
return ev;
}
if (ev<APP_MIN_VAL) tipo|=TIPO_NULL_TH;
}
#endif

//_ASSERT(x1==hash_key);

//-------------------------------------------------------------------
//THREAD EXTENSION
//-------------------------------------------------------------------

#ifdef OPTION_THREAD_EXTENSION
if (tipo&TIPO_NULL_TH) depth++;
#endif

//-------------------------------------------------------------------
//NUEVO NIVEL
//-------------------------------------------------------------------

arbol.nuevo_nivel();

//-------------------------------------------------------------------

#ifdef OPTION_HASH_MOVE
if (turno==BLANCO) ttmove=hash.best_move_b (hash_key);
if (turno==NEGRO)  ttmove=hash.best_move_n (hash_key);
#endif

//-------------------------------------------------------------------
//IID
//-------------------------------------------------------------------

#ifdef OPTION_IID
if ((ttmove==0)&&(depth>5))
{
ev=-alfabeta(-beta,-alfa,min((depth-2),(depth/2)),ply+1,tipo);
if (setb[Id]&th_stop)
{
arbol.borra_nivel();
return 0;//STOP!!!
}
if (turno==BLANCO) ttmove=hash.best_move_b (hash_key);
if (turno==NEGRO)  ttmove=hash.best_move_n (hash_key);
}
#endif

//-------------------------------------------------------------------
//HASH MOVE
//-------------------------------------------------------------------

#ifdef OPTION_HASH_MOVE
if (ttmove!=0)
if (legal(ttmove))
{
mueve(ttmove);nmov++;
ev=-alfabeta(-beta,-alfa,depth-1,ply+1,tipo);
desmueve();
if (setb[Id]&th_stop)
{
arbol.borra_nivel();
return 0;//STOP!!!
}
if (ev>current)
{
current=ev;
if (ev>=beta)
{
if (nmov<255) orden_db[nmov-1]+=1; else orden_db[255]+=1;
arbol.borra_nivel();
NODES+=nmov;
return ev;
}
if(ev>alfa)
{
//if (depth==1) actualiza_pv (ply);
alfa=ev;
ttflag=EXACT;
}
}
}
/*
else
{
muestra ();
print_mov (ttmove);
cout<<ttmove<<endl;
}
*/
#endif

//-------------------------------------------------------------------
//CAPTURAS
//-------------------------------------------------------------------

if (turno) gencap_b ();
else       gencap_n ();
int N3=dim();

//-------------------------------------------------------------------
//ORDENA CAPTURAS
//-------------------------------------------------------------------

int N1=0;
N1=ordena_capturas_1 ();

//-------------------------------------------------------------------
//CAPTURAS BUENAS
//-------------------------------------------------------------------

for(int i=0;i<N1;i++)
#ifdef OPTION_HASH_MOVE
if (arbol[i]!=ttmove)
#endif
{
mueve(arbol[i]);nmov++;
ev=-alfabeta(-beta,-alfa,depth-1,ply+1,tipo);
desmueve();

if (setb[Id]&th_stop)
{
arbol.borra_nivel();
return 0;//STOP!!!
}

if (ev>current)
{
current=ev;
bestmove=arbol[i];

if (ev>=beta)
{
//Estadisticas de ordenamiento
if (nmov<255) orden_db[nmov-1]+=1; else orden_db[255]+=1;
//Beta cut
arbol.borra_nivel();
(turno)?hash.admovb (hash_key,ev,bestmove,LOWER,depth,ply):hash.admovn (hash_key,ev,bestmove,LOWER,depth,ply);
NODES+=nmov;
return ev;
}

if(ev>alfa)
{
//Actualizamos la PV
//if (depth==1) actualiza_pv (ply);
alfa=ev;
ttflag=EXACT;
}
}
}

//-------------------------------------------------------------------
//KILLER 1
//-------------------------------------------------------------------

int k1=0;
#ifdef OPTION_KILLER1
k1=arbol.killer1();
#ifdef OPTION_HASH_MOVE
if (k1!=ttmove)
#endif
if (legal(k1))
{
mueve(k1);nmov++;
ev=-alfabeta(-beta,-alfa,depth-1,ply+1,tipo);
desmueve();
if (setb[Id]&th_stop)
{
arbol.borra_nivel();
return 0;//STOP!!!
}
if (ev>current)
{
current=ev;
bestmove=k1;
if (ev>=beta)
{
if (nmov<255) orden_db[nmov-1]+=1; else orden_db[255]+=1;
arbol.borra_nivel();
(turno)?hash.admovb (hash_key,ev,bestmove,LOWER,depth,ply):hash.admovn (hash_key,ev,bestmove,LOWER,depth,ply);
NODES+=nmov;
return ev;
}
if(ev>alfa)
{
//if (depth==1) actualiza_pv (ply);
alfa=ev;
ttflag=EXACT;
}
}
}
#endif

//-------------------------------------------------------------------
//KILLER 2
//-------------------------------------------------------------------

int k2=0;
#ifdef OPTION_KILLER2
k2=arbol.killer2();
#ifdef OPTION_HASH_MOVE
if (k2!=ttmove)
#endif
if (k2!=k1)
if (legal(k2))
{
mueve(k2);nmov++;
ev=-alfabeta(-beta,-alfa,depth-1,ply+1,tipo);
desmueve();
if (setb[Id]&th_stop)
{
arbol.borra_nivel();
return 0;//STOP!!!
}
if (ev>current)
{
current=ev;
bestmove=k2;
if (ev>=beta)
{
if (nmov<255) orden_db[nmov-1]+=1; else orden_db[255]+=1;
arbol.borra_nivel();
(turno)?hash.admovb (hash_key,ev,bestmove,LOWER,depth,ply):hash.admovn (hash_key,ev,bestmove,LOWER,depth,ply);
NODES+=nmov;
return ev;
}
if(ev>alfa)
{
//if (depth==1) actualiza_pv (ply);
alfa=ev;
ttflag=EXACT;
}
}
}
#endif

//-------------------------------------------------------------------
//MOVIMIENTOS
//-------------------------------------------------------------------

if (turno) genmov_b ();
else       genmov_n ();
int N2=dim ();
ordena_ab_movs (N3,N2);

//-------------------------------------------------------------------

for(int i=N3;i<N2;i++)
#ifdef OPTION_HASH_MOVE
if (arbol[i]!=ttmove)
#endif
#ifdef OPTION_KILLER1
if (arbol[i]!=k1)
#endif
#ifdef OPTION_KILLER2
if (arbol[i]!=k2)
#endif
{
mueve(arbol[i]);nmov++;
if ((nmov>=K_HISTORY)&&(ply>=P_HISTORY)&&(!(tipo&TIPO_JQ)))
{
ev=-alfabeta(-alfa-1,-alfa,depth-2,ply+1,tipo);
if (ev>alfa) ev=-alfabeta(-beta,-alfa,depth-1,ply+1,tipo);
}
else ev=-alfabeta(-beta,-alfa,depth-1,ply+1,tipo);
desmueve();

if (setb[Id]&th_stop)
{
arbol.borra_nivel();
return 0;//STOP!!!
}

if (ev>current)
{
current=ev;
bestmove=arbol[i];
if (ev>=beta)
{
if (nmov<255) orden_db[nmov-1]+=1; else orden_db[255]+=1;
arbol.add_killer (bestmove,depth,turno);
arbol.borra_nivel();
(turno)?hash.admovb (hash_key,ev,bestmove,LOWER,depth,ply):hash.admovn (hash_key,ev,bestmove,LOWER,depth,ply);
NODES+=nmov;
return ev;
}

if(ev>alfa)
{
//if (depth==1) actualiza_pv (ply);
alfa=ev;
ttflag=EXACT;
}
}
}

//-------------------------------------------------------------------
//CAPTURAS MALAS
//-------------------------------------------------------------------

for(int i=N1;i<N3;i++)
#ifdef OPTION_HASH_MOVE
if (arbol[i]!=ttmove)
#endif
#ifdef OPTION_KILLER1
if (arbol[i]!=k1)
#endif
#ifdef OPTION_KILLER2
if (arbol[i]!=k2)
#endif
{
mueve(arbol[i]);nmov++;
if ((nmov>=K_HISTORY)&&(ply>=P_HISTORY)&&(!(tipo&TIPO_JQ)))
{
ev=-alfabeta(-alfa-1,-alfa,depth-2,ply+1,tipo);
if (ev>alfa) ev=-alfabeta(-beta,-alfa,depth-1,ply+1,tipo);
}
else ev=-alfabeta(-beta,-alfa,depth-1,ply+1,tipo);
desmueve();

if (setb[Id]&th_stop)
{
arbol.borra_nivel();
return 0;//STOP!!!
}

if (ev>current)
{
current=ev;
bestmove=arbol[i];

if (ev>=beta)
{
//Estadisticas de ordenamiento
if (nmov<255) orden_db[nmov-1]+=1; else orden_db[255]+=1;
//Aqui podemos añadir los killer moves
//Beta cut
arbol.borra_nivel();
(turno)?hash.admovb (hash_key,ev,bestmove,LOWER,depth,ply):hash.admovn (hash_key,ev,bestmove,LOWER,depth,ply);
NODES+=nmov;
return ev;
}

if(ev>alfa)
{
//Actualizamos la PV
//if (depth==1) actualiza_pv (ply);
alfa=ev;
ttflag=EXACT;
}
}
}

//-------------------------------------------------------------------
//UNDERPROMOTIONS
//-------------------------------------------------------------------

if (turno) genund_b ();
else       genund_n ();
int N4=dim();

//-------------------------------------------------------------------

for(int i=N2;i<N4;i++)
#ifdef OPTION_HASH_MOVE
if (arbol[i]!=ttmove)
#endif
#ifdef OPTION_KILLER1
if (arbol[i]!=k1)
#endif
#ifdef OPTION_KILLER2
if (arbol[i]!=k2)
#endif
{
mueve(arbol[i]);nmov++;
if ((nmov>=K_HISTORY)&&(ply>=P_HISTORY)&&(!(tipo&TIPO_JQ)))
{
ev=-alfabeta(-alfa-1,-alfa,depth-2,ply+1,tipo);
if (ev>alfa) ev=-alfabeta(-beta,-alfa,depth-1,ply+1,tipo);
}
else ev=-alfabeta(-beta,-alfa,depth-1,ply+1,tipo);
desmueve();

if (setb[Id]&th_stop)
{
arbol.borra_nivel();
return 0;//STOP!!!
}

if (ev>current)
{
current=ev;
bestmove=arbol[i];


if (ev>=beta)
{
//Estadisticas de ordenamiento
if (nmov<255) orden_db[nmov-1]+=1; else orden_db[255]+=1;
//Aqui podemos añadir los killer moves
//Beta cut
arbol.borra_nivel();
(turno)?hash.admovb (hash_key,ev,bestmove,LOWER,depth,ply):hash.admovn (hash_key,ev,bestmove,LOWER,depth,ply);
NODES+=nmov;
return ev;
}

if(ev>alfa)
{
alfa=ev;
ttflag=EXACT;
}
}
}

//-------------------------------------------------------------------
//MATE/STALEMATE
//-------------------------------------------------------------------

if (dim()==0)
{
arbol.borra_nivel();
if (turno==BLANCO)
{
if (jaqueb(lsb(rb)))
return MIN_VAL+ply;
return 0;
}
if (turno==NEGRO)
{
if (jaquen(lsb(rn)))
return MIN_VAL+ply;
return 0;
}
}

//-------------------------------------------------------------------
//FIN
//-------------------------------------------------------------------

_ASSERT(nmov==dim());

arbol.borra_nivel();
(turno)?hash.admovb (hash_key,current,bestmove,ttflag,depth,ply):hash.admovn (hash_key,current,bestmove,ttflag,depth,ply);
NODES+=nmov;
return current;
}

//-------------------------------------------------------------------
Manuel Peña
 
Posts: 15
Joined: 28 Jan 2008, 14:06

Re: Null move

Postby Pedro Castro » 19 Feb 2008, 18:08

Hola Manuel,

he mirado un poco tu alphabeta.

Como haces para saber que las capturas son malas?, te pregunto esto porque creo que reduces las capturas malas, para saber si una captura es mala o no debería pasar por un stactic exchange evaluation (SEE), de lo contrario una captura de una dama por un peon aunque está comiendo algo de inferior valor no es una mala captura si el peon no está defendido, yo en general no reduzco ninguna captura, tampoco reduzco las promociones ni los movimientos killer que tu tampoco lo haces.

Por otra parte había alguién que Talkchess pedía un logo para tu programa, me permití hacer uno aunque soy un pésimo diseñador, no estoy seguro si el nombre de tu anterior programa zoidberg se refiere al motivo del logo, lo puedes ver:

http://www.talkchess.com/forum/viewtopic.php?t=19701
Best wishes,

Pedro Castro
User avatar
Pedro Castro
 
Posts: 180
Joined: 28 Jan 2005, 01:09
Location: Pays Basque (Spain)

Re: Null move

Postby Manuel Peña » 20 Feb 2008, 12:37

Deduzco que son malas mediante sse pero no las reduzco las pospongo a los movimientos N1-N2 N2-N3 N3-N4 son los intervalos...

utilizo las funciones ordenar que ordenan las listas

gracias por el logo :wink:
Manuel Peña
 
Posts: 15
Joined: 28 Jan 2008, 14:06


Return to Programming and Technical Discussions

Who is online

Users browsing this forum: No registered users and 30 guests