internal MaterialTable() { pieceCount[0] = new int[6]; pieceCount[1] = new int[6]; for (int i = 0; i < Constants.MaterialTableSize; i++) { entries[i] = new MaterialEntry(); } }
/// MaterialTable::material_info() takes a position object as input, /// computes or looks up a MaterialInfo object, and returns a pointer to it. /// If the material configuration is not already present in the table, it /// is stored there, so we don't have to recompute everything when the /// same material configuration occurs again. internal void probe(Position pos, out MaterialEntry e) { var key = pos.material_key(); e = this.entries[((uint)key) & Constants.MaterialTableMask]; // 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 (e.key == key) { return; } // Initialize MaterialInfo entry var npm = pos.non_pawn_material(ColorC.WHITE) + pos.non_pawn_material(ColorC.BLACK); e.value = 0; e.scalingFunctionWHITE = null; e.scalingFunctionBLACK = null; e.spaceWeight = 0; e.key = key; e.factorWHITE = e.factorBLACK = ScaleFactorC.SCALE_FACTOR_NORMAL; e.gamePhase = npm >= MidgameLimit ? PhaseC.PHASE_MIDGAME : npm <= EndgameLimit ? PhaseC.PHASE_ENDGAME : (((npm - EndgameLimit) * 128) / (MidgameLimit - EndgameLimit)); // Let's look if we have a specialized evaluation function for this // particular material configuration. First we look for a fixed // configuration one, then a generic one if previous search failed. if ((e.evaluationFunction = Endgame.probeValue(key, out e.evaluationFunctionColor)) != null) { return; } if (is_KXK(ColorC.WHITE, pos)) { e.evaluationFunction = Endgame.Endgame_KXK; e.evaluationFunctionColor = ColorC.WHITE; return; } if (is_KXK(ColorC.BLACK, pos)) { e.evaluationFunction = Endgame.Endgame_KXK; e.evaluationFunctionColor = ColorC.BLACK; return; } if ((pos.pieces_PT(PieceTypeC.PAWN) == 0) && (pos.pieces_PT(PieceTypeC.ROOK) == 0) && (pos.pieces_PT(PieceTypeC.QUEEN) == 0)) { // Minor piece endgame with at least one minor piece per side and // no pawns. Note that the case KmmK is already handled by KXK. Debug.Assert( (pos.pieces_PTC(PieceTypeC.KNIGHT, ColorC.WHITE) | pos.pieces_PTC(PieceTypeC.BISHOP, ColorC.WHITE)) != 0); Debug.Assert( (pos.pieces_PTC(PieceTypeC.KNIGHT, ColorC.BLACK) | pos.pieces_PTC(PieceTypeC.BISHOP, ColorC.BLACK)) != 0); if (pos.piece_count(ColorC.WHITE, PieceTypeC.BISHOP) + pos.piece_count(ColorC.WHITE, PieceTypeC.KNIGHT) <= 2 && pos.piece_count(ColorC.BLACK, PieceTypeC.BISHOP) + pos.piece_count(ColorC.BLACK, PieceTypeC.KNIGHT) <= 2) { e.evaluationFunction = Endgame.Endgame_KmmKm; e.evaluationFunctionColor = pos.sideToMove; return; } } // OK, we didn't find any special evaluation function for the current // material configuration. Is there a suitable scaling function? // // We face problems when there are several conflicting applicable // scaling functions and we need to decide which one to use. EndgameScaleFactor sf; int c; if ((sf = Endgame.probeScaleFactor(key, out c)) != null) { if (c == ColorC.WHITE) { e.scalingFunctionWHITE = sf; } else { e.scalingFunctionBLACK = sf; } return; } // Generic scaling functions that refer to more then one material // distribution. Should be probed after the specialized ones. // Note that these ones don't return after setting the function. if (is_KBPsKs(ColorC.WHITE, pos)) { e.scalingFunctionWHITE = Endgame.Endgame_KBPsK; } if (is_KBPsKs(ColorC.BLACK, pos)) { e.scalingFunctionBLACK = Endgame.Endgame_KBPsK; } if (is_KQKRPs(ColorC.WHITE, pos)) { e.scalingFunctionWHITE = Endgame.Endgame_KQKRPs; } else if (is_KQKRPs(ColorC.BLACK, pos)) { e.scalingFunctionBLACK = Endgame.Endgame_KQKRPs; } var npm_w = pos.non_pawn_material(ColorC.WHITE); var npm_b = pos.non_pawn_material(ColorC.BLACK); if (npm_w + npm_b == ValueC.VALUE_ZERO) { if (pos.piece_count(ColorC.BLACK, PieceTypeC.PAWN) == 0) { Debug.Assert(pos.piece_count(ColorC.WHITE, PieceTypeC.PAWN) >= 2); e.scalingFunctionWHITE = Endgame.Endgame_KPsK; } else if (pos.piece_count(ColorC.WHITE, PieceTypeC.PAWN) == 0) { Debug.Assert(pos.piece_count(ColorC.BLACK, PieceTypeC.PAWN) >= 2); e.scalingFunctionBLACK = Endgame.Endgame_KPsK; } else if (pos.piece_count(ColorC.WHITE, PieceTypeC.PAWN) == 1 && pos.piece_count(ColorC.BLACK, PieceTypeC.PAWN) == 1) { // This is a special case because we set scaling functions // for both colors instead of only one. e.scalingFunctionWHITE = Endgame.Endgame_KPKP; e.scalingFunctionBLACK = Endgame.Endgame_KPKP; } } // No pawns makes it difficult to win, even with a material advantage if (pos.piece_count(ColorC.WHITE, PieceTypeC.PAWN) == 0 && npm_w - npm_b <= Constants.BishopValueMidgame) { e.factorWHITE = (byte) (npm_w == npm_b || npm_w < Constants.RookValueMidgame ? 0 : NoPawnsSF[Math.Min(pos.piece_count(ColorC.WHITE, PieceTypeC.BISHOP), 2)]); } if (pos.piece_count(ColorC.BLACK, PieceTypeC.PAWN) == 0 && npm_b - npm_w <= Constants.BishopValueMidgame) { e.factorBLACK = (byte) (npm_w == npm_b || npm_b < Constants.RookValueMidgame ? 0 : NoPawnsSF[Math.Min(pos.piece_count(ColorC.BLACK, PieceTypeC.BISHOP), 2)]); } // Compute the space weight if (npm_w + npm_b >= 2 * Constants.QueenValueMidgame + 4 * Constants.RookValueMidgame + 2 * Constants.KnightValueMidgame) { var minorPieceCount = pos.piece_count(ColorC.WHITE, PieceTypeC.KNIGHT) + pos.piece_count(ColorC.WHITE, PieceTypeC.BISHOP) + pos.piece_count(ColorC.BLACK, PieceTypeC.KNIGHT) + pos.piece_count(ColorC.BLACK, PieceTypeC.BISHOP); e.spaceWeight = minorPieceCount * minorPieceCount; } // Evaluate the material imbalance. We use PIECE_TYPE_NONE as a place holder // for the bishop pair "extended piece", this allow us to be more flexible // in defining bishop pair bonuses. this.pieceCount[0][0] = pos.piece_count(ColorC.WHITE, PieceTypeC.BISHOP) > 1 ? 1 : 0; this.pieceCount[0][1] = pos.piece_count(ColorC.WHITE, PieceTypeC.PAWN); this.pieceCount[0][2] = pos.piece_count(ColorC.WHITE, PieceTypeC.KNIGHT); this.pieceCount[0][3] = pos.piece_count(ColorC.WHITE, PieceTypeC.BISHOP); this.pieceCount[0][4] = pos.piece_count(ColorC.WHITE, PieceTypeC.ROOK); this.pieceCount[0][5] = pos.piece_count(ColorC.WHITE, PieceTypeC.QUEEN); this.pieceCount[1][0] = pos.piece_count(ColorC.BLACK, PieceTypeC.BISHOP) > 1 ? 1 : 0; this.pieceCount[1][1] = pos.piece_count(ColorC.BLACK, PieceTypeC.PAWN); this.pieceCount[1][2] = pos.piece_count(ColorC.BLACK, PieceTypeC.KNIGHT); this.pieceCount[1][3] = pos.piece_count(ColorC.BLACK, PieceTypeC.BISHOP); this.pieceCount[1][4] = pos.piece_count(ColorC.BLACK, PieceTypeC.ROOK); this.pieceCount[1][5] = pos.piece_count(ColorC.BLACK, PieceTypeC.QUEEN); e.value = (short)((this.imbalance(ColorC.WHITE) - this.imbalance(ColorC.BLACK)) / 16); }
/// MaterialTable::material_info() takes a position object as input, /// computes or looks up a MaterialInfo object, and returns a pointer to it. /// If the material configuration is not already present in the table, it /// is stored there, so we don't have to recompute everything when the /// same material configuration occurs again. internal void probe(Position pos, out MaterialEntry e) { Key key = pos.material_key(); e = entries[((UInt32)key) & Constants.MaterialTableMask]; // 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 (e.key == key) { return; } // Initialize MaterialInfo entry Value npm = pos.non_pawn_material(ColorC.WHITE) + pos.non_pawn_material(ColorC.BLACK); e.value = 0; e.scalingFunctionWHITE = null; e.scalingFunctionBLACK = null; e.spaceWeight = 0; e.key = key; e.factorWHITE = e.factorBLACK = ScaleFactorC.SCALE_FACTOR_NORMAL; e.gamePhase = npm >= MidgameLimit ? PhaseC.PHASE_MIDGAME : npm <= EndgameLimit ? PhaseC.PHASE_ENDGAME : (((npm - EndgameLimit) * 128) / (MidgameLimit - EndgameLimit)); // Let's look if we have a specialized evaluation function for this // particular material configuration. First we look for a fixed // configuration one, then a generic one if previous search failed. if ((e.evaluationFunction = Endgame.probeValue(key, out e.evaluationFunctionColor)) != null) { return; } if (is_KXK(ColorC.WHITE, pos)) { e.evaluationFunction = Endgame.Endgame_KXK; e.evaluationFunctionColor = ColorC.WHITE; return; } if (is_KXK(ColorC.BLACK, pos)) { e.evaluationFunction = Endgame.Endgame_KXK; e.evaluationFunctionColor = ColorC.BLACK; return; } if ((pos.pieces_PT(PieceTypeC.PAWN) == 0) && (pos.pieces_PT(PieceTypeC.ROOK) == 0) && (pos.pieces_PT(PieceTypeC.QUEEN) == 0)) { // Minor piece endgame with at least one minor piece per side and // no pawns. Note that the case KmmK is already handled by KXK. Debug.Assert((pos.pieces_PTC(PieceTypeC.KNIGHT, ColorC.WHITE) | pos.pieces_PTC(PieceTypeC.BISHOP, ColorC.WHITE)) != 0); Debug.Assert((pos.pieces_PTC(PieceTypeC.KNIGHT, ColorC.BLACK) | pos.pieces_PTC(PieceTypeC.BISHOP, ColorC.BLACK)) != 0); if (pos.piece_count(ColorC.WHITE, PieceTypeC.BISHOP) + pos.piece_count(ColorC.WHITE, PieceTypeC.KNIGHT) <= 2 && pos.piece_count(ColorC.BLACK, PieceTypeC.BISHOP) + pos.piece_count(ColorC.BLACK, PieceTypeC.KNIGHT) <= 2) { e.evaluationFunction = Endgame.Endgame_KmmKm; e.evaluationFunctionColor = pos.sideToMove; return; } } // OK, we didn't find any special evaluation function for the current // material configuration. Is there a suitable scaling function? // // We face problems when there are several conflicting applicable // scaling functions and we need to decide which one to use. EndgameScaleFactor sf; Color c; if ((sf = Endgame.probeScaleFactor(key, out c)) != null) { if (c == ColorC.WHITE) { e.scalingFunctionWHITE = sf; } else { e.scalingFunctionBLACK = sf; } return; } // Generic scaling functions that refer to more then one material // distribution. Should be probed after the specialized ones. // Note that these ones don't return after setting the function. if (is_KBPsKs(ColorC.WHITE, pos)) { e.scalingFunctionWHITE = Endgame.Endgame_KBPsK; } if (is_KBPsKs(ColorC.BLACK, pos)) { e.scalingFunctionBLACK = Endgame.Endgame_KBPsK; } if (is_KQKRPs(ColorC.WHITE, pos)) { e.scalingFunctionWHITE = Endgame.Endgame_KQKRPs; } else if (is_KQKRPs(ColorC.BLACK, pos)) { e.scalingFunctionBLACK = Endgame.Endgame_KQKRPs; } Value npm_w = pos.non_pawn_material(ColorC.WHITE); Value npm_b = pos.non_pawn_material(ColorC.BLACK); if (npm_w + npm_b == ValueC.VALUE_ZERO) { if (pos.piece_count(ColorC.BLACK, PieceTypeC.PAWN) == 0) { Debug.Assert(pos.piece_count(ColorC.WHITE, PieceTypeC.PAWN) >= 2); e.scalingFunctionWHITE = Endgame.Endgame_KPsK; } else if (pos.piece_count(ColorC.WHITE, PieceTypeC.PAWN) == 0) { Debug.Assert(pos.piece_count(ColorC.BLACK, PieceTypeC.PAWN) >= 2); e.scalingFunctionBLACK = Endgame.Endgame_KPsK; } else if (pos.piece_count(ColorC.WHITE, PieceTypeC.PAWN) == 1 && pos.piece_count(ColorC.BLACK, PieceTypeC.PAWN) == 1) { // This is a special case because we set scaling functions // for both colors instead of only one. e.scalingFunctionWHITE = Endgame.Endgame_KPKP; e.scalingFunctionBLACK = Endgame.Endgame_KPKP; } } // No pawns makes it difficult to win, even with a material advantage if (pos.piece_count(ColorC.WHITE, PieceTypeC.PAWN) == 0 && npm_w - npm_b <= Constants.BishopValueMidgame) { e.factorWHITE = (byte) (npm_w == npm_b || npm_w < Constants.RookValueMidgame ? 0 : NoPawnsSF[Math.Min(pos.piece_count(ColorC.WHITE, PieceTypeC.BISHOP), 2)]); } if (pos.piece_count(ColorC.BLACK, PieceTypeC.PAWN) == 0 && npm_b - npm_w <= Constants.BishopValueMidgame) { e.factorBLACK = (byte) (npm_w == npm_b || npm_b < Constants.RookValueMidgame ? 0 : NoPawnsSF[Math.Min(pos.piece_count(ColorC.BLACK, PieceTypeC.BISHOP), 2)]); } // Compute the space weight if (npm_w + npm_b >= 2 * Constants.QueenValueMidgame + 4 * Constants.RookValueMidgame + 2 * Constants.KnightValueMidgame) { int minorPieceCount = pos.piece_count(ColorC.WHITE, PieceTypeC.KNIGHT) + pos.piece_count(ColorC.WHITE, PieceTypeC.BISHOP) + pos.piece_count(ColorC.BLACK, PieceTypeC.KNIGHT) + pos.piece_count(ColorC.BLACK, PieceTypeC.BISHOP); e.spaceWeight = minorPieceCount * minorPieceCount; } // Evaluate the material imbalance. We use PIECE_TYPE_NONE as a place holder // for the bishop pair "extended piece", this allow us to be more flexible // in defining bishop pair bonuses. pieceCount[0][0] = pos.piece_count(ColorC.WHITE, PieceTypeC.BISHOP) > 1 ? 1 : 0; pieceCount[0][1] = pos.piece_count(ColorC.WHITE, PieceTypeC.PAWN); pieceCount[0][2] = pos.piece_count(ColorC.WHITE, PieceTypeC.KNIGHT); pieceCount[0][3] = pos.piece_count(ColorC.WHITE, PieceTypeC.BISHOP); pieceCount[0][4] = pos.piece_count(ColorC.WHITE, PieceTypeC.ROOK); pieceCount[0][5] = pos.piece_count(ColorC.WHITE, PieceTypeC.QUEEN); pieceCount[1][0] = pos.piece_count(ColorC.BLACK, PieceTypeC.BISHOP) > 1 ? 1 : 0; pieceCount[1][1] = pos.piece_count(ColorC.BLACK, PieceTypeC.PAWN); pieceCount[1][2] = pos.piece_count(ColorC.BLACK, PieceTypeC.KNIGHT); pieceCount[1][3] = pos.piece_count(ColorC.BLACK, PieceTypeC.BISHOP); pieceCount[1][4] = pos.piece_count(ColorC.BLACK, PieceTypeC.ROOK); pieceCount[1][5] = pos.piece_count(ColorC.BLACK, PieceTypeC.QUEEN); e.value = (Int16)((imbalance(ColorC.WHITE) - imbalance(ColorC.BLACK)) / 16); return; }