diff options
| author | Scott Gasch <[email protected]> | 2016-06-01 18:58:58 -0700 |
|---|---|---|
| committer | Scott Gasch <[email protected]> | 2016-06-01 18:58:58 -0700 |
| commit | 3fd43cd5fcb22bb65bf2a92a25d95d801b11c9e0 (patch) | |
| tree | 9b6443235d16ba17f094a1a1c7ae53d2bcb9267b /src/recogn.c | |
Initial checkin for typhoon chess engine.
Diffstat (limited to 'src/recogn.c')
| -rw-r--r-- | src/recogn.c | 1347 |
1 files changed, 1347 insertions, 0 deletions
diff --git a/src/recogn.c b/src/recogn.c new file mode 100644 index 0000000..c0b1bb8 --- /dev/null +++ b/src/recogn.c @@ -0,0 +1,1347 @@ +/** + +Copyright (c) Scott Gasch + +Module Name: + + recogn.c + +Abstract: + + This code is based on an article by Ernst A. Heinz: "Efficient + Interior-Node Recognition" * ICCA Journal Volume 21, No. 3, pp + 156-167 (also "Scalable Search in Computer Chess" pp 65-81). This + code also borrows ideas from Thorsten Greiner's AMY chess program. + +Author: + + Scott Gasch ([email protected]) 16 Oct 2005 + +Revision History: + +**/ + +#include "chess.h" + +extern ULONG g_uIterateDepth; +static COOR QUEENING_SQUARE_BY_COLOR_FILE[2][8] = +{ + { A1, B1, C1, D1, E1, F1, G1, H1 }, + { A8, B8, C8, D8, E8, F8, G8, H8 } +}; + +#define RECOGN_INDEX(w, b) \ + (((b) | (w)) + (32 * ((w) && (b)))) + +typedef ULONG RECOGNIZER(SEARCHER_THREAD_CONTEXT *ctx, SCORE *piScore); + +static RECOGNIZER *g_pRecognizers[64]; +static BITV g_bvRecognizerAvailable[32]; + +static ULONG +_MakeMaterialSig(IN FLAG fPawn, + IN FLAG fKnight, + IN FLAG fBishop, + IN FLAG fRook, + IN FLAG fQueen) +/** + +Routine description: + + Make a material signature ULONG out of flags representing the + presence of pawns, knights, bishops, rooks, queens on the board. + +Parameters: + + FLAG fPawn, + FLAG fKnight, + FLAG fBishop, + FLAG fRook, + FLAG fQueen + +Return value: + + static ULONG + +**/ +{ + ULONG x; + + ASSERT(IS_VALID_FLAG(fPawn)); + ASSERT(IS_VALID_FLAG(fKnight)); + ASSERT(IS_VALID_FLAG(fBishop)); + ASSERT(IS_VALID_FLAG(fRook)); + ASSERT(IS_VALID_FLAG(fQueen)); + + x = fPawn | (fKnight << 1) | (fBishop << 2) | (fRook << 3) | (fQueen << 4); + + ASSERT((0 <= x) && (x <= 31)); + return(x); +} + + +#ifdef DEBUG + + +static FLAG +_TablebasesSaySideWins(IN SEARCHER_THREAD_CONTEXT *ctx, + IN ULONG uSide) +{ + SCORE iScore; + if (TRUE == ProbeEGTB(ctx, &iScore)) + { + if (ctx->sPosition.uToMove == uSide) + { + return iScore > 0; + } + else + { + return iScore < 0; + } + } + return TRUE; +} + +static FLAG +_TablebasesSayDraw(IN SEARCHER_THREAD_CONTEXT *ctx) +{ + SCORE iScore; + if (TRUE == ProbeEGTB(ctx, &iScore)) + { + return iScore == 0; + } + return TRUE; +} + +static FLAG +_TablebasesSayDrawOrWin(IN SEARCHER_THREAD_CONTEXT *ctx, + IN ULONG uSide) +{ + SCORE iScore; + if (TRUE == ProbeEGTB(ctx, &iScore)) + { + return ((iScore == 0) || + ((iScore > 0) && (ctx->sPosition.uToMove == uSide)) || + ((iScore < 0) && (ctx->sPosition.uToMove != uSide))); + } + return TRUE; +} + + +static FLAG +_SanityCheckRecognizers(IN SEARCHER_THREAD_CONTEXT *ctx, + IN SCORE iScore, + IN ULONG uVal) { + ULONG uToMove = ctx->sPosition.uToMove; + switch(uVal) { + case UNRECOGNIZED: + return TRUE; + case RECOGN_EXACT: + if (iScore == 0) { + return _TablebasesSayDraw(ctx); + } else if (iScore > 0) { + return _TablebasesSaySideWins(ctx, uToMove); + } else { + ASSERT(iScore < 0); + return _TablebasesSaySideWins(ctx, !uToMove); + } + case RECOGN_LOWER: + if (iScore == 0) { + return _TablebasesSayDrawOrWin(ctx, uToMove); + } else if (iScore > 0) { + return _TablebasesSaySideWins(ctx, uToMove); + } else { + ASSERT(iScore < 0); + return _TablebasesSaySideWins(ctx, !uToMove); + } + case RECOGN_UPPER: + if (iScore == 0) { + return _TablebasesSayDrawOrWin(ctx, !uToMove); + } else if (iScore > 0) { + return _TablebasesSayDrawOrWin(ctx, !uToMove); + } else { + ASSERT(iScore < 0); + return _TablebasesSaySideWins(ctx, !uToMove); + } + default: + ASSERT(FALSE); + return FALSE; + } +} + +static FLAG +_NothingBut(IN POSITION *pos, + IN PIECE p, + IN ULONG uColor) +/** + +Routine description: + + Support routine to assert that nothing but the piece bits + enumerated in parameter p are present in the position pos for side + uColor. + +Parameters: + + POSITION *pos, + PIECE p, + ULONG uColor + +Return value: + + #ifdef DEBUG +static FLAG + +**/ +{ + static PIECE q[] = { KNIGHT, BISHOP, ROOK, QUEEN }; + ULONG u; + + if (!(p & PAWN)) + { + if (pos->uPawnCount[uColor] > 0) return(FALSE); + } + + for (u = 0; u < ARRAY_LENGTH(q); u++) + { + if (!(p & q[u])) + { + if (pos->uNonPawnCount[uColor][q[u]] > 0) return(FALSE); + } + } + return(TRUE); +} +#endif + +static ULONG +_RecognizeKK(IN SEARCHER_THREAD_CONTEXT *ctx, + IN OUT SCORE *piScore) +/** + +Routine description: + + Encode the knowledge that two lone kings on the board are a draw. + +Parameters: + + POSITION *pos, + SCORE *piScore + +Return value: + + static ULONG + +**/ +{ + *piScore = 0; + return(RECOGN_EXACT); +} + +static ULONG +_RecognizeKBK(IN SEARCHER_THREAD_CONTEXT *ctx, + IN OUT SCORE *piScore) +/** + +Routine description: + + Attempt to recognize KB*KB+ positions. + +Parameters: + + POSITION *pos, + SCORE *piScore + +Return value: + + static ULONG + +**/ +{ + ULONG uStrong; + ULONG uBishops; + COOR cWeakKing; + ULONG u; + ULONG uAdjacent; + POSITION *pos = &ctx->sPosition; + + ASSERT((pos->uNonPawnCount[WHITE][0] <= 3) && + (pos->uNonPawnCount[BLACK][0] <= 3)); + ASSERT(_NothingBut(pos, BISHOP, WHITE)); + ASSERT(_NothingBut(pos, BISHOP, BLACK)); + + // + // Recognize KBKB as a draw unless there's a cornered king (in + // which case it may be a mate-in-1) + // + if ((pos->uNonPawnCount[WHITE][0] == 2) && + (pos->uNonPawnCount[BLACK][0] == 2)) + { + if ((CORNER_DISTANCE(pos->cNonPawns[WHITE][0]) != 0) && + (CORNER_DISTANCE(pos->cNonPawns[BLACK][0]) != 0)) + { + *piScore = 0; + return(RECOGN_EXACT); + } + } + + // + // Otherwise we want to deal with KB+ vs lone K. KBKBB etc are + // too hard to recognize. + // + if ((pos->uNonPawnCount[WHITE][0] != 1) && + (pos->uNonPawnCount[BLACK][0] != 1)) + { + return(UNRECOGNIZED); + } + + // + // If we get here then one side has no pieces (except the king). + // + uStrong = BLACK; + if (pos->uNonPawnCount[WHITE][0] > 1) + { + uStrong = WHITE; + } + ASSERT(pos->uNonPawnCount[uStrong][0] > 1); + ASSERT(pos->uNonPawnCount[FLIP(uStrong)][0] == 1); + + // + // KB vs K is a draw, KB+ vs K is still a draw if all bishops are the + // same color. + // + uBishops = pos->uNonPawnCount[uStrong][BISHOP]; + if ((uBishops == 1) || + (pos->uWhiteSqBishopCount[uStrong] == 0) || + (pos->uWhiteSqBishopCount[uStrong] == uBishops)) + { + *piScore = 0; + return(RECOGN_EXACT); + } + + // + // If we get here the strong side has more than one bishop and has + // at least one bishop on each color. + // + + // + // If the weak king is next to a strong side piece, fail to + // recognize since the weak king may take the bishop with the + // move. Note: we allow the weak king to be adjacent to up to one + // enemy bishop as long as it's the strong side's turn to move. + // + cWeakKing = pos->cNonPawns[FLIP(uStrong)][0]; + ASSERT(DISTANCE(cWeakKing, pos->cNonPawns[uStrong][0]) > 1); + uAdjacent = 0; + for (u = 1; u < pos->uNonPawnCount[uStrong][0]; u++) + { + uAdjacent += DISTANCE(cWeakKing, pos->cNonPawns[uStrong][u] == 1); + } + if (uAdjacent > 1) + { + return(UNRECOGNIZED); + } + + // + // If it's the weak side's turn and the strong king is close + // enough that the weak side may be stalemated, fail to recognize. + // + if (pos->uToMove != uStrong) + { + ASSERT(pos->uToMove == FLIP(uStrong)); + if (uAdjacent == 1) + { + return(UNRECOGNIZED); + } + + ASSERT(IS_ON_BOARD(pos->cNonPawns[uStrong][0])); + ASSERT(IS_KING(pos->rgSquare[pos->cNonPawns[uStrong][0]].pPiece)); + u = DISTANCE(cWeakKing, pos->cNonPawns[uStrong][0]); + ASSERT(u != 1); + if ((u == 2) && (ON_EDGE(cWeakKing))) + { + return(UNRECOGNIZED); + } + } + + // + // This is a recognized win for the strong side. Compute a score + // that encourages cornering the weak king and making progress + // towards a checkmate. + // + *piScore = (pos->iMaterialBalance[uStrong] + VALUE_QUEEN - + (u * 16) - (CORNER_DISTANCE(cWeakKing) * 32)); + ASSERT(IS_VALID_SCORE(*piScore)); + if (pos->uToMove != uStrong) + { + *piScore *= -1; + return(RECOGN_UPPER); + } + return(RECOGN_LOWER); +} + +static ULONG +_RecognizeKNK(IN SEARCHER_THREAD_CONTEXT *ctx, + IN OUT SCORE *piScore) +/** + +Routine description: + + Recognize KN*KN+ positions. + +Parameters: + + POSITION *pos, + SCORE *piScore + +Return value: + + static ULONG + +**/ +{ + POSITION *pos = &ctx->sPosition; + ULONG uStrong; + + ASSERT((pos->uNonPawnCount[WHITE][0] <= 3) && + (pos->uNonPawnCount[BLACK][0] <= 3)); + ASSERT(_NothingBut(pos, KNIGHT, WHITE)); + ASSERT(_NothingBut(pos, KNIGHT, BLACK)); + + // + // KNKN is a draw unless someone has a K in the corner (in which case, + // with the friend knight in the way, there's a possible mate) + // + if ((pos->uNonPawnCount[WHITE][0] == 2) && + (pos->uNonPawnCount[BLACK][0] == 2)) + { + if ((CORNER_DISTANCE(pos->cNonPawns[WHITE][0]) != 0) && + (CORNER_DISTANCE(pos->cNonPawns[BLACK][0]) != 0)) + { + *piScore = 0; + return(RECOGN_EXACT); + } + return(UNRECOGNIZED); + } + + // + // KNNKN etc... unrecognized. Heinz says "exceptional wins possible for + // any side by mates in seven or less moves." TODO: add this knowledge. + // + if ((pos->uNonPawnCount[WHITE][0] != 1) || + (pos->uNonPawnCount[BLACK][0] != 1)) + { + return(UNRECOGNIZED); + } + + // + // If we get here somebody has no pieces (except a lone king). + // + uStrong = WHITE; + if (pos->uNonPawnCount[BLACK][0] > 1) + { + uStrong = BLACK; + } + ASSERT(pos->uNonPawnCount[uStrong][0] > 1); + ASSERT(pos->uNonPawnCount[FLIP(uStrong)][0] == 1); + + // + // Call KNNK a draw unless the weak K is on the edge of the board + // where a checkmate is possible (e.g. 8/8/8/8/8/N2N4/4K3/2k5 b - -) + // Everything else in here is a draw. + // + ASSERT(pos->uNonPawnCount[uStrong][0] < 4); + if (ON_EDGE(pos->cNonPawns[FLIP(uStrong)][0])) + { + return(UNRECOGNIZED); + } + *piScore = 0; + return(RECOGN_EXACT); +} + + +static ULONG +_RecognizeKBNK(IN SEARCHER_THREAD_CONTEXT *ctx, + IN OUT SCORE *piScore) +/** + +Routine description: + +Parameters: + + POSITION *pos, + SCORE *piScore + +Return value: + + static ULONG + +**/ +{ + ULONG uStrong; + COOR cWeakKing; + COOR cKnight; + ULONG u; + ULONG uDist; + ULONG uAdjacent; + POSITION *pos = &ctx->sPosition; + + ASSERT((pos->uNonPawnCount[WHITE][0] <= 3) && + (pos->uNonPawnCount[BLACK][0] <= 3)); + ASSERT(_NothingBut(pos, BISHOP | KNIGHT, WHITE)); + ASSERT(_NothingBut(pos, BISHOP | KNIGHT, BLACK)); + + if ((pos->uNonPawnCount[WHITE][0] > 1) && + (pos->uNonPawnCount[BLACK][0] > 1)) + { + // + // Do not recognize stuff like KNNKB or KNKBB etc... + // + if (pos->uNonPawnCount[WHITE][0] + pos->uNonPawnCount[BLACK][0] > 4) + { + return(UNRECOGNIZED); + } + + // + // This is KNKB; unless someone's king is on the edge, + // recognize a draw. + // + ASSERT((pos->uNonPawnCount[WHITE][0] == 2) && + (pos->uNonPawnCount[BLACK][0] == 2)); + if (ON_EDGE(pos->cNonPawns[WHITE][0]) || + ON_EDGE(pos->cNonPawns[BLACK][0])) + { + return(UNRECOGNIZED); + } + *piScore = 0; + return(RECOGN_EXACT); + } + + // + // If we get here we are in a KBNK endgame. + // + uStrong = WHITE; + if (pos->uNonPawnCount[BLACK][0] > 1) + { + uStrong = BLACK; + } + ASSERT(pos->uNonPawnCount[uStrong][0] > 1); + ASSERT(pos->uNonPawnCount[FLIP(uStrong)][0] == 1); + cWeakKing = pos->cNonPawns[FLIP(uStrong)][0]; + ASSERT(IS_ON_BOARD(cWeakKing)); + ASSERT(IS_KING(pos->rgSquare[cWeakKing].pPiece)); + + // + // Don't recognize anything where the N is in a corner near the + // lone king. It's possible for the lone king to trap it there + // thus drawing the game. + // + cKnight = pos->cNonPawns[uStrong][KNIGHT]; + if ((CORNER_DISTANCE(cKnight) == 0) && + (DISTANCE(cWeakKing, cKnight) == 1)) + { + return(UNRECOGNIZED); + } + + // + // Don't recognize anything if the weak king is next to a strong side's + // piece. + // + uAdjacent = 0; + for (u = 1; u < pos->uNonPawnCount[uStrong][0]; u++) + { + uAdjacent = (DISTANCE(pos->cNonPawns[uStrong][u], cWeakKing) == 1); + } + if (!uAdjacent) + { + return(UNRECOGNIZED); + } + + // + // Don't recognize if the two kings are close enough to each other + // that there might be a stalemate if the weak side is on move and + // on the edge. + // + if (pos->uToMove != uStrong) + { + ASSERT(pos->uToMove == FLIP(uStrong)); + if (uAdjacent == 1) + { + return(UNRECOGNIZED); + } + + ASSERT(IS_ON_BOARD(pos->cNonPawns[uStrong][0])); + ASSERT(IS_KING(pos->rgSquare[pos->cNonPawns[uStrong][0]].pPiece)); + u = DISTANCE(cWeakKing, pos->cNonPawns[uStrong][0]); + ASSERT(u != 1); + if ((u == 2) && (ON_EDGE(cWeakKing))) + { + return(UNRECOGNIZED); + } + } + + // + // Calculate a score that grabs the search's attention and makes + // progress towards driving the weak king to the correct corner to + // mate him. + // + if (pos->uWhiteSqBishopCount[uStrong] > 0) + { + uDist = WHITE_CORNER_DISTANCE(cWeakKing); + } + else + { + uDist = BLACK_CORNER_DISTANCE(cWeakKing); + } + ASSERT((0 <= uDist) && (uDist <= 7)); + + *piScore = (pos->iMaterialBalance[uStrong] + (7 * VALUE_PAWN) + - (uDist * 32) - (u * 16)); + ASSERT(IS_VALID_SCORE(*piScore)); + if (pos->uToMove != uStrong) + { + *piScore *= -1; + return(RECOGN_UPPER); + } + return(RECOGN_LOWER); +} + + +static ULONG +_RecognizeKNKP(IN SEARCHER_THREAD_CONTEXT *ctx, + IN OUT SCORE *piScore) +/** + +Routine description: + + Recognize KN+KP+ positions. + +Parameters: + + POSITION *pos, + SCORE *piScore + +Return value: + + static ULONG + +**/ +{ + ULONG uStrong; + POSITION *pos = &ctx->sPosition; + + ASSERT((pos->uNonPawnCount[WHITE][0] <= 3) && + (pos->uNonPawnCount[BLACK][0] <= 3)); + ASSERT(_NothingBut(pos, PAWN | KNIGHT, WHITE)); + ASSERT(_NothingBut(pos, PAWN | KNIGHT, BLACK)); + + // + // Call the side with knight(s) "strong" + // + uStrong = WHITE; + if (pos->uNonPawnCount[BLACK][0] > 1) + { + uStrong = BLACK; + } + ASSERT(pos->uNonPawnCount[uStrong][0] > 1); + ASSERT(pos->uNonPawnCount[FLIP(uStrong)][0] == 1); + + // + // Don't recognize KNNKP or KNKP with K on edge + // + if ((pos->uNonPawnCount[uStrong][KNIGHT] > 2) || + (ON_EDGE(pos->cNonPawns[FLIP(uStrong)][0]))) + { + return(UNRECOGNIZED); + } + + // + // This is at least a draw for the side with the pawn(s) and at + // best a draw for the side with the knight(s) + // + *piScore = 0; + if (pos->uToMove == uStrong) + { + return(RECOGN_UPPER); + } + return(RECOGN_LOWER); +} + + +static ULONG +_RecognizeKBKP(IN SEARCHER_THREAD_CONTEXT *ctx, + IN OUT SCORE *piScore) +/** + +Routine description: + + Recognize KB+P*KP+ positions including knowledge about "wrong color + bishop(s)". + +Parameters: + + POSITION *pos, + SCORE *piScore + +Return value: + + static ULONG + +**/ +{ + ULONG uStrong; + BITBOARD bb; + ULONG u; + COOR c; + COOR cWeakKing; + POSITION *pos = &ctx->sPosition; + + ASSERT((pos->uNonPawnCount[WHITE][0] <= 3) && + (pos->uNonPawnCount[BLACK][0] <= 3)); + ASSERT(_NothingBut(pos, PAWN | BISHOP, WHITE)); + ASSERT(_NothingBut(pos, PAWN | BISHOP, BLACK)); + + // + // Call the side with bishop(s) "strong" + // + uStrong = WHITE; + if (pos->uNonPawnCount[BLACK][BISHOP] > 0) + { + uStrong = BLACK; + } + ASSERT(pos->uNonPawnCount[uStrong][0] > 1); + ASSERT(pos->uNonPawnCount[FLIP(uStrong)][0] == 1); + + cWeakKing = pos->cNonPawns[FLIP(uStrong)][0]; + ASSERT(IS_ON_BOARD(cWeakKing)); + ASSERT(IS_KING(pos->rgSquare[cWeakKing].pPiece)); + + // + // Construct a strong side bitboard of pawn locations + // + bb = 0ULL; + for (u = 0; u < pos->uPawnCount[uStrong]; u++) + { + c = pos->cPawns[uStrong][u]; + ASSERT(IS_ON_BOARD(c)); + bb |= COOR_TO_BB(c); + } + + if ((pos->uNonPawnCount[BLACK][0] + pos->uPawnCount[BLACK] > 1) && + (pos->uNonPawnCount[WHITE][0] + pos->uPawnCount[WHITE] > 1)) + { + // + // Neither side has a lone king. This is either KBKP+ or + // KBP+KP+. + // + if (pos->uPawnCount[uStrong] > 0) + { + // + // Strong side can maybe take an adjacent pawn and survive the + // bad bishop. + // + if (uStrong == pos->uToMove) + { + return(UNRECOGNIZED); + } + + // + // Make sure the strong side has the right color bishop + // for his pawns. + // + if (uStrong == WHITE) + { + if (!(bb & ~BBFILE[A]) && + (pos->uWhiteSqBishopCount[WHITE] == 0) && + (DISTANCE(cWeakKing, A8) <= 1)) + { + goto at_best_draw_for_strong; + } + + if (!(bb & ~BBFILE[H]) && + (pos->uWhiteSqBishopCount[WHITE] == + pos->uNonPawnCount[WHITE][BISHOP]) && + (DISTANCE(cWeakKing, H8) <= 1)) + { + goto at_best_draw_for_strong; + } + } + else + { + if (!(bb & ~BBFILE[A]) && + (pos->uWhiteSqBishopCount[BLACK] == + pos->uNonPawnCount[BLACK][BISHOP]) && + (DISTANCE(cWeakKing, A1) <= 1)) + { + goto at_best_draw_for_strong; + } + + if (!(bb & ~BBFILE[H]) && + (pos->uWhiteSqBishopCount[BLACK] == 0) && + (DISTANCE(cWeakKing, H1) <= 1)) + { + goto at_best_draw_for_strong; + } + } + return(UNRECOGNIZED); + } + else + { + // + // Strong side has no pawns... there's a mate-in-1 here if + // the weak king is stuck in the corner. + // + if ((pos->uNonPawnCount[uStrong][BISHOP] > 1) || + (CORNER_DISTANCE(cWeakKing) == 0)) + { + return(UNRECOGNIZED); + } + goto at_best_draw_for_strong; + } + } + else + { + // + // KBPK: make sure the bishop is the right color. This time + // there is no need to check for on-move. + // + ASSERT(pos->uNonPawnCount[FLIP(uStrong)][0] == 1); + ASSERT(pos->uNonPawnCount[uStrong][0] > 1); + + if (uStrong == WHITE) + { + if (!(bb & ~BBFILE[A]) && + (pos->uWhiteSqBishopCount[WHITE] == 0) && + (DISTANCE(cWeakKing, A8) <= 1)) + { + goto draw; + } + if (!(bb & ~BBFILE[H]) && + (pos->uWhiteSqBishopCount[WHITE] == + pos->uNonPawnCount[WHITE][BISHOP]) && + (DISTANCE(cWeakKing, H8) <= 1)) + { + goto draw; + } + } + else + { + if (!(bb & ~BBFILE[A]) && + (pos->uWhiteSqBishopCount[BLACK] == + pos->uNonPawnCount[BLACK][BISHOP]) && + (DISTANCE(cWeakKing, A1) <= 1)) + { + goto draw; + } + if (!(bb & ~BBFILE[H]) && + (pos->uWhiteSqBishopCount[BLACK] == 0) && + (DISTANCE(cWeakKing, H1) <= 1)) + { + goto draw; + } + } + return(UNRECOGNIZED); + } +#ifdef DEBUG + UtilPanic(SHOULD_NOT_GET_HERE, + NULL, NULL, NULL, NULL, + __FILE__, __LINE__); +#endif + + at_best_draw_for_strong: + *piScore = 0; + if (pos->uToMove == uStrong) + { + return(RECOGN_UPPER); + } + return(RECOGN_LOWER); + + draw: + *piScore = 0; + return(RECOGN_EXACT); +} + +static void +_GetPassersCriticalSquares(IN ULONG uColor, + IN COOR cPawn, + IN OUT COOR *cSquare) +/** + +Routine description: + + In an effort to classify KPK positions I read some stuff in + "Pandolfini's Endgame Course" about critical squares. A critical + square of a passed pawn is one that, if occupied by the friendly + king, is sufficient to queen that pawn by force. This code is a + helper routing for the KPK recognizer that returns the critical + square given a pawn's location for color uColor. + + Note: rook pawns only have one critical square so this code + returns the same square three times in this case. + +Parameters: + + ULONG uColor, + COOR cPawn, + COOR *cSquare + +Return value: + + static void + +**/ +{ + static COOR cCriticalSquare[2][128] = + { + { + 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0,0,0,0,0,0,0,0, + 0x61, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x66, 0,0,0,0,0,0,0,0, + 0x61, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x66, 0,0,0,0,0,0,0,0, + 0x61, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x66, 0,0,0,0,0,0,0,0, + 0x61, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x66, 0,0,0,0,0,0,0,0, + 0x61, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x66, 0,0,0,0,0,0,0,0, + 0x61, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x66, 0,0,0,0,0,0,0,0, + 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0,0,0,0,0,0,0,0, + }, + { + 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0,0,0,0,0,0,0,0, + 0x11, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x16, 0,0,0,0,0,0,0,0, + 0x11, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x16, 0,0,0,0,0,0,0,0, + 0x11, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x16, 0,0,0,0,0,0,0,0, + 0x11, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x16, 0,0,0,0,0,0,0,0, + 0x11, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x16, 0,0,0,0,0,0,0,0, + 0x11, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x16, 0,0,0,0,0,0,0,0, + 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0,0,0,0,0,0,0,0, + } + }; + ULONG uFile = FILE(cPawn); + + ASSERT(IS_VALID_COLOR(uColor)); + ASSERT(IS_ON_BOARD(cPawn)); + ASSERT((RANK(cPawn) != 1) && (RANK(cPawn) != 8)); + + if ((uFile == A) || (uFile == H)) + { + cSquare[0] = cCriticalSquare[uColor][cPawn]; + cSquare[1] = cCriticalSquare[uColor][cPawn]; + cSquare[2] = cCriticalSquare[uColor][cPawn]; + goto end; + } + cSquare[1] = cCriticalSquare[uColor][cPawn]; + cSquare[0] = cSquare[1] - 1; + cSquare[2] = cSquare[1] + 1; + + end: + ASSERT(cSquare[0] != 0); + ASSERT(cSquare[1] != 0); + ASSERT(cSquare[2] != 0); + ASSERT(IS_ON_BOARD(cSquare[0])); + ASSERT(IS_ON_BOARD(cSquare[1])); + ASSERT(IS_ON_BOARD(cSquare[2])); +} + + +static ULONG +_RecognizeKPK(IN SEARCHER_THREAD_CONTEXT *ctx, + IN OUT SCORE *piScore) +/** + +Routine description: + + Attempt to recognize KP+KP* positions. + +Parameters: + + POSITION *pos, + SCORE *piScore + +Return value: + + static ULONG + +**/ +{ + ULONG uStrong; + ULONG uWeak; + COOR cPawn, cQueen; + COOR cCritical[3]; + ULONG uDist[2]; + ULONG u; + PAWN_HASH_ENTRY *pHash; + POSITION *pos = &ctx->sPosition; + + ASSERT(pos->uNonPawnCount[WHITE][0] == 1); + ASSERT(pos->uNonPawnCount[BLACK][0] == 1); + ASSERT(_NothingBut(pos, PAWN, WHITE)); + ASSERT(_NothingBut(pos, PAWN, BLACK)); + + // + // Look to see if one side has a winning passed pawn + // + pHash = PawnHashLookup(ctx); + ASSERT(pHash != NULL); + if (pHash->u64Key == pos->u64PawnSig) + { + pos->iScore[BLACK] = pos->iScore[WHITE] = 0; + if (TRUE == EvalPasserRaces(pos, pHash)) + { + // + // Someone wins. + // + ASSERT((pos->iScore[BLACK] == 0) || (pos->iScore[WHITE] == 0)); + ASSERT((pos->iScore[BLACK] != 0) || (pos->iScore[WHITE] != 0)); + if (pos->iScore[BLACK] > 0) + { + ASSERT(pos->iScore[WHITE] == 0); + *piScore = (pos->iMaterialBalance[BLACK] + 2 * VALUE_PAWN + + pos->iScore[BLACK]); + ASSERT(IS_VALID_SCORE(*piScore)); + if (pos->uToMove == WHITE) + { + *piScore *= -1; + return(RECOGN_UPPER); + } + return(RECOGN_LOWER); + } + else + { + ASSERT(pos->iScore[WHITE] > 0); + *piScore = (pos->iMaterialBalance[WHITE] + 2 * VALUE_PAWN + + pos->iScore[WHITE]); + ASSERT(IS_VALID_SCORE(*piScore)); + if (pos->uToMove == BLACK) + { + *piScore *= -1; + return(RECOGN_UPPER); + } + return(RECOGN_LOWER); + } + } + } + + // + // No one has a winning passer or we did not find the hash entry. + // + if ((pos->uPawnCount[WHITE] > 0) && (pos->uPawnCount[BLACK] > 0)) + { + return(UNRECOGNIZED); + } + + // + // If we get here then no one has a winning passer and one side + // has only a lone king -- no pawns. Do some more "sophisticated" + // analysis looking for a won passer. Also: this is at best a + // draw for the weak side. + // + uStrong = WHITE; + if (pos->uPawnCount[WHITE] == 0) + { + uStrong = BLACK; + } + ASSERT(pos->uPawnCount[uStrong] > 0); + uWeak = FLIP(uStrong); + ASSERT(pos->uPawnCount[uWeak] == 0); + + if (pos->uPawnCount[uStrong] > 1) + { + *piScore = 0; + if (pos->uToMove == uStrong) + { + return(RECOGN_LOWER); + } + return(RECOGN_UPPER); + } + + // + // The side with pawns has only one pawn, do some more + // sophisticated analysis here to spot winning KPK + // configurations earlier by using "critical squares" + // + ASSERT(pos->uPawnCount[uStrong] == 1); + cPawn = pos->cPawns[uStrong][0]; + ASSERT(IS_ON_BOARD(cPawn)); + ASSERT(IS_PAWN(pos->rgSquare[cPawn].pPiece)); + + // + // Step 1: the strong king must be closer to the pawn than + // the weak king. + // + uDist[uStrong] = DISTANCE(pos->cNonPawns[uStrong][0], cPawn); + ASSERT((1 <= uDist[uStrong]) && (uDist[uStrong] <= 7)); + uDist[uWeak] = DISTANCE(pos->cNonPawns[uWeak][0], cPawn); + ASSERT((1 <= uDist[uWeak]) && (uDist[uWeak] <= 7)); + if (uDist[uStrong] <= uDist[uWeak]) + { + // + // Step 2: the strong king must be closer to one of the + // passer's critical squares than the weak king. + // + _GetPassersCriticalSquares(uStrong, cPawn, cCritical); + for (u = 0; u < 3; u++) + { + uDist[uStrong] = DISTANCE(pos->cNonPawns[uStrong][0], + cCritical[u]); + ASSERT((0 <= uDist[uStrong]) && (uDist[uStrong] <= 7)); + uDist[uWeak] = DISTANCE(pos->cNonPawns[uWeak][0], + cCritical[u]); + + // + // Assume if the weak side is on move he will move + // towards the critical square. Also assume that + // in so doing he will increase the path that the + // strong king must take to get to the critical + // square. + // + if (uDist[uWeak] > 0) { + uDist[uWeak] -= (pos->uToMove == uWeak); + } + if (uDist[uStrong] > 0) { + uDist[uStrong] += (pos->uToMove == uWeak) * 2; + } + ASSERT((0 <= uDist[uWeak]) && (uDist[uWeak] <= 7)); + if (uDist[uStrong] < uDist[uWeak]) + { + cQueen = + QUEENING_SQUARE_BY_COLOR_FILE[uStrong][FILE(cPawn)]; + *piScore = (pos->iMaterialBalance[uStrong] + + VALUE_QUEEN + (2 * VALUE_PAWN) - + (RANK_DISTANCE(cPawn, cQueen) * 32)); + if (pos->uToMove == uWeak) + { + *piScore *= -1; + return(RECOGN_UPPER); + } + return(RECOGN_LOWER); + } + } + } + + // + // If we get here then the weak king is as close to or + // closer to the lone passer than the strong king -or- the + // weak king is as close to or closer to the passer's + // critical squares than the strong king. This may still + // be won for the strong side but we don't know -- the + // logic is ugly. This is still at best drawn for the + // weak side though. + // + *piScore = 0; + if (pos->uToMove == uStrong) + { + return(RECOGN_LOWER); + } + return(RECOGN_UPPER); +} + + +static void +_NewRecognizer(IN RECOGNIZER *pFunct, + IN ULONG uWhiteSig, + IN ULONG uBlackSig) +/** + +Routine description: + + Register a recognizer function. + +Parameters: + + RECOGNIZER *pFunct, + ULONG uWhiteSig, + ULONG uBlackSig + +Return value: + + static void + +**/ +{ + g_bvRecognizerAvailable[uWhiteSig] |= (1 << uBlackSig); + g_bvRecognizerAvailable[uBlackSig] |= (1 << uWhiteSig); + g_pRecognizers[RECOGN_INDEX(uWhiteSig, uBlackSig)] = pFunct; +} + +void +InitializeInteriorNodeRecognizers(void) +/** + +Routine description: + + Init this recognizer module + +Parameters: + + void + +Return value: + + void + +**/ +{ + memset(g_pRecognizers, 0, sizeof(g_pRecognizers)); + memset(g_bvRecognizerAvailable, 0, sizeof(g_bvRecognizerAvailable)); + + // KK P N B R Q + _NewRecognizer(_RecognizeKK, + _MakeMaterialSig(0, 0, 0, 0, 0), + _MakeMaterialSig(0, 0, 0, 0, 0)); + + // KB+K P N B R Q + _NewRecognizer(_RecognizeKBK, + _MakeMaterialSig(0, 0, 1, 0, 0), + _MakeMaterialSig(0, 0, 0, 0, 0)); + + // KB+KB+ P N B R Q + _NewRecognizer(_RecognizeKBK, + _MakeMaterialSig(0, 0, 1, 0, 0), + _MakeMaterialSig(0, 0, 1, 0, 0)); + + // KN+K P N B R Q + _NewRecognizer(_RecognizeKNK, + _MakeMaterialSig(0, 1, 0, 0, 0), + _MakeMaterialSig(0, 0, 0, 0, 0)); + + // KN+KN+ P N B R Q + _NewRecognizer(_RecognizeKNK, + _MakeMaterialSig(0, 1, 0, 0, 0), + _MakeMaterialSig(0, 1, 0, 0, 0)); + + // KN+KB+ P N B R Q + _NewRecognizer(_RecognizeKBNK, + _MakeMaterialSig(0, 1, 0, 0, 0), + _MakeMaterialSig(0, 0, 1, 0, 0)); + + // KN+B+K P N B R Q + _NewRecognizer(_RecognizeKBNK, + _MakeMaterialSig(0, 1, 1, 0, 0), + _MakeMaterialSig(0, 0, 0, 0, 0)); + + // KN+KP+ P N B R Q + _NewRecognizer(_RecognizeKNKP, + _MakeMaterialSig(1, 0, 0, 0, 0), + _MakeMaterialSig(0, 1, 0, 0, 0)); + + // KB+KP+ P N B R Q + _NewRecognizer(_RecognizeKBKP, + _MakeMaterialSig(1, 0, 0, 0, 0), + _MakeMaterialSig(0, 0, 1, 0, 0)); + + // KP+B+KP+ P N B R Q + _NewRecognizer(_RecognizeKBKP, + _MakeMaterialSig(1, 0, 1, 0, 0), + _MakeMaterialSig(1, 0, 0, 0, 0)); + + + // KP+B+K P N B R Q + _NewRecognizer(_RecognizeKBKP, + _MakeMaterialSig(1, 0, 1, 0, 0), + _MakeMaterialSig(0, 0, 0, 0, 0)); + + // KP+K P N B R Q + _NewRecognizer(_RecognizeKPK, + _MakeMaterialSig(0, 0, 0, 0, 0), + _MakeMaterialSig(1, 0, 0, 0, 0)); + + // KP+KP+ P N B R Q + _NewRecognizer(_RecognizeKPK, + _MakeMaterialSig(1, 0, 0, 0, 0), + _MakeMaterialSig(1, 0, 0, 0, 0)); +} + + +ULONG +RecognLookup(IN SEARCHER_THREAD_CONTEXT *ctx, + IN OUT SCORE *piScore, + IN FLAG fProbeEGTB) +/** + +Routine description: + + This is the interface to the recogn module; it is called from + Search and Qsearch. Its job is to: 1. check high speed internal + node recognizer functions if a recognizable material signature is + present on the board and 2. probe on-disk EGTB files if no useful + recognizer result is found. + +Parameters: + + SEARCHER_THREAD_CONTEXT *ctx, + SCORE *piScore, + FLAG fProbeEGTB + +Return value: + + ULONG + +**/ +{ + ULONG uVal; + POSITION *pos = &ctx->sPosition; + ULONG uSig[2]; + RECOGNIZER *pRec; + SCORE iScore; + + // + // Try interior node recognizers + // + if ((pos->uNonPawnCount[WHITE][0] <= 3) && + (pos->uNonPawnCount[BLACK][0] <= 3)) + { + uSig[BLACK] = _MakeMaterialSig((pos->uPawnCount[BLACK] > 0), + (pos->uNonPawnCount[BLACK][KNIGHT] > 0), + (pos->uNonPawnCount[BLACK][BISHOP] > 0), + (pos->uNonPawnCount[BLACK][ROOK] > 0), + (pos->uNonPawnCount[BLACK][QUEEN] > 0)); + uSig[WHITE] = _MakeMaterialSig((pos->uPawnCount[WHITE] > 0), + (pos->uNonPawnCount[WHITE][KNIGHT] > 0), + (pos->uNonPawnCount[WHITE][BISHOP] > 0), + (pos->uNonPawnCount[WHITE][ROOK] > 0), + (pos->uNonPawnCount[WHITE][QUEEN] > 0)); + pRec = g_pRecognizers[RECOGN_INDEX(uSig[WHITE], uSig[BLACK])]; + if (NULL != pRec) + { + if (g_bvRecognizerAvailable[uSig[WHITE]] & (1 << uSig[BLACK])) + { + uVal = pRec(ctx, &iScore); + if (UNRECOGNIZED != uVal) + { + *piScore = iScore; + ASSERT((-NMATE < iScore) && (iScore < +NMATE)); + ASSERT(_SanityCheckRecognizers(ctx, iScore, uVal)); + return(uVal); + } + } + } + } + + // + // Try EGTB probe as long as some conditions are met + // + if ((FALSE != fProbeEGTB) && + ((pos->uNonPawnCount[WHITE][0] + pos->uNonPawnCount[BLACK][0] + + pos->uPawnCount[WHITE] + pos->uPawnCount[BLACK]) <= 5)) + { + if (TRUE == ProbeEGTB(ctx, &iScore)) + { + // + // We have an exact score from EGTB but if it's a mate in N + // we still have to adjust it for the present ply. + // + if (iScore >= +NMATE) { + iScore -= ctx->uPly; + } + else if (iScore <= -NMATE) { + iScore += ctx->uPly; + } +#ifdef DEBUG + else + { + ASSERT(iScore == 0); // iDrawValue[side] + } +#endif + *piScore = iScore; + return(RECOGN_EXACT); + } + } + return(UNRECOGNIZED); +} |
