static int Quiescent(BOARD *board, int alpha, int beta, int root, CONTROL *control, MOVE killers[][2]) { int nmoves, nlegal = 0; int val; MOVE moves[MAXMOVES]; if(!control->ponder && clock() - control->init_time >= control->max_time*CPMS){ control->stop = 1; } val = LazyEval(board); if(val-LAZYBETA >= beta) return beta; if(val+LAZYALPHA < alpha) return alpha; val = StaticEval(board); UpdateTable(&hash_table, board->zobrist_key, val, 0, 0, HASH_EXACT); if (val >= beta) return beta; if (val > alpha) alpha = val; nmoves = CaptureGen(board, moves); nmoves = FilterWinning(board, moves, nmoves); // nmoves = SortMoves(board, moves, nmoves, killers[root]); for(int i = 0; i < nmoves; i++){ control->node_count++; MakeMove(board, &moves[i]); if(!LeftInCheck(board)){ nlegal++; val = -Quiescent(board, -beta, -alpha, root+1, control, killers); Takeback(board, moves[i]); if(val >= beta){ return beta; } if(val > alpha){ alpha = val; } }else Takeback(board, moves[i]); } return alpha; }
static int AlphaBeta (BOARD *board, int depth, int alpha, int beta, int root, CONTROL *control, char skip_null, MOVE killers[][2]) { int nmoves, good = 0, nlegal = 0; MOVE moves[MAXMOVES]; MOVE best_move = 0; char str_mov[MVLEN]; int val = ERRORVALUE; char hash_flag = HASH_ALPHA; int in_check = InCheck(board, 0); if (root > control->seldepth) control->seldepth = root; if (depth > 0 || in_check) { if (root > 0) { val = GetHashEval(&hash_table, board->zobrist_key, depth, alpha, beta); if (val != ERRORVALUE) return val; } if (depth > 2 && !in_check) { if (!skip_null && board->piece_material[board->white_to_move] != 0) { MOVE null_mv = NULL_MOVE; MakeMove(board, &null_mv); val = -AlphaBeta(board, depth-3, -beta, -beta+1, root+1, control, 1, killers); Takeback(board, null_mv); if (val >= beta) return beta; } } nmoves = MoveGen(board, moves, 1); good = SortMoves(board, moves, nmoves, killers[root]); } else { if (!control->ponder && clock() - control->init_time >= control->max_time*CPMS) { control->stop = 1; } val = LazyEval(board); if (val-LAZYBETA >= beta) return beta; if (val+LAZYALPHA < alpha) return alpha; val = StaticEval(board); UpdateTable(&hash_table, board->zobrist_key, val, 0, 0, HASH_EXACT); if (val >= beta) return beta; if (val > alpha) alpha = val; nmoves = CaptureGen(board, moves); nmoves = FilterWinning(board, moves, nmoves); } for (int i = 0; i < nmoves; i++) { MakeMove(board, &moves[i]); control->node_count++; if (LeftInCheck(board)) { Takeback(board, moves[i]); continue; } if (root == 0) { if (!control->best_move) control->best_move = moves[i]; /* Better than nothing. */ if (depth > 6 && !control->ponder) { MoveToAlgeb(moves[i], str_mov); printf("info depth %i seldepth %i hashfull %i currmove %s currmovenumber %i\n", depth, control->seldepth, hash_table.full/(hash_table.size/1000), str_mov, i+1); } } nlegal++; val = AssessDraw(board, control->contempt); if (val == ERRORVALUE) { int ext = 0; //InCheck(board, 0) ? 1 : 0; if (best_move) { int LMR = (depth > 2 && !in_check && i > good && !CAPTMASK(moves[i]) && !InCheck(board, 0)) ? 1 : 0; val = -AlphaBeta(board, depth+ext-LMR-1, -alpha-1, -alpha, root+1, control, 0, killers); if (val > alpha) { val = -AlphaBeta(board, depth+ext-1, -alpha-1, -alpha, root+1, control, 0, killers); if (val > alpha && val < beta) { val = -AlphaBeta(board, depth+ext-1, -beta, -alpha, root+1, control, 0, killers); } } } else { val = -AlphaBeta(board, depth+ext-1, -beta, -alpha, root+1, control, 0, killers); } } Takeback(board, moves[i]); if (!control->ponder && control->stop) return alpha; if (val >= beta) { UpdateTable(&hash_table, board->zobrist_key, val, moves[i], depth, HASH_BETA); if (CAPTMASK(moves[i]) == 0 && killers[root][0] != moves[i] && killers[root][1] != moves[i]) { killers[root][1] = killers[root][0]; killers[root][0] = moves[i]; } return beta; } if (val > alpha) { alpha = val; hash_flag = HASH_EXACT; best_move = moves[i]; if (root == 0) control->best_move = best_move; } if (root == 0 && ((clock() - control->init_time) > control->wish_time*CPMS)) { /* if short of time, don't search anymore after current move */ control->stop = 1; return alpha; } } if (nlegal == 0) { if (in_check) { /*UpdateTable(&hash_table, board->zobrist_key, MATE_VALUE+root, 0, depth, HASH_EXACT, hash_table.entries);*/ return MATE_VALUE; } else if (depth > 0) { /*UpdateTable(&hash_table, board->zobrist_key, DRAW_VALUE, 0, depth, HASH_EXACT, hash_table.entries);*/ return DRAW_VALUE; /*Stalemate*/ } } else { UpdateTable(&hash_table, board->zobrist_key, alpha, best_move, depth, hash_flag); } return alpha; }
/* Evaluate the current board position for the side stored in the board * structure. Score for white. (Score for black = -1* this.) We also pass the values * of alpha and beta to this routine on the expectation that we might be able to get a * cutoff if the score is particularly high or low without having to do the time consuming * part of the eval(). */ int EvalMain(Board *B, int alpha, int beta) { int sq,p,npw=0,tpts=B->WPts+B->BPts,sq2,def, score=0; int *SpDef, *SpWon, lazyscore; BITBOARD pieces = B->All; BOOL one_sided; #ifdef DEBUG_EVAL int control = 0, oldscore = 0, contestcount = 0; #endif /* Check for a drawn position */ if (IsDrawnMaterial(B)) return ((Current_Board.side == WHITE) ? (DRAW_SCORE) : -(DRAW_SCORE)); /* Check if the game is theoretically won/lost */ score = IsWonGame(B); #ifdef DEBUG_EVAL if (score != 0) fprintf(stdout,"\nPosition Theoretically Won : %d\n\n",score); #endif /* Check to see if we can just cutoff here - this score is so good that * we needn't bother working it out exactly - it's going to cause a cutoff. * We have to be very careful because the score difference gained by doing * a proper eval here might be huge, therefore we only cutoff if this * position is theoretically won, and beta isn't, or it is theoretically * lost and alpha isn't. We can't just use standard futility cutoffs like * we do below, because in theoretically won positions, the score returned * by LazyEval() will almost always be much larger than EVAL_FUTILITY. */ if (USE_EVAL_SC && score!=0 && ((score > 0 && beta<T_WIN_BOUND) || (score < 0 && alpha>-(T_WIN_BOUND)))) { EvalCuts++; #ifdef DEBUG_EVAL fprintf(stdout,"Early Cut [1] %d (A=%d,B=%d)\n",score,alpha,beta); #endif return score; } /* Get a lazy score evaluation * (material score & simple positional terms plus passed pawns) */ lazyscore = LazyEval(B) + score; /* Check to see if we can just cutoff here. The expectation is that the LazyEval * is alway within EVAL_FUTILITY of the true score. Of course this isn't always * true, but we hope that it is true most of the time. */ if (USE_EVAL_SC && (lazyscore > (beta+EVAL_FUTILITY) || lazyscore < (alpha-EVAL_FUTILITY))) { EvalCuts++; #ifdef DEBUG_EVAL fprintf(stdout,"Early Cut [2] %d (A=%d,B=%d)\n",lazyscore,alpha,beta); #endif return lazyscore; } /* We didn't get a cutoff so we have to be more careful and evaluate the board properly. * Begin with the LazyEval() score we obtained BEFORE any possible truncation due to lack * of mating material (we don't want to do this twice!). */ score += prematscore; #ifdef DEBUG_EVAL fprintf(stdout,"\nFull Eval\n---------\n"); #endif /* Generate arrays storing how many times each side attacks each square on the board. * This takes quite some time, but it is worth the effort! */ GenerateAttacks(B); /* Now loop through all the pieces on the board and sum their contributions. * Also include the contributions of the empty spaces, measuring board * domination and territorial control. * There is the specialised version for the endgame below. */ if (B->Gamestage < Endgame) { /* Work out how much square possession is worth */ SpWon = SpaceWon[B->Gamestage]; SpDef = SpaceDefended[B->Gamestage]; /* Loop through the board */ for (sq=0;sq<64;sq++) { p = B->pieces[sq]; #ifdef DEBUG_EVAL fprintf(stdout,"Square %c%d [",File(sq)+97,8-Rank(sq)); PrintPiece(p); fprintf(stdout,"] : "); #endif switch(p) { /* White Piece */ case (wpawn) : score += TactPawn(sq,B,WHITE); npw += SquareColour[sq]; break; case (wrook) : score += TactRook(sq,B,WHITE); break; case (wknight): score += TactKnight(sq,B,WHITE); break; case (wbishop): score += TactBishop(sq,B,WHITE); break; case (wqueen) : score += TactQueen(sq,B,WHITE); break; case (wking) : score += TactKing(sq,B,WHITE); break; /* Black Piece */ case (bpawn) : score -= TactPawn(sq,B,BLACK); npw += SquareColour[sq]; break; case (brook) : score -= TactRook(sq,B,BLACK); break; case (bknight): score -= TactKnight(sq,B,BLACK); break; case (bbishop): score -= TactBishop(sq,B,BLACK); break; case (bqueen) : score -= TactQueen(sq,B,BLACK); break; case (bking) : score -= TactKing(sq,B,BLACK); break; /* Empty square - reward for possession by one side */ case (empty) : sq2=ConvertSq[sq]; #ifdef DEBUG_EVAL oldscore = score; #endif /* If only one side is attacking a square, then it is won * by that side. Otherwise it can only be defended. */ one_sided = (!(B->WAttacks[sq2]) || !(B->BAttacks[sq2])); def = B->WAttacks[sq2] - B->BAttacks[sq2]; if (one_sided) { if (def>0) score += SpWon[sq]; else if (def<0) score -= SpWon[Flip[sq]]; } else { /* We have no clear winner, so evaluate the ownership of the square (slow!!) */ def = EvaluateOwnership(B,sq); if (def > 0) score += SpDef[sq]; else if (def < 0) score -= SpDef[Flip[sq]]; #ifdef DEBUG_EVAL contestcount++; #endif } #ifdef DEBUG_EVAL control += score-oldscore; fprintf(stdout,"%d", score-oldscore); #endif break; } #ifdef DEBUG_EVAL fprintf(stdout,"\n"); #endif } } /* If we're in the endgame then use this simpler version */ else { /* Loop through the pieces */ while (pieces) { sq = FirstPiece(pieces); p = B->pieces[sq]; #ifdef DEBUG_EVAL fprintf(stdout,"Square %c%d [",File(sq)+97,8-Rank(sq)); PrintPiece(p); fprintf(stdout,"] : "); #endif switch(p) { /* White Piece */ case (wpawn) : score += TactPawn(sq,B,WHITE); break; case (wrook) : score += TactRook(sq,B,WHITE); break; case (wknight): score += TactKnight(sq,B,WHITE); break; case (wbishop): score += TactBishop(sq,B,WHITE); break; case (wqueen) : score += TactQueen(sq,B,WHITE); break; case (wking) : score += TactKing(sq,B,WHITE); break; /* Black Piece */ case (bpawn) : score -= TactPawn(sq,B,BLACK); break; case (brook) : score -= TactRook(sq,B,BLACK); break; case (bknight): score -= TactKnight(sq,B,BLACK); break; case (bbishop): score -= TactBishop(sq,B,BLACK); break; case (bqueen) : score -= TactQueen(sq,B,BLACK); break; case (bking) : score -= TactKing(sq,B,BLACK); break; } RemoveFirst(pieces); #ifdef DEBUG_EVAL fprintf(stdout,"\n"); #endif } } #ifdef DEBUG_EVAL fprintf(stdout,"After Piece Tactics : %d\n",score); fprintf(stdout,"Control Balance = %d\n",control); fprintf(stdout,"Contested Empty Squares : %d\n\n",contestcount); #endif /* Add on general positional score */ if (B->Gamestage <= Middle) score += TacticsPositional(B); /* -- General score modifiers -- */ /* Modifiers for pawns blocking bishops. Your pawns should be on different squares to your * own bishops, but the same as your opponent's */ /* White Bishops */ if (B->WhiteBishops) { /* Only black square bishops */ if (!(B->WhiteBishops & WhiteMask)) score += npw*PAWN_BLOCK; /* Only white square bishops */ else if (!(B->WhiteBishops & BlackMask)) score -= npw*PAWN_BLOCK; /* Bonus for having two bishops (or more) on opposite colours */ else score += TWO_BISHOPS; #ifdef DEBUG_EVAL if (npw!=0) { if (!(B->WhiteBishops & WhiteMask)) fprintf(stdout,"White has only black square bishops : Pawn Block = %d\n",-npw*PAWN_BLOCK); else if (!(B->WhiteBishops & BlackMask)) fprintf(stdout,"White has only white square bishops : Pawn Block = %d\n",npw*PAWN_BLOCK); } if ((B->WhiteBishops & WhiteMask) && (B->WhiteBishops & BlackMask)) fprintf(stdout,"White has a bishop pair [+%d]\n",TWO_BISHOPS); #endif } /* Black Bishops */ if (B->BlackBishops) { /* Only black square bishops */ if (!(B->BlackBishops & WhiteMask)) score -= npw*PAWN_BLOCK; /* Only white square bishops */ else if (!(B->BlackBishops & BlackMask)) score += npw*PAWN_BLOCK; /* Penalty for opponent having two bishops (or more) on opposite colours */ else score -= TWO_BISHOPS; #ifdef DEBUG_EVAL if (npw!=0) { if (!(B->BlackBishops & WhiteMask)) fprintf(stdout,"Black has only black square bishops : Pawn Block = %d\n",npw*PAWN_BLOCK); else if (!(B->BlackBishops & BlackMask)) fprintf(stdout,"Black has only white square bishops : Pawn Block = %d\n",-npw*PAWN_BLOCK); } if ((B->BlackBishops & WhiteMask) && (B->BlackBishops & BlackMask)) fprintf(stdout,"Black has a bishop pair [-%d]\n",TWO_BISHOPS); #endif } /* Penalty for having no pawns */ if (!B->WhitePawns) score -= NO_PAWNS; if (!B->BlackPawns) score += NO_PAWNS; /* If there is no mating material for one side then the score cannot favour that side * (Note - we calculated this in the LazyEval and retained the results) */ if (!wmat) score = min(0,score); if (!bmat) score = max(0,score); #ifdef DEBUG_EVAL if (!wmat) fprintf(stdout,"No mating material for white\n"); if (!bmat) fprintf(stdout,"No mating material for black\n"); #endif #ifdef DRAWISH_ENDINGS /* Test for a 'drawish' ending, and reduce the score if so */ if (Drawish(B)) score = (score * (50+tpts)) / 100; #ifdef DEBUG_EVAL if (Drawish(B)) fprintf(stdout,"Drawish Position (score reduced)\n"); #endif #endif #ifdef DEBUG_EVAL fprintf(stdout,"Final Score : %d [Delta %d]\n",score,score-lazyscore); #endif // Return score, possibly altered by the skill level if (Skill == 10) return score; return score + Random(((10-Skill) * 4) + 1) + 2 * Skill - 20; }