void cEval::ScoreHanging(POS *p, eData *e, int sd) { int pc, sq, sc; int op = Opp(sd); U64 bbHanging = p->cl_bb[op] & ~e->bbPawnTakes[op]; U64 bbThreatened = p->cl_bb[op] & e->bbPawnTakes[sd]; bbHanging |= bbThreatened; // piece attacked by our pawn isn't well defended bbHanging &= e->bbAllAttacks[sd]; // hanging piece has to be attacked bbHanging &= ~p->Pawns(op); // currently we don't evaluate threats against pawns U64 bbDefended = p->cl_bb[op] & e->bbAllAttacks[op]; bbDefended &= e->bbEvAttacks[sd]; // N, B, R attacks (pieces attacked by pawns are scored as hanging) bbDefended &= ~e->bbPawnTakes[sd]; // no defense against pawn attack bbDefended &= ~p->Pawns(op); // currently we don't evaluate threats against pawns // hanging pieces (attacked and undefended) while (bbHanging) { sq = BB.PopFirstBit(&bbHanging); pc = TpOnSq(p, sq); sc = tp_value[pc] / 64; Add(e, sd, F_PRESSURE, 10 + sc, 18 + sc); } // defended pieces under attack while (bbDefended) { sq = BB.PopFirstBit(&bbDefended); pc = TpOnSq(p, sq); sc = tp_value[pc] / 96; Add(e, sd, F_PRESSURE, 5 + sc, 9 + sc); } }
int KPKdraw(POS *p, int sd) { int op = Opp(sd); U64 bbPawn = p->Pawns(sd); U64 bbStrongKing = p->Kings(sd); U64 bbWeakKing = p->Kings(op); // opposition through a pawn if (p->side == sd && (bbWeakKing & BB.ShiftFwd(bbPawn, sd)) && (bbStrongKing & BB.ShiftFwd(bbPawn, op)) ) return 1; // weaker side can create opposition through a pawn in one move if (p->side == op && (BB.KingAttacks(p->king_sq[op]) & BB.ShiftFwd(bbPawn, sd)) && (bbStrongKing & BB.ShiftFwd(bbPawn, op)) ) if (!Illegal(p)) return 1; // opposition next to a pawn if (p->side == sd && (bbStrongKing & BB.ShiftSideways(bbPawn)) && (bbWeakKing & BB.ShiftFwd(BB.ShiftFwd(bbStrongKing,sd) ,sd)) ) return 1; // TODO: pawn checks king return 0; }
void cEval::ScoreKingFile(POS * p, int sd, U64 bbFile, int *shield, int *storm) { int shelter = ScoreFileShelter(bbFile & p->Pawns(sd), sd); if (bbFile & bbCentralFile) shelter /= 2; *shield += shelter; *storm += ScoreFileStorm(bbFile & p->Pawns(Opp(sd)), sd); }
int cEval::ScoreKingFile(POS * p, int sd, U64 bbFile) { int shelter = ScoreFileShelter(bbFile & p->Pawns(sd), sd); int storm = ScoreFileStorm(bbFile & p->Pawns(Opp(sd)), sd); if (bbFile & bbCentralFile) return (shelter / 2) + storm; else return shelter + storm; }
void cEval::ScorePawns(POS *p, eData *e, int sd) { U64 bbPieces, bbSpan, fl_phalanx; int sq, fl_unopposed, fl_weak, fl_defended; int op = Opp(sd); U64 bbOwnPawns = p->Pawns(sd); U64 bbOppPawns = p->Pawns(op); // Is color OK? assert(sd == WC || sd == BC); // Loop through the pawns, evaluating each one bbPieces = bbOwnPawns; while (bbPieces) { sq = BB.PopFirstBit(&bbPieces); // Get some information about the pawn we are evaluating bbSpan = BB.GetFrontSpan(SqBb(sq), sd); fl_defended = ((SqBb(sq) & e->bbPawnTakes[sd]) != 0); fl_unopposed = ((bbSpan & bbOppPawns) == 0); fl_weak = ((Mask.supported[sd][sq] & bbOwnPawns) == 0); fl_phalanx = (BB.ShiftSideways(SqBb(sq)) & bbOwnPawns); // Candidate passer if (fl_unopposed) { if (fl_phalanx) { if (BB.PopCnt((Mask.passed[sd][sq] & bbOppPawns)) == 1) Add(e, sd, F_PAWNS, passed_bonus_mg[sd][Rank(sq)] / 3, passed_bonus_eg[sd][Rank(sq)] / 3); } } // Doubled pawn if (bbSpan & bbOwnPawns) Add(e, sd, F_PAWNS, Param.doubled_malus_mg, Param.doubled_malus_eg); // Supported pawn if (fl_phalanx) Add(e, sd, F_PAWNS, Param.phalanx[sd][sq] , 2); else if (fl_defended) Add(e, sd, F_PAWNS, Param.defended[sd][sq], 1); // Weak pawn (two flavours) if (fl_weak) { if (!(Mask.adjacent[File(sq)] & bbOwnPawns)) Add(e, sd, F_PAWNS, Param.isolated_malus_mg + Param.isolated_open_malus * fl_unopposed, Param.isolated_malus_eg); // isolated pawn else Add(e, sd, F_PAWNS, Param.backward_malus_mg[File(sq)] + Param.backward_open_malus * fl_unopposed, Param.backward_malus_eg); // backward pawn } } }
int Attacked(POS *p, int sq, int sd) { return (p->Pawns(sd) & BB.PawnAttacks(Opp(sd),sq) ) || (p->Knights(sd) & BB.KnightAttacks(sq)) || (p->DiagMovers(sd) & BB.BishAttacks(OccBb(p), sq)) || (p->StraightMovers(sd) & BB.RookAttacks(OccBb(p), sq)) || (p->Kings(sd) & BB.KingAttacks(sq)); }
void SetAsymmetricEval(int sd) { int op = Opp(sd); Eval.prog_side = sd; curr_weights[sd][SD_ATT] = dyn_weights[DF_OWN_ATT]; curr_weights[op][SD_ATT] = dyn_weights[DF_OPP_ATT]; curr_weights[sd][SD_MOB] = dyn_weights[DF_OWN_MOB]; curr_weights[op][SD_MOB] = dyn_weights[DF_OPP_MOB]; }
void sEvaluator::SinglePawnScore(sPosition *p, int side) { int sq, flagIsOpen, flagIsWeak; int oppo = Opp(side); U64 flagIsPhalanx, flagPhalanx2, flagIsDoubled, bbFrontSpan; U64 bbPieces = bbPc(p, side, P); U64 bbOwnPawns = bbPieces; while (bbPieces) { sq = PopFirstBit(&bbPieces); // gather information about a pawn that is evaluated bbFrontSpan = GetFrontSpan(SqBb(sq), side ); flagIsDoubled = bbFrontSpan & bbOwnPawns; flagIsOpen = !( bbFrontSpan & bbPc(p, oppo, P) ); flagIsPhalanx = ShiftEast(SqBb(sq) ) & bbOwnPawns; flagPhalanx2 = ShiftWest(SqBb(sq) ) & bbOwnPawns; flagIsWeak = !( bbPawnSupport[side][sq] & bbOwnPawns ); if (flagIsDoubled) { pawnScoreMg[side] += doubledPawn[MG][File(sq)]; pawnScoreEg[side] += doubledPawn[EG][File(sq)]; } if (flagIsPhalanx) { pawnScoreMg[side] += Data.pawnProperty[PHALANX][MG][side][sq]; //pawnScoreEg[side] += Data.phalanxEg[side][sq]; } if (flagIsOpen) { U64 bbObstacles = bbPassedMask[side][sq] & bbPc(p, oppo, P); // passed pawn (some more eval will be done in ScoreP() in eval_pieces.c) if (!bbObstacles) { if (flagIsDoubled) AddPasserScore(CANDIDATE,side,sq); // back doubled passer is scored like candidate, else AddPasserScore(PASSED,side,sq); // only frontmost passer gets full credit } // candidate passer if (flagIsPhalanx || flagPhalanx2) { // test lower bonus when !flagIsWeak if (PopCntSparse(bbObstacles) == 1) AddPasserScore(CANDIDATE,side,sq); } } if (flagIsWeak) { if (!(bbAdjacentMask[File(sq)] & bbOwnPawns)) { // isolated AddPawnProperty(ISOLATED,side,sq); if (flagIsOpen) pawnScoreMg[side] += pawnIsolatedOnOpen; } else { // backward AddPawnProperty(BACKWARD,side,sq); if (flagIsOpen) pawnScoreMg[side] += pawnBackwardOnOpen; } } } }
void sEvaluator::EvalPawnCenter(sPosition *p, int side) { int oppo = Opp(side); if (bbPc(p, side, P) & RelSqBb(D4,side) ) { // defend central pawns (pawns side by side evaluated as "phalanx") if (bbPc(p, side, P) & RelSqBb(E3,side) ) pawnScoreMg[side] += centDefense; if (bbPc(p, side, P) & RelSqBb(C3,side) ) pawnScoreMg[side] += centDefense; } if (bbPc(p, side, P) & RelSqBb(E4,side) ) { if ( bbPc(p, side, P) & RelSqBb(D3,side) ) pawnScoreMg[side] += centDefense; } }
int KPKdraw(sPosition *p, int stronger) { int weaker = Opp(stronger); U64 bbPawn = bbPc(p, stronger, P); // opposition through a pawn if ( p->side == stronger && (SqBb(p->kingSquare[weaker]) & ShiftFwd(bbPawn, stronger) ) && (SqBb(p->kingSquare[stronger]) & ShiftFwd(bbPawn, weaker) ) ) return 1; // weaker side can create opposition through a pawn in one move if ( p->side == weaker && (bbKingAttacks[p->kingSquare[weaker]] & ShiftFwd(bbPawn, stronger) ) && (SqBb(p->kingSquare[stronger]) & ShiftFwd(bbPawn, weaker) ) ) if ( !IllegalPosition(p) ) return 1; // TODO: opposition next to a pawn return 0; }
void cEval::ScoreMaterial(POS * p, eData *e, int sd) { int op = Opp(sd); // Piece configurations int tmp = Param.np_table[p->cnt[sd][P]] * p->cnt[sd][N] // knights lose value as pawns disappear - Param.rp_table[p->cnt[sd][P]] * p->cnt[sd][R]; // rooks gain value as pawns disappear if (p->cnt[sd][N] > 1) tmp += Param.knight_pair; if (p->cnt[sd][R] > 1) tmp += Param.rook_pair_malus; if (p->cnt[sd][B] > 1) // Bishop pair Add(e, sd, F_OTHERS, SCALE(Param.bish_pair, Param.mat_perc), SCALE((Param.bish_pair+10), Param.mat_perc)); // "elephantiasis correction" for queen, idea by H.G.Mueller (nb. rookVsQueen doesn't help) if (p->cnt[sd][Q]) tmp -= Param.minorVsQueen * (p->cnt[op][N] + p->cnt[op][B]); Add(e, sd, F_OTHERS, SCALE(tmp, Param.mat_perc)); }
void cEval::ScoreOutpost(POS * p, eData * e, int sd, int pc, int sq) { int mul = 0; int tmp = Param.sp_pst_data[sd][pc][sq]; if (tmp) { if (SqBb(sq) & ~e->bbPawnCanTake[Opp(sd)]) mul += 2; // in the hole of enemy pawn structure if (SqBb(sq) & e->bbPawnTakes[sd]) mul += 1; // defended by own pawn if (SqBb(sq) & e->bbTwoPawnsTake[sd]) mul += 1; // defended by two pawns tmp *= mul; tmp /= 2; Add(e, sd, F_OUTPOST, tmp); } // Pawn in front of a minor if (SqBb(sq) & bbHomeZone[sd]) { U64 bbStop = BB.ShiftFwd(SqBb(sq), sd); if (bbStop & PcBb(p, sd, P)) Add(e, sd, F_OUTPOST, Param.minorBehindPawn); } }
void cEval::ScorePassers(POS * p, eData *e, int sd) { U64 bbPieces = p->Pawns(sd); int sq, mul, mg_tmp, eg_tmp; int op = Opp(sd); U64 bbOwnPawns = p->Pawns(sd); U64 bbStop; while (bbPieces) { sq = BB.PopFirstBit(&bbPieces); // Passed pawn if (!(Mask.passed[sd][sq] & p->Pawns(op))) { bbStop = BB.ShiftFwd(SqBb(sq), sd); mg_tmp = passed_bonus_mg[sd][Rank(sq)]; eg_tmp = passed_bonus_eg[sd][Rank(sq)] - ((passed_bonus_eg[sd][Rank(sq)] * Param.dist[sq][p->king_sq[op]]) / 30); mul = 100; // blocked passers score less if (bbStop & OccBb(p)) mul -= 20; // TODO: only with a blocker of opp color // our control of stop square else if ( (bbStop & e->bbAllAttacks[sd]) && (bbStop & ~e->bbAllAttacks[op]) ) mul += 10; // add final score Add(e, sd, F_PASSERS, (mg_tmp * mul) / 100, (eg_tmp * mul) / 100); } } }
int Quiesce(POS *p, int ply, int alpha, int beta, int *pv) { int best, score, move, new_pv[MAX_PLY]; MOVES m[1]; UNDO u[1]; int op = Opp(p->side); // Statistics and attempt at quick exit if (InCheck(p)) return QuiesceFlee(p, ply, alpha, beta, pv); nodes++; CheckTimeout(); if (abort_search) return 0; *pv = 0; if (IsDraw(p)) return DrawScore(p); if (ply >= MAX_PLY - 1) return Eval.Return(p, 1); // Get a stand-pat score and adjust bounds // (exiting if eval exceeds beta) best = Eval.Return(p, 1); if (best >= beta) return best; if (best > alpha) alpha = best; #ifdef USE_QS_HASH // Transposition table read if (TransRetrieve(p->hash_key, &move, &score, alpha, beta, 0, ply)) return score; #endif InitCaptures(p, m); // Main loop while ((move = NextCapture(m))) { // Pruning in quiescence search // (not applicable if we are capturing last enemy piece) if (p->cnt[op][N] + p->cnt[op][B] + p->cnt[op][R] + p->cnt[op][Q] > 1) { // 1. Delta pruning if (best + tp_value[TpOnSq(p, Tsq(move))] + 300 < alpha) continue; // 2. SEE-based pruning of bad captures if (BadCapture(p, move)) continue; } p->DoMove(move, u); if (Illegal(p)) { p->UndoMove(move, u); continue; } score = -Quiesce(p, ply + 1, -beta, -alpha, new_pv); p->UndoMove(move, u); if (abort_search) return 0; // Beta cutoff if (score >= beta) { #ifdef USE_QS_HASH TransStore(p->hash_key, *pv, best, LOWER, 0, ply); #endif return score; } // Adjust alpha and score if (score > best) { best = score; if (score > alpha) { alpha = score; BuildPv(pv, new_pv, move); } } } #ifdef USE_QS_HASH if (*pv) TransStore(p->hash_key, *pv, best, EXACT, 0, ply); else TransStore(p->hash_key, 0, best, UPPER, 0, ply); #endif return best; }
int cEval::ScoreChains(POS *p, int sd) { int mgResult = 0; int sq = p->king_sq[sd]; int op = Opp(sd); // basic pointy chain if (SqBb(sq) & bbKSCastle[sd]) { if (OPP_PAWN(E4)) { if (CONTAINS(opPawns, D5, C6)) { // c6-d5-e4 triad mgResult -= (CONTAINS(sdPawns, D4, E3)) ? bigChainScore : smallChainScore; } if (CONTAINS(opPawns, D5, F3)) { // d5-e4-f3 triad mgResult -= (OWN_PAWN(E3)) ? bigChainScore : smallChainScore; } } if (OPP_PAWN(E5)) { if (CONTAINS(opPawns, F4, D6)) { // d6-e5-f4 triad // storm of a "g" pawn in the King's Indian if (OPP_PAWN(G5)) { mgResult -= 4; if (OPP_PAWN(H4)) return 0; // this is not how you handle pawn chains } if (OPP_PAWN(G4)) mgResult -= 12; mgResult -= (CONTAINS(sdPawns, E4, D5)) ? bigChainScore : smallChainScore; } if (CONTAINS(opPawns, G3, F4)) { // e5-f4-g3 triad mgResult -= (OWN_PAWN(F3)) ? bigChainScore : smallChainScore; } } } if (SqBb(sq) & bbQSCastle[sd]) { // basic pointy chain if (OPP_PAWN(D4)) { if (CONTAINS(opPawns, E5, F6)) { mgResult -= (CONTAINS(sdPawns, E4, D3)) ? bigChainScore : smallChainScore; } if (CONTAINS(opPawns, F5, C3)) { mgResult -= (SQ(D3) & sdPawns) ? bigChainScore : smallChainScore; } } if (OPP_PAWN(D5)) { if (CONTAINS(opPawns, C4, E6)) { // storm of a "b" pawn if (OPP_PAWN(B5)) { mgResult -= 4; if (OPP_PAWN(A4)) return 0; // this is not how you handle pawn chains } if (OPP_PAWN(B4)) mgResult -= 12; mgResult -= (CONTAINS(sdPawns, E4, D5)) ? bigChainScore : smallChainScore; } if (CONTAINS(opPawns, B3, C4)) { mgResult -= (OWN_PAWN(C3)) ? bigChainScore : smallChainScore; } } } return mgResult; }
void sManipulator::DoMove(sPosition *p, int move, UNDO *u) { int side = p->side; // moving side int fsq = Fsq(move); // start square int tsq = Tsq(move); // target square int ftp = TpOnSq(p, fsq); // moving piece int ttp = TpOnSq(p, tsq); // captured piece U64 bbMove = SqBb(fsq) | SqBb(tsq); // optimization from Stockfish // save data for undoing a move u->ttp = ttp; u->castleFlags = p->castleFlags; u->epSquare = p->epSquare; u->reversibleMoves = p->reversibleMoves; u->hashKey = p->hashKey; u->pawnKey = p->pawnKey; p->repetitionList[p->head++] = p->hashKey; // update reversible move counter (zeroing is done on captures and pawn moves) p->reversibleMoves++; p->hashKey ^= zobCastle[p->castleFlags]; p->castleFlags &= castleMask[fsq] & castleMask[tsq]; p->hashKey ^= zobCastle[p->castleFlags]; // clear en passant square if (p->epSquare != NO_SQ) { p->hashKey ^= zobEp[File(p->epSquare)]; p->epSquare = NO_SQ; } // move a piece from start square p->pc[fsq] = NO_PC; p->pc[tsq] = Pc(side, ftp); p->hashKey ^= zobPiece[Pc(side, ftp)][fsq] ^ zobPiece[Pc(side, ftp)][tsq]; if (ftp == P) { p->reversibleMoves = 0; p->pawnKey ^= zobPiece[Pc(side, ftp)][fsq] ^ zobPiece[Pc(side, ftp)][tsq]; } p->bbCl[side] ^= bbMove; p->bbTp[ftp] ^= bbMove; p->pstMg[side] += Data.pstMg[side][ftp][tsq] - Data.pstMg[side][ftp][fsq]; p->pstEg[side] += Data.pstEg[side][ftp][tsq] - Data.pstEg[side][ftp][fsq]; // on a king move update king location data if (ftp == K) p->kingSquare[side] = tsq; // capture if (ttp != NO_TP) { p->reversibleMoves = 0; p->hashKey ^= zobPiece[Pc(Opp(side), ttp)][tsq]; if (ttp == P) p->pawnKey ^= zobPiece[Pc(Opp(side), ttp)][tsq]; p->bbCl[Opp(side)] ^= SqBb(tsq); p->bbTp[ttp] ^= SqBb(tsq); p->pcCount[Opp(side)][ttp]--; p->pieceMat[Opp(side)] -= Data.matValue[ttp]; p->phase -= Data.phaseValue[ttp]; p->pstMg[Opp(side)] -= Data.pstMg[Opp(side)][ttp][tsq]; p->pstEg[Opp(side)] -= Data.pstEg[Opp(side)][ttp][tsq]; } switch (MoveType(move)) { case NORMAL: break; case CASTLE: if (tsq > fsq) { fsq += 3; tsq -= 1; } else { fsq -= 4; tsq += 1; } p->pc[fsq] = NO_PC; p->pc[tsq] = Pc(side, R); p->hashKey ^= zobPiece[Pc(side, R)][fsq] ^ zobPiece[Pc(side, R)][tsq]; p->bbCl[side] ^= SqBb(fsq) | SqBb(tsq); p->bbTp[R] ^= SqBb(fsq) | SqBb(tsq); p->pstMg[side] += Data.pstMg[side][R][tsq] - Data.pstMg[side][R][fsq]; p->pstEg[side] += Data.pstEg[side][R][tsq] - Data.pstEg[side][R][fsq]; break; case EP_CAP: tsq ^= 8; p->pc[tsq] = NO_PC; p->hashKey ^= zobPiece[Pc(Opp(side), P)][tsq]; p->pawnKey ^= zobPiece[Pc(Opp(side), P)][tsq]; p->bbCl[Opp(side)] ^= SqBb(tsq); p->bbTp[P] ^= SqBb(tsq); p->pcCount[Opp(side)][P]--; p->phase -= Data.phaseValue[P]; p->pstMg[Opp(side)] -= Data.pstMg[Opp(side)][P][tsq]; p->pstEg[Opp(side)] -= Data.pstEg[Opp(side)][P][tsq]; break; case EP_SET: tsq ^= 8; if (bbPawnAttacks[side][tsq] & bbPc(p, Opp(side), P)) { p->epSquare = tsq; p->hashKey ^= zobEp[File(tsq)]; } break; // promotion: (1) add promoted piece and add values associated with it case N_PROM: // (2) remove promoted pawn and substract values associated with it case B_PROM: case R_PROM: case Q_PROM: ftp = PromType(move); p->pc[tsq] = Pc(side, ftp); p->hashKey ^= zobPiece[Pc(side, P)][tsq] ^ zobPiece[Pc(side, ftp)][tsq]; p->pawnKey ^= zobPiece[Pc(side, P)][tsq]; p->bbTp[P] ^= SqBb(tsq); p->bbTp[ftp]^= SqBb(tsq); p->pcCount[side][ftp]++; p->pcCount[side][P]--; p->pieceMat[side] += Data.matValue[ftp]; p->phase += Data.phaseValue[ftp] - Data.phaseValue[P]; p->pstMg[side] += Data.pstMg[side][ftp][tsq] - Data.pstMg[side][P][tsq]; p->pstEg[side] += Data.pstEg[side][ftp][tsq] - Data.pstEg[side][P][tsq]; break; } p->side ^= 1; p->hashKey ^= SIDE_RANDOM; }
void cEval::ScorePieces(POS *p, eData *e, int sd) { U64 bbPieces, bbMob, bbAtt, bbFile, bbContact; int op, sq, cnt, tmp, ksq, att = 0, wood = 0; int own_pawn_cnt, opp_pawn_cnt; int r_on_7th = 0; int fwd_weight = 0; int fwd_cnt = 0; // Is color OK? assert(sd == WC || sd == BC); // Init variables op = Opp(sd); ksq = KingSq(p, op); U64 bbExcluded = p->Pawns(sd) /*| p->Kings(sd)*/; // Init enemy king zone for attack evaluation. We mark squares where the king // can move plus two or three more squares facing enemy position. U64 bbZone = Mask.king_zone[sd][ksq]; // Init bitboards to detect check threats U64 bbKnightChk = BB.KnightAttacks(ksq); U64 bbStr8Chk = BB.RookAttacks(OccBb(p), ksq); U64 bbDiagChk = BB.BishAttacks(OccBb(p), ksq); U64 bbQueenChk = bbStr8Chk | bbDiagChk; // Knight bbPieces = p->Knights(sd); while (bbPieces) { sq = BB.PopFirstBit(&bbPieces); // Knight tropism to enemy king Add(e, sd, F_TROPISM, tropism_mg[N] * Param.dist[sq][ksq], tropism_eg[N] * Param.dist[sq][ksq]); // Knight forwardness if (SqBb(sq) & bbAwayZone[sd]) { fwd_weight += 1; fwd_cnt += 1; } // Knight mobility bbMob = BB.KnightAttacks(sq) & ~p->cl_bb[sd]; // knight is tricky, cnt = BB.PopCnt(bbMob &~e->bbPawnTakes[op]); // better to have it mobile than defending stuff Add(e, sd, F_MOB, Param.n_mob_mg[cnt], Param.n_mob_eg[cnt]); // mobility bonus if ((bbMob &~e->bbPawnTakes[op]) & bbKnightChk) att += chk_threat[N]; // check threat bonus e->bbAllAttacks[sd] |= BB.KnightAttacks(sq); e->bbEvAttacks[sd] |= bbMob; // Knight attacks on enemy king zone bbAtt = BB.KnightAttacks(sq); if (bbAtt & bbZone) { wood++; att += king_att[N] * BB.PopCnt(bbAtt & bbZone); } // Knight outpost ScoreOutpost(p, e, sd, N, sq); } // end of knight eval // Bishop bbPieces = p->Bishops(sd); while (bbPieces) { sq = BB.PopFirstBit(&bbPieces); // Bishop tropism to enemy king Add(e, sd, F_TROPISM, tropism_mg[B] * Param.dist[sq][ksq], tropism_eg[B] * Param.dist[sq][ksq]); // Bishop forwardness if (SqBb(sq) & bbAwayZone[sd]) { fwd_weight += 1; fwd_cnt += 1; } // Bishop mobility bbMob = BB.BishAttacks(OccBb(p), sq); if (!(bbMob & bbAwayZone[sd])) // penalty for bishops unable to reach enemy half of the board Add(e, sd, F_MOB, Param.bishConfined); // (idea from Andscacs) cnt = BB.PopCnt(bbMob &~e->bbPawnTakes[op] &~bbExcluded); Add(e, sd, F_MOB, Param.b_mob_mg[cnt], Param.b_mob_eg[cnt]); // mobility bonus if ((bbMob &~e->bbPawnTakes[op]) & ~p->cl_bb[sd] & bbDiagChk) att += chk_threat[B]; // check threat bonus e->bbAllAttacks[sd] |= bbMob; e->bbEvAttacks[sd] |= bbMob; // Bishop attacks on enemy king zone (including attacks through a queen) bbAtt = BB.BishAttacks(OccBb(p) ^ p->Queens(sd) , sq); if (bbAtt & bbZone) { wood++; att += king_att[B] * BB.PopCnt(bbAtt & bbZone); } // Bishop outpost ScoreOutpost(p, e, sd, B, sq); // Bishops side by side if (ShiftNorth(SqBb(sq)) & p->Bishops(sd) ) Add(e, sd, F_OTHERS, 4); if (ShiftEast(SqBb(sq)) & p->Bishops(sd)) Add(e, sd, F_OTHERS, 4); // Pawns on the same square color as our bishop if (bbWhiteSq & SqBb(sq)) { own_pawn_cnt = BB.PopCnt(bbWhiteSq & p->Pawns(sd)) - 4; opp_pawn_cnt = BB.PopCnt(bbWhiteSq & p->Pawns(op)) - 4; } else { own_pawn_cnt = BB.PopCnt(bbBlackSq & p->Pawns(sd)) - 4; opp_pawn_cnt = BB.PopCnt(bbBlackSq & p->Pawns(op)) - 4; } Add(e, sd, F_OTHERS, -3 * own_pawn_cnt - opp_pawn_cnt); } // end of bishop eval // Rook bbPieces = p->Rooks(sd); while (bbPieces) { sq = BB.PopFirstBit(&bbPieces); // Rook tropism to enemy king Add(e, sd, F_TROPISM, tropism_mg[R] * Param.dist[sq][ksq], tropism_eg[R] * Param.dist[sq][ksq]); // Rook forwardness if (SqBb(sq) & bbAwayZone[sd]) { fwd_weight += 2; fwd_cnt += 1; } // Rook mobility bbMob = BB.RookAttacks(OccBb(p), sq); cnt = BB.PopCnt(bbMob &~bbExcluded); Add(e, sd, F_MOB, Param.r_mob_mg[cnt], Param.r_mob_eg[cnt]); // mobility bonus if (((bbMob &~e->bbPawnTakes[op]) & ~p->cl_bb[sd] & bbStr8Chk) // check threat bonus && p->cnt[sd][Q]) { att += chk_threat[R]; bbContact = (bbMob & BB.KingAttacks(ksq)) & bbStr8Chk; while (bbContact) { int contactSq = BB.PopFirstBit(&bbContact); // rook exchanges are accepted as contact checks if (Swap(p, sq, contactSq) >= 0) { att += r_contact_check; break; } } } e->bbAllAttacks[sd] |= bbMob; e->bbEvAttacks[sd] |= bbMob; // Rook attacks on enemy king zone (also through a rook or through a queen) bbAtt = BB.RookAttacks(OccBb(p) ^ p->StraightMovers(sd), sq); if (bbAtt & bbZone) { wood++; att += king_att[R] * BB.PopCnt(bbAtt & bbZone); } // Get rook file bbFile = BB.FillNorthSq(sq) | BB.FillSouthSq(sq); // better this way than using front span // Queen on rook's file (which might be closed) if (bbFile & p->Queens(op)) Add(e, sd, F_LINES, Param.rookOnQueen); // Rook on (half) open file if (!(bbFile & p->Pawns(sd))) { if (!(bbFile & p->Pawns(op))) { Add(e, sd, F_LINES, Param.rookOnOpenMg, Param.rookOnOpenEg); //if (BB.GetFrontSpan(SqBb(sq), sd) & p->Rooks(sd)) Add(e, sd, F_LINES, 4, 2); // equal } else { // score differs depending on whether half-open file is blocked by defended enemy pawn if ((bbFile & p->Pawns(op)) & e->bbPawnTakes[op]) Add(e, sd, F_LINES, Param.rookOnBadHalfOpenMg, Param.rookOnBadHalfOpenEg); else { Add(e, sd, F_LINES, Param.rookOnGoodHalfOpenMg, Param.rookOnGoodHalfOpenEg); } } } // Rook on the 7th rank attacking pawns or cutting off enemy king if (SqBb(sq) & bbRelRank[sd][RANK_7]) { if (p->Pawns(op) & bbRelRank[sd][RANK_7] || p->Kings(op) & bbRelRank[sd][RANK_8]) { Add(e, sd, F_LINES, Param.rookOn7thMg, Param.rookOn7thEg); r_on_7th++; } } } // end of rook eval // Queen bbPieces = p->Queens(sd); while (bbPieces) { sq = BB.PopFirstBit(&bbPieces); // Queen tropism to enemy king Add(e, sd, F_TROPISM, tropism_mg[Q] * Param.dist[sq][ksq], tropism_eg[Q] * Param.dist[sq][ksq]); // Queen forwardness if (SqBb(sq) & bbAwayZone[sd]) { fwd_weight += 4; fwd_cnt += 1; } // Queen mobility bbMob = BB.QueenAttacks(OccBb(p), sq); cnt = BB.PopCnt(bbMob &~bbExcluded); Add(e, sd, F_MOB, Param.q_mob_mg[cnt], Param.q_mob_eg[cnt]); // mobility bonus if ((bbMob &~e->bbPawnTakes[op]) & ~p->cl_bb[sd] & bbQueenChk) { // check threat bonus att += chk_threat[Q]; // Queen contact checks bbContact = bbMob & BB.KingAttacks(ksq); while (bbContact) { int contactSq = BB.PopFirstBit(&bbContact); // queen exchanges are accepted as contact checks if (Swap(p, sq, contactSq) >= 0) { att += q_contact_check; break; } } } e->bbAllAttacks[sd] |= bbMob; // Queen attacks on enemy king zone bbAtt = BB.BishAttacks(OccBb(p) ^ p->DiagMovers(sd), sq); bbAtt |= BB.RookAttacks(OccBb(p) ^ p->StraightMovers(sd), sq); if (bbAtt & bbZone) { wood++; att += king_att[Q] * BB.PopCnt(bbAtt & bbZone); } // Queen on 7th rank if (SqBb(sq) & bbRelRank[sd][RANK_7]) { if (p->Pawns(op) & bbRelRank[sd][RANK_7] || p->Kings(op) & bbRelRank[sd][RANK_8]) { Add(e, sd, F_LINES, Param.queenOn7thMg, Param.queenOn7thEg); } } } // end of queen eval // Score terms using information gathered during piece eval if (r_on_7th > 1) // two rooks on 7th rank Add(e, sd, F_LINES, Param.twoRooksOn7thMg, Param.twoRooksOn7thEg); // forwardness (from Toga II 3.0) Add(e, sd, (fwd_bonus[fwd_cnt] * fwd_weight * Param.forwardness) / 100, 0); // Score king attacks if own queen is present and there are at least 2 attackers if (wood > 1 && p->cnt[sd][Q]) { if (att > 399) att = 399; Add(e, sd, F_ATT, Param.danger[att]); } }
void POS::UndoMove(int move, UNDO *u) { int sd = Opp(side); int op = side; int fsq = Fsq(move); int tsq = Tsq(move); int ftp = Tp(pc[tsq]); // moving piece int ttp = u->ttp; castle_flags = u->castle_flags; ep_sq = u->ep_sq; rev_moves = u->rev_moves; pawn_key = u->pawn_key; hash_key = u->hash_key; head--; pc[fsq] = Pc(sd, ftp); pc[tsq] = NO_PC; cl_bb[sd] ^= SqBb(fsq) | SqBb(tsq); tp_bb[ftp] ^= SqBb(fsq) | SqBb(tsq); #ifndef LEAF_PST mg_pst[sd] += Param.mg_pst_data[sd][ftp][fsq] - Param.mg_pst_data[sd][ftp][tsq]; eg_pst[sd] += Param.eg_pst_data[sd][ftp][fsq] - Param.eg_pst_data[sd][ftp][tsq]; #endif // Update king location if (ftp == K) king_sq[sd] = fsq; // Undo capture if (ttp != NO_TP) { pc[tsq] = Pc(op, ttp); cl_bb[op] ^= SqBb(tsq); tp_bb[ttp] ^= SqBb(tsq); phase += phase_value[ttp]; #ifndef LEAF_PST mg_pst[op] += Param.mg_pst_data[op][ttp][tsq]; eg_pst[op] += Param.eg_pst_data[op][ttp][tsq]; #endif cnt[op][ttp]++; } switch (MoveType(move)) { case NORMAL: break; case CASTLE: // define complementary rook move switch (tsq) { case C1: { fsq = A1; tsq = D1; break; } case G1: { fsq = H1; tsq = F1; break; } case C8: { fsq = A8; tsq = D8; break; } case G8: { fsq = H8; tsq = F8; break; } } pc[tsq] = NO_PC; pc[fsq] = Pc(sd, R); cl_bb[sd] ^= SqBb(fsq) | SqBb(tsq); tp_bb[R] ^= SqBb(fsq) | SqBb(tsq); #ifndef LEAF_PST mg_pst[sd] += Param.mg_pst_data[sd][R][fsq] - Param.mg_pst_data[sd][R][tsq]; eg_pst[sd] += Param.eg_pst_data[sd][R][fsq] - Param.eg_pst_data[sd][R][tsq]; #endif break; case EP_CAP: tsq ^= 8; pc[tsq] = Pc(op, P); cl_bb[op] ^= SqBb(tsq); tp_bb[P] ^= SqBb(tsq); phase += phase_value[P]; #ifndef LEAF_PST mg_pst[op] += Param.mg_pst_data[op][P][tsq]; eg_pst[op] += Param.eg_pst_data[op][P][tsq]; #endif cnt[op][P]++; break; case EP_SET: break; case N_PROM: case B_PROM: case R_PROM: case Q_PROM: pc[fsq] = Pc(sd, P); tp_bb[P] ^= SqBb(fsq); tp_bb[ftp] ^= SqBb(fsq); phase += phase_value[P] - phase_value[ftp]; #ifndef LEAF_PST mg_pst[sd] += Param.mg_pst_data[sd][P][fsq] - Param.mg_pst_data[sd][ftp][fsq]; eg_pst[sd] += Param.eg_pst_data[sd][P][fsq] - Param.eg_pst_data[sd][ftp][fsq]; #endif cnt[sd][P]++; cnt[sd][ftp]--; break; } side ^= 1; }
int GetDrawFactor(POS *p, int sd) { int op = Opp(sd); // Case 1: KPK with edge pawn (else KBPK recognizer would break) if (PcMatNone(p, sd) && PcMatNone(p, op) && p->cnt[sd][P] == 1 // TODO: all pawns of a stronger side on a rim && p->cnt[op][P] == 0) { // TODO: accept pawns for a weaker side if (p->Pawns(sd) & FILE_H_BB && p->Kings(op) & bbKingBlockH[sd]) return 0; if (p->Pawns(sd) & FILE_A_BB && p->Kings(op) & bbKingBlockA[sd]) return 0; } // Case 2: KBPK(P) draws with edge pawn and wrong bishop if (PcMatB(p, sd) && PcMatNone(p, op) && p->cnt[sd][P] == 1) { // TODO: all pawns of a stronger side on a rim if (p->Pawns(sd) & FILE_H_BB && NotOnBishColor(p, sd, REL_SQ(H8, sd)) && p->Kings(op) & bbKingBlockH[sd]) return 0; if (p->Pawns(sd) & FILE_A_BB && NotOnBishColor(p, sd, REL_SQ(A8, sd)) && p->Kings(op) & bbKingBlockA[sd]) return 0; } // Case 3: KBP vs Km // drawn when defending king stands on pawn's path and can't be driven out // by a bishop (must be dealt with before opposite bishops ending) if (PcMatB(p, sd) && PcMat1Minor(p, op) && p->cnt[sd][P] == 1 && p->cnt[op][P] == 0 && (SqBb(p->king_sq[op]) & BB.GetFrontSpan(p->Pawns(sd), sd)) && NotOnBishColor(p, sd, p->king_sq[op]) ) return 0; // Case 4: Bishops of opposite color if (PcMatB(p, sd) && PcMatB(p, op) && DifferentBishops(p)) { // 4a: single bishop cannot win without pawns if (p->cnt[sd][P] == 0) return 0; // 4b: different bishops with a single pawn on the own half of the board if (p->cnt[sd][P] == 1 && p->cnt[op][P] == 0) { if (bbHomeZone[sd] & p->Pawns(sd)) return 0; // TODO: 4c: distant bishop controls a square on pawn's path } // 4d: halve the score for pure BOC endings return 32; } if (p->cnt[sd][P] == 0) { // Case 5: low and almost equal material with no pawns if (p->cnt[op][P] == 0) { if (PcMatRm(p, sd) && PcMatRm(p, op)) return 8; if (PcMatR (p, sd) && PcMatR (p, op)) return 8; if (PcMatQ (p, sd) && PcMatQ (p, op)) return 8; if (PcMat2Minors(p, sd) && PcMatR(p, op)) return 8; } // Case 6: two knights if (PcMatNN(p, sd)) { if (p->cnt[op][P] == 0) return 0; else return 4; } // Case 7: K(m) vs K(m) or Km vs Kp(p) if (p->cnt[sd][Q] + p->cnt[sd][R] == 0 && p->cnt[sd][B] + p->cnt[sd][N] < 2 ) return 0; // Case 8: KR vs Km(p) if (PcMatR(p, sd) && PcMat1Minor(p, op) ) return 16; // Case 9: KRm vs KR(p) if (p->cnt[sd][R] == 1 && p->cnt[sd][Q] == 0 && p->cnt[sd][B] + p->cnt[sd][N] == 1 && PcMatR(p, op) ) return 16; // Case 10: KQm vs KQ(p) if (p->cnt[sd][Q] == 1 && p->cnt[sd][R] == 0 && p->cnt[sd][B] + p->cnt[sd][N] == 1 && PcMatQ(p, op) ) return 32; // Case 11: Kmm vs KB(p) if (PcMat2Minors(p,sd) && PcMatB(p, op) ) return 16; // Case 12: KBN vs Km(p) if (PcMatBN(p, sd) && PcMat1Minor(p, op) ) return 16; // Case 13: KRR vs KRm(p) if (PcMatRR(p, sd) && PcMatRm(p, op)) return 16; // Case 14: KRRm vs KRR(p) if (p->cnt[sd][R] == 2 && p->cnt[sd][Q] == 0 && p->cnt[sd][B] + p->cnt[sd][N] == 1 && PcMatRR(p, op)) return 16; } // Case 15: KRP vs KR if (PcMatR(p, sd) && PcMatR(p, op) && p->cnt[sd][P] == 1 && p->cnt[op][P] == 0) { // 15a: good defensive position with a king on pawn's path increases drawing chances if ((SqBb(p->king_sq[op]) & BB.GetFrontSpan(p->Pawns(sd), sd))) return 32; // 1/2 // 15b: draw code for rook endgame with edge pawn if ((RelSqBb(A7, sd) & p->Pawns(sd)) && ( RelSqBb(A8, sd) & p->Rooks(sd)) && ( FILE_A_BB & p->Rooks(op)) && ((RelSqBb(H7, sd) & p->Kings(op)) || (RelSqBb(G7, sd) & p->Kings(op))) ) return 0; // dead draw if ((RelSqBb(H7, sd) & p->Pawns(sd)) && ( RelSqBb(H8, sd) & p->Rooks(sd)) && ( FILE_H_BB & p->Rooks(op)) && ((RelSqBb(A7, sd) & p->Kings(op)) || (RelSqBb(B7, sd) & p->Kings(op))) ) return 0; // dead draw } return 64; }
void SetPosition(POS *p, char *epd) { int j, pc; static const char pc_char[13] = "PpNnBbRrQqKk"; for (int sd = 0; sd < 2; sd++) { p->cl_bb[sd] = 0ULL; #ifndef LEAF_PST p->mg_pst[sd] = 0; p->eg_pst[sd] = 0; #endif } p->phase = 0; for (int pc = 0; pc < 6; pc++) { p->tp_bb[pc] = 0ULL; p->cnt[WC][pc] = 0; p->cnt[BC][pc] = 0; } p->castle_flags = 0; p->rev_moves = 0; p->head = 0; for (int i = 56; i >= 0; i -= 8) { j = 0; while (j < 8) { if (*epd >= '1' && *epd <= '8') for (pc = 0; pc < *epd - '0'; pc++) { p->pc[i + j] = NO_PC; j++; } else { for (pc = 0; pc_char[pc] != *epd; pc++) ; p->pc[i + j] = pc; p->cl_bb[Cl(pc)] ^= SqBb(i + j); p->tp_bb[Tp(pc)] ^= SqBb(i + j); if (Tp(pc) == K) p->king_sq[Cl(pc)] = i + j; p->phase += phase_value[Tp(pc)]; #ifndef LEAF_PST p->mg_pst[Cl(pc)] += Param.mg_pst_data[Cl(pc)][Tp(pc)][i + j]; p->eg_pst[Cl(pc)] += Param.eg_pst_data[Cl(pc)][Tp(pc)][i + j]; #endif p->cnt[Cl(pc)][Tp(pc)]++; j++; } epd++; } epd++; } // Setting side to move if (*epd++ == 'w') p->side = WC; else p->side = BC; // Setting castling rights epd++; if (*epd == '-') epd++; else { if (*epd == 'K') { p->castle_flags |= 1; epd++; } if (*epd == 'Q') { p->castle_flags |= 2; epd++; } if (*epd == 'k') { p->castle_flags |= 4; epd++; } if (*epd == 'q') { p->castle_flags |= 8; epd++; } } // Setting en passant square (only if capture is possible) epd++; if (*epd == '-') p->ep_sq = NO_SQ; else { p->ep_sq = Sq(*epd - 'a', *(epd + 1) - '1'); if (!(BB.PawnAttacks(Opp(p->side), p->ep_sq) & p->Pawns(p->side))) p->ep_sq = NO_SQ; } // Calculating hash keys p->hash_key = InitHashKey(p); p->pawn_key = InitPawnKey(p); }