// // Determine whether a Draw should be sought or avoided, by adding // the estimated handicap to an equal evaluation of Zero depending // on the game phase, and the relative strength of the two players. // protected Eval contempt() { GameState.AtomicIncrement(ref State.DrawTotal); var mDrawValue = (Eval)(-State.Contempt); // Strength advantage of White over Black return(mDrawValue); }
// ~2.3 MHz: slowed mostly by IsLegal() and only slightly by resetMove() protected Boolean tryMove(ref Move move, Boolean bFindRepetition = true, Boolean bQxnt = false) { CurrentMove = move; // Current Pseudo Move (bool bPrevented, bool bRestricted) = isPinned(move); if (bPrevented) // Skip moves which violate known pins { GameState.AtomicIncrement(ref State.PinSkipTotal); return(false); } //[Timer]timeMove(move); // //[Note]resetMove() is called here because it is needed for every subsequent move. This leaves // a window between the time initNode() initializes a node and when resetMove() is first called. // resetMove(); this.move(ref move); if (IsDraw0()) { clrEval(); // Captures and Pawn moves invalidate staticEval() } //[Note]If En Passant was possible, any move ends a Transposition Group if (Parent.IsPassed()) { setDraw0(); } testHash(); // Conditional var bLegal = IsLegal(bFindRepetition, bRestricted); if (isDefined(move)) { if (bLegal) { CurrentMove = move = annotateEarly(move); } else { restrictPiece(move); } } State.IncMove(bLegal, bQxnt); State.MonitorBound(this); // Pass position so heartbeat() can build getCurrentMoves() return(bLegal); }
protected void findRepetition() { GameState.AtomicIncrement(ref State.RepetitionSearches); clrRepetition(); if (IsDraw0()) { return; } var bNullMade = IsNullMade(); // //[Note]This search for 3-fold repetition extends to the initial position; // not just to State.MovePosition where the current search began. // for (var position = Parent; position is not null; position = position.Parent) { GameState.AtomicIncrement(ref State.RepetitionPlies); // // Include Positions for both sides, stopping when the Transposition Group ends. // if (Equals(position)) { if (bNullMade) { // // Null Moves do not count as repetition of the position; but the // Draw3 and Draw2 flags are copied to expedite subsequent search: // FlagsDraw |= position.fdr(); } else { setRepetition(position.fdr() != 0); } break; } else if (position.IsDraw0()) { break; // End of Transposition Group } } #if DebugDraw2 validateDraw2(); #endif }
// // Lazy Capture avoids calling getPieceIndex() until it becomes necessary. // The purpose of the following method is to update the move and to avoid // calling getPieceIndex() again. // // SaveCapture is required to show captures in AppendAN() if bExpandFrom. // protected Byte captureIndex(Int32 nTo, ref Move move, out Boolean bEnPassant) { bEnPassant = false; var vCapture = vPieceNull; var uCapture = captured(move); var capture = (Piece)uCapture; if (capture == Piece.Capture) { #if CountCapturedPiece GameState.AtomicIncrement(ref State.CapturedPieceTotal); #endif // // Between 2% and 20% of all Pseudo Moves require a call to getPieceIndex(). // At the rate of 72 MHz when a Pawn is found; 68 MHz otherwise, the 1.5% // cost would be quite low even were it incurred for every generated move. // vCapture = getPieceIndex(nTo); Debug.Assert(vCapture != vPieceNull, "vCapture == vPieceNull", $"There is no piece to capture on {(sq)nTo}."); #if SaveCapture var captive = indexPiece(vCapture); move &= ~Move.CaptiveMask; move |= (Move)((UInt32)captive << nCaptiveBit); #endif } else if (capture == Piece.EP) { bEnPassant = true; vCapture = vP6; } else if (capture == Piece.None) { Debug.Assert(capture != Piece.None, "Unexpected Non-Capture"); } else { vCapture = pieceIndex(uCapture); } Debug.Assert(vCapture != vK6, "Unknown Captive", $"No captive found for {(sq)nTo}."); return(vCapture); }
protected Eval final() { // // The game is over if the side to move has no move. // If the King is in Check, this is a Checkmate and // the lowest possible evaluation is given. // if (InCheck()) { GameState.AtomicIncrement(ref State.MateTotal); return(debitMate(-MateMax, SearchPly)); } // // If the King is not in Check, this is a Stalemate // and an equal evaluation is returned. // var bWTM = WTM(); var mValue = contempt(); return(reflectValue(bWTM, mValue)); }
protected Boolean nullMove() { CurrentMove = Move.NullMove; // Current Pseudo Move resetMove(); skipTurn(); //[Note]If En Passant was possible, any move ends a Transposition Group if (Parent.IsPassed()) { setDraw0(); } testHash(); // Conditional var bLegal = !InCheck(); if (!bLegal) { throw new BoardException("Illegal Null Move"); } GameState.AtomicIncrement(ref State.NullMoveTotal); State.MonitorBound(this); return(bLegal); }
private Eval fullEval() { // //[Note]StaticEvaluations = TotalEvaluations - FullEvaluations // Draws are included; because they exit early. // GameState.AtomicIncrement(ref State.FullEvals); var mValue = staticEval(out PawnPosition pp); // // Load PawnFeature Deltas from the PawnPositions hash table, // based on HashPawn, the Zobrist Hashcode summed over Pawns. // if (Pawn == 0) // PawnHash == default(Hashcode)} { #if EvalKBNvKMateCorner if ((FlagsEG & EGFlags.KBNvK) != 0) { var mReward = rewardKBNvKMateCorner(); mValue += mReward; } #endif } else { #if EvalOutsideSquare if ((FlagsEG & EGFlags.OutsideSquare) != 0) { var mReward = punishOutsideSquare(); mValue += mReward; } #endif #if EvalKQvKPDistance if ((FlagsEG & EGFlags.KQvKP) != 0) { var mReward = rewardKQvKPProximity(); mValue += mReward; } #endif #if EvalRookBehindPasser if (Rook != 0) { #if PawnPositionByValue var bDefault = (pp.BlackPRP & PRPFlags.IsValid) == 0; #else var bDefault = pp == default(PawnPosition); #endif if (bDefault) { pp = State.GetPXP(this); } const Boolean bWhiteRook = true; var mRooksBehindBlack = rookBehindPasser(!bWhiteRook, pp.BlackPassers); var mRooksBehindWhite = rookBehindPasser(bWhiteRook, pp.WhitePassers); #if TestRookBehindPasser if (mRooksBehindBlack != 0 || mRooksBehindWhite != 0) { DisplayCurrent("pieceval()"); } #endif mValue += mRooksBehindWhite; mValue -= mRooksBehindBlack; } #endif } var mAbs = Abs(mValue); Debug.Assert(mAbs < MateMin, "Mate value returned by staticEval()"); if (mAbs <= mDeltaBaseWeight) //[ToDo]Define a new threshold { #if BuildAtxTo buildAtxTo(RankPiece); #endif #if Mobility mValue += mobility(); #endif } // // The following helps find Draw3 // if (IsDraw2() && mAbs < MateMin) { mValue /= 4; //[IBV]Care will be needed to protect the EvalType here } return(mValue); }
// //[ToDo]Add evaluation of practical Draws where helpmate is possible: // // KKNN or KNKNN // KBKB opposite color // KNKBN, KBKBN. KBKBB pair [Note: KNKBB pair can be won; but perhaps not in 50 moves] // // More Complex Draws, assuming weaker side in time to defend: // // KKP if weaker side maintains opposition // Distant Opposition vs. Key/Critical Squares vs Corresponding or Relative Squares!? // // KBKBP of opposite color // KPKQ if P to queen on ACFH-file and sronger K too far to help // // // Development [initial move per piece, delaying heavy pieces] // Control of the Center by Pawns // Control of Squares around King // Castling and King Safety [Corner Squares] vs King Activity in the Endgame // // Rooks on Open Files // Rook or Queen on 7th [or 2nd] Rank // Protected Piece and Connected Rook Bonus // Knight Scope and Outpost [free of Pawn harrassment] // Bishop Scope [and Bad Bishop Detection] // // Bonus for Bishop with color of Promotion Square for any Passed Pawns, especially // Rook Pawns. Applies to defense and Passed Opposing Pawns, as well as to offense. // // Attack Pawn Chains at the base // Bonus for Passed Pawn Couples // // Piece Values depending on Opening, Middle, End Game Phase and Relative Advantage: // Stronger side favors [vice versa Weaker side is averse to] // Bishops of Opposite Colors in Middle Game; and Bishops of Same Color in Endgame. // // // Technical and Tablebase Draws: // KRKRP if weaker side can attain Philidor Position // // EGTB Alternatives: Syzygy by Ronald de Man, Gaviota EGTB by Miguel A. Ballicora, // Nalimov by Eugene Nalimov. Syzygy is preferred by Houdini. It is compact; but // does not provide Distance to Mate (DTM). They provide Wind-Draw-Loss (WDL) and // Distance to Zero (DTZ). // // The Syzygy 6-man EGTB is available at http://tablebase.sesse.net/syzygy, where // the (290) 3-4-5-men files require 938 MB; and (730) 6-men files require 149 GB. // protected Eval staticEval(out PawnPosition pp) //[New]~9.666 MHz vs ~13.333 MHz w/o EvalRookBehindPasser { pp = default; if (IsInsufficient()) { return(contempt()); } GameState.AtomicIncrement(ref State.TotalEvals); // vs. FullEvaluations setEndGameFlags(); if (EvalUndefined < StaticDelta) { return(StaticDelta); } // // Retrieve material balance from the Composition: // getValue(out Eval mDelta, out Eval mTotal); if (Pawn != 0) // Else PawnHash == default(Hashcode) { pp = State.GetPXP(this); mDelta += pp.Delta; mTotal += pp.Total; #if EvalWrongBishop if (punishWrongBishop(pp.BlackPRP & PRPFlags.Both, Side[Black].FlagsHi)) { mDelta += mWrongBishopWeight; // Black has Wrong Bishop } if (punishWrongBishop(pp.WhitePRP & PRPFlags.Both, Side[White].FlagsHi)) { mDelta -= mWrongBishopWeight; // White has Wrong Bishop } #endif } #if TradePieces if (mTotal > 0) { // // The following provides an incentive for the stronger // side to exchange material and for the weaker side to // avoid such exchanges. The value is Zero in an equal // position, and grows to a maximum of one centipawn if // the weaker side has been stripped of all material. // // Intuitively: Exchanges reduce the Total material but // leave the Delta unaffected; and Delta can range from // Zero to the Total. Thus, their quotient ranges from // Zero to One. // //[Note]A refinement is needed to prefer trading Pieces // over Pawns in the endgame. // var mIncentive = (Eval)(mPawnWeight * mDelta / mTotal); mDelta += mIncentive; } #endif // //[Note]staticEval() prepares StaticTotal for any isEndgame() tests // StaticTotal = mTotal; // Update for isEndgame() StaticDelta = mDelta; return(mDelta); }