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.