MaterialInfo *MaterialInfoTable::get_material_info(const Position &pos) { Key key = pos.get_material_key(); int index = key & (size - 1); MaterialInfo *mi = entries + index; // If mi->key matches the position's material hash key, it means that we // have analysed this material configuration before, and we can simply // return the information we found the last time instead of recomputing it: if(mi->key == key) return mi; // Clear the MaterialInfo object, and set its key: mi->clear(); mi->key = key; // A special case before looking for a specialized evaluation function: // KNN vs K is a draw: if(key == KNNKMaterialKey || key == KKNNMaterialKey) { mi->factor[WHITE] = mi->factor[BLACK] = 0; return mi; } // Let's look if we have a specialized evaluation function for this // particular material configuration: if(key == KPKMaterialKey) { mi->evaluationFunction = &EvaluateKPK; return mi; } else if(key == KKPMaterialKey) { mi->evaluationFunction = &EvaluateKKP; return mi; } else if(key == KBNKMaterialKey) { mi->evaluationFunction = &EvaluateKBNK; return mi; } else if(key == KKBNMaterialKey) { mi->evaluationFunction = &EvaluateKKBN; return mi; } else if(key == KRKPMaterialKey) { mi->evaluationFunction = &EvaluateKRKP; return mi; } else if(key == KPKRMaterialKey) { mi->evaluationFunction = &EvaluateKPKR; return mi; } else if(key == KRKBMaterialKey) { mi->evaluationFunction = &EvaluateKRKB; return mi; } else if(key == KBKRMaterialKey) { mi->evaluationFunction = &EvaluateKBKR; return mi; } else if(key == KRKNMaterialKey) { mi->evaluationFunction = &EvaluateKRKN; return mi; } else if(key == KNKRMaterialKey) { mi->evaluationFunction = &EvaluateKNKR; return mi; } else if(key == KQKRMaterialKey) { mi->evaluationFunction = &EvaluateKQKR; return mi; } else if(key == KRKQMaterialKey) { mi->evaluationFunction = &EvaluateKRKQ; return mi; } else if(key == KBBKNMaterialKey) { mi->evaluationFunction = &EvaluateKBBKN; return mi; } else if(key == KNKBBMaterialKey) { mi->evaluationFunction = &EvaluateKNKBB; return mi; } else if(pos.non_pawn_material(BLACK) == Value(0) && pos.pawn_count(BLACK) == 0 && pos.non_pawn_material(WHITE) >= RookValueEndgame) { mi->evaluationFunction = &EvaluateKXK; return mi; } else if(pos.non_pawn_material(WHITE) == Value(0) && pos.pawn_count(WHITE) == 0 && pos.non_pawn_material(BLACK) >= RookValueEndgame) { mi->evaluationFunction = &EvaluateKKX; return mi; } else if(pos.pawns() == EmptyBoardBB && pos.rooks() == EmptyBoardBB && pos.queens() == EmptyBoardBB) { // Minor piece endgame with at least one minor piece per side, // and no pawns. assert(pos.knights(WHITE) | pos.bishops(WHITE)); assert(pos.knights(BLACK) | pos.bishops(BLACK)); if(pos.bishop_count(WHITE) + pos.knight_count(WHITE) <= 2 && pos.bishop_count(BLACK) + pos.knight_count(BLACK) <= 2) { mi->evaluationFunction = &EvaluateKmmKm; return mi; } } // OK, we didn't find any special evaluation function for the current // material configuration. Is there a suitable scaling function? // // The code below is rather messy, and it could easily get worse later, // if we decide to add more special cases. We face problems when there // are several conflicting applicable scaling functions and we need to // decide which one to use. if(key == KRPKRMaterialKey) { mi->scalingFunction[WHITE] = &ScaleKRPKR; return mi; } if(key == KRKRPMaterialKey) { mi->scalingFunction[BLACK] = &ScaleKRKRP; return mi; } if(key == KRPPKRPMaterialKey) { mi->scalingFunction[WHITE] = &ScaleKRPPKRP; return mi; } else if(key == KRPKRPPMaterialKey) { mi->scalingFunction[BLACK] = &ScaleKRPKRPP; return mi; } if(key == KBPKBMaterialKey) { mi->scalingFunction[WHITE] = &ScaleKBPKB; return mi; } if(key == KBKBPMaterialKey) { mi->scalingFunction[BLACK] = &ScaleKBKBP; return mi; } if(key == KBPKNMaterialKey) { mi->scalingFunction[WHITE] = &ScaleKBPKN; return mi; } if(key == KNKBPMaterialKey) { mi->scalingFunction[BLACK] = &ScaleKNKBP; return mi; } if(key == KNPKMaterialKey) { mi->scalingFunction[WHITE] = &ScaleKNPK; return mi; } if(key == KKNPMaterialKey) { mi->scalingFunction[BLACK] = &ScaleKKNP; return mi; } if(pos.non_pawn_material(WHITE) == BishopValueMidgame && pos.bishop_count(WHITE) == 1 && pos.pawn_count(WHITE) >= 1) mi->scalingFunction[WHITE] = &ScaleKBPK; if(pos.non_pawn_material(BLACK) == BishopValueMidgame && pos.bishop_count(BLACK) == 1 && pos.pawn_count(BLACK) >= 1) mi->scalingFunction[BLACK] = &ScaleKKBP; if(pos.pawn_count(WHITE) == 0 && pos.non_pawn_material(WHITE) == QueenValueMidgame && pos.queen_count(WHITE) == 1 && pos.rook_count(BLACK) == 1 && pos.pawn_count(BLACK) >= 1) mi->scalingFunction[WHITE] = &ScaleKQKRP; else if(pos.pawn_count(BLACK) == 0 && pos.non_pawn_material(BLACK) == QueenValueMidgame && pos.queen_count(BLACK) == 1 && pos.rook_count(WHITE) == 1 && pos.pawn_count(WHITE) >= 1) mi->scalingFunction[BLACK] = &ScaleKRPKQ; if(pos.non_pawn_material(WHITE) + pos.non_pawn_material(BLACK) == Value(0)) { if(pos.pawn_count(BLACK) == 0) { assert(pos.pawn_count(WHITE) >= 2); mi->scalingFunction[WHITE] = &ScaleKPsK; } else if(pos.pawn_count(WHITE) == 0) { assert(pos.pawn_count(BLACK) >= 2); mi->scalingFunction[BLACK] = &ScaleKKPs; } else if(pos.pawn_count(WHITE) == 1 && pos.pawn_count(BLACK) == 1) { mi->scalingFunction[WHITE] = &ScaleKPKPw; mi->scalingFunction[BLACK] = &ScaleKPKPb; } } // Compute the space weight if(pos.non_pawn_material(WHITE) + pos.non_pawn_material(BLACK) >= 2*QueenValueMidgame + 4*RookValueMidgame + 2*KnightValueMidgame) { int minorPieceCount = pos.knight_count(WHITE) + pos.knight_count(BLACK) + pos.bishop_count(WHITE) + pos.bishop_count(BLACK); mi->spaceWeight = minorPieceCount * minorPieceCount; } // Evaluate the material balance. Color c; int sign; Value egValue = Value(0), mgValue = Value(0); for(c = WHITE, sign = 1; c <= BLACK; c++, sign = -sign) { // No pawns makes it difficult to win, even with a material advantage: if(pos.pawn_count(c) == 0 && pos.non_pawn_material(c) - pos.non_pawn_material(opposite_color(c)) <= BishopValueMidgame) { if(pos.non_pawn_material(c) == pos.non_pawn_material(opposite_color(c))) mi->factor[c] = 0; else if(pos.non_pawn_material(c) < RookValueMidgame) mi->factor[c] = 0; else { switch(pos.bishop_count(c)) { case 2: mi->factor[c] = 32; break; case 1: mi->factor[c] = 12; break; case 0: mi->factor[c] = 6; break; } } } // Bishop pair: if(pos.bishop_count(c) >= 2) { mgValue += sign * BishopPairMidgameBonus; egValue += sign * BishopPairEndgameBonus; } // Knights are stronger when there are many pawns on the board. The // formula is taken from Larry Kaufman's paper "The Evaluation of Material // Imbalances in Chess": // http://mywebpages.comcast.net/danheisman/Articles/evaluation_of_material_imbalance.htm mgValue += sign * Value(pos.knight_count(c)*(pos.pawn_count(c)-5)*16); egValue += sign * Value(pos.knight_count(c)*(pos.pawn_count(c)-5)*16); // Redundancy of major pieces, again based on Kaufman's paper: if(pos.rook_count(c) >= 1) { Value v = Value((pos.rook_count(c) - 1) * 32 + pos.queen_count(c) * 16); mgValue -= sign * v; egValue -= sign * v; } } mi->mgValue = int16_t(mgValue); mi->egValue = int16_t(egValue); return mi; }