```cpp // ai_zz_chess.cpp // 第一届“人工智障”杯——LLM 单文件 C...

创建于:2025年8月17日

使用 GPT-5 Thinking 回答 Chat01

问题

cpp
// ai_zz_chess.cpp // 第一届“人工智障”杯——LLM 单文件 C++ 国际象棋引擎 // UCI 引擎名称:Chtholly-Engine 1.0 // 作者:LLM (ChatGPT) // // 本版更新要点: // - 引擎名改为 Chtholly-Engine 1.0(珂朵莉英文) // - 修复/缓解 TT 导致的“直接用 TT 内容走棋而送子”问题: // * 根节点不再用 TT 命中直接提早返回(仅用于排序与静态评估参考);保证根节点每次迭代都实际搜索至少一手。 // * bestmove 生成更稳妥(验证合法性,stop 回退更合理)。 // * 修复 setoption RandomSeed 读取 bug。 // - 评估增强(开局常见结构/观念): // * 中心兵权重提升(中心4格、中心16格、d/e兵加成) // * 中心连兵(d/e 同排)加成 // * 前哨马(无敌兵可挑战,相邻列向前无敌兵;且有己方兵支持时更高) // * 堡垒象(g2/b2/g7/b7)结构加成,若配合王翼易位、g/h 兵结构进一步加成 // * 轻微开发奖励/未开发惩罚(早期) // - 保留:走法排序随机抖动、2/3 PST 缩放、SEE 升变吃子修复、谨慎 NullMove/LMR/QS、劣势少兑子偏置等 #include <bits/stdc++.h> using namespace std; using U64 = unsigned long long; static inline int popcount(U64 b) { return (int)__builtin_popcountll(b); } static inline int lsb(U64 b) { return __builtin_ctzll(b); } static inline U64 poplsb(U64 &b) { U64 x = b & -b; b -= x; return x; } static inline int msb(U64 b) { return 63 - __builtin_clzll(b); } constexpr int WHITE = 0; constexpr int BLACK = 1; constexpr int PAWN = 0; constexpr int KNIGHT = 1; constexpr int BISHOP = 2; constexpr int ROOK = 3; constexpr int QUEEN = 4; constexpr int KING = 5; constexpr int NO_PIECE = -1; constexpr int MAX_PLY = 128; constexpr int INF = 32000; constexpr int MATE_SCORE = 30000; constexpr int MATE_IN_MAX_PLY = MATE_SCORE - MAX_PLY; constexpr U64 FileA = 0x0101010101010101ULL; constexpr U64 FileH = FileA << 7; constexpr U64 Rank1 = 0xFFULL; constexpr U64 Rank2 = Rank1 << 8; constexpr U64 Rank7 = Rank1 << 48; constexpr U64 Rank8 = Rank1 << 56; static inline int sq(int file, int rank) { return rank * 8 + file; } static inline int file_of(int s) { return s & 7; } static inline int rank_of(int s) { return s >> 3; } static inline int mirror64(int s) { return s ^ 56; } enum Dir { N=8, S=-8, E=1, W=-1, NE=9, NW=7, SE=-7, SW=-9 }; // ----------------------------- Zobrist ----------------------------- struct SplitMix64 { uint64_t x; explicit SplitMix64(uint64_t seed = 0x9e3779b97f4a7c15ULL) : x(seed) {} uint64_t next() { uint64_t z = (x += 0x9e3779b97f4a7c15ULL); z = (z ^ (z >> 30)) * 0xbf58476d1ce4e5b9ULL; z = (z ^ (z >> 27)) * 0x94d049bb133111ebULL; return z ^ (z >> 31); } }; uint64_t ZPiece[12][64]; uint64_t ZCastle[16]; uint64_t ZEP[8]; uint64_t ZSide; static void init_zobrist() { SplitMix64 rng(0xA1B2C3D4E5F60789ULL); for (int p=0;p<12;p++) for (int s=0;s<64;s++) ZPiece[p][s] = rng.next(); for (int i=0;i<16;i++) ZCastle[i] = rng.next(); for (int f=0;f<8;f++) ZEP[f] = rng.next(); ZSide = rng.next(); } // ----------------------------- 攻击/掩码 ----------------------------- U64 KnightAtt[64], KingAtt[64]; U64 PawnAtt[2][64]; // [color][sq] U64 FileMask[8], RankMask[8]; U64 PassedMask[2][64]; U64 AdjacentFiles[8]; // 新增:中心掩码/前进掩码 U64 Center4Mask = 0ULL; // d4,e4,d5,e5 U64 Center16Mask = 0ULL; // c3..f6 U64 ForwardMask[2][64]; // 从该格“向对面方向”的所有格(用于前哨马检测) static bool on_board(int f, int r) { return f>=0 && f<8 && r>=0 && r<8; } static void init_attack_tables() { for (int f=0; f<8; ++f) { U64 m = 0; for (int r=0;r<8;r++) m |= 1ULL << sq(f, r); FileMask[f] = m; AdjacentFiles[f] = (f>0?FileMask[f-1]:0ULL) | (f<7?FileMask[f+1]:0ULL); } for (int r=0;r<8;r++) { U64 m = 0; for (int f=0;f<8;f++) m |= 1ULL << sq(f, r); RankMask[r] = m; } for (int s=0;s<64;s++) { int f = file_of(s), r = rank_of(s); // Knight U64 k = 0; int nf[] = {f-2,f-1,f+1,f+2,f-2,f-1,f+1,f+2}; int nr[] = {r-1,r-2,r-2,r-1,r+1,r+2,r+2,r+1}; for(int i=0;i<8;i++) if(on_board(nf[i], nr[i])) k |= 1ULL<<sq(nf[i], nr[i]); KnightAtt[s] = k; // King U64 g=0; for(int df=-1;df<=1;df++) for(int dr=-1;dr<=1;dr++) if(df||dr){ int nf=f+df, nr=r+dr; if(on_board(nf,nr)) g |= 1ULL<<sq(nf,nr); } KingAtt[s]=g; // Pawns U64 pw=0, pb=0; if (r<7) { if (f>0) pw |= 1ULL<<sq(f-1, r+1); if (f<7) pw |= 1ULL<<sq(f+1, r+1); } if (r>0) { if (f>0) pb |= 1ULL<<sq(f-1, r-1); if (f<7) pb |= 1ULL<<sq(f+1, r-1); } PawnAtt[WHITE][s]=pw; PawnAtt[BLACK][s]=pb; // Passed pawn mask U64 pmW = 0, pmB=0; U64 sameAdj = FileMask[f] | AdjacentFiles[f]; for (int rr=r+1; rr<8; rr++) pmW |= sameAdj & RankMask[rr]; for (int rr=r-1; rr>=0; rr--) pmB |= sameAdj & RankMask[rr]; PassedMask[WHITE][s]=pmW; PassedMask[BLACK][s]=pmB; // Forward masks(用于前哨马判定) U64 fwdW = 0, fwdB = 0; for (int rr=r+1; rr<8; ++rr) fwdW |= RankMask[rr]; for (int rr=r-1; rr>=0; --rr) fwdB |= RankMask[rr]; ForwardMask[WHITE][s] = fwdW; ForwardMask[BLACK][s] = fwdB; } // 中心掩码 Center4Mask |= 1ULL<<sq(3,3); Center4Mask |= 1ULL<<sq(4,3); Center4Mask |= 1ULL<<sq(3,4); Center4Mask |= 1ULL<<sq(4,4); for (int f=2; f<=5; ++f) for (int r=2; r<=5; ++r) Center16Mask |= 1ULL<<sq(f,r); } static inline U64 ray_attacks_dir(int s, int df, int dr, U64 occ) { U64 attacks = 0ULL; int f = file_of(s), r = rank_of(s); int nf = f + df, nr = r + dr; while (on_board(nf, nr)) { int ns = sq(nf, nr); attacks |= 1ULL << ns; if ( (occ >> ns) & 1ULL ) break; nf += df; nr += dr; } return attacks; } static inline U64 rook_attacks(int s, U64 occ) { return ray_attacks_dir(s, +1, 0, occ) | ray_attacks_dir(s, -1, 0, occ) | ray_attacks_dir(s, 0, +1, occ) | ray_attacks_dir(s, 0, -1, occ); } static inline U64 bishop_attacks(int s, U64 occ) { return ray_attacks_dir(s, +1, +1, occ) | ray_attacks_dir(s, -1, +1, occ) | ray_attacks_dir(s, +1, -1, occ) | ray_attacks_dir(s, -1, -1, occ); } static inline U64 queen_attacks(int s, U64 occ) { return rook_attacks(s, occ) | bishop_attacks(s, occ); } // ----------------------------- 随机抖动控制 ----------------------------- struct RandJit { uint64_t x = 0x9e3779b97f4a7c15ULL; void seed(uint64_t s) { x = s ? s : 0x9e3779b97f4a7c15ULL; } uint64_t next() { uint64_t z = (x += 0x9e3779b97f4a7c15ULL); z = (z ^ (z >> 30)) * 0xbf58476d1ce4e5b9ULL; z = (z ^ (z >> 27)) * 0x94d049bb133111ebULL; return z ^ (z >> 31); } } RJ; struct RandomCtrl { int jitter = 0; // 走法排序扰动幅度 [-jitter, +jitter] uint64_t seed = 0; // 0=按时间播种;非0=固定种子 bool seeded = false; } RC; static inline int rand_jitter() { if (RC.jitter <= 0) return 0; if (!RC.seeded) { uint64_t t = (uint64_t)chrono::steady_clock::now().time_since_epoch().count(); RC.seed = (RC.seed ? RC.seed : t); RJ.seed(RC.seed); RC.seeded = true; } uint64_t r = RJ.next(); int amp = RC.jitter; int v = (int)(r % (uint64_t)(2*amp + 1)) - amp; // [-amp, +amp] return v; } // ----------------------------- 棋盘与状态 ----------------------------- struct State { uint64_t key; int castling; // 4 bits: WK=1,WQ=2,BK=4,BQ=8 int ep; // -1 无;否则 0..63 int halfmove; int captured; // 捕获的棋子代码(0..11)或 -1 uint32_t move; // 最近一步 int fullmove; // 保存全回合计数(用于评估) }; struct Board { U64 bb[12]; U64 occ[2]; U64 occAll; int side; // to move int castling; // KQkq bits: 1,2,4,8 int ep; // en passant sq or -1 int halfmove; // 50-move int fullmove; // FEN 全回合计数(黑走完一步+1) int board[64]; // -1 无子;否则 0..11 int kingSq[2]; uint64_t key; State st[MAX_PLY+2048]; int sp; Board() { reset(); } void reset() { memset(bb, 0, sizeof(bb)); occ[0]=occ[1]=occAll=0; side=WHITE; castling=0; ep=-1; halfmove=0; fullmove=1; for (int i=0;i<64;i++) board[i]=NO_PIECE; kingSq[0]=kingSq[1]=-1; key=0; sp=0; } static int code(int color, int pt) { return color*6 + pt; } inline void put_piece(int code, int s) { bb[code] |= 1ULL<<s; occ[code/6] |= 1ULL<<s; occAll |= 1ULL<<s; board[s] = code; if ((code%6)==KING) kingSq[code/6] = s; key ^= ZPiece[code][s]; } inline void remove_piece(int s) { int code = board[s]; if (code == NO_PIECE) return; bb[code] &= ~(1ULL<<s); occ[code/6] &= ~(1ULL<<s); occAll &= ~(1ULL<<s); board[s] = NO_PIECE; if ((code%6)==KING) kingSq[code/6] = -1; key ^= ZPiece[code][s]; } inline void move_piece(int from, int to) { int code = board[from]; bb[code] ^= (1ULL<<from) | (1ULL<<to); occ[code/6] ^= (1ULL<<from) | (1ULL<<to); occAll ^= (1ULL<<from) | (1ULL<<to); board[from] = NO_PIECE; board[to] = code; key ^= ZPiece[code][from]; key ^= ZPiece[code][to]; if ((code%6)==KING) kingSq[code/6] = to; } void set_startpos() { reset(); string fen = "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1"; set_fen(fen); } bool set_fen(const string& fen) { reset(); istringstream ss(fen); string boardPart, stm, castle, epstr; int hm=0, fm=1; if (!(ss >> boardPart >> stm >> castle >> epstr)) return false; if (!(ss >> hm)) hm = 0; if (!(ss >> fm)) fm = 1; int sqi = 56; for (char c : boardPart) { if (c == '/') { sqi -= 16; continue; } if (isdigit((unsigned char)c)) { sqi += c - '0'; continue; } int color = isupper((unsigned char)c)? WHITE:BLACK; char pc = (char)tolower((unsigned char)c); int pt = -1; if (pc=='p') pt=PAWN; else if (pc=='n') pt=KNIGHT; else if (pc=='b') pt=BISHOP; else if (pc=='r') pt=ROOK; else if (pc=='q') pt=QUEEN; else if (pc=='k') pt=KING; if (pt==-1) return false; put_piece(code(color, pt), sqi++); } side = (stm=="w")?WHITE:BLACK; key ^= (side==WHITE?0:ZSide); castling = 0; if (castle.find('K')!=string::npos) castling |= 1; if (castle.find('Q')!=string::npos) castling |= 2; if (castle.find('k')!=string::npos) castling |= 4; if (castle.find('q')!=string::npos) castling |= 8; key ^= ZCastle[castling]; if (epstr != "-" && epstr.size()==2) { int f = epstr[0]-'a'; int r = epstr[1]-'1'; int e = sq(f,r); ep = e; if (r==2 || r==5) { key ^= ZEP[f]; } } else ep = -1; halfmove = hm; fullmove = fm; return true; } inline bool square_attacked(int s, int bySide) const { if ( (PawnAtt[bySide^1][s] & bb[code(bySide, PAWN)]) ) return true; if ( KnightAtt[s] & bb[code(bySide, KNIGHT)] ) return true; if ( KingAtt[s] & bb[code(bySide, KING)] ) return true; U64 occNow = occAll; if ( bishop_attacks(s, occNow) & (bb[code(bySide,BISHOP)] | bb[code(bySide,QUEEN)]) ) return true; if ( rook_attacks(s, occNow) & (bb[code(bySide,ROOK)] | bb[code(bySide,QUEEN)]) ) return true; return false; } uint64_t compute_key() const { uint64_t k=0; for(int p=0;p<12;p++){ U64 bbb=bb[p]; while(bbb){ int s=lsb(bbb); bbb&=bbb-1; k^=ZPiece[p][s]; } } k ^= ZCastle[castling]; if (side==BLACK) k ^= ZSide; if (ep!=-1) k ^= ZEP[file_of(ep)]; return k; } }; // ----------------------------- Castling 权利更新表 ----------------------------- int CastleMaskFromTo[64]; int CastleMaskOnCapture[64]; static void init_castle_masks() { for(int i=0;i<64;i++){ CastleMaskFromTo[i]=15; CastleMaskOnCapture[i]=15; } // 白 CastleMaskFromTo[sq(4,0)] = 15 & ~1 & ~2; CastleMaskFromTo[sq(7,0)] = 15 & ~1; CastleMaskFromTo[sq(0,0)] = 15 & ~2; CastleMaskOnCapture[sq(7,0)] = 15 & ~1; CastleMaskOnCapture[sq(0,0)] = 15 & ~2; // 黑 CastleMaskFromTo[sq(4,7)] = 15 & ~4 & ~8; CastleMaskFromTo[sq(7,7)] = 15 & ~4; CastleMaskFromTo[sq(0,7)] = 15 & ~8; CastleMaskOnCapture[sq(7,7)] = 15 & ~4; CastleMaskOnCapture[sq(0,7)] = 15 & ~8; } // ----------------------------- 着法编码 ----------------------------- static inline uint32_t enc_move(int from, int to, int piece, int captured, int promoType, bool ep, bool castle, bool dpp) { return (from) | (to<<6) | (piece<<12) | ((captured==-1?15:captured)<<16) | ((promoType==-1?15:promoType)<<20) | (ep?1u<<24:0) | (castle?1u<<25:0) | (dpp?1u<<26:0); } static inline int m_from(uint32_t m){ return m & 63; } static inline int m_to(uint32_t m){ return (m>>6) & 63; } static inline int m_piece(uint32_t m){ return (m>>12) & 15; } static inline int m_captured(uint32_t m){ int x=(m>>16)&15; return x==15?-1:x; } static inline int m_promo(uint32_t m){ int x=(m>>20)&15; return x==15?-1:x; } static inline bool m_ep(uint32_t m){ return (m>>24)&1; } static inline bool m_castle(uint32_t m){ return (m>>25)&1; } static inline bool m_dpp(uint32_t m){ return (m>>26)&1; } struct MoveEntry { uint32_t m; int score; }; struct MoveList { MoveEntry mv[256]; int size=0; void clear(){ size=0; } void add(uint32_t m, int score=0){ mv[size++]={m,score}; } }; // ----------------------------- TT/历史/Killer ----------------------------- int SEE_Value[12] = { 100, 320, 330, 500, 900, 20000, 100, 320, 330, 500, 900, 20000 }; struct TTEntry { uint64_t key; uint32_t move; int16_t score; int16_t eval; uint8_t depth; uint8_t flag; // 0 exact, 1 lower, 2 upper uint8_t age; uint8_t pad; }; struct TransTable { vector<TTEntry> t; uint64_t mask=0; uint8_t age=0; size_t sizeEntries=0; void resizeMB(size_t MB) { if (MB < 1) MB = 1; size_t bytes = MB * 1024ULL * 1024ULL; size_t n = bytes / sizeof(TTEntry); size_t pow2=1; while(pow2 < n) pow2 <<= 1; n = pow2; t.assign(n, TTEntry{0,0,0,0,0,0,0,0}); mask = n-1; sizeEntries = n; age=0; } void clear() { for (auto &e : t) e = TTEntry{0,0,0,0,0,0,0,0}; age=0; } inline TTEntry* probe(uint64_t key, bool &found) { TTEntry* e = &t[key & mask]; found = (e->key == key); return e; } } TT; int History[2][64][64]; uint32_t Killers1[MAX_PLY], Killers2[MAX_PLY]; // ----------------------------- 统计/时间 ----------------------------- struct SearchStats { uint64_t nodes=0; uint64_t qnodes=0; chrono::steady_clock::time_point start; int seldepth=0; void reset(){ nodes=0; qnodes=0; seldepth=0; start=chrono::steady_clock::now(); } uint64_t elapsed_ms() const { return (uint64_t)chrono::duration_cast<chrono::milliseconds>(chrono::steady_clock::now()-start).count(); } uint64_t nps() const { auto ms = elapsed_ms(); if (ms==0) ms=1; return (nodes*1000ULL)/ms; } } Stats; struct Limits { int depth = INT_MAX; uint64_t nodes = ULLONG_MAX; int movetime = -1; int wtime = -1, btime = -1, winc = 0, binc = 0, movestogo = 30; bool ponder = false; }; static int game_phase(const Board& b) { const int phaseW[6] = {0,1,1,2,4,0}; int phase = 0; for (int color=0;color<2;color++) { for (int pt=PAWN; pt<=KING; ++pt) { int code = Board::code(color, pt); U64 bbp = b.bb[code]; while (bbp) { poplsb(bbp); phase += phaseW[pt]; } } } if (phase > 24) phase = 24; return phase; } struct TimeMgr { uint64_t softStopTimeMs = ULLONG_MAX; // 建议停止 uint64_t stopTimeMs = ULLONG_MAX; // 硬停止 int timeForMoveMs = 1000; void setup(const Board& b, const Limits& lim) { using namespace chrono; auto nowms = (uint64_t)duration_cast<milliseconds>(chrono::steady_clock::now().time_since_epoch()).count(); if (lim.movetime > 0) { timeForMoveMs = lim.movetime; softStopTimeMs = nowms + (int)(timeForMoveMs * 0.93); stopTimeMs = nowms + timeForMoveMs; return; } int myTime = (b.side == WHITE ? lim.wtime : lim.btime); int myInc = (b.side == WHITE ? lim.winc : lim.binc); if (myTime < 0) myTime = 1000; if (myInc < 0) myInc = 0; double mgf = game_phase(b) / 24.0; double targetOpenMs = 4500.0; double targetMidMs = 12000.0; double targetEndMs = 6000.0; double targetMs = 0.0; if (mgf >= 0.66) { targetMs = targetOpenMs; } else if (mgf >= 0.33) { double t = (mgf - 0.33) / (0.66 - 0.33); targetMs = targetMidMs * (1.0 - t) + targetOpenMs * t; } else { double t = (mgf - 0.00) / (0.33 - 0.00); targetMs = targetEndMs * (1.0 - t) + targetMidMs * t; } bool inCheck = b.square_attacked(b.kingSq[b.side], b.side^1); if (inCheck) targetMs *= 1.3; double capFrac = (mgf >= 0.66 ? 0.12 : (mgf >= 0.33 ? 0.16 : 0.12)); double capByBank = myTime * capFrac; targetMs = min(targetMs, capByBank); if (myInc > 0) { targetMs = min(targetMs + myInc * 0.35, capByBank); } if (mgf <= 0.33 && myTime < myInc * 10) { targetMs = min<double>(targetMs, max(80.0, myInc * 0.90)); } targetMs = max(60.0, targetMs); double hardCap = (mgf >= 0.66 ? 8000.0 : (mgf >= 0.33 ? 20000.0 : 12000.0)); targetMs = min(targetMs, hardCap); timeForMoveMs = (int)targetMs; softStopTimeMs = nowms + (uint64_t)(timeForMoveMs * 0.93); stopTimeMs = nowms + (uint64_t) timeForMoveMs; } bool time_up() const { using namespace chrono; auto nowms = (uint64_t)duration_cast<milliseconds>(chrono::steady_clock::now().time_since_epoch()).count(); return nowms >= stopTimeMs; } bool soft_time_up() const { using namespace chrono; auto nowms = (uint64_t)duration_cast<milliseconds>(steady_clock::now().time_since_epoch()).count(); return nowms >= softStopTimeMs; } } TM; // ----------------------------- 重复/不足子力 ----------------------------- static bool is_threefold(const Board& b) { int limit = min(b.sp, b.halfmove); int reps = 1; for (int i = 1; i <= limit; ++i) { if (b.st[b.sp - i].key == b.key) { reps++; if (reps >= 3) return true; } } return false; } static bool insufficient_material(const Board& b) { U64 wp = b.bb[Board::code(WHITE, PAWN)] | b.bb[Board::code(BLACK, PAWN)]; U64 wrq = b.bb[Board::code(WHITE, ROOK)] | b.bb[Board::code(WHITE, QUEEN)] | b.bb[Board::code(BLACK, ROOK)] | b.bb[Board::code(BLACK, QUEEN)]; if (wp || wrq) return false; int wn = popcount(b.bb[Board::code(WHITE, KNIGHT)]); int wb = popcount(b.bb[Board::code(WHITE, BISHOP)]); int bn = popcount(b.bb[Board::code(BLACK, KNIGHT)]); int bbp= popcount(b.bb[Board::code(BLACK, BISHOP)]); int minor = wn+wb+bn+bbp; if (minor==0) return true; if (minor==1) return true; if (minor==2 && wn==1 && bn==1 && wb==0 && bbp==0) return true; if (minor==2 && wb==1 && bbp==1 && wn==0 && bn==0) return true; return false; } // ----------------------------- 走子/悔棋 ----------------------------- static bool make_move(Board& b, uint32_t m) { int from = m_from(m), to = m_to(m); int pc = m_piece(m); int side = b.side; int pt = pc % 6; State &st = b.st[b.sp++]; st.key = b.key; st.castling = b.castling; st.ep = b.ep; st.halfmove = b.halfmove; st.captured = -1; st.move = m; st.fullmove = b.fullmove; if (b.ep != -1) b.key ^= ZEP[file_of(b.ep)]; b.ep = -1; if (pt==PAWN || m_captured(m)!=-1) b.halfmove = 0; else b.halfmove++; if (m_ep(m)) { int capSq = to + (side==WHITE ? -8 : 8); st.captured = Board::code(side^1, PAWN); b.remove_piece(capSq); } else if (m_captured(m) != -1) { st.captured = m_captured(m); b.remove_piece(to); b.castling &= CastleMaskOnCapture[to]; } b.move_piece(from, to); if (m_promo(m) != -1) { int promoPt = m_promo(m); int promoCode = Board::code(side, promoPt); b.remove_piece(to); b.put_piece(promoCode, to); } if (m_castle(m)) { if (side==WHITE) { if (to == sq(6,0)) b.move_piece(sq(7,0), sq(5,0)); else if (to == sq(2,0)) b.move_piece(sq(0,0), sq(3,0)); } else { if (to == sq(6,7)) b.move_piece(sq(7,7), sq(5,7)); else if (to == sq(2,7)) b.move_piece(sq(0,7), sq(3,7)); } } if (m_dpp(m)) { int epSq = to + (side==WHITE ? -8 : 8); b.ep = epSq; b.key ^= ZEP[file_of(b.ep)]; } b.castling &= CastleMaskFromTo[from]; b.castling &= CastleMaskFromTo[to]; b.key ^= ZCastle[st.castling]; b.key ^= ZCastle[b.castling]; b.side ^= 1; b.key ^= ZSide; if (side == BLACK) b.fullmove++; int us = side; int ksq = b.kingSq[us]; if (b.square_attacked(ksq, us^1)) { b.side ^= 1; b.key ^= ZSide; b.key ^= ZCastle[b.castling]; b.castling = st.castling; b.key ^= ZCastle[b.castling]; if (b.ep!=-1) b.key ^= ZEP[file_of(b.ep)]; b.ep = st.ep; if (b.ep!=-1) b.key ^= ZEP[file_of(b.ep)]; b.halfmove = st.halfmove; b.fullmove = st.fullmove; if (m_castle(m)) { if (us==WHITE) { if (to == sq(6,0)) b.move_piece(sq(5,0), sq(7,0)); else if (to == sq(2,0)) b.move_piece(sq(3,0), sq(0,0)); } else { if (to == sq(6,7)) b.move_piece(sq(5,7), sq(7,7)); else if (to == sq(2,7)) b.move_piece(sq(3,7), sq(0,7)); } } if (m_promo(m) != -1) { b.remove_piece(to); b.put_piece(Board::code(us, PAWN), to); } b.move_piece(to, from); if (m_ep(m)) { int capSq = to + (us==WHITE ? -8 : 8); b.put_piece(Board::code(us^1, PAWN), capSq); } else if (st.captured != -1) { b.put_piece(st.captured, to); } b.sp--; return false; } return true; } static void unmake_move(Board& b) { State &st = b.st[--b.sp]; uint32_t m = st.move; int from = m_from(m), to = m_to(m); int us = b.side ^ 1; b.side ^= 1; b.key ^= ZSide; b.fullmove = st.fullmove; b.key ^= ZCastle[b.castling]; b.castling = st.castling; b.key ^= ZCastle[b.castling]; if (b.ep != -1) b.key ^= ZEP[file_of(b.ep)]; b.ep = st.ep; if (b.ep != -1) b.key ^= ZEP[file_of(b.ep)]; b.halfmove = st.halfmove; if (m_castle(m)) { if (us==WHITE) { if (to == sq(6,0)) b.move_piece(sq(5,0), sq(7,0)); else if (to == sq(2,0)) b.move_piece(sq(3,0), sq(0,0)); } else { if (to == sq(6,7)) b.move_piece(sq(5,7), sq(7,7)); else if (to == sq(2,7)) b.move_piece(sq(3,7), sq(0,7)); } } if (m_promo(m) != -1) { b.remove_piece(to); b.put_piece(Board::code(us, PAWN), to); } b.move_piece(to, from); if (m_ep(m)) { int capSq = to + (us==WHITE ? -8 : 8); b.put_piece(Board::code(us^1, PAWN), capSq); } else if (st.captured != -1) { b.put_piece(st.captured, to); } } // ----------------------------- 着法生成 ----------------------------- static void gen_moves(const Board& b, MoveList& list, bool capsOnly=false) { list.clear(); int us = b.side, them = us^1; U64 occUs = b.occ[us], occThem = b.occ[them], occAll = b.occAll; // Pawns(逐个 from) U64 pawns = b.bb[Board::code(us, PAWN)]; if (us == WHITE) { U64 p = pawns; while (p) { int s = lsb(p); p &= p-1; int r = rank_of(s), f = file_of(s); if (f>0) { int to = s + 7; if (to<64 && ((occThem >> to)&1ULL)) { int captured = b.board[to]; if (r==6) { int promoPts[4]={QUEEN,ROOK,BISHOP,KNIGHT}; for (int i=0;i<4;i++) list.add(enc_move(s,to,Board::code(us,PAWN), captured, promoPts[i], false, false, false), 200000 + SEE_Value[captured] + 50*i); } else { list.add(enc_move(s,to,Board::code(us,PAWN), captured, -1, false, false, false), 100000 + SEE_Value[captured] - 10); } } if (to==b.ep) { list.add(enc_move(s,to,Board::code(us,PAWN), Board::code(them,PAWN), -1, true, false, false), 99950); } } if (f<7) { int to = s + 9; if (to<64 && ((occThem >> to)&1ULL)) { int captured = b.board[to]; if (r==6) { int promoPts[4]={QUEEN,ROOK,BISHOP,KNIGHT}; for (int i=0;i<4;i++) list.add(enc_move(s,to,Board::code(us,PAWN), captured, promoPts[i], false, false, false), 200000 + SEE_Value[captured] + 50*i); } else { list.add(enc_move(s,to,Board::code(us,PAWN), captured, -1, false, false, false), 100000 + SEE_Value[captured] - 10); } } if (to==b.ep) { list.add(enc_move(s,to,Board::code(us,PAWN), Board::code(them,PAWN), -1, true, false, false), 99950); } } if (capsOnly) continue; int to1 = s + 8; if (to1<64 && ((occAll >> to1)&1ULL)==0) { if (r==6) { int promoPts[4]={QUEEN,ROOK,BISHOP,KNIGHT}; for (int i=0;i<4;i++) list.add(enc_move(s,to1,Board::code(us,PAWN), -1, promoPts[i], false, false, false), 150000 + 50*i); } else { list.add(enc_move(s,to1,Board::code(us,PAWN), -1, -1, false, false, false), 10); if (r==1) { int to2 = s + 16; if (((occAll >> to2)&1ULL)==0) { list.add(enc_move(s,to2,Board::code(us,PAWN), -1, -1, false, false, true), 9); } } } } } } else { U64 p = pawns; while (p) { int s = lsb(p); p &= p-1; int r = rank_of(s), f = file_of(s); if (f>0) { int to = s - 9; if (to>=0 && ((occThem >> to)&1ULL)) { int captured = b.board[to]; if (r==1) { int promoPts[4]={QUEEN,ROOK,BISHOP,KNIGHT}; for (int i=0;i<4;i++) list.add(enc_move(s,to,Board::code(us,PAWN), captured, promoPts[i], false, false, false), 200000 + SEE_Value[captured] + 50*i); } else { list.add(enc_move(s,to,Board::code(us,PAWN), captured, -1, false, false, false), 100000 + SEE_Value[captured] - 10); } } if (to==b.ep) { list.add(enc_move(s,to,Board::code(us,PAWN), Board::code(them,PAWN), -1, true, false, false), 99950); } } if (f<7) { int to = s - 7; if (to>=0 && ((occThem >> to)&1ULL)) { int captured = b.board[to]; if (r==1) { int promoPts[4]={QUEEN,ROOK,BISHOP,KNIGHT}; for (int i=0;i<4;i++) list.add(enc_move(s,to,Board::code(us,PAWN), captured, promoPts[i], false, false, false), 200000 + SEE_Value[captured] + 50*i); } else { list.add(enc_move(s,to,Board::code(us,PAWN), captured, -1, false, false, false), 100000 + SEE_Value[captured] - 10); } } if (to==b.ep) { list.add(enc_move(s,to,Board::code(us,PAWN), Board::code(them,PAWN), -1, true, false, false), 99950); } } if (capsOnly) continue; int to1 = s - 8; if (to1>=0 && ((occAll >> to1)&1ULL)==0) { if (r==1) { int promoPts[4]={QUEEN,ROOK,BISHOP,KNIGHT}; for (int i=0;i<4;i++) list.add(enc_move(s,to1,Board::code(us,PAWN), -1, promoPts[i], false, false, false), 150000 + 50*i); } else { list.add(enc_move(s,to1,Board::code(us,PAWN), -1, -1, false, false, false), 10); if (r==6) { int to2 = s - 16; if (((occAll >> to2)&1ULL)==0) { list.add(enc_move(s,to2,Board::code(us,PAWN), -1, -1, false, false, true), 9); } } } } } } // Knights { U64 bbk = b.bb[Board::code(us, KNIGHT)]; while (bbk) { int s = lsb(bbk); bbk &= bbk-1; U64 att = KnightAtt[s] & ~occUs; U64 x = capsOnly ? (att & occThem) : att; while (x) { int to = lsb(x); x &= x-1; int captured = b.board[to]; bool cap = captured!=-1; list.add(enc_move(s,to,Board::code(us,KNIGHT), captured, -1, false, false, false), cap ? 90000 + SEE_Value[captured] - 5 : 20); } } } // Bishops { U64 bbb = b.bb[Board::code(us, BISHOP)]; while (bbb) { int s = lsb(bbb); bbb &= bbb-1; U64 att = bishop_attacks(s, b.occAll) & ~occUs; U64 x = capsOnly ? (att & occThem) : att; while (x) { int to = lsb(x); x &= x-1; int captured = b.board[to]; bool cap = captured!=-1; list.add(enc_move(s,to,Board::code(us,BISHOP), captured, -1, false, false, false), cap ? 91000 + SEE_Value[captured] : 19); } } } // Rooks { U64 bbr = b.bb[Board::code(us, ROOK)]; while (bbr) { int s = lsb(bbr); bbr &= bbr-1; U64 att = rook_attacks(s, b.occAll) & ~occUs; U64 x = capsOnly ? (att & occThem) : att; while (x) { int to = lsb(x); x &= x-1; int captured = b.board[to]; bool cap = captured!=-1; list.add(enc_move(s,to,Board::code(us,ROOK), captured, -1, false, false, false), cap ? 92000 + SEE_Value[captured] : 18); } } } // Queens { U64 bbq = b.bb[Board::code(us, QUEEN)]; while (bbq) { int s = lsb(bbq); bbq &= bbq-1; U64 att = queen_attacks(s, b.occAll) & ~occUs; U64 x = capsOnly ? (att & occThem) : att; while (x) { int to = lsb(x); x &= x-1; int captured = b.board[to]; bool cap = captured!=-1; list.add(enc_move(s,to,Board::code(us,QUEEN), captured, -1, false, false, false), cap ? 93000 + SEE_Value[captured] : 17); } } } // King { int s = b.kingSq[us]; U64 att = KingAtt[s] & ~occUs; U64 x = capsOnly ? (att & occThem) : att; while (x) { int to = lsb(x); x &= x-1; int captured = b.board[to]; bool cap = captured!=-1; list.add(enc_move(s,to,Board::code(us,KING), captured, -1, false, false, false), cap ? 94000 + SEE_Value[captured] : 16); } if (!capsOnly) { int them = us^1; if (us==WHITE && s==sq(4,0)) { bool inCheck = b.square_attacked(s, them); if ((b.castling & 1) && !inCheck && b.board[sq(5,0)]==NO_PIECE && b.board[sq(6,0)]==NO_PIECE && !b.square_attacked(sq(5,0), them) && !b.square_attacked(sq(6,0), them)) { list.add(enc_move(s, sq(6,0), Board::code(us,KING), -1, -1, false, true, false), 5); } if ((b.castling & 2) && !inCheck && b.board[sq(3,0)]==NO_PIECE && b.board[sq(2,0)]==NO_PIECE && b.board[sq(1,0)]==NO_PIECE && !b.square_attacked(sq(3,0), them) && !b.square_attacked(sq(2,0), them)) { list.add(enc_move(s, sq(2,0), Board::code(us,KING), -1, -1, false, true, false), 5); } } else if (us==BLACK && s==sq(4,7)) { bool inCheck = b.square_attacked(s, them); if ((b.castling & 4) && !inCheck && b.board[sq(5,7)]==NO_PIECE && b.board[sq(6,7)]==NO_PIECE && !b.square_attacked(sq(5,7), them) && !b.square_attacked(sq(6,7), them)) { list.add(enc_move(s, sq(6,7), Board::code(us,KING), -1, -1, false, true, false), 5); } if ((b.castling & 8) && !inCheck && b.board[sq(3,7)]==NO_PIECE && b.board[sq(2,7)]==NO_PIECE && b.board[sq(1,7)]==NO_PIECE && !b.square_attacked(sq(3,7), them) && !b.square_attacked(sq(2,7), them)) { list.add(enc_move(s, sq(2,7), Board::code(us,KING), -1, -1, false, true, false), 5); } } } } } // 静态搜索用:吃子 + 非吃子升变 static void gen_qmoves(const Board& b, MoveList& list) { gen_moves(b, list, true); int us = b.side; if (us == WHITE) { U64 pawns = b.bb[Board::code(WHITE, PAWN)]; U64 single = ((pawns << 8) & ~b.occAll) & Rank8; U64 pp = single; while (pp) { int to = lsb(pp); pp &= pp-1; int from = to - 8; int promoPts[4]={QUEEN,ROOK,BISHOP,KNIGHT}; for (int i=0;i<4;i++) { list.add(enc_move(from,to,Board::code(us,PAWN), -1, promoPts[i], false, false, false), 160000 + 50*i); } } } else { U64 pawns = b.bb[Board::code(BLACK, PAWN)]; U64 single = ((pawns >> 8) & ~b.occAll) & Rank1; U64 pp = single; while (pp) { int to = lsb(pp); pp &= pp-1; int from = to + 8; int promoPts[4]={QUEEN,ROOK,BISHOP,KNIGHT}; for (int i=0;i<4;i++) { list.add(enc_move(from,to,Board::code(us,PAWN), -1, promoPts[i], false, false, false), 160000 + 50*i); } } } } // ----------------------------- SEE / 攻击者 ----------------------------- static inline U64 attackers_to_sq(const U64 bb[12], int color, int s, U64 occ) { U64 att = 0ULL; att |= PawnAtt[color^1][s] & bb[Board::code(color, PAWN)]; att |= KnightAtt[s] & bb[Board::code(color, KNIGHT)]; att |= KingAtt[s] & bb[Board::code(color, KING)]; att |= bishop_attacks(s, occ) & (bb[Board::code(color, BISHOP)] | bb[Board::code(color, QUEEN)]); att |= rook_attacks(s, occ) & (bb[Board::code(color, ROOK)] | bb[Board::code(color, QUEEN)]); return att; } // 修复:正确处理“升变吃子”在交换序列中的子力类型 static int see_move(const Board& b, uint32_t m) { int from = m_from(m), to = m_to(m); int mover = m_piece(m); int us = mover/6, them = us^1; int captured = m_captured(m); int promo = m_promo(m); if (captured == -1 && !m_ep(m)) return 0; U64 workBB[12]; memcpy(workBB, b.bb, sizeof(workBB)); U64 occ = b.occAll; U64 occSide[2] = { b.occ[WHITE], b.occ[BLACK] }; if (m_ep(m)) { int capSq = to + (us==WHITE ? -8 : 8); workBB[Board::code(them, PAWN)] &= ~(1ULL<<capSq); occ &= ~(1ULL<<capSq); occSide[them] &= ~(1ULL<<capSq); captured = Board::code(them, PAWN); } else if (captured != -1) { workBB[captured] &= ~(1ULL<<to); occ &= ~(1ULL<<to); occSide[captured/6] &= ~(1ULL<<to); } workBB[mover] &= ~(1ULL<<from); occ &= ~(1ULL<<from); occSide[us] &= ~(1ULL<<from); int curPieceCode; if (promo != -1) { int promoCode = Board::code(us, promo); workBB[promoCode] |= 1ULL<<to; curPieceCode = promoCode; } else { workBB[mover] |= 1ULL<<to; curPieceCode = mover; } occ |= 1ULL<<to; occSide[us] |= 1ULL<<to; int curPieceType = curPieceCode % 6; int gain[32]; int d=0; gain[0] = SEE_Value[captured==-1?Board::code(them,PAWN):captured]; int side = them; while (true) { U64 att = attackers_to_sq(workBB, side, to, occ); if (!att) break; int codes[6] = {PAWN, KNIGHT, BISHOP, ROOK, QUEEN, KING}; int atkCode = -1, atkSq=-1, attackerType=-1; for (int i=0;i<6;i++) { int code = Board::code(side, codes[i]); U64 a = att & workBB[code]; if (a) { attackerType=codes[i]; atkCode=code; atkSq=lsb(a); break; } } if (atkCode == -1) break; ++d; gain[d] = SEE_Value[curPieceType] - gain[d-1]; workBB[curPieceCode] &= ~(1ULL<<to); occ &= ~(1ULL<<to); occSide[curPieceCode/6] &= ~(1ULL<<to); workBB[atkCode] &= ~(1ULL<<atkSq); occ &= ~(1ULL<<atkSq); occSide[side] &= ~(1ULL<<atkSq); workBB[atkCode] |= 1ULL<<to; occ |= 1ULL<<to; occSide[side] |= 1ULL<<to; curPieceCode = atkCode; curPieceType = attackerType; side ^= 1; } while (d) { gain[d-1] = -max(-gain[d-1], gain[d]); --d; } return gain[0]; } static inline bool see_ge(const Board& b, uint32_t m, int threshold) { return see_move(b, m) >= threshold; } // ----------------------------- 评估(2/3 缩放 + 机动性扣分 + 新增结构项) ----------------------------- const int mg_value[6] = {82, 337, 365, 477, 1025, 0}; const int eg_value[6] = {94, 281, 297, 512, 936, 0}; const int mg_pawn_table[64] = { 0, 0, 0, 0, 0, 0, 0, 0, 98, 134, 61, 95, 68, 126, 34, -11, -6, 7, 26, 31, 65, 56, 25, -20, -14, 13, 26, 21, 23, 12, 7, -23, -27, -2, 5, 12, 17, -4, -10, -25, -26, -4, -4, -10, 3, 3, 33, -12, -35, -1, -20, -23, -15, 24, 38, -22, 0, 0, 0, 0, 0, 0, 0, 0 }; const int eg_pawn_table[64] = { 0, 0, 0, 0, 0, 0, 0, 0, 178, 173, 158, 134, 147, 132, 165, 187, 94, 100, 85, 67, 56, 53, 82, 84, 32, 24, 23, 5, 8, 14, 17, 17, 13, 9, 3, 3, 3, -8, 3, -1, 4, 7, -6, 1, 0, -5, -1, -8, 13, 8, 8, 10, 13, 0, 2, -7, 0, 0, 0, 0, 0, 0, 0, 0 }; const int mg_knight_table[64] = { -167,-89,-34,-49,61,-97,-15,-107, -73,-41,72,36,23,62,7,-17, -47,60,37,65,84,129,73,44, -9,17,19,53,37,69,18,22, -13,4,16,13,28,19,21,-8, -23,-9,12,10,19,17,25,-16, -29,-53,-12,-3,-1,18,-14,-19, -105,-11,-58,-33,-17,-28,-9,-23 }; const int eg_knight_table[64] = { -58,-38,-13,-28,-31,-27,-63,-99, -25,-8,-25,-2,-9,-25,-24,-52, -24,-20,10,9,-1,-9,-19,-41, -17,3,22,22,22,11,8,-18, -18,-6,16,25,16,17,4,-18, -23,-3,-1,15,10,-3,-20,-22, -42,-20,-10,-5,-2,-20,-23,-44, -29,-51,-23,-15,-22,-18,-50,-64 }; const int mg_bishop_table[64] = { -29,4,-82,-37,-25,-42,7,-8, -26,16,-18,-13,30,59,18,-47, -16,37,43,40,35,50,37,-2, -4,5,19,50,37,37,7,-2, -6,13,13,26,34,12,10,4, 0,15,15,15,14,27,18,10, 4,15,16,0,7,21,33,1, -33,-3,-14,-21,-13,-12,-39,-21 }; const int eg_bishop_table[64] = { -14,-21,-11,-8,-7,-9,-17,-24, -8,-4,7,-12,-3,-13,-4,-14, 2,-8,0,-1,-2,6,0,4, -3,9,12,9,14,10,3,2, -6,3,13,19,7,10,-3,-9, -12,-3,8,10,13,3,-7,-15, -14,-18,-7,-1,4,-9,-15,-27, -23,-9,-23,-5,-9,-16,-5,-17 }; const int mg_rook_table[64] = { 32,42,32,51,63,9,31,43, 27,32,58,62,80,67,26,44, -5,19,26,36,17,45,61,16, -24,-11,7,26,24,35,-8,-20, -36,-26,-12,-1,9,-7,6,-23, -45,-25,-16,-17,3,0,-5,-33, -44,-16,-20,-9,-1,11,-6,-71, -19,-13,1,17,16,7,-37,-26 }; const int eg_rook_table[64] = { 13,10,18,15,12,12,8,5, 11,13,13,11,-3,3,8,3, 7,7,7,5,4,-3,-5,-3, 4,3,13,1,2,1,-1,2, 3,5,8,4,-5,-6,-8,-11, -4,0,-5,-1,-7,-12,-8,-16, -6,-6,0,2,-9,-9,-11,-3, -9,2,3,-1,-5,-13,4,-20 }; const int mg_queen_table[64] = { -28,0,29,12,59,44,43,45, -24,-39,-5,1,-16,57,28,54, -13,-17,7,8,29,56,47,57, -27,-27,-16,-16,-1,17,-2,1, -9,-26,-9,-10,-2,-4,3,-3, -14,2,-11,-2,-5,2,14,5, -35,-8,11,2,8,15,-3,1, -1,-18,-9,10,-15,-25,-31,-50 }; const int eg_queen_table[64] = { -9,22,22,27,27,19,10,20, -17,20,32,41,58,25,30,0, -20,6,9,49,47,35,19,9, 3,22,24,45,57,40,57,36, -18,28,19,47,31,34,39,23, -16,-27,15,6,9,17,10,5, -22,-23,-30,-16,-16,-23,-36,-32, -33,-28,-22,-43,-5,-32,-20,-41 }; const int mg_king_table[64] = { -65,23,16,-15,-56,-34,2,13, 29,-1,-20,-7,-8,-4,-38,-29, -9,-26,-9,-10,-2,-4,3,-3, -14,-13,-22,-46,-44,-30,-15,-27, -27,-27,-16,-16,-1,17,-2,1, -9,-17,19,-3,-9,-6,14,13, -25,-8,-25,-2,-9,-25,-24,-52, -167,-89,-34,-49,61,-97,-15,-107 }; const int eg_king_table[64] = { -74,-35,-18,-18,-11,15,4,-17, -12,17,14,17,17,38,23,11, 10,17,23,15,20,45,44,13, -8,22,24,27,26,33,26,3, -18,-4,21,24,27,23,9,-11, -19,-3,11,21,23,16,7,-9, -27,-11,4,13,14,4,-5,-17, -53,-34,-21,-11,-28,-14,-24,-43 }; static inline int pst_mg(int pt, int sq) { switch(pt){ case PAWN: return mg_pawn_table[sq]; case KNIGHT: return mg_knight_table[sq]; case BISHOP: return mg_bishop_table[sq]; case ROOK: return mg_rook_table[sq]; case QUEEN: return mg_queen_table[sq]; case KING: return mg_king_table[sq]; } return 0; } static inline int pst_eg(int pt, int sq) { switch(pt){ case PAWN: return eg_pawn_table[sq]; case KNIGHT: return eg_knight_table[sq]; case BISHOP: return eg_bishop_table[sq]; case ROOK: return eg_rook_table[sq]; case QUEEN: return eg_queen_table[sq]; case KING: return eg_king_table[sq]; } return 0; } static inline U64 attackers_to_sq(const Board& b, int color, int s) { return attackers_to_sq(b.bb, color, s, b.occAll); } // 物质平衡(不含王),用于“少兑子”排序偏置(>0 说明当前方领先) static int material_balance_side(const Board& b, int color) { int sum[2] = {0,0}; for (int c=0;c<2;c++) { sum[c] += mg_value[PAWN] * popcount(b.bb[Board::code(c,PAWN)]); sum[c] += mg_value[KNIGHT] * popcount(b.bb[Board::code(c,KNIGHT)]); sum[c] += mg_value[BISHOP] * popcount(b.bb[Board::code(c,BISHOP)]); sum[c] += mg_value[ROOK] * popcount(b.bb[Board::code(c,ROOK)]); sum[c] += mg_value[QUEEN] * popcount(b.bb[Board::code(c,QUEEN)]); } return sum[color] - sum[color^1]; } // [新增] 机动性/垃圾棋子扣分(马/象/车/后),越不动挨罚越多;被攻不受守时加罚;有吃子时减半 static void mobility_penalty_terms(const Board& b, int mgAdd[2], int egAdd[2]) { mgAdd[0]=mgAdd[1]=egAdd[0]=egAdd[1]=0; for (int color=0;color<2;color++){ int them = color^1; U64 occUs = b.occ[color]; U64 occAll = b.occAll; auto apply_piece = [&](int pt, U64 bbp){ while (bbp){ int s = lsb(bbp); bbp &= bbp-1; U64 att=0, cap=0; int mobility=0; switch(pt){ case KNIGHT: att = KnightAtt[s] & ~occUs; cap = KnightAtt[s] & b.occ[them]; break; case BISHOP: att = bishop_attacks(s, occAll) & ~occUs; cap = bishop_attacks(s, occAll) & b.occ[them]; break; case ROOK: att = rook_attacks(s, occAll) & ~occUs; cap = rook_attacks(s, occAll) & b.occ[them]; break; case QUEEN: att = queen_attacks(s, occAll) & ~occUs; cap = queen_attacks(s, occAll) & b.occ[them]; break; default: break; } if (pt==KNIGHT || pt==BISHOP || pt==ROOK || pt==QUEEN) { mobility = popcount(att); int mgPen=0, egPen=0; // 基础“低机动”阈值和每格扣分 if (pt==KNIGHT){ int thr=4; int k = max(0, thr - mobility); mgPen += 6*k; egPen += 4*k; // 边线马小扣分 int f=file_of(s), r=rank_of(s); if (f==0||f==7||r==0||r==7){ mgPen += 6; egPen += 2; } // 完全被堵 if (mobility==0){ mgPen += 10; egPen += 6; } }else if (pt==BISHOP){ int thr=5; int k = max(0, thr - mobility); mgPen += 5*k; egPen += 3*k; if (mobility==0){ mgPen += 10; egPen += 6; } }else if (pt==ROOK){ int thr=7; int k = max(0, thr - mobility); mgPen += 3*k; egPen += 2*k; if (mobility<=1){ mgPen += 12; egPen += 8; } }else if (pt==QUEEN){ int thr=8; int k = max(0, thr - mobility); mgPen += 2*k; egPen += 1*k; if (mobility<=1){ mgPen += 16; egPen += 10; } } // 被攻不受守时,加罚(非送子受限) bool attacked = attackers_to_sq(b.bb, them, s, occAll)!=0; bool defended = attackers_to_sq(b.bb, color, s, occAll)!=0; if (attacked && !defended){ if (pt==KNIGHT||pt==BISHOP){ mgPen += 8; egPen += 4; } else if (pt==ROOK){ mgPen += 10; egPen += 5; } else if (pt==QUEEN){ mgPen += 12; egPen += 6; } } // 若有直接吃子,对扣分打 0.75 系数 if (cap){ mgPen = (mgPen*3)/4; egPen = (egPen*3)/4; } mgAdd[color] -= mgPen; egAdd[color] -= egPen; } } }; apply_piece(KNIGHT, b.bb[Board::code(color, KNIGHT)]); apply_piece(BISHOP, b.bb[Board::code(color, BISHOP)]); apply_piece(ROOK, b.bb[Board::code(color, ROOK)]); apply_piece(QUEEN, b.bb[Board::code(color, QUEEN)]); } } // 新增:结构/开局常识项(中心兵、连兵、前哨马、堡垒象、开发) // 辅助:判断是否“前哨”(color 在 s 上的马),基于相邻列前方无敌兵 static bool is_knight_outpost(const Board& b, int color, int s) { int them = color^1; int f = file_of(s), r = rank_of(s); if (color==WHITE) { if (r < 2) return false; // 太靠后 } else { if (r > 5) return false; } U64 enemyPawns = b.bb[Board::code(them, PAWN)]; U64 lanes = AdjacentFiles[f] & ForwardMask[color][s]; if (lanes & enemyPawns) return false; return true; } static int eval_fianchetto_bishop(const Board& b, int color) { int mg=0, eg=0; int s1 = (color==WHITE? sq(6,1):sq(6,6)); // g2/g7 int s2 = (color==WHITE? sq(1,1):sq(1,6)); // b2/b7 int codeB = Board::code(color, BISHOP); U64 myPawns = b.bb[Board::code(color, PAWN)]; bool kingsideCastled = (color==WHITE? (b.kingSq[WHITE]==sq(6,0)): (b.kingSq[BLACK]==sq(6,7))); // g2/g7 if ((b.bb[codeB] >> s1) & 1ULL) { bool base = ((myPawns >> (color==WHITE? sq(6,1):sq(6,6))) & 1ULL) || // g2/g7 有兵(开局结构) ((myPawns >> (color==WHITE? sq(6,2):sq(6,5))) & 1ULL); // g3/g6 mg += base? 18:12; eg += base? 12:8; if (kingsideCastled) { mg += 6; eg += 4; } // 长对角线清空小加成(附近无己方阻挡) int mid = (color==WHITE? sq(5,2):sq(5,5)); // f3/f6 if (b.board[mid]==NO_PIECE) { mg += 3; eg += 2; } } // b2/b7 if ((b.bb[codeB] >> s2) & 1ULL) { bool base = ((myPawns >> (color==WHITE? sq(1,1):sq(1,6))) & 1ULL) || ((myPawns >> (color==WHITE? sq(1,2):sq(1,5))) & 1ULL); mg += base? 14:10; eg += base? 10:6; } return (mg<<16) | (eg & 0xFFFF); } struct Eval { static int evaluate(const Board& b) { if (b.halfmove >= 100) return 0; if (is_threefold(b)) return 0; if (insufficient_material(b)) return 0; int mgScore[2]={0,0}, egScore[2]={0,0}; int phase=0; const int phaseW[6]={0,1,1,2,4,0}; // 材料 + PST(2/3 缩放) for (int color=0;color<2;color++){ for (int pt=PAWN; pt<=KING; ++pt){ int code = Board::code(color, pt); U64 bbp = b.bb[code]; while(bbp){ int s = lsb(bbp); bbp &= bbp-1; int sqw = color==WHITE? s : mirror64(s); mgScore[color] += mg_value[pt] + (pst_mg(pt, sqw)*2)/3; egScore[color] += eg_value[pt] + (pst_eg(pt, sqw)*2)/3; phase += phaseW[pt]; } } } // 象双 if (popcount(b.bb[Board::code(WHITE,BISHOP)]) >= 2) { mgScore[WHITE]+=20; egScore[WHITE]+=30; } if (popcount(b.bb[Board::code(BLACK,BISHOP)]) >= 2) { mgScore[BLACK]+=20; egScore[BLACK]+=30; } // 通过兵(适度) { U64 wp = b.bb[Board::code(WHITE, PAWN)]; U64 bp = b.bb[Board::code(BLACK, PAWN)]; U64 t = wp; while (t){ int s = lsb(t); t &= t-1; if ( (bp & PassedMask[WHITE][s]) == 0ULL ) { static const int bonusMg[8] = {0,6,10,16,24,36,50,0}; static const int bonusEg[8] = {0,8,12,20,30,45,70,0}; int r = rank_of(s); mgScore[WHITE] += bonusMg[r]; egScore[WHITE] += bonusEg[r]; } } t = bp; while (t){ int s = lsb(t); t &= t-1; if ( (wp & PassedMask[BLACK][s]) == 0ULL ) { static const int bonusMg[8] = {0,6,10,16,24,36,50,0}; static const int bonusEg[8] = {0,8,12,20,30,45,70,0}; int r = 7 - rank_of(s); mgScore[BLACK] += bonusMg[r]; egScore[BLACK] += bonusEg[r]; } } } // 挂子惩罚(受攻不受守) auto hanging = [&](int color){ int them=color^1; int mg=0, eg=0; U64 occ = b.occAll; for (int pt=PAWN; pt<=QUEEN; ++pt) { U64 bbp = b.bb[Board::code(color, pt)]; while (bbp) { int s = lsb(bbp); bbp&=bbp-1; bool attacked = attackers_to_sq(b.bb, them, s, occ)!=0; bool defended = attackers_to_sq(b.bb, color, s, occ)!=0; if (attacked && !defended) { int pen = (pt==PAWN?10: pt==KNIGHT||pt==BISHOP?24: pt==ROOK?40:70); mg -= pen; eg -= pen/2; } } } return pair<int,int>{mg,eg}; }; auto hW = hanging(WHITE), hB = hanging(BLACK); mgScore[WHITE]+=hW.first; egScore[WHITE]+=hW.second; mgScore[BLACK]+=hB.first; egScore[BLACK]+=hB.second; // [新增] 机动性/垃圾棋子扣分 { int mgM[2], egM[2]; mobility_penalty_terms(b, mgM, egM); mgScore[WHITE]+=mgM[WHITE]; egScore[WHITE]+=egM[WHITE]; mgScore[BLACK]+=mgM[BLACK]; egScore[BLACK]+=egM[BLACK]; } // [新增] 中心兵权重(中心 4 格、中心 16 格、小幅控制) auto center_terms = [&](int color){ int mg=0, eg=0; U64 pawns = b.bb[Board::code(color, PAWN)]; U64 pCenter4 = pawns & Center4Mask; int c4 = popcount(pCenter4); mg += 16 * c4; eg += 8 * c4; U64 pCenter16 = pawns & Center16Mask; int c16 = popcount(pCenter16); mg += 4 * c16; eg += 2 * c16; // d/e 兵额外(开局中局) U64 dFile = pawns & FileMask[3]; U64 eFile = pawns & FileMask[4]; mg += 6 * popcount(dFile); mg += 6 * popcount(eFile); // 控制中心4格的小奖励(由兵控制) U64 attAll = 0ULL; U64 p = pawns; while (p) { int s=lsb(p); p&=p-1; attAll |= PawnAtt[color][s]; } int ctrl4 = popcount(attAll & Center4Mask); mg += 3 * ctrl4; eg += 2 * ctrl4; return pair<int,int>{mg,eg}; }; auto cW = center_terms(WHITE), cB = center_terms(BLACK); mgScore[WHITE]+=cW.first; egScore[WHITE]+=cW.second; mgScore[BLACK]+=cB.first; egScore[BLACK]+=cB.second; // [新增] 中心连兵(d/e 同排,例如 d4+e4 或 d5+e5) auto central_connected_pawns = [&](int color){ int mg=0, eg=0; U64 pawns = b.bb[Board::code(color, PAWN)]; for (int r=2; r<=5; ++r) { bool dHere = (pawns & (1ULL<<sq(3,r))) != 0; bool eHere = (pawns & (1ULL<<sq(4,r))) != 0; if (dHere && eHere) { mg += 18; // 中局价值高 eg += 10; } } return pair<int,int>{mg,eg}; }; auto ccW = central_connected_pawns(WHITE), ccB = central_connected_pawns(BLACK); mgScore[WHITE]+=ccW.first; egScore[WHITE]+=ccW.second; mgScore[BLACK]+=ccB.first; egScore[BLACK]+=ccB.second; // [新增] 前哨马(有己方兵支持时更高) auto outpost_knight_terms = [&](int color){ int mg=0, eg=0; int them = color^1; U64 knights = b.bb[Board::code(color, KNIGHT)]; U64 myPawns = b.bb[Board::code(color, PAWN)]; while (knights){ int s = lsb(knights); knights&=knights-1; if (!is_knight_outpost(b, color, s)) continue; bool supportedByPawn = (PawnAtt[color][s] & myPawns) != 0ULL; int baseMg = 18, baseEg = 24; if (supportedByPawn) { baseMg += 10; baseEg += 12; } // 更靠近中心更值钱 if ((Center16Mask >> s) & 1ULL) { baseMg += 4; baseEg += 6; } // 若此格不易被轻子换掉小加成(粗略:敌方轻子对该格攻击少) int attN = popcount((U64)attackers_to_sq(b.bb, them, s, b.occAll) & (b.bb[Board::code(them,KNIGHT)]|b.bb[Board::code(them,BISHOP)])); if (attN==0){ baseMg += 4; baseEg += 6; } mg += baseMg; eg += baseEg; } return pair<int,int>{mg,eg}; }; auto opW = outpost_knight_terms(WHITE), opB = outpost_knight_terms(BLACK); mgScore[WHITE]+=opW.first; egScore[WHITE]+=opW.second; mgScore[BLACK]+=opB.first; egScore[BLACK]+=opB.second; // [新增] 堡垒象(g2/b2/g7/b7) { int vW = eval_fianchetto_bishop(b, WHITE); int vB = eval_fianchetto_bishop(b, BLACK); mgScore[WHITE] += (vW>>16); egScore[WHITE] += (vW & 0xFFFF); mgScore[BLACK] += (vB>>16); egScore[BLACK] += (vB & 0xFFFF); } // 易位激励更强 + 未易位轻度惩罚 { bool wCastled = (b.kingSq[WHITE]==sq(6,0) || b.kingSq[WHITE]==sq(2,0)); bool bCastled = (b.kingSq[BLACK]==sq(6,7) || b.kingSq[BLACK]==sq(2,7)); if (wCastled) { mgScore[WHITE] += 50; egScore[WHITE] += 20; } if (bCastled) { mgScore[BLACK] += 50; egScore[BLACK] += 20; } if (!wCastled && b.kingSq[WHITE]==sq(4,0) && (b.castling & 3)) { int p = max(0, b.fullmove - 6); int pen = min(20, p*2); mgScore[WHITE] -= pen; } if (!bCastled && b.kingSq[BLACK]==sq(4,7) && (b.castling & 12)) { int p = max(0, b.fullmove - 6); int pen = min(20, p*2); mgScore[BLACK] -= pen; } if (!wCastled && b.kingSq[WHITE]!=sq(4,0) && !b.square_attacked(b.kingSq[WHITE], BLACK)) { int p = max(0, 12 - b.fullmove); int pen = min(18, 2*p); mgScore[WHITE] -= pen; } if (!bCastled && b.kingSq[BLACK]!=sq(4,7) && !b.square_attacked(b.kingSq[BLACK], WHITE)) { int p = max(0, 12 - b.fullmove); int pen = min(18, 2*p); mgScore[BLACK] -= pen; } } // 轻微“开发奖励/未开发惩罚”(仅中度开局阶段生效) { int gp = game_phase(b); // 0..24;开局>中局>残局 if (gp >= 16) { // 奖励:白/黑的两马两象离开初始格 auto dev = [&](int color){ int mg=0, eg=0; int homeRank = (color==WHITE? 0:7); // 未开发轻子小惩罚 if ((b.bb[Board::code(color, KNIGHT)] & (1ULL<<sq(1,homeRank)))==0ULL) mg+=2; if ((b.bb[Board::code(color, KNIGHT)] & (1ULL<<sq(6,homeRank)))==0ULL) mg+=2; if ((b.bb[Board::code(color, BISHOP)] & (1ULL<<sq(2,homeRank)))==0ULL) mg+=2; if ((b.bb[Board::code(color, BISHOP)] & (1ULL<<sq(5,homeRank)))==0ULL) mg+=2; return pair<int,int>{mg,eg}; }; auto dW = dev(WHITE), dB = dev(BLACK); mgScore[WHITE]+=dW.first; egScore[WHITE]+=dW.second; mgScore[BLACK]+=dB.first; egScore[BLACK]+=dB.second; } } // Tapered eval int mg = mgScore[WHITE] - mgScore[BLACK]; int eg = egScore[WHITE] - egScore[BLACK]; int phaseMax = 24; if (phase > phaseMax) phase = phaseMax; int score = (mg * phase + eg * (phaseMax - phase)) / phaseMax; const int tempo = 8; return (b.side == WHITE) ? (score + tempo) : (-(score + tempo)); } }; // ----------------------------- 搜索 ----------------------------- static inline int mate_in(int ply) { return MATE_SCORE - ply; } static inline int mated_in(int ply) { return -MATE_SCORE + ply; } static int score_to_tt(int score, int ply) { if (score >= MATE_IN_MAX_PLY) return score + ply; if (score <= -MATE_IN_MAX_PLY) return score - ply; return score; } static int score_from_tt(int score, int ply) { if (score >= MATE_IN_MAX_PLY) return score - ply; if (score <= -MATE_IN_MAX_PLY) return score + ply; return score; } static bool allow_null(const Board& b) { if (b.square_attacked(b.kingSq[b.side], b.side^1)) return false; if ( (b.bb[Board::code(b.side, QUEEN)] | b.bb[Board::code(b.side, ROOK)] | b.bb[Board::code(b.side, BISHOP)] | b.bb[Board::code(b.side, KNIGHT)]) == 0ULL && popcount(b.bb[Board::code(b.side, PAWN)]) <= 2) return false; return true; } static inline bool is_hanging_major(const Board& b, int color) { U64 occ = b.occAll; int them = color ^ 1; U64 valuable = b.bb[Board::code(color, QUEEN)] | b.bb[Board::code(color, ROOK)] | b.bb[Board::code(color, BISHOP)]| b.bb[Board::code(color, KNIGHT)]; while (valuable) { int s = lsb(valuable); valuable &= valuable - 1; bool attacked = attackers_to_sq(b.bb, them, s, occ) != 0; bool defended = attackers_to_sq(b.bb, color, s, occ) != 0; if (attacked && !defended) return true; } return false; } static int Quiescence(Board& b, int alpha, int beta, int ply) { Stats.qnodes++; if (ply > Stats.seldepth) Stats.seldepth = ply; if (b.halfmove >= 100) return 0; if (is_threefold(b)) return 0; bool inCheck = b.square_attacked(b.kingSq[b.side], b.side^1); if (!inCheck) { int stand = Eval::evaluate(b); if (stand >= beta) return stand; if (stand > alpha) alpha = stand; MoveList list; gen_qmoves(b, list); for (int i=0;i<list.size;i++){ uint32_t m = list.mv[i].m; int sc=0; if (m_promo(m)!=-1 && m_captured(m)==-1) sc = 300000 + 10*m_promo(m); else if (m_captured(m)!=-1) sc = 200000 + SEE_Value[m_captured(m)] - (SEE_Value[m_piece(m)]/10); sc += rand_jitter(); // 随机抖动 list.mv[i].score=sc; } sort(list.mv, list.mv+list.size, [](const MoveEntry&a,const MoveEntry&b){return a.score>b.score;}); for (int i=0; i<list.size; ++i) { uint32_t m = list.mv[i].m; bool isCap = m_captured(m)!=-1; bool isEp = m_ep(m); if (isCap && !isEp) { int capVal = SEE_Value[m_captured(m)]; if (stand + capVal + 50 < alpha) continue; // delta 剪枝 // 更谨慎 SEE 阈值 if (!see_ge(b, m, -10)) continue; } if (!make_move(b, m)) continue; int score = -Quiescence(b, -beta, -alpha, ply+1); unmake_move(b); if (score >= beta) return score; if (score > alpha) alpha = score; } return alpha; } else { MoveList list; gen_moves(b, list, false); for (int i=0;i<list.size;i++){ uint32_t m = list.mv[i].m; int sc=0; if (m_captured(m)!=-1) sc = 200000 + SEE_Value[m_captured(m)]; else if (m_castle(m)) sc = 150000; else sc = History[b.side][m_from(m)][m_to(m)]; sc += rand_jitter(); // 随机抖动 list.mv[i].score = sc; } sort(list.mv, list.mv+list.size, [](const MoveEntry&a,const MoveEntry&b){return a.score>b.score;}); for (int i=0;i<list.size;i++){ uint32_t m = list.mv[i].m; if (!make_move(b, m)) continue; int score = -Quiescence(b, -beta, -alpha, ply+1); unmake_move(b); if (score >= beta) return score; if (score > alpha) alpha = score; } return alpha; } } // “少兑子”偏置:基于物质形势与 SEE 对吃子的排序进行小幅调整 static inline int trade_bias_for_sorting(const Board& b, uint32_t m) { if (m_captured(m)==-1) return 0; int mb = material_balance_side(b, b.side); // >0 领先;<0 落后 int see = see_move(b, m); if (see > 50 || see < -50) return 0; if (mb > 150) return +3000; // 领先:鼓励交换 if (mb < -150) return -3000; // 落后:减少交换 return 0; } // 开局走法排序微偏置:中心兵推进/轻子开发 static inline int opening_bias_score(const Board& b, uint32_t m) { int gp = game_phase(b); // 0..24(开局越大) if (gp < 16) return 0; // 中后期不加 int us = b.side; int piece = m_piece(m)%6; int from = m_from(m), to = m_to(m); int bonus = 0; // 中心兵推进(到中心16格,尤其 d/e 兵) if (piece == PAWN) { if ((Center16Mask >> to) & 1ULL) bonus += 1200; int f = file_of(from); if (f==3 || f==4) bonus += 800; // d/e 兵 } // 轻子开发:从初始排往外,非王翼无意义来回 if (piece == KNIGHT || piece == BISHOP) { int homeRank = (us==WHITE?0:7); if (rank_of(from)==homeRank && rank_of(to)!=homeRank) bonus += 900; } return bonus; } static int Search(Board& b, int depth, int alpha, int beta, int ply, bool doNull, uint32_t pvTable[MAX_PLY][MAX_PLY], int pvLen[MAX_PLY]) { Stats.nodes++; if (ply > Stats.seldepth) Stats.seldepth = ply; if (TM.time_up()) return Eval::evaluate(b); bool inCheck = b.square_attacked(b.kingSq[b.side], b.side^1); if (depth <= 0) return Quiescence(b, alpha, beta, ply); if (b.halfmove >= 100) return 0; if (is_threefold(b)) return 0; if (insufficient_material(b)) return 0; bool pvNode = (beta - alpha > 1); bool dangerNode = inCheck || is_hanging_major(b, b.side); bool found=false; TTEntry* tte = TT.probe(b.key, found); uint32_t ttMove = 0; if (found && tte->depth >= depth) { int score = score_from_tt(tte->score, ply); // 注意:根节点不再允许用 TT 直接返回(避免“凭 TT 就下子”) if (ply > 0) { if (tte->flag == 0) return score; if (tte->flag == 1 && score >= beta) return score; if (tte->flag == 2 && score <= alpha) return score; } ttMove = tte->move; } int staticEval = (found ? tte->eval : Eval::evaluate(b)); // 反向 futility(危险节点不做) if (!pvNode && !dangerNode && !inCheck && depth <= 2) { int rbeta = alpha - 110*depth; if (staticEval <= rbeta) { int qs = Quiescence(b, rbeta, rbeta+1, ply); if (qs <= rbeta) return qs; } } // Null move(谨慎) if (doNull && !pvNode && !dangerNode && !inCheck && depth >= 3 && allow_null(b) && staticEval >= beta + 120) { State &st = b.st[b.sp++]; st.key = b.key; st.castling=b.castling; st.ep=b.ep; st.halfmove=b.halfmove; st.captured=-1; st.move=0; st.fullmove=b.fullmove; if (b.ep!=-1) b.key ^= ZEP[file_of(b.ep)]; b.ep = -1; b.side ^= 1; b.key ^= ZSide; int R = 2 + (depth >= 5 ? 1 : 0); int score = -Search(b, depth - 1 - R, -beta, -beta+1, ply+1, false, pvTable, pvLen); b.side ^= 1; b.key ^= ZSide; if (b.ep!=-1) b.key ^= ZEP[file_of(b.ep)]; b.ep = st.ep; if (b.ep!=-1) b.key ^= ZEP[file_of(b.ep)]; b.castling = st.castling; b.halfmove = st.halfmove; b.fullmove = st.fullmove; b.sp--; if (score >= beta) return score; } MoveList list; gen_moves(b, list, false); // 排序:TT > captures(+少兑子) > Killers > castle > history (+ 开局偏置) + 随机抖动 for (int i=0;i<list.size;i++){ uint32_t m = list.mv[i].m; int score = 0; if ((int)m == (int)ttMove) score = 1<<30; else if (m_captured(m)!=-1) { score = (1<<29) + SEE_Value[m_captured(m)] - (SEE_Value[m_piece(m)]/10); score += trade_bias_for_sorting(b, m); } else if (m == Killers1[ply]) score = 1<<28; else if (m == Killers2[ply]) score = (1<<28)-1; else if (m_castle(m)) score = (1<<28)-2; // 易位优先于普通 history else score = History[b.side][m_from(m)][m_to(m)]; // 降低“安静的非易位王走子”优先级 if (!inCheck && (m_piece(m) % 6) == KING && m_captured(m) == -1 && !m_castle(m) && m_promo(m) == -1) { score -= (1 << 27); } // 开局微偏置(中心兵推进/轻子开发) score += opening_bias_score(b, m); // 随机抖动 score += rand_jitter(); list.mv[i].score = score; } sort(list.mv, list.mv + list.size, [](const MoveEntry& a, const MoveEntry& b){ return a.score > b.score; }); int origAlpha = alpha; int bestScore = -INF; uint32_t bestMove = 0; pvLen[ply] = 0; int legalMoves=0; for (int i=0;i<list.size;i++){ uint32_t m = list.mv[i].m; // 仅把“吃子/升变”视为战术步;易位算安静步 bool isTactical = (m_captured(m)!=-1) || (m_promo(m)!=-1); // 叶子 futility(危险节点不剪) if (!pvNode && !dangerNode && !inCheck && depth == 1 && !isTactical) { if (staticEval + 90 <= alpha) continue; } // LMP(危险节点不做) if (!pvNode && !dangerNode && !inCheck && depth <= 3 && !isTactical && i > 6 + 2*depth) continue; // SEE 负收益吃子剪枝(更谨慎) if (!pvNode && !dangerNode && !inCheck && m_captured(m)!=-1 && depth <= 5 && !see_ge(b, m, -10)) continue; if (!make_move(b, m)) continue; legalMoves++; bool nextInCheck = b.square_attacked(b.kingSq[b.side], b.side^1); int ext = nextInCheck ? 1 : 0; int newDepth = depth - 1 + ext; int score; // LMR 更保守 if (!pvNode && !dangerNode && !inCheck && !isTactical && depth >= 3 && !nextInCheck) { int r = 1 + (depth>=6) + (i>=8); score = -Search(b, newDepth - r, -alpha-1, -alpha, ply+1, true, pvTable, pvLen); } else { score = -Search(b, newDepth, -alpha-1, -alpha, ply+1, true, pvTable, pvLen); } if (score > alpha && score < beta) { score = -Search(b, newDepth, -beta, -alpha, ply+1, true, pvTable, pvLen); } unmake_move(b); if (score > bestScore) { bestScore = score; bestMove = m; pvTable[ply][0] = m; for (int k=0;k<pvLen[ply+1];k++) pvTable[ply][k+1] = pvTable[ply+1][k]; pvLen[ply] = pvLen[ply+1] + 1; } if (score > alpha) { alpha = score; if (m_captured(m)==-1 && !m_castle(m)) { History[b.side][m_from(m)][m_to(m)] += depth * depth; if (Killers1[ply] != m) { Killers2[ply] = Killers1[ply]; Killers1[ply] = m; } } if (alpha >= beta) { break; } } } if (legalMoves==0) { if (inCheck) return mated_in(ply); return 0; } if (tte) { tte->key = b.key; tte->move = bestMove; tte->depth = depth; tte->eval = staticEval; tte->age = TT.age; if (bestScore <= origAlpha) tte->flag = 2; else if (bestScore >= beta) tte->flag = 1; else tte->flag = 0; tte->score = (int16_t)score_to_tt(bestScore, ply); } return bestScore; } // 复原 PV static int get_pv_line(Board& b, uint32_t pv[MAX_PLY]) { int cnt=0; Board copy = b; for (int i=0;i<MAX_PLY;i++){ bool found=false; TTEntry* e = TT.probe(copy.key, found); if (!found || e->move==0) break; uint32_t m = e->move; if (!make_move(copy, m)) break; pv[cnt++] = m; } return cnt; } static string move_to_uci(uint32_t m) { int from = m_from(m), to = m_to(m); char s[8]; s[0] = 'a' + file_of(from); s[1] = '1' + rank_of(from); s[2] = 'a' + file_of(to); s[3] = '1' + rank_of(to); int promo = m_promo(m); if (promo!=-1) { s[4] = "nbrq"[ (promo==KNIGHT)?0 : (promo==BISHOP)?1 : (promo==ROOK)?2 : 3 ]; s[5] = 0; } else { s[4] = 0; } return string(s); } static uint32_t parse_move_uci(Board& b, const string& str) { if (str.size()<4) return 0; int ff = str[0]-'a'; int fr = str[1]-'1'; int tf = str[2]-'a'; int tr = str[3]-'1'; int from = sq(ff,fr), to = sq(tf,tr); int pc = b.board[from]; if (pc == NO_PIECE) return 0; int captured = b.board[to]; bool ep=false, castle=false, dpp=false; int promoType = -1; if ((pc%6)==KING && ((from==sq(4,0) && (to==sq(6,0)||to==sq(2,0))) || (from==sq(4,7) && (to==sq(6,7)||to==sq(2,7))))) { castle = true; } if ((pc%6)==PAWN && to == b.ep && b.ep!=-1 && captured==-1) { ep = true; captured = Board::code(b.side^1, PAWN); } if ((pc%6)==PAWN && abs(tr-fr)==2) dpp = true; if (str.size()>=5) { char c = (char)tolower((unsigned char)str[4]); if (c=='q') promoType = QUEEN; else if (c=='r') promoType = ROOK; else if (c=='b') promoType = BISHOP; else if (c=='n') promoType = KNIGHT; } return enc_move(from,to,pc,captured,promoType,ep,castle,dpp); } // ----------------------------- 迭代加深与 UCI 输出 ----------------------------- static uint32_t LastBestMoveRoot = 0; static void print_info_line(int depth, int score, const vector<uint32_t>& pv) { cout << "info depth " << depth << " seldepth " << Stats.seldepth << " score "; if (score >= MATE_IN_MAX_PLY) { int mateIn = (MATE_SCORE - score + 1) / 2; cout << "mate " << mateIn; } else if (score <= -MATE_IN_MAX_PLY) { int mateIn = (MATE_SCORE + score + 1) / 2; cout << "mate " << -mateIn; } else { cout << "cp " << score; } cout << " time " << Stats.elapsed_ms() << " nodes " << Stats.nodes << " nps " << Stats.nps() << " pv"; for (auto m : pv) cout << " " << move_to_uci(m); cout << endl; cout.flush(); } static uint32_t iterative_deepening(Board& b, const Limits& lim) { Stats.reset(); TM.setup(b, lim); TT.age++; memset(History, 0, sizeof(History)); memset(Killers1, 0, sizeof(Killers1)); memset(Killers2, 0, sizeof(Killers2)); uint32_t bestMove = 0; int bestScore = -INF; uint32_t pvTable[MAX_PLY][MAX_PLY] = {}; int pvLen[MAX_PLY] = {}; int startDepth = 1; int maxDepth = min(lim.depth, MAX_PLY-2); int aspiration = 16; int prevScore = Eval::evaluate(b); for (int depth = startDepth; depth <= maxDepth; ++depth) { int alpha = max(-INF, prevScore - aspiration); int beta = min(+INF, prevScore + aspiration); int score; while (true) { Stats.seldepth = 0; score = Search(b, depth, alpha, beta, 0, true, pvTable, pvLen); if (TM.time_up()) break; if (score <= alpha) { alpha = max(-INF, score - aspiration - 8); aspiration = min(300, aspiration<<1); continue; } if (score >= beta) { beta = min(+INF, score + aspiration + 8); aspiration = min(300, aspiration<<1); continue; } break; } if (TM.time_up()) break; prevScore = score; bestScore = score; vector<uint32_t> pv; for (int i=0;i<pvLen[0];i++) pv.push_back(pvTable[0][i]); if (pv.empty()) { uint32_t line[MAX_PLY]; int l = get_pv_line(b, line); for (int i=0;i<l;i++) pv.push_back(line[i]); } // 选择 bestmove:优先 pv[0],否则校验 TT/合法走子 if (!pv.empty()) { // 验证 pv 第一手是否可走 if (make_move(b, pv[0])) { unmake_move(b); bestMove = pv[0]; } } if (bestMove == 0) { MoveList list; gen_moves(b, list, false); for (int i=0;i<list.size;i++){ if (make_move(b, list.mv[i].m)) { bestMove = list.mv[i].m; unmake_move(b); break; } } } if (bestMove) LastBestMoveRoot = bestMove; print_info_line(depth, score, pv); if (TM.time_up()) break; if (lim.movetime > 0) { if (Stats.elapsed_ms() >= (uint64_t)lim.movetime) break; } else { if (TM.soft_time_up()) break; } } if (bestMove == 0) { // 回退策略:用上次根最佳,或任一合法 if (LastBestMoveRoot && make_move(b, LastBestMoveRoot)) { unmake_move(b); bestMove = LastBestMoveRoot; } else { MoveList list; gen_moves(b, list, false); for (int i=0;i<list.size;i++){ if (make_move(b, list.mv[i].m)) { bestMove = list.mv[i].m; unmake_move(b); break; } } } } return bestMove; } // ----------------------------- UCI ----------------------------- static void uci_id() { cout << "id name Chtholly-Engine 1.0" << endl; cout << "id author LLM" << endl; cout << "option name Hash type spin default 128 min 1 max 4096" << endl; cout << "option name Randomness type spin default 0 min 0 max 100000" << endl; cout << "option name RandomSeed type spin default 0 min 0 max 4294967295" << endl; cout << "uciok" << endl; cout.flush(); } static void uci_isready() { cout << "readyok" << endl << flush; } static void uci_ucinewgame() { TT.clear(); // 若随机幅度>0且未固定种子,每盘重播种(时间种子) if (RC.jitter > 0 && RC.seed == 0) RC.seeded = false; } static void setoption(const string& name, const string& value) { if (name == "Hash") { int MB = stoi(value); TT.resizeMB(MB); } else if (name == "Randomness") { int A = stoi(value); RC.jitter = max(0, min(100000, A)); } else if (name == "RandomSeed") { uint64_t s = 0; try { s = stoull(value); } catch(...) { s = 0; } RC.seed = s; RJ.seed(RC.seed ? RC.seed : (uint64_t)chrono::steady_clock::now().time_since_epoch().count()); RC.seeded = true; } } static void uci_position(Board& b, istringstream& iss) { string token; if (!(iss >> token)) return; if (token == "startpos") { b.set_startpos(); if (iss >> token) { if (token == "moves") { while (iss >> token) { uint32_t m = parse_move_uci(b, token); if (m==0 || !make_move(b, m)) { MoveList list; gen_moves(b, list, false); bool done=false; for (int i=0;i<list.size;i++) { if (move_to_uci(list.mv[i].m) == token) { if (make_move(b, list.mv[i].m)) { done=true; break; } } } } } } } } else if (token == "fen") { string fen, tmp; vector<string> parts; for (int i=0;i<6 && (iss >> tmp); ++i) parts.push_back(tmp); fen = ""; for (int i=0;i<(int)parts.size();i++){ if (i) fen += " "; fen += parts[i]; } if (parts.size()<4) return; b.set_fen(fen); if (iss >> token) { if (token == "moves") { while (iss >> token) { uint32_t m = parse_move_uci(b, token); if (m==0 || !make_move(b, m)) { MoveList list; gen_moves(b, list, false); for (int i=0;i<list.size;i++) { if (move_to_uci(list.mv[i].m) == token) { if (make_move(b, list.mv[i].m)) { break; } } } } } } } } } static void uci_go(Board& b, istringstream& iss) { Limits lim; string token; while (iss >> token) { if (token == "wtime") iss >> lim.wtime; else if (token == "btime") iss >> lim.btime; else if (token == "winc") iss >> lim.winc; else if (token == "binc") iss >> lim.binc; else if (token == "movestogo") iss >> lim.movestogo; else if (token == "movetime") iss >> lim.movetime; else if (token == "depth") iss >> lim.depth; else if (token == "nodes") { uint64_t nd; iss >> nd; lim.nodes = nd; } else if (token == "ponder") lim.ponder = true; else if (token == "infinite") { lim.movetime = -1; } } uint32_t best = iterative_deepening(b, lim); cout << "bestmove " << move_to_uci(best) << endl; cout.flush(); } // ----------------------------- 主程序 ----------------------------- int main() { ios::sync_with_stdio(false); cin.tie(nullptr); init_zobrist(); init_attack_tables(); init_castle_masks(); TT.resizeMB(128); Board board; board.set_startpos(); string line; while (std::getline(cin, line)) { if (line.empty()) continue; istringstream iss(line); string cmd; iss >> cmd; if (cmd == "uci") uci_id(); else if (cmd == "isready") uci_isready(); else if (cmd == "ucinewgame") uci_ucinewgame(); else if (cmd == "position") uci_position(board, iss); else if (cmd == "go") uci_go(board, iss); else if (cmd == "setoption") { string name, tmp, value; iss >> tmp; // name iss >> tmp; // actual name name = tmp; while (iss >> tmp && tmp!="value") name += " " + tmp; getline(iss, value); while (!value.empty() && isspace((unsigned char)value.front())) value.erase(value.begin()); while (!value.empty() && isspace((unsigned char)value.back())) value.pop_back(); setoption(name, value); } else if (cmd == "stop") { // 优先使用上一次根节点的最佳着作为回退 uint32_t best = 0; if (LastBestMoveRoot && make_move(board, LastBestMoveRoot)) { unmake_move(board); best = LastBestMoveRoot; } else { uint32_t pv[MAX_PLY]; int l = get_pv_line(board, pv); if (l>0 && make_move(board, pv[0])) { unmake_move(board); best = pv[0]; } else { MoveList list; gen_moves(board, list, false); for (int i=0;i<list.size;i++){ if (make_move(board, list.mv[i].m)) { best = list.mv[i].m; unmake_move(board); break; } } } } cout << "bestmove " << move_to_uci(best) << endl; cout.flush(); } else if (cmd == "quit") break; } return 0; }

进行增加500行的大版本更新,完整代码务必一次性发给我,谢谢

思考

Considering chess options

I’m brainstorming new features to enhance the chess engine. I could add things like a "countermove" table, include "pawn hash" for cost-efficient evaluations, or allow multiple principal variations (Multi-PV). I’m also considering adding UCI options like "Threads", "Contempt," "Move Overhead," and "UseBook." Some ideas, like "Nodestime" and "chess960 castling," are worth exploring, but I want to be mindful of current limitations.

Exploring more chess features

I’m thinking about adding options like "Futility Pruning," "IID," and "Late Move Pruning," which could improve efficiency. Also, "Check extensions," "repetition table history," and "move legality bug fixes" could be useful. For performance, ideas like "Killer heuristics" improvements or "Quiet history" and "Capture history" will refine sorting.

Preparing major version update

I need to rename the engine to "Chtholly-Engine 2.0" and make sure to bump the version. The user asked for a large update with 500+ new lines of code, so I'll focus on adding new features and detailed comments in Chinese. The goal is to ensure the code compiles and includes the necessary updates.

Adding new UCI options & enhancements

I’m planning new UCI options like MultiPV, Contempt, MoveOverhead, UseBook, and Chess960. "SyzygyPath" isn't implemented yet, and "Clear Hash" is mainly covered by "ucinewgame." The transposition table will see improvements with a new age-based replacement scheme. I’m also exploring better move ordering and search improvements like ProbCut.

Implementing various engine improvements

I’m planning several updates, such as improved move capture strategies, razoring, and iterative deepening (IID). I'll also improve repetition detection and hash management. For evaluation, I’m working on king safety and material scaling. The Polyglot book will allow for random or best-book moves up to certain points. Also, MultiPV will be added with an option for multiple PV lines. The Chess960 and Hashfull info functionality will be improved but kept optional.

Implementing engine optimizations

I’m planning several updates to improve search functionality. The changes include adding controlled randomness, tweaking the evaluation to account for contempt, and enhancing move ordering with new techniques like Razoring and IID. I’ll also expand quiescence to consider checks, and add a "root exclusion" for MultiPV. Overall, I need to add better hash management, refine stats checks, and incorporate various UCI commands like ‘stop’ and ‘hashfull’. Each feature will be integrated carefully for consistency.

Thought for 1m 8s

回答

cpp
// ai_zz_chess.cpp // 第二届“人工智障”杯——LLM 单文件 C++ 国际象棋引擎(大版本) // UCI 引擎名称:Chtholly-Engine 2.0 // 作者:LLM (ChatGPT) // // =========================== 2.0 大版本更新要点 =========================== // (本版在 1.0 基础上,新增/增强约 500+ 行,功能更稳更强,详见下列条目) // // - 版本与元数据: // * 引擎名升级为 Chtholly-Engine 2.0 // * 支持更多 UCI 选项:MultiPV、Contempt、MoveOverhead、UseBook、QChecks、ProbCut、Razoring、IID // * 新增 "setoption name Clear Hash" 清空置换表 // // - 搜索框架强化: // * 新增根节点 RootSearch(PVS)与 MultiPV(最多多主变),配合更稳妥的根节点选择逻辑 // * 新增 ProbCut、Razoring(小深度剃刀)、IID(内部迭代加深)等常见剪枝/启发 // * 更保守的 LMR/LMP 参数与危险节点判断;改进 NullMove(可变 R);静态/反向 futility 优化 // * Quiescence 支持可选“将军扩展”(QChecks),在非将军静态节点尝试少量非吃子将军 // * 改善 SEE 阈值与 delta 剪枝;更稳健的根回退策略;更合理的 stop/节点/时间判定 // // - 排序与启发: // * 新增 CaptureHistory(吃子历史)与 CounterMove(反击表),用于安静步/吃子步排序微调 // * TT 命中优先级保留;开局偏置(中心兵推进/轻子开发)保留并小幅微调 // * 更稳妥的“少兑子”排序偏置;更合理的“王安静步降权”处理 // // - 评估增强: // * 新增简单王安全项(兵盾/暴露惩罚)、车在开放/半开放线加成、后过早出动轻罚 // * 新增轻量兵形:双兵/孤兵/滞后兵(backward)识别与小幅惩罚 // * 维持 2/3 PST 缩放、机动性扣分、中心结构、前哨马、堡垒象、开发激励与易位激励 // * 新增 Contempt(偏好不和棋的取向):在和棋/重复场合偏向当前行棋方 // // - 时间管理: // * 新增 MoveOverhead(通信/GUI 开销)与更稳健的相位分配;软/硬停止更合理 // * 支持节点上限;NPS 统计保留;增加 hashfull 输出 // // - 开局库(内置微型): // * 默认启用 UseBook:起始局面与少量分支(e4/d4/Nf3/c4/...)可选择“最佳/随机”策略 // * 无需外部文件;中后期自动退回搜索 // // - 兼容性/稳定性: // * setoption RandomSeed 读取修复沿用 // * 根节点绝不直接用 TT 返回;始终至少实搜一手(保留 1.0 修复) // * bestmove 生成更稳妥(合法性验证/回退) // * 保留:走法排序随机抖动、2/3 PST 缩放、SEE 升变吃子修复、谨慎 NullMove/LMR/QS、劣势少兑子偏置等 // // ======================================================================== #include <bits/stdc++.h> using namespace std; using U64 = unsigned long long; static inline int popcount(U64 b) { return (int)__builtin_popcountll(b); } static inline int lsb(U64 b) { return __builtin_ctzll(b); } static inline U64 poplsb(U64 &b) { U64 x = b & -b; b -= x; return x; } static inline int msb(U64 b) { return 63 - __builtin_clzll(b); } constexpr int WHITE = 0; constexpr int BLACK = 1; constexpr int PAWN = 0; constexpr int KNIGHT = 1; constexpr int BISHOP = 2; constexpr int ROOK = 3; constexpr int QUEEN = 4; constexpr int KING = 5; constexpr int NO_PIECE = -1; constexpr int MAX_PLY = 128; constexpr int INF = 32000; constexpr int MATE_SCORE = 30000; constexpr int MATE_IN_MAX_PLY = MATE_SCORE - MAX_PLY; constexpr U64 FileA = 0x0101010101010101ULL; constexpr U64 FileH = FileA << 7; constexpr U64 Rank1 = 0xFFULL; constexpr U64 Rank2 = Rank1 << 8; constexpr U64 Rank7 = Rank1 << 48; constexpr U64 Rank8 = Rank1 << 56; static inline int sq(int file, int rank) { return rank * 8 + file; } static inline int file_of(int s) { return s & 7; } static inline int rank_of(int s) { return s >> 3; } static inline int mirror64(int s) { return s ^ 56; } enum Dir { N=8, S=-8, E=1, W=-1, NE=9, NW=7, SE=-7, SW=-9 }; // ----------------------------- 引擎可调选项 ----------------------------- struct EngineOptions { int MultiPV = 1; // 多主变数量(>=1) int Contempt = 0; // 和棋蔑视(centipawns,偏向当前手不和棋) int MoveOverhead = 20; // 额外时间开销(毫秒) bool UseBook = true; // 使用内置微型开局库 bool BookRandom = true; // 开局库随机选 bool QChecks = true; // QS 中尝试少量非吃子将军 bool ProbCut = true; // 启用 ProbCut bool Razoring = true; // 启用剃刀 bool IID = true; // 启用内部迭代加深 bool Chess960 = false; // 预留(当前未完整支持) } OPT; // ----------------------------- Zobrist ----------------------------- struct SplitMix64 { uint64_t x; explicit SplitMix64(uint64_t seed = 0x9e3779b97f4a7c15ULL) : x(seed) {} uint64_t next() { uint64_t z = (x += 0x9e3779b97f4a7c15ULL); z = (z ^ (z >> 30)) * 0xbf58476d1ce4e5b9ULL; z = (z ^ (z >> 27)) * 0x94d049bb133111ebULL; return z ^ (z >> 31); } }; uint64_t ZPiece[12][64]; uint64_t ZCastle[16]; uint64_t ZEP[8]; uint64_t ZSide; static void init_zobrist() { SplitMix64 rng(0xA1B2C3D4E5F60789ULL); for (int p=0;p<12;p++) for (int s=0;s<64;s++) ZPiece[p][s] = rng.next(); for (int i=0;i<16;i++) ZCastle[i] = rng.next(); for (int f=0;f<8;f++) ZEP[f] = rng.next(); ZSide = rng.next(); } // ----------------------------- 攻击/掩码 ----------------------------- U64 KnightAtt[64], KingAtt[64]; U64 PawnAtt[2][64]; // [color][sq] U64 FileMask[8], RankMask[8]; U64 PassedMask[2][64]; U64 AdjacentFiles[8]; // 新增:中心掩码/前进掩码 U64 Center4Mask = 0ULL; // d4,e4,d5,e5 U64 Center16Mask = 0ULL; // c3..f6 U64 ForwardMask[2][64]; // 从该格“向对面方向”的所有格(用于前哨马检测) static bool on_board(int f, int r) { return f>=0 && f<8 && r>=0 && r<8; } static void init_attack_tables() { for (int f=0; f<8; ++f) { U64 m = 0; for (int r=0;r<8;r++) m |= 1ULL << sq(f, r); FileMask[f] = m; AdjacentFiles[f] = (f>0?FileMask[f-1]:0ULL) | (f<7?FileMask[f+1]:0ULL); } for (int r=0;r<8;r++) { U64 m = 0; for (int f=0;f<8;f++) m |= 1ULL << sq(f, r); RankMask[r] = m; } for (int s=0;s<64;s++) { int f = file_of(s), r = rank_of(s); // Knight U64 k = 0; int nf[] = {f-2,f-1,f+1,f+2,f-2,f-1,f+1,f+2}; int nr[] = {r-1,r-2,r-2,r-1,r+1,r+2,r+2,r+1}; for(int i=0;i<8;i++) if(on_board(nf[i], nr[i])) k |= 1ULL<<sq(nf[i], nr[i]); KnightAtt[s] = k; // King U64 g=0; for(int df=-1;df<=1;df++) for(int dr=-1;dr<=1;dr++) if(df||dr){ int nf=f+df, nr=r+dr; if(on_board(nf,nr)) g |= 1ULL<<sq(nf,nr); } KingAtt[s]=g; // Pawns U64 pw=0, pb=0; if (r<7) { if (f>0) pw |= 1ULL<<sq(f-1, r+1); if (f<7) pw |= 1ULL<<sq(f+1, r+1); } if (r>0) { if (f>0) pb |= 1ULL<<sq(f-1, r-1); if (f<7) pb |= 1ULL<<sq(f+1, r-1); } PawnAtt[WHITE][s]=pw; PawnAtt[BLACK][s]=pb; // Passed pawn mask U64 pmW = 0, pmB=0; U64 sameAdj = FileMask[f] | AdjacentFiles[f]; for (int rr=r+1; rr<8; rr++) pmW |= sameAdj & RankMask[rr]; for (int rr=r-1; rr>=0; rr--) pmB |= sameAdj & RankMask[rr]; PassedMask[WHITE][s]=pmW; PassedMask[BLACK][s]=pmB; // Forward masks(用于前哨马判定) U64 fwdW = 0, fwdB = 0; for (int rr=r+1; rr<8; ++rr) fwdW |= RankMask[rr]; for (int rr=r-1; rr>=0; --rr) fwdB |= RankMask[rr]; ForwardMask[WHITE][s] = fwdW; ForwardMask[BLACK][s] = fwdB; } // 中心掩码 Center4Mask |= 1ULL<<sq(3,3); Center4Mask |= 1ULL<<sq(4,3); Center4Mask |= 1ULL<<sq(3,4); Center4Mask |= 1ULL<<sq(4,4); for (int f=2; f<=5; ++f) for (int r=2; r<=5; ++r) Center16Mask |= 1ULL<<sq(f,r); } static inline U64 ray_attacks_dir(int s, int df, int dr, U64 occ) { U64 attacks = 0ULL; int f = file_of(s), r = rank_of(s); int nf = f + df, nr = r + dr; while (on_board(nf, nr)) { int ns = sq(nf, nr); attacks |= 1ULL << ns; if ( (occ >> ns) & 1ULL ) break; nf += df; nr += dr; } return attacks; } static inline U64 rook_attacks(int s, U64 occ) { return ray_attacks_dir(s, +1, 0, occ) | ray_attacks_dir(s, -1, 0, occ) | ray_attacks_dir(s, 0, +1, occ) | ray_attacks_dir(s, 0, -1, occ); } static inline U64 bishop_attacks(int s, U64 occ) { return ray_attacks_dir(s, +1, +1, occ) | ray_attacks_dir(s, -1, +1, occ) | ray_attacks_dir(s, +1, -1, occ) | ray_attacks_dir(s, -1, -1, occ); } static inline U64 queen_attacks(int s, U64 occ) { return rook_attacks(s, occ) | bishop_attacks(s, occ); } // ----------------------------- 随机抖动控制 ----------------------------- struct RandJit { uint64_t x = 0x9e3779b97f4a7c15ULL; void seed(uint64_t s) { x = s ? s : 0x9e3779b97f4a7c15ULL; } uint64_t next() { uint64_t z = (x += 0x9e3779b97f4a7c15ULL); z = (z ^ (z >> 30)) * 0xbf58476d1ce4e5b9ULL; z = (z ^ (z >> 27)) * 0x94d049bb133111ebULL; return z ^ (z >> 31); } } RJ; struct RandomCtrl { int jitter = 0; // 走法排序扰动幅度 [-jitter, +jitter] uint64_t seed = 0; // 0=按时间播种;非0=固定种子 bool seeded = false; } RC; static inline int rand_jitter() { if (RC.jitter <= 0) return 0; if (!RC.seeded) { uint64_t t = (uint64_t)chrono::steady_clock::now().time_since_epoch().count(); RC.seed = (RC.seed ? RC.seed : t); RJ.seed(RC.seed); RC.seeded = true; } uint64_t r = RJ.next(); int amp = RC.jitter; int v = (int)(r % (uint64_t)(2*amp + 1)) - amp; // [-amp, +amp] return v; } // ----------------------------- 棋盘与状态 ----------------------------- struct State { uint64_t key; int castling; // 4 bits: WK=1,WQ=2,BK=4,BQ=8 int ep; // -1 无;否则 0..63 int halfmove; int captured; // 捕获的棋子代码(0..11)或 -1 uint32_t move; // 最近一步 int fullmove; // 保存全回合计数(用于评估) }; struct Board { U64 bb[12]; U64 occ[2]; U64 occAll; int side; // to move int castling; // KQkq bits: 1,2,4,8 int ep; // en passant sq or -1 int halfmove; // 50-move int fullmove; // FEN 全回合计数(黑走完一步+1) int board[64]; // -1 无子;否则 0..11 int kingSq[2]; uint64_t key; State st[MAX_PLY+2048]; int sp; Board() { reset(); } void reset() { memset(bb, 0, sizeof(bb)); occ[0]=occ[1]=occAll=0; side=WHITE; castling=0; ep=-1; halfmove=0; fullmove=1; for (int i=0;i<64;i++) board[i]=NO_PIECE; kingSq[0]=kingSq[1]=-1; key=0; sp=0; } static int code(int color, int pt) { return color*6 + pt; } inline void put_piece(int code, int s) { bb[code] |= 1ULL<<s; occ[code/6] |= 1ULL<<s; occAll |= 1ULL<<s; board[s] = code; if ((code%6)==KING) kingSq[code/6] = s; key ^= ZPiece[code][s]; } inline void remove_piece(int s) { int code = board[s]; if (code == NO_PIECE) return; bb[code] &= ~(1ULL<<s); occ[code/6] &= ~(1ULL<<s); occAll &= ~(1ULL<<s); board[s] = NO_PIECE; if ((code%6)==KING) kingSq[code/6] = -1; key ^= ZPiece[code][s]; } inline void move_piece(int from, int to) { int code = board[from]; bb[code] ^= (1ULL<<from) | (1ULL<<to); occ[code/6] ^= (1ULL<<from) | (1ULL<<to); occAll ^= (1ULL<<from) | (1ULL<<to); board[from] = NO_PIECE; board[to] = code; key ^= ZPiece[code][from]; key ^= ZPiece[code][to]; if ((code%6)==KING) kingSq[code/6] = to; } void set_startpos() { reset(); string fen = "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1"; set_fen(fen); } bool set_fen(const string& fen) { reset(); istringstream ss(fen); string boardPart, stm, castle, epstr; int hm=0, fm=1; if (!(ss >> boardPart >> stm >> castle >> epstr)) return false; if (!(ss >> hm)) hm = 0; if (!(ss >> fm)) fm = 1; int sqi = 56; for (char c : boardPart) { if (c == '/') { sqi -= 16; continue; } if (isdigit((unsigned char)c)) { sqi += c - '0'; continue; } int color = isupper((unsigned char)c)? WHITE:BLACK; char pc = (char)tolower((unsigned char)c); int pt = -1; if (pc=='p') pt=PAWN; else if (pc=='n') pt=KNIGHT; else if (pc=='b') pt=BISHOP; else if (pc=='r') pt=ROOK; else if (pc=='q') pt=QUEEN; else if (pc=='k') pt=KING; if (pt==-1) return false; put_piece(code(color, pt), sqi++); } side = (stm=="w")?WHITE:BLACK; key ^= (side==WHITE?0:ZSide); castling = 0; if (castle.find('K')!=string::npos) castling |= 1; if (castle.find('Q')!=string::npos) castling |= 2; if (castle.find('k')!=string::npos) castling |= 4; if (castle.find('q')!=string::npos) castling |= 8; key ^= ZCastle[castling]; if (epstr != "-" && epstr.size()==2) { int f = epstr[0]-'a'; int r = epstr[1]-'1'; int e = sq(f,r); ep = e; if (r==2 || r==5) { key ^= ZEP[f]; } } else ep = -1; halfmove = hm; fullmove = fm; return true; } inline bool square_attacked(int s, int bySide) const { if ( (PawnAtt[bySide^1][s] & bb[code(bySide, PAWN)]) ) return true; if ( KnightAtt[s] & bb[code(bySide, KNIGHT)] ) return true; if ( KingAtt[s] & bb[code(bySide, KING)] ) return true; U64 occNow = occAll; if ( bishop_attacks(s, occNow) & (bb[code(bySide,BISHOP)] | bb[code(bySide,QUEEN)]) ) return true; if ( rook_attacks(s, occNow) & (bb[code(bySide,ROOK)] | bb[code(bySide,QUEEN)]) ) return true; return false; } uint64_t compute_key() const { uint64_t k=0; for(int p=0;p<12;p++){ U64 bbb=bb[p]; while(bbb){ int s=lsb(bbb); bbb&=bbb-1; k^=ZPiece[p][s]; } } k ^= ZCastle[castling]; if (side==BLACK) k ^= ZSide; if (ep!=-1) k ^= ZEP[file_of(ep)]; return k; } }; // ----------------------------- Castling 权利更新表 ----------------------------- int CastleMaskFromTo[64]; int CastleMaskOnCapture[64]; static void init_castle_masks() { for(int i=0;i<64;i++){ CastleMaskFromTo[i]=15; CastleMaskOnCapture[i]=15; } // 白 CastleMaskFromTo[sq(4,0)] = 15 & ~1 & ~2; CastleMaskFromTo[sq(7,0)] = 15 & ~1; CastleMaskFromTo[sq(0,0)] = 15 & ~2; CastleMaskOnCapture[sq(7,0)] = 15 & ~1; CastleMaskOnCapture[sq(0,0)] = 15 & ~2; // 黑 CastleMaskFromTo[sq(4,7)] = 15 & ~4 & ~8; CastleMaskFromTo[sq(7,7)] = 15 & ~4; CastleMaskFromTo[sq(0,7)] = 15 & ~8; CastleMaskOnCapture[sq(7,7)] = 15 & ~4; CastleMaskOnCapture[sq(0,7)] = 15 & ~8; } // ----------------------------- 着法编码 ----------------------------- static inline uint32_t enc_move(int from, int to, int piece, int captured, int promoType, bool ep, bool castle, bool dpp) { return (from) | (to<<6) | (piece<<12) | ((captured==-1?15:captured)<<16) | ((promoType==-1?15:promoType)<<20) | (ep?1u<<24:0) | (castle?1u<<25:0) | (dpp?1u<<26:0); } static inline int m_from(uint32_t m){ return m & 63; } static inline int m_to(uint32_t m){ return (m>>6) & 63; } static inline int m_piece(uint32_t m){ return (m>>12) & 15; } static inline int m_captured(uint32_t m){ int x=(m>>16)&15; return x==15?-1:x; } static inline int m_promo(uint32_t m){ int x=(m>>20)&15; return x==15?-1:x; } static inline bool m_ep(uint32_t m){ return (m>>24)&1; } static inline bool m_castle(uint32_t m){ return (m>>25)&1; } static inline bool m_dpp(uint32_t m){ return (m>>26)&1; } struct MoveEntry { uint32_t m; int score; }; struct MoveList { MoveEntry mv[256]; int size=0; void clear(){ size=0; } void add(uint32_t m, int score=0){ mv[size++]={m,score}; } }; // ----------------------------- TT/历史/Killer/反击/吃史 ----------------------------- int SEE_Value[12] = { 100, 320, 330, 500, 900, 20000, 100, 320, 330, 500, 900, 20000 }; struct TTEntry { uint64_t key; uint32_t move; int16_t score; int16_t eval; uint8_t depth; uint8_t flag; // 0 exact, 1 lower, 2 upper uint8_t age; uint8_t pad; }; struct TransTable { vector<TTEntry> t; uint64_t mask=0; uint8_t age=0; size_t sizeEntries=0; void resizeMB(size_t MB) { if (MB < 1) MB = 1; size_t bytes = MB * 1024ULL * 1024ULL; size_t n = bytes / sizeof(TTEntry); size_t pow2=1; while(pow2 < n) pow2 <<= 1; n = pow2; t.assign(n, TTEntry{0,0,0,0,0,0,0,0}); mask = n-1; sizeEntries = n; age=0; } void clear() { for (auto &e : t) e = TTEntry{0,0,0,0,0,0,0,0}; age=0; } inline TTEntry* probe(uint64_t key, bool &found) { TTEntry* e = &t[key & mask]; found = (e->key == key); return e; } // 近似 hashfull(千分值):采样 1000 项;非零 key 视作占用(或 age 最近) int hashfull() const { if (t.empty()) return 0; int sample = (int)min<size_t>(1000, t.size()); int used=0; for (int i=0;i<sample;i++){ if (t[i].key!=0) used++; } return (used*1000)/sample; } } TT; int History[2][64][64]; uint32_t Killers1[MAX_PLY], Killers2[MAX_PLY]; // 新增:反击表(上一步 from->to 记录的最佳回应) uint32_t CounterMove[64][64]; // 新增:吃子历史(按我方棋子类型与落点累计奖励) int CaptureHistory[2][6][64]; // ----------------------------- 统计/时间 ----------------------------- struct SearchStats { uint64_t nodes=0; uint64_t qnodes=0; chrono::steady_clock::time_point start; int seldepth=0; void reset(){ nodes=0; qnodes=0; seldepth=0; start=chrono::steady_clock::now(); } uint64_t elapsed_ms() const { return (uint64_t)chrono::duration_cast<chrono::milliseconds>(chrono::steady_clock::now()-start).count(); } uint64_t nps() const { auto ms = elapsed_ms(); if (ms==0) ms=1; return (nodes*1000ULL)/ms; } } Stats; struct Limits { int depth = INT_MAX; uint64_t nodes = ULLONG_MAX; int movetime = -1; int wtime = -1, btime = -1, winc = 0, binc = 0, movestogo = 30; bool ponder = false; }; static int game_phase(const Board& b) { const int phaseW[6] = {0,1,1,2,4,0}; int phase = 0; for (int color=0;color<2;color++) { for (int pt=PAWN; pt<=KING; ++pt) { int code = Board::code(color, pt); U64 bbp = b.bb[code]; while (bbp) { poplsb(bbp); phase += phaseW[pt]; } } } if (phase > 24) phase = 24; return phase; } struct TimeMgr { uint64_t softStopTimeMs = ULLONG_MAX; // 建议停止 uint64_t stopTimeMs = ULLONG_MAX; // 硬停止 int timeForMoveMs = 1000; Limits limCache; void setup(const Board& b, const Limits& lim) { using namespace chrono; limCache = lim; auto nowms = (uint64_t)duration_cast<milliseconds>(chrono::steady_clock::now().time_since_epoch()).count(); if (lim.movetime > 0) { timeForMoveMs = max(0, lim.movetime - OPT.MoveOverhead); softStopTimeMs = nowms + (int)(timeForMoveMs * 0.93); stopTimeMs = nowms + timeForMoveMs; return; } int myTime = (b.side == WHITE ? lim.wtime : lim.btime); int myInc = (b.side == WHITE ? lim.winc : lim.binc); if (myTime < 0) myTime = 1000; if (myInc < 0) myInc = 0; double mgf = game_phase(b) / 24.0; double targetOpenMs = 4500.0; double targetMidMs = 12000.0; double targetEndMs = 6000.0; double targetMs = 0.0; if (mgf >= 0.66) { targetMs = targetOpenMs; } else if (mgf >= 0.33) { double t = (mgf - 0.33) / (0.66 - 0.33); targetMs = targetMidMs * (1.0 - t) + targetOpenMs * t; } else { double t = (mgf - 0.00) / (0.33 - 0.00); targetMs = targetEndMs * (1.0 - t) + targetMidMs * t; } bool inCheck = b.square_attacked(b.kingSq[b.side], b.side^1); if (inCheck) targetMs *= 1.3; double capFrac = (mgf >= 0.66 ? 0.12 : (mgf >= 0.33 ? 0.16 : 0.12)); double capByBank = myTime * capFrac; targetMs = min(targetMs, capByBank); if (myInc > 0) { targetMs = min(targetMs + myInc * 0.35, capByBank); } if (mgf <= 0.33 && myTime < myInc * 10) { targetMs = min<double>(targetMs, max(80.0, myInc * 0.90)); } targetMs = max(60.0, targetMs); double hardCap = (mgf >= 0.66 ? 8000.0 : (mgf >= 0.33 ? 20000.0 : 12000.0)); targetMs = min(targetMs, hardCap); timeForMoveMs = (int)targetMs - OPT.MoveOverhead; if (timeForMoveMs < 10) timeForMoveMs = 10; softStopTimeMs = nowms + (uint64_t)(timeForMoveMs * 0.93); stopTimeMs = nowms + (uint64_t) timeForMoveMs; } bool time_up() const { using namespace chrono; // 同时受节点上限约束 if (Stats.nodes >= limCache.nodes) return true; auto nowms = (uint64_t)duration_cast<milliseconds>(chrono::steady_clock::now().time_since_epoch()).count(); return nowms >= stopTimeMs; } bool soft_time_up() const { using namespace chrono; auto nowms = (uint64_t)duration_cast<milliseconds>(steady_clock::now().time_since_epoch()).count(); return nowms >= softStopTimeMs; } } TM; // ----------------------------- 重复/不足子力 ----------------------------- static bool is_threefold(const Board& b) { int limit = min(b.sp, b.halfmove); int reps = 1; for (int i = 1; i <= limit; ++i) { if (b.st[b.sp - i].key == b.key) { reps++; if (reps >= 3) return true; } } return false; } static bool insufficient_material(const Board& b) { U64 wp = b.bb[Board::code(WHITE, PAWN)] | b.bb[Board::code(BLACK, PAWN)]; U64 wrq = b.bb[Board::code(WHITE, ROOK)] | b.bb[Board::code(WHITE, QUEEN)] | b.bb[Board::code(BLACK, ROOK)] | b.bb[Board::code(BLACK, QUEEN)]; if (wp || wrq) return false; int wn = popcount(b.bb[Board::code(WHITE, KNIGHT)]); int wb = popcount(b.bb[Board::code(WHITE, BISHOP)]); int bn = popcount(b.bb[Board::code(BLACK, KNIGHT)]); int bbp= popcount(b.bb[Board::code(BLACK, BISHOP)]); int minor = wn+wb+bn+bbp; if (minor==0) return true; if (minor==1) return true; if (minor==2 && wn==1 && bn==1 && wb==0 && bbp==0) return true; if (minor==2 && wb==1 && bbp==1 && wn==0 && bn==0) return true; return false; } // ----------------------------- 走子/悔棋 ----------------------------- static bool make_move(Board& b, uint32_t m) { int from = m_from(m), to = m_to(m); int pc = m_piece(m); int side = b.side; int pt = pc % 6; State &st = b.st[b.sp++]; st.key = b.key; st.castling = b.castling; st.ep = b.ep; st.halfmove = b.halfmove; st.captured = -1; st.move = m; st.fullmove = b.fullmove; if (b.ep != -1) b.key ^= ZEP[file_of(b.ep)]; b.ep = -1; if (pt==PAWN || m_captured(m)!=-1) b.halfmove = 0; else b.halfmove++; if (m_ep(m)) { int capSq = to + (side==WHITE ? -8 : 8); st.captured = Board::code(side^1, PAWN); b.remove_piece(capSq); } else if (m_captured(m) != -1) { st.captured = m_captured(m); b.remove_piece(to); b.castling &= CastleMaskOnCapture[to]; } b.move_piece(from, to); if (m_promo(m) != -1) { int promoPt = m_promo(m); int promoCode = Board::code(side, promoPt); b.remove_piece(to); b.put_piece(promoCode, to); } if (m_castle(m)) { if (side==WHITE) { if (to == sq(6,0)) b.move_piece(sq(7,0), sq(5,0)); else if (to == sq(2,0)) b.move_piece(sq(0,0), sq(3,0)); } else { if (to == sq(6,7)) b.move_piece(sq(7,7), sq(5,7)); else if (to == sq(2,7)) b.move_piece(sq(0,7), sq(3,7)); } } if (m_dpp(m)) { int epSq = to + (side==WHITE ? -8 : 8); b.ep = epSq; b.key ^= ZEP[file_of(b.ep)]; } b.castling &= CastleMaskFromTo[from]; b.castling &= CastleMaskFromTo[to]; b.key ^= ZCastle[st.castling]; b.key ^= ZCastle[b.castling]; b.side ^= 1; b.key ^= ZSide; if (side == BLACK) b.fullmove++; int us = side; int ksq = b.kingSq[us]; if (b.square_attacked(ksq, us^1)) { b.side ^= 1; b.key ^= ZSide; b.key ^= ZCastle[b.castling]; b.castling = st.castling; b.key ^= ZCastle[b.castling]; if (b.ep!=-1) b.key ^= ZEP[file_of(b.ep)]; b.ep = st.ep; if (b.ep!=-1) b.key ^= ZEP[file_of(b.ep)]; b.halfmove = st.halfmove; b.fullmove = st.fullmove; if (m_castle(m)) { if (us==WHITE) { if (to == sq(6,0)) b.move_piece(sq(5,0), sq(7,0)); else if (to == sq(2,0)) b.move_piece(sq(3,0), sq(0,0)); } else { if (to == sq(6,7)) b.move_piece(sq(5,7), sq(7,7)); else if (to == sq(2,7)) b.move_piece(sq(3,7), sq(0,7)); } } if (m_promo(m) != -1) { b.remove_piece(to); b.put_piece(Board::code(us, PAWN), to); } b.move_piece(to, from); if (m_ep(m)) { int capSq = to + (us==WHITE ? -8 : 8); b.put_piece(Board::code(us^1, PAWN), capSq); } else if (st.captured != -1) { b.put_piece(st.captured, to); } b.sp--; return false; } return true; } static void unmake_move(Board& b) { State &st = b.st[--b.sp]; uint32_t m = st.move; int from = m_from(m), to = m_to(m); int us = b.side ^ 1; b.side ^= 1; b.key ^= ZSide; b.fullmove = st.fullmove; b.key ^= ZCastle[b.castling]; b.castling = st.castling; b.key ^= ZCastle[b.castling]; if (b.ep != -1) b.key ^= ZEP[file_of(b.ep)]; b.ep = st.ep; if (b.ep != -1) b.key ^= ZEP[file_of(b.ep)]; b.halfmove = st.halfmove; if (m_castle(m)) { if (us==WHITE) { if (to == sq(6,0)) b.move_piece(sq(5,0), sq(7,0)); else if (to == sq(2,0)) b.move_piece(sq(3,0), sq(0,0)); } else { if (to == sq(6,7)) b.move_piece(sq(5,7), sq(7,7)); else if (to == sq(2,7)) b.move_piece(sq(3,7), sq(0,7)); } } if (m_promo(m) != -1) { b.remove_piece(to); b.put_piece(Board::code(us, PAWN), to); } b.move_piece(to, from); if (m_ep(m)) { int capSq = to + (us==WHITE ? -8 : 8); b.put_piece(Board::code(us^1, PAWN), capSq); } else if (st.captured != -1) { b.put_piece(st.captured, to); } } // ----------------------------- 着法生成 ----------------------------- static void gen_moves(const Board& b, MoveList& list, bool capsOnly=false) { list.clear(); int us = b.side, them = us^1; U64 occUs = b.occ[us], occThem = b.occ[them], occAll = b.occAll; // Pawns(逐个 from) U64 pawns = b.bb[Board::code(us, PAWN)]; if (us == WHITE) { U64 p = pawns; while (p) { int s = lsb(p); p &= p-1; int r = rank_of(s), f = file_of(s); if (f>0) { int to = s + 7; if (to<64 && ((occThem >> to)&1ULL)) { int captured = b.board[to]; if (r==6) { int promoPts[4]={QUEEN,ROOK,BISHOP,KNIGHT}; for (int i=0;i<4;i++) list.add(enc_move(s,to,Board::code(us,PAWN), captured, promoPts[i], false, false, false), 200000 + SEE_Value[captured] + 50*i); } else { list.add(enc_move(s,to,Board::code(us,PAWN), captured, -1, false, false, false), 100000 + SEE_Value[captured] - 10); } } if (to==b.ep) { list.add(enc_move(s,to,Board::code(us,PAWN), Board::code(them,PAWN), -1, true, false, false), 99950); } } if (f<7) { int to = s + 9; if (to<64 && ((occThem >> to)&1ULL)) { int captured = b.board[to]; if (r==6) { int promoPts[4]={QUEEN,ROOK,BISHOP,KNIGHT}; for (int i=0;i<4;i++) list.add(enc_move(s,to,Board::code(us,PAWN), captured, promoPts[i], false, false, false), 200000 + SEE_Value[captured] + 50*i); } else { list.add(enc_move(s,to,Board::code(us,PAWN), captured, -1, false, false, false), 100000 + SEE_Value[captured] - 10); } } if (to==b.ep) { list.add(enc_move(s,to,Board::code(us,PAWN), Board::code(them,PAWN), -1, true, false, false), 99950); } } if (capsOnly) continue; int to1 = s + 8; if (to1<64 && ((occAll >> to1)&1ULL)==0) { if (r==6) { int promoPts[4]={QUEEN,ROOK,BISHOP,KNIGHT}; for (int i=0;i<4;i++) list.add(enc_move(s,to1,Board::code(us,PAWN), -1, promoPts[i], false, false, false), 150000 + 50*i); } else { list.add(enc_move(s,to1,Board::code(us,PAWN), -1, -1, false, false, false), 10); if (r==1) { int to2 = s + 16; if (((occAll >> to2)&1ULL)==0) { list.add(enc_move(s,to2,Board::code(us,PAWN), -1, -1, false, false, true), 9); } } } } } } else { U64 p = pawns; while (p) { int s = lsb(p); p &= p-1; int r = rank_of(s), f = file_of(s); if (f>0) { int to = s - 9; if (to>=0 && ((occThem >> to)&1ULL)) { int captured = b.board[to]; if (r==1) { int promoPts[4]={QUEEN,ROOK,BISHOP,KNIGHT}; for (int i=0;i<4;i++) list.add(enc_move(s,to,Board::code(us,PAWN), captured, promoPts[i], false, false, false), 200000 + SEE_Value[captured] + 50*i); } else { list.add(enc_move(s,to,Board::code(us,PAWN), captured, -1, false, false, false), 100000 + SEE_Value[captured] - 10); } } if (to==b.ep) { list.add(enc_move(s,to,Board::code(us,PAWN), Board::code(them,PAWN), -1, true, false, false), 99950); } } if (f<7) { int to = s - 7; if (to>=0 && ((occThem >> to)&1ULL)) { int captured = b.board[to]; if (r==1) { int promoPts[4]={QUEEN,ROOK,BISHOP,KNIGHT}; for (int i=0;i<4;i++) list.add(enc_move(s,to,Board::code(us,PAWN), captured, promoPts[i], false, false, false), 200000 + SEE_Value[captured] + 50*i); } else { list.add(enc_move(s,to,Board::code(us,PAWN), captured, -1, false, false, false), 100000 + SEE_Value[captured] - 10); } } if (to==b.ep) { list.add(enc_move(s,to,Board::code(us,PAWN), Board::code(them,PAWN), -1, true, false, false), 99950); } } if (capsOnly) continue; int to1 = s - 8; if (to1>=0 && ((occAll >> to1)&1ULL)==0) { if (r==1) { int promoPts[4]={QUEEN,ROOK,BISHOP,KNIGHT}; for (int i=0;i<4;i++) list.add(enc_move(s,to1,Board::code(us,PAWN), -1, promoPts[i], false, false, false), 150000 + 50*i); } else { list.add(enc_move(s,to1,Board::code(us,PAWN), -1, -1, false, false, false), 10); if (r==6) { int to2 = s - 16; if (((occAll >> to2)&1ULL)==0) { list.add(enc_move(s,to2,Board::code(us,PAWN), -1, -1, false, false, true), 9); } } } } } } // Knights { U64 bbk = b.bb[Board::code(us, KNIGHT)]; while (bbk) { int s = lsb(bbk); bbk &= bbk-1; U64 att = KnightAtt[s] & ~occUs; U64 x = capsOnly ? (att & occThem) : att; while (x) { int to = lsb(x); x &= x-1; int captured = b.board[to]; bool cap = captured!=-1; list.add(enc_move(s,to,Board::code(us,KNIGHT), captured, -1, false, false, false), cap ? 90000 + SEE_Value[captured] - 5 : 20); } } } // Bishops { U64 bbb = b.bb[Board::code(us, BISHOP)]; while (bbb) { int s = lsb(bbb); bbb &= bbb-1; U64 att = bishop_attacks(s, b.occAll) & ~occUs; U64 x = capsOnly ? (att & occThem) : att; while (x) { int to = lsb(x); x &= x-1; int captured = b.board[to]; bool cap = captured!=-1; list.add(enc_move(s,to,Board::code(us,BISHOP), captured, -1, false, false, false), cap ? 91000 + SEE_Value[captured] : 19); } } } // Rooks { U64 bbr = b.bb[Board::code(us, ROOK)]; while (bbr) { int s = lsb(bbr); bbr &= bbr-1; U64 att = rook_attacks(s, b.occAll) & ~occUs; U64 x = capsOnly ? (att & occThem) : att; while (x) { int to = lsb(x); x &= x-1; int captured = b.board[to]; bool cap = captured!=-1; list.add(enc_move(s,to,Board::code(us,ROOK), captured, -1, false, false, false), cap ? 92000 + SEE_Value[captured] : 18); } } } // Queens { U64 bbq = b.bb[Board::code(us, QUEEN)]; while (bbq) { int s = lsb(bbq); bbq &= bbq-1; U64 att = queen_attacks(s, b.occAll) & ~occUs; U64 x = capsOnly ? (att & occThem) : att; while (x) { int to = lsb(x); x &= x-1; int captured = b.board[to]; bool cap = captured!=-1; list.add(enc_move(s,to,Board::code(us,QUEEN), captured, -1, false, false, false), cap ? 93000 + SEE_Value[captured] : 17); } } } // King { int s = b.kingSq[us]; U64 att = KingAtt[s] & ~occUs; U64 x = capsOnly ? (att & occThem) : att; while (x) { int to = lsb(x); x &= x-1; int captured = b.board[to]; bool cap = captured!=-1; list.add(enc_move(s,to,Board::code(us,KING), captured, -1, false, false, false), cap ? 94000 + SEE_Value[captured] : 16); } if (!capsOnly) { int them = us^1; if (us==WHITE && s==sq(4,0)) { bool inCheck = b.square_attacked(s, them); if ((b.castling & 1) && !inCheck && b.board[sq(5,0)]==NO_PIECE && b.board[sq(6,0)]==NO_PIECE && !b.square_attacked(sq(5,0), them) && !b.square_attacked(sq(6,0), them)) { list.add(enc_move(s, sq(6,0), Board::code(us,KING), -1, -1, false, true, false), 5); } if ((b.castling & 2) && !inCheck && b.board[sq(3,0)]==NO_PIECE && b.board[sq(2,0)]==NO_PIECE && b.board[sq(1,0)]==NO_PIECE && !b.square_attacked(sq(3,0), them) && !b.square_attacked(sq(2,0), them)) { list.add(enc_move(s, sq(2,0), Board::code(us,KING), -1, -1, false, true, false), 5); } } else if (us==BLACK && s==sq(4,7)) { bool inCheck = b.square_attacked(s, them); if ((b.castling & 4) && !inCheck && b.board[sq(5,7)]==NO_PIECE && b.board[sq(6,7)]==NO_PIECE && !b.square_attacked(sq(5,7), them) && !b.square_attacked(sq(6,7), them)) { list.add(enc_move(s, sq(6,7), Board::code(us,KING), -1, -1, false, true, false), 5); } if ((b.castling & 8) && !inCheck && b.board[sq(3,7)]==NO_PIECE && b.board[sq(2,7)]==NO_PIECE && b.board[sq(1,7)]==NO_PIECE && !b.square_attacked(sq(3,7), them) && !b.square_attacked(sq(2,7), them)) { list.add(enc_move(s, sq(2,7), Board::code(us,KING), -1, -1, false, true, false), 5); } } } } } // 静态搜索用:吃子 + 非吃子升变 static void gen_qmoves(const Board& b, MoveList& list) { gen_moves(b, list, true); int us = b.side; if (us == WHITE) { U64 pawns = b.bb[Board::code(WHITE, PAWN)]; U64 single = ((pawns << 8) & ~b.occAll) & Rank8; U64 pp = single; while (pp) { int to = lsb(pp); pp &= pp-1; int from = to - 8; int promoPts[4]={QUEEN,ROOK,BISHOP,KNIGHT}; for (int i=0;i<4;i++) { list.add(enc_move(from,to,Board::code(us,PAWN), -1, promoPts[i], false, false, false), 160000 + 50*i); } } } else { U64 pawns = b.bb[Board::code(BLACK, PAWN)]; U64 single = ((pawns >> 8) & ~b.occAll) & Rank1; U64 pp = single; while (pp) { int to = lsb(pp); pp &= pp-1; int from = to + 8; int promoPts[4]={QUEEN,ROOK,BISHOP,KNIGHT}; for (int i=0;i<4;i++) { list.add(enc_move(from,to,Board::code(us,PAWN), -1, promoPts[i], false, false, false), 160000 + 50*i); } } } } // ----------------------------- SEE / 攻击者 ----------------------------- static inline U64 attackers_to_sq(const U64 bb[12], int color, int s, U64 occ) { U64 att = 0ULL; att |= PawnAtt[color^1][s] & bb[Board::code(color, PAWN)]; att |= KnightAtt[s] & bb[Board::code(color, KNIGHT)]; att |= KingAtt[s] & bb[Board::code(color, KING)]; att |= bishop_attacks(s, occ) & (bb[Board::code(color, BISHOP)] | bb[Board::code(color, QUEEN)]); att |= rook_attacks(s, occ) & (bb[Board::code(color, ROOK)] | bb[Board::code(color, QUEEN)]); return att; } // 修复:正确处理“升变吃子”在交换序列中的子力类型 static int see_move(const Board& b, uint32_t m) { int from = m_from(m), to = m_to(m); int mover = m_piece(m); int us = mover/6, them = us^1; int captured = m_captured(m); int promo = m_promo(m); if (captured == -1 && !m_ep(m)) return 0; U64 workBB[12]; memcpy(workBB, b.bb, sizeof(workBB)); U64 occ = b.occAll; U64 occSide[2] = { b.occ[WHITE], b.occ[BLACK] }; if (m_ep(m)) { int capSq = to + (us==WHITE ? -8 : 8); workBB[Board::code(them, PAWN)] &= ~(1ULL<<capSq); occ &= ~(1ULL<<capSq); occSide[them] &= ~(1ULL<<capSq); captured = Board::code(them, PAWN); } else if (captured != -1) { workBB[captured] &= ~(1ULL<<to); occ &= ~(1ULL<<to); occSide[captured/6] &= ~(1ULL<<to); } workBB[mover] &= ~(1ULL<<from); occ &= ~(1ULL<<from); occSide[us] &= ~(1ULL<<from); int curPieceCode; if (promo != -1) { int promoCode = Board::code(us, promo); workBB[promoCode] |= 1ULL<<to; curPieceCode = promoCode; } else { workBB[mover] |= 1ULL<<to; curPieceCode = mover; } occ |= 1ULL<<to; occSide[us] |= 1ULL<<to; int curPieceType = curPieceCode % 6; int gain[32]; int d=0; gain[0] = SEE_Value[captured==-1?Board::code(them,PAWN):captured]; int side = them; while (true) { U64 att = attackers_to_sq(workBB, side, to, occ); if (!att) break; int codes[6] = {PAWN, KNIGHT, BISHOP, ROOK, QUEEN, KING}; int atkCode = -1, atkSq=-1, attackerType=-1; for (int i=0;i<6;i++) { int code = Board::code(side, codes[i]); U64 a = att & workBB[code]; if (a) { attackerType=codes[i]; atkCode=code; atkSq=lsb(a); break; } } if (atkCode == -1) break; ++d; gain[d] = SEE_Value[curPieceType] - gain[d-1]; workBB[curPieceCode] &= ~(1ULL<<to); occ &= ~(1ULL<<to); occSide[curPieceCode/6] &= ~(1ULL<<to); workBB[atkCode] &= ~(1ULL<<atkSq); occ &= ~(1ULL<<atkSq); occSide[side] &= ~(1ULL<<atkSq); workBB[atkCode] |= 1ULL<<to; occ |= 1ULL<<to; occSide[side] |= 1ULL<<to; curPieceCode = atkCode; curPieceType = attackerType; side ^= 1; } while (d) { gain[d-1] = -max(-gain[d-1], gain[d]); --d; } return gain[0]; } static inline bool see_ge(const Board& b, uint32_t m, int threshold) { return see_move(b, m) >= threshold; } // ----------------------------- 评估(扩展:王安/兵形/车线/后出动/Contempt) ----------------------------- const int mg_value[6] = {82, 337, 365, 477, 1025, 0}; const int eg_value[6] = {94, 281, 297, 512, 936, 0}; const int mg_pawn_table[64] = { 0, 0, 0, 0, 0, 0, 0, 0, 98, 134, 61, 95, 68, 126, 34, -11, -6, 7, 26, 31, 65, 56, 25, -20, -14, 13, 26, 21, 23, 12, 7, -23, -27, -2, 5, 12, 17, -4, -10, -25, -26, -4, -4, -10, 3, 3, 33, -12, -35, -1, -20, -23, -15, 24, 38, -22, 0, 0, 0, 0, 0, 0, 0, 0 }; const int eg_pawn_table[64] = { 0, 0, 0, 0, 0, 0, 0, 0, 178, 173, 158, 134, 147, 132, 165, 187, 94, 100, 85, 67, 56, 53, 82, 84, 32, 24, 23, 5, 8, 14, 17, 17, 13, 9, 3, 3, 3, -8, 3, -1, 4, 7, -6, 1, 0, -5, -1, -8, 13, 8, 8, 10, 13, 0, 2, -7, 0, 0, 0, 0, 0, 0, 0, 0 }; const int mg_knight_table[64] = { -167,-89,-34,-49,61,-97,-15,-107, -73,-41,72,36,23,62,7,-17, -47,60,37,65,84,129,73,44, -9,17,19,53,37,69,18,22, -13,4,16,13,28,19,21,-8, -23,-9,12,10,19,17,25,-16, -29,-53,-12,-3,-1,18,-14,-19, -105,-11,-58,-33,-17,-28,-9,-23 }; const int eg_knight_table[64] = { -58,-38,-13,-28,-31,-27,-63,-99, -25,-8,-25,-2,-9,-25,-24,-52, -24,-20,10,9,-1,-9,-19,-41, -17,3,22,22,22,11,8,-18, -18,-6,16,25,16,17,4,-18, -23,-3,-1,15,10,-3,-20,-22, -42,-20,-10,-5,-2,-20,-23,-44, -29,-51,-23,-15,-22,-18,-50,-64 }; const int mg_bishop_table[64] = { -29,4,-82,-37,-25,-42,7,-8, -26,16,-18,-13,30,59,18,-47, -16,37,43,40,35,50,37,-2, -4,5,19,50,37,37,7,-2, -6,13,13,26,34,12,10,4, 0,15,15,15,14,27,18,10, 4,15,16,0,7,21,33,1, -33,-3,-14,-21,-13,-12,-39,-21 }; const int eg_bishop_table[64] = { -14,-21,-11,-8,-7,-9,-17,-24, -8,-4,7,-12,-3,-13,-4,-14, 2,-8,0,-1,-2,6,0,4, -3,9,12,9,14,10,3,2, -6,3,13,19,7,10,-3,-9, -12,-3,8,10,13,3,-7,-15, -14,-18,-7,-1,4,-9,-15,-27, -23,-9,-23,-5,-9,-16,-5,-17 }; const int mg_rook_table[64] = { 32,42,32,51,63,9,31,43, 27,32,58,62,80,67,26,44, -5,19,26,36,17,45,61,16, -24,-11,7,26,24,35,-8,-20, -36,-26,-12,-1,9,-7,6,-23, -45,-25,-16,-17,3,0,-5,-33, -44,-16,-20,-9,-1,11,-6,-71, -19,-13,1,17,16,7,-37,-26 }; const int eg_rook_table[64] = { 13,10,18,15,12,12,8,5, 11,13,13,11,-3,3,8,3, 7,7,7,5,4,-3,-5,-3, 4,3,13,1,2,1,-1,2, 3,5,8,4,-5,-6,-8,-11, -4,0,-5,-1,-7,-12,-8,-16, -6,-6,0,2,-9,-9,-11,-3, -9,2,3,-1,-5,-13,4,-20 }; const int mg_queen_table[64] = { -28,0,29,12,59,44,43,45, -24,-39,-5,1,-16,57,28,54, -13,-17,7,8,29,56,47,57, -27,-27,-16,-16,-1,17,-2,1, -9,-26,-9,-10,-2,-4,3,-3, -14,2,-11,-2,-5,2,14,5, -35,-8,11,2,8,15,-3,1, -1,-18,-9,10,-15,-25,-31,-50 }; const int eg_queen_table[64] = { -9,22,22,27,27,19,10,20, -17,20,32,41,58,25,30,0, -20,6,9,49,47,35,19,9, 3,22,24,45,57,40,57,36, -18,28,19,47,31,34,39,23, -16,-27,15,6,9,17,10,5, -22,-23,-30,-16,-16,-23,-36,-32, -33,-28,-22,-43,-5,-32,-20,-41 }; const int mg_king_table[64] = { -65,23,16,-15,-56,-34,2,13, 29,-1,-20,-7,-8,-4,-38,-29, -9,-26,-9,-10,-2,-4,3,-3, -14,-13,-22,-46,-44,-30,-15,-27, -27,-27,-16,-16,-1,17,-2,1, -9,-17,19,-3,-9,-6,14,13, -25,-8,-25,-2,-9,-25,-24,-52, -167,-89,-34,-49,61,-97,-15,-107 }; const int eg_king_table[64] = { -74,-35,-18,-18,-11,15,4,-17, -12,17,14,17,17,38,23,11, 10,17,23,15,20,45,44,13, -8,22,24,27,26,33,26,3, -18,-4,21,24,27,23,9,-11, -19,-3,11,21,23,16,7,-9, -27,-11,4,13,14,4,-5,-17, -53,-34,-21,-11,-28,-14,-24,-43 }; static inline int pst_mg(int pt, int sq) { switch(pt){ case PAWN: return mg_pawn_table[sq]; case KNIGHT: return mg_knight_table[sq]; case BISHOP: return mg_bishop_table[sq]; case ROOK: return mg_rook_table[sq]; case QUEEN: return mg_queen_table[sq]; case KING: return mg_king_table[sq]; } return 0; } static inline int pst_eg(int pt, int sq) { switch(pt){ case PAWN: return eg_pawn_table[sq]; case KNIGHT: return eg_knight_table[sq]; case BISHOP: return eg_bishop_table[sq]; case ROOK: return eg_rook_table[sq]; case QUEEN: return eg_queen_table[sq]; case KING: return eg_king_table[sq]; } return 0; } static inline U64 attackers_to_sq(const Board& b, int color, int s) { return attackers_to_sq(b.bb, color, s, b.occAll); } // 物质平衡(不含王),用于“少兑子”排序偏置(>0 说明当前方领先) static int material_balance_side(const Board& b, int color) { int sum[2] = {0,0}; for (int c=0;c<2;c++) { sum[c] += mg_value[PAWN] * popcount(b.bb[Board::code(c,PAWN)]); sum[c] += mg_value[KNIGHT] * popcount(b.bb[Board::code(c,KNIGHT)]); sum[c] += mg_value[BISHOP] * popcount(b.bb[Board::code(c,BISHOP)]); sum[c] += mg_value[ROOK] * popcount(b.bb[Board::code(c,ROOK)]); sum[c] += mg_value[QUEEN] * popcount(b.bb[Board::code(c,QUEEN)]); } return sum[color] - sum[color^1]; } // [新增] 机动性/垃圾棋子扣分(马/象/车/后),越不动挨罚越多;被攻不受守时加罚;有吃子时减半 static void mobility_penalty_terms(const Board& b, int mgAdd[2], int egAdd[2]) { mgAdd[0]=mgAdd[1]=egAdd[0]=egAdd[1]=0; for (int color=0;color<2;color++){ int them = color^1; U64 occUs = b.occ[color]; U64 occAll = b.occAll; auto apply_piece = [&](int pt, U64 bbp){ while (bbp){ int s = lsb(bbp); bbp &= bbp-1; U64 att=0, cap=0; int mobility=0; switch(pt){ case KNIGHT: att = KnightAtt[s] & ~occUs; cap = KnightAtt[s] & b.occ[them]; break; case BISHOP: att = bishop_attacks(s, occAll) & ~occUs; cap = bishop_attacks(s, occAll) & b.occ[them]; break; case ROOK: att = rook_attacks(s, occAll) & ~occUs; cap = rook_attacks(s, occAll) & b.occ[them]; break; case QUEEN: att = queen_attacks(s, occAll) & ~occUs; cap = queen_attacks(s, occAll) & b.occ[them]; break; default: break; } if (pt==KNIGHT || pt==BISHOP || pt==ROOK || pt==QUEEN) { mobility = popcount(att); int mgPen=0, egPen=0; // 基础“低机动”阈值和每格扣分 if (pt==KNIGHT){ int thr=4; int k = max(0, thr - mobility); mgPen += 6*k; egPen += 4*k; // 边线马小扣分 int f=file_of(s), r=rank_of(s); if (f==0||f==7||r==0||r==7){ mgPen += 6; egPen += 2; } // 完全被堵 if (mobility==0){ mgPen += 10; egPen += 6; } }else if (pt==BISHOP){ int thr=5; int k = max(0, thr - mobility); mgPen += 5*k; egPen += 3*k; if (mobility==0){ mgPen += 10; egPen += 6; } }else if (pt==ROOK){ int thr=7; int k = max(0, thr - mobility); mgPen += 3*k; egPen += 2*k; if (mobility<=1){ mgPen += 12; egPen += 8; } }else if (pt==QUEEN){ int thr=8; int k = max(0, thr - mobility); mgPen += 2*k; egPen += 1*k; if (mobility<=1){ mgPen += 16; egPen += 10; } } // 被攻不受守时,加罚(非送子受限) bool attacked = attackers_to_sq(b.bb, them, s, occAll)!=0; bool defended = attackers_to_sq(b.bb, color, s, occAll)!=0; if (attacked && !defended){ if (pt==KNIGHT||pt==BISHOP){ mgPen += 8; egPen += 4; } else if (pt==ROOK){ mgPen += 10; egPen += 5; } else if (pt==QUEEN){ mgPen += 12; egPen += 6; } } // 若有直接吃子,对扣分打 0.75 系数 if (cap){ mgPen = (mgPen*3)/4; egPen = (egPen*3)/4; } mgAdd[color] -= mgPen; egAdd[color] -= egPen; } } }; apply_piece(KNIGHT, b.bb[Board::code(color, KNIGHT)]); apply_piece(BISHOP, b.bb[Board::code(color, BISHOP)]); apply_piece(ROOK, b.bb[Board::code(color, ROOK)]); apply_piece(QUEEN, b.bb[Board::code(color, QUEEN)]); } } // 兵形小工具 static bool is_isolated_pawn(const Board& b, int color, int s) { int f=file_of(s); U64 adj = AdjacentFiles[f]; U64 pawns = b.bb[Board::code(color, PAWN)]; return (adj & pawns) == 0ULL; } static bool is_doubled_pawn(const Board& b, int color, int s) { int f=file_of(s); U64 pawns = b.bb[Board::code(color, PAWN)] & FileMask[f]; return popcount(pawns) >= 2; } static bool is_backward_pawn(const Board& b, int color, int s) { // 粗略:相邻线己方兵控制不到前进格,且前方被敌兵控制 int r=rank_of(s), f=file_of(s); int forward = (color==WHITE? +1 : -1); int rf = r + forward; if (rf<0 || rf>7) return false; int to = sq(f, rf); U64 myPawns = b.bb[Board::code(color, PAWN)]; U64 theirPawns = b.bb[Board::code(color^1, PAWN)]; bool noSupport = ( (PawnAtt[color^1][to] & myPawns) == 0 ); bool blockedByEnemyPawnCtrl = ( (PawnAtt[color][to] & theirPawns) != 0 ); return noSupport && blockedByEnemyPawnCtrl; } // 简单王安全:兵盾与暴露 static void king_safety_terms(const Board& b, int mgAdd[2], int egAdd[2]) { mgAdd[0]=mgAdd[1]=egAdd[0]=egAdd[1]=0; for(int color=0;color<2;color++){ int ks = b.kingSq[color]; int f=file_of(ks), r=rank_of(ks); // 兵盾:王翼/后翼前三格兵存在加分(越靠近开局越有效) int mg=0, eg=0; int forward = (color==WHITE? +1 : -1); for(int df=-1; df<=1; ++df){ int nf = f+df, nr = r - forward; // 王前一排 if (nf>=0 && nf<8 && nr>=0 && nr<8){ int s = sq(nf, nr); if ( (b.bb[Board::code(color,PAWN)] >> s) & 1ULL ) { mg += 6; eg += 3; } } } // 暴露:王周围空旷且敌方控制多,轻罚 U64 enemyAtt = attackers_to_sq(b.bb, color^1, ks, b.occAll); int around=0; int empty=0; for(int df=-1;df<=1;df++)for(int dr=-1;dr<=1;dr++){ if(!df && !dr) continue; int nf=f+df, nr=r+dr; if(nf<0||nf>7||nr<0||nr>7) continue; around++; int s = sq(nf,nr); if (b.board[s]==NO_PIECE) empty++; } if (empty>=5) mg -= 8; if (enemyAtt) mg -= 6; mgAdd[color]+=mg; egAdd[color]+=eg; } } // 新增:结构/开局常识项(中心兵、连兵、前哨马、堡垒象、开发) // 辅助:判断是否“前哨”(color 在 s 上的马),基于相邻列前方无敌兵 static bool is_knight_outpost(const Board& b, int color, int s) { int them = color^1; (void)them; int f = file_of(s), r = rank_of(s); if (color==WHITE) { if (r < 2) return false; // 太靠后 } else { if (r > 5) return false; } U64 enemyPawns = b.bb[Board::code(them, PAWN)]; U64 lanes = AdjacentFiles[f] & ForwardMask[color][s]; if (lanes & enemyPawns) return false; return true; } static int eval_fianchetto_bishop(const Board& b, int color) { int mg=0, eg=0; int s1 = (color==WHITE? sq(6,1):sq(6,6)); // g2/g7 int s2 = (color==WHITE? sq(1,1):sq(1,6)); // b2/b7 int codeB = Board::code(color, BISHOP); U64 myPawns = b.bb[Board::code(color, PAWN)]; bool kingsideCastled = (color==WHITE? (b.kingSq[WHITE]==sq(6,0)): (b.kingSq[BLACK]==sq(6,7))); // g2/g7 if ((b.bb[codeB] >> s1) & 1ULL) { bool base = ((myPawns >> (color==WHITE? sq(6,1):sq(6,6))) & 1ULL) || // g2/g7 有兵(开局结构) ((myPawns >> (color==WHITE? sq(6,2):sq(6,5))) & 1ULL); // g3/g6 mg += base? 18:12; eg += base? 12:8; if (kingsideCastled) { mg += 6; eg += 4; } // 长对角线清空小加成(附近无己方阻挡) int mid = (color==WHITE? sq(5,2):sq(5,5)); // f3/f6 if (b.board[mid]==NO_PIECE) { mg += 3; eg += 2; } } // b2/b7 if ((b.bb[codeB] >> s2) & 1ULL) { bool base = ((myPawns >> (color==WHITE? sq(1,1):sq(1,6))) & 1ULL) || ((myPawns >> (color==WHITE? sq(1,2):sq(1,5))) & 1ULL); mg += base? 14:10; eg += base? 10:6; } return (mg<<16) | (eg & 0xFFFF); } struct Eval { // 开放/半开放线(对车)与后早出动轻罚 static void rook_file_and_queen_early(const Board& b, int& mgW, int& egW, int& mgB, int& egB) { auto side_bonus = [&](int color, int& mg, int& eg){ U64 rooks = b.bb[Board::code(color, ROOK)]; U64 pawnsUs = b.bb[Board::code(color, PAWN)]; U64 pawnsTh = b.bb[Board::code(color^1, PAWN)]; while (rooks){ int s=lsb(rooks); rooks&=rooks-1; int f=file_of(s); bool own = (pawnsUs & FileMask[f])!=0ULL; bool opp = (pawnsTh & FileMask[f])!=0ULL; if (!own && !opp){ mg+=10; eg+=8; } // 开放线 else if (!own && opp){ mg+=6; eg+=4; } // 半开放线 } // 后过早出动(开局) if (game_phase(b) >= 18){ U64 q = b.bb[Board::code(color, QUEEN)]; if (q){ int qs = lsb(q); int rankHome = (color==WHITE?0:7); if (rank_of(qs) != rankHome) { mg -= 6; } // 轻微 } } }; side_bonus(WHITE, mgW, egW); side_bonus(BLACK, mgB, egB); } // 简单兵形小惩罚 static void pawn_structure_small(const Board& b, int& mgW, int& egW, int& mgB, int& egB) { auto side = [&](int color, int& mg, int& eg){ U64 p = b.bb[Board::code(color, PAWN)]; while (p){ int s=lsb(p); p&=p-1; if (is_doubled_pawn(b, color, s)) { mg -= 6; eg -= 4; } if (is_isolated_pawn(b, color, s)) { mg -= 8; eg -= 6; } if (is_backward_pawn(b, color, s)) { mg -= 5; eg -= 4; } } }; side(WHITE, mgW, egW); side(BLACK, mgB, egB); } static int evaluate(const Board& b) { // 和棋返回 Contempt(偏向当前行棋方) if (b.halfmove >= 100) return (b.side==WHITE? OPT.Contempt : -OPT.Contempt); if (is_threefold(b)) return (b.side==WHITE? OPT.Contempt : -OPT.Contempt); if (insufficient_material(b)) return 0; int mgScore[2]={0,0}, egScore[2]={0,0}; int phase=0; const int phaseW[6]={0,1,1,2,4,0}; // 材料 + PST(2/3 缩放) for (int color=0;color<2;color++){ for (int pt=PAWN; pt<=KING; ++pt){ int code = Board::code(color, pt); U64 bbp = b.bb[code]; while(bbp){ int s = lsb(bbp); bbp &= bbp-1; int sqw = color==WHITE? s : mirror64(s); mgScore[color] += mg_value[pt] + (pst_mg(pt, sqw)*2)/3; egScore[color] += eg_value[pt] + (pst_eg(pt, sqw)*2)/3; phase += phaseW[pt]; } } } // 象双 if (popcount(b.bb[Board::code(WHITE,BISHOP)]) >= 2) { mgScore[WHITE]+=20; egScore[WHITE]+=30; } if (popcount(b.bb[Board::code(BLACK,BISHOP)]) >= 2) { mgScore[BLACK]+=20; egScore[BLACK]+=30; } // 通过兵(适度) { U64 wp = b.bb[Board::code(WHITE, PAWN)]; U64 bp = b.bb[Board::code(BLACK, PAWN)]; U64 t = wp; while (t){ int s = lsb(t); t &= t-1; if ( (bp & PassedMask[WHITE][s]) == 0ULL ) { static const int bonusMg[8] = {0,6,10,16,24,36,50,0}; static const int bonusEg[8] = {0,8,12,20,30,45,70,0}; int r = rank_of(s); mgScore[WHITE] += bonusMg[r]; egScore[WHITE] += bonusEg[r]; } } t = bp; while (t){ int s = lsb(t); t &= t-1; if ( (wp & PassedMask[BLACK][s]) == 0ULL ) { static const int bonusMg[8] = {0,6,10,16,24,36,50,0}; static const int bonusEg[8] = {0,8,12,20,30,45,70,0}; int r = 7 - rank_of(s); mgScore[BLACK] += bonusMg[r]; egScore[BLACK] += bonusEg[r]; } } } // 挂子惩罚(受攻不受守) auto hanging = [&](int color){ int them=color^1; int mg=0, eg=0; U64 occ = b.occAll; for (int pt=PAWN; pt<=QUEEN; ++pt) { U64 bbp = b.bb[Board::code(color, pt)]; while (bbp) { int s = lsb(bbp); bbp&=bbp-1; bool attacked = attackers_to_sq(b.bb, them, s, occ)!=0; bool defended = attackers_to_sq(b.bb, color, s, occ)!=0; if (attacked && !defended) { int pen = (pt==PAWN?10: pt==KNIGHT||pt==BISHOP?24: pt==ROOK?40:70); mg -= pen; eg -= pen/2; } } } return pair<int,int>{mg,eg}; }; auto hW = hanging(WHITE), hB = hanging(BLACK); mgScore[WHITE]+=hW.first; egScore[WHITE]+=hW.second; mgScore[BLACK]+=hB.first; egScore[BLACK]+=hB.second; // [新增] 机动性/垃圾棋子扣分 { int mgM[2], egM[2]; mobility_penalty_terms(b, mgM, egM); mgScore[WHITE]+=mgM[WHITE]; egScore[WHITE]+=egM[WHITE]; mgScore[BLACK]+=mgM[BLACK]; egScore[BLACK]+=egM[BLACK]; } // [新增] 中心兵权重(中心 4 格、中心 16 格、小幅控制) auto center_terms = [&](int color){ int mg=0, eg=0; U64 pawns = b.bb[Board::code(color, PAWN)]; U64 pCenter4 = pawns & Center4Mask; int c4 = popcount(pCenter4); mg += 16 * c4; eg += 8 * c4; U64 pCenter16 = pawns & Center16Mask; int c16 = popcount(pCenter16); mg += 4 * c16; eg += 2 * c16; // d/e 兵额外(开局中局) U64 dFile = pawns & FileMask[3]; U64 eFile = pawns & FileMask[4]; mg += 6 * popcount(dFile); mg += 6 * popcount(eFile); // 控制中心4格的小奖励(由兵控制) U64 attAll = 0ULL; U64 p = pawns; while (p) { int s=lsb(p); p&=p-1; attAll |= PawnAtt[color][s]; } int ctrl4 = popcount(attAll & Center4Mask); mg += 3 * ctrl4; eg += 2 * ctrl4; return pair<int,int>{mg,eg}; }; auto cW = center_terms(WHITE), cB = center_terms(BLACK); mgScore[WHITE]+=cW.first; egScore[WHITE]+=cW.second; mgScore[BLACK]+=cB.first; egScore[BLACK]+=cB.second; // [新增] 中心连兵(d/e 同排) auto central_connected_pawns = [&](int color){ int mg=0, eg=0; U64 pawns = b.bb[Board::code(color, PAWN)]; for (int r=2; r<=5; ++r) { bool dHere = (pawns & (1ULL<<sq(3,r))) != 0; bool eHere = (pawns & (1ULL<<sq(4,r))) != 0; if (dHere && eHere) { mg += 18; eg += 10; } } return pair<int,int>{mg,eg}; }; auto ccW = central_connected_pawns(WHITE), ccB = central_connected_pawns(BLACK); mgScore[WHITE]+=ccW.first; egScore[WHITE]+=ccW.second; mgScore[BLACK]+=ccB.first; egScore[BLACK]+=ccB.second; // [新增] 前哨马 auto outpost_knight_terms = [&](int color){ int mg=0, eg=0; int them = color^1; U64 knights = b.bb[Board::code(color, KNIGHT)]; U64 myPawns = b.bb[Board::code(color, PAWN)]; while (knights){ int s = lsb(knights); knights&=knights-1; if (!is_knight_outpost(b, color, s)) continue; bool supportedByPawn = (PawnAtt[color][s] & myPawns) != 0ULL; int baseMg = 18, baseEg = 24; if (supportedByPawn) { baseMg += 10; baseEg += 12; } if ((Center16Mask >> s) & 1ULL) { baseMg += 4; baseEg += 6; } int attN = popcount((U64)attackers_to_sq(b.bb, them, s, b.occAll) & (b.bb[Board::code(them,KNIGHT)]|b.bb[Board::code(them,BISHOP)])); if (attN==0){ baseMg += 4; baseEg += 6; } mg += baseMg; eg += baseEg; } return pair<int,int>{mg,eg}; }; auto opW = outpost_knight_terms(WHITE), opB = outpost_knight_terms(BLACK); mgScore[WHITE]+=opW.first; egScore[WHITE]+=opW.second; mgScore[BLACK]+=opB.first; egScore[BLACK]+=opB.second; // 堡垒象 { int vW = eval_fianchetto_bishop(b, WHITE); int vB = eval_fianchetto_bishop(b, BLACK); mgScore[WHITE] += (vW>>16); egScore[WHITE] += (vW & 0xFFFF); mgScore[BLACK] += (vB>>16); egScore[BLACK] += (vB & 0xFFFF); } // 易位激励/未易位轻罚 { bool wCastled = (b.kingSq[WHITE]==sq(6,0) || b.kingSq[WHITE]==sq(2,0)); bool bCastled = (b.kingSq[BLACK]==sq(6,7) || b.kingSq[BLACK]==sq(2,7)); if (wCastled) { mgScore[WHITE] += 50; egScore[WHITE] += 20; } if (bCastled) { mgScore[BLACK] += 50; egScore[BLACK] += 20; } if (!wCastled && b.kingSq[WHITE]==sq(4,0) && (b.castling & 3)) { int p = max(0, b.fullmove - 6); int pen = min(20, p*2); mgScore[WHITE] -= pen; } if (!bCastled && b.kingSq[BLACK]==sq(4,7) && (b.castling & 12)) { int p = max(0, b.fullmove - 6); int pen = min(20, p*2); mgScore[BLACK] -= pen; } if (!wCastled && b.kingSq[WHITE]!=sq(4,0) && !b.square_attacked(b.kingSq[WHITE], BLACK)) { int p = max(0, 12 - b.fullmove); int pen = min(18, 2*p); mgScore[WHITE] -= pen; } if (!bCastled && b.kingSq[BLACK]!=sq(4,7) && !b.square_attacked(b.kingSq[BLACK], WHITE)) { int p = max(0, 12 - b.fullmove); int pen = min(18, 2*p); mgScore[BLACK] -= pen; } } // 轻微“开发奖励/未开发惩罚”(仅中度开局阶段) { int gp = game_phase(b); if (gp >= 16) { auto dev = [&](int color){ int mg=0, eg=0; int homeRank = (color==WHITE? 0:7); if ((b.bb[Board::code(color, KNIGHT)] & (1ULL<<sq(1,homeRank)))==0ULL) mg+=2; if ((b.bb[Board::code(color, KNIGHT)] & (1ULL<<sq(6,homeRank)))==0ULL) mg+=2; if ((b.bb[Board::code(color, BISHOP)] & (1ULL<<sq(2,homeRank)))==0ULL) mg+=2; if ((b.bb[Board::code(color, BISHOP)] & (1ULL<<sq(5,homeRank)))==0ULL) mg+=2; return pair<int,int>{mg,eg}; }; auto dW = dev(WHITE), dB = dev(BLACK); mgScore[WHITE]+=dW.first; egScore[WHITE]+=dW.second; mgScore[BLACK]+=dB.first; egScore[BLACK]+=dB.second; } } // 王安全与兵形补充 { int mgWA=0,egWA=0, mgBA=0,egBA=0; king_safety_terms(b, (int[2]){0}, (int[2]){0}); // 为了编译器静默 // 调用两次以便取到到位的数组:直接复用函数形式 int ksMg[2], ksEg[2]; king_safety_terms(b, ksMg, ksEg); mgScore[WHITE]+=ksMg[WHITE]; egScore[WHITE]+=ksEg[WHITE]; mgScore[BLACK]+=ksMg[BLACK]; egScore[BLACK]+=ksEg[BLACK]; int rMgW=0,rEgW=0,rMgB=0,rEgB=0; rook_file_and_queen_early(b, rMgW, rEgW, rMgB, rEgB); mgScore[WHITE]+=rMgW; egScore[WHITE]+=rEgW; mgScore[BLACK]+=rMgB; egScore[BLACK]+=rEgB; int psMgW=0,psEgW=0,psMgB=0,psEgB=0; pawn_structure_small(b, psMgW, psEgW, psMgB, psEgB); mgScore[WHITE]+=psMgW; egScore[WHITE]+=psEgW; mgScore[BLACK]+=psMgB; egScore[BLACK]+=psEgB; } // Tapered eval int mg = mgScore[WHITE] - mgScore[BLACK]; int eg = egScore[WHITE] - egScore[BLACK]; int phaseMax = 24; if (phase > phaseMax) phase = phaseMax; int score = (mg * phase + eg * (phaseMax - phase)) / phaseMax; const int tempo = 8; int final = (b.side == WHITE) ? (score + tempo) : (-(score + tempo)); // 小幅加入 contempt 倾向(不是强制),避免和棋倾向 if (abs(score) < 5) final += (b.side==WHITE? OPT.Contempt/2 : -OPT.Contempt/2); return final; } }; // ----------------------------- 搜索 ----------------------------- static inline int mate_in(int ply) { return MATE_SCORE - ply; } static inline int mated_in(int ply) { return -MATE_SCORE + ply; } static int score_to_tt(int score, int ply) { if (score >= MATE_IN_MAX_PLY) return score + ply; if (score <= -MATE_IN_MAX_PLY) return score - ply; return score; } static int score_from_tt(int score, int ply) { if (score >= MATE_IN_MAX_PLY) return score - ply; if (score <= -MATE_IN_MAX_PLY) return score + ply; return score; } static bool allow_null(const Board& b) { if (b.square_attacked(b.kingSq[b.side], b.side^1)) return false; if ( (b.bb[Board::code(b.side, QUEEN)] | b.bb[Board::code(b.side, ROOK)] | b.bb[Board::code(b.side, BISHOP)] | b.bb[Board::code(b.side, KNIGHT)]) == 0ULL && popcount(b.bb[Board::code(b.side, PAWN)]) <= 2) return false; return true; } static inline bool is_hanging_major(const Board& b, int color) { U64 occ = b.occAll; int them = color ^ 1; U64 valuable = b.bb[Board::code(color, QUEEN)] | b.bb[Board::code(color, ROOK)] | b.bb[Board::code(color, BISHOP)]| b.bb[Board::code(color, KNIGHT)]; while (valuable) { int s = lsb(valuable); valuable &= valuable - 1; bool attacked = attackers_to_sq(b.bb, them, s, occ) != 0; bool defended = attackers_to_sq(b.bb, color, s, occ) != 0; if (attacked && !defended) return true; } return false; } // 是否给将(辅助:用于 QS 中的非吃子将军) static bool move_gives_check(Board& b, uint32_t m) { int us = b.side; if (!make_move(b, m)) return false; bool chk = b.square_attacked(b.kingSq[b.side], b.side^1); // 敌王被 us 攻击? unmake_move(b); return chk; } // ProbCut:尝试在非 PV、非危险、较深时对“够强的吃子”执行浅窗搜索 static bool try_probcut(Board& b, int depth, int beta, int ply) { if (!OPT.ProbCut) return false; if (depth < 5) return false; if (TM.time_up()) return false; MoveList list; gen_moves(b, list, true); // 仅吃子 // 简单排序:按被吃子价值 for (int i=0;i<list.size;i++){ uint32_t m = list.mv[i].m; if (m_captured(m)!=-1) { int sc = SEE_Value[m_captured(m)] - SEE_Value[m_piece(m)]/10; sc += rand_jitter(); list.mv[i].score = sc; } else list.mv[i].score = -1000000; } sort(list.mv, list.mv+list.size, [](const MoveEntry&a,const MoveEntry&b){return a.score>b.score;}); int margin = 120; // 阈值 for (int i=0;i<min(list.size, 6); ++i) { uint32_t m = list.mv[i].m; if (m_captured(m)==-1) continue; if (!see_ge(b, m, 0)) continue; // 至少不亏 if (!make_move(b, m)) continue; int score = -Eval::evaluate(b); // 粗略静态 if (score >= beta + margin) { // 探测:低深度验证 score = -Eval::evaluate(b); unmake_move(b); if (score >= beta) return true; continue; } unmake_move(b); } return false; } static int Quiescence(Board& b, int alpha, int beta, int ply) { Stats.qnodes++; if (ply > Stats.seldepth) Stats.seldepth = ply; if (b.halfmove >= 100) return (b.side==WHITE? OPT.Contempt : -OPT.Contempt); if (is_threefold(b)) return (b.side==WHITE? OPT.Contempt : -OPT.Contempt); bool inCheck = b.square_attacked(b.kingSq[b.side], b.side^1); if (!inCheck) { int stand = Eval::evaluate(b); if (stand >= beta) return stand; if (stand > alpha) alpha = stand; MoveList list; gen_qmoves(b, list); for (int i=0;i<list.size;i++){ uint32_t m = list.mv[i].m; int sc=0; if (m_promo(m)!=-1 && m_captured(m)==-1) sc = 300000 + 10*m_promo(m); else if (m_captured(m)!=-1) sc = 200000 + SEE_Value[m_captured(m)] - (SEE_Value[m_piece(m)]/10); sc += rand_jitter(); // 随机抖动 list.mv[i].score=sc; } sort(list.mv, list.mv+list.size, [](const MoveEntry&a,const MoveEntry&b){return a.score>b.score;}); for (int i=0; i<list.size; ++i) { uint32_t m = list.mv[i].m; bool isCap = m_captured(m)!=-1; bool isEp = m_ep(m); if (isCap && !isEp) { int capVal = SEE_Value[m_captured(m)]; if (stand + capVal + 50 < alpha) continue; // delta 剪枝 if (!see_ge(b, m, -10)) continue; } if (!make_move(b, m)) continue; int score = -Quiescence(b, -beta, -alpha, ply+1); unmake_move(b); if (score >= beta) return score; if (score > alpha) alpha = score; } // 可选:尝试若干非吃子“将军” if (OPT.QChecks && alpha < beta - 1) { MoveList ml; gen_moves(b, ml, false); int cntTried = 0; for (int i=0;i<ml.size && cntTried<4;i++){ uint32_t m = ml.mv[i].m; if (m_captured(m)!=-1 || m_castle(m) || m_promo(m)!=-1) continue; if (!move_gives_check(b, m)) continue; if (!make_move(b, m)) continue; int score = -Quiescence(b, -beta, -alpha, ply+1); unmake_move(b); if (score >= beta) return score; if (score > alpha) alpha = score; cntTried++; } } return alpha; } else { MoveList list; gen_moves(b, list, false); for (int i=0;i<list.size;i++){ uint32_t m = list.mv[i].m; int sc=0; if (m_captured(m)!=-1) sc = 200000 + SEE_Value[m_captured(m)]; else if (m_castle(m)) sc = 150000; else sc = History[b.side][m_from(m)][m_to(m)]; sc += rand_jitter(); // 随机抖动 list.mv[i].score = sc; } sort(list.mv, list.mv+list.size, [](const MoveEntry&a,const MoveEntry&b){return a.score>b.score;}); for (int i=0;i<list.size;i++){ uint32_t m = list.mv[i].m; if (!make_move(b, m)) continue; int score = -Quiescence(b, -beta, -alpha, ply+1); unmake_move(b); if (score >= beta) return score; if (score > alpha) alpha = score; } return alpha; } } // “少兑子”偏置:基于物质形势与 SEE 对吃子的排序进行小幅调整 static inline int trade_bias_for_sorting(const Board& b, uint32_t m) { if (m_captured(m)==-1) return 0; int mb = material_balance_side(b, b.side); // >0 领先;<0 落后 int see = see_move(b, m); if (see > 50 || see < -50) return 0; if (mb > 150) return +3000; // 领先:鼓励交换 if (mb < -150) return -3000; // 落后:减少交换 return 0; } // 开局走法排序微偏置:中心兵推进/轻子开发 static inline int opening_bias_score(const Board& b, uint32_t m) { int gp = game_phase(b); // 0..24(开局越大) if (gp < 16) return 0; // 中后期不加 int us = b.side; int piece = m_piece(m)%6; int from = m_from(m), to = m_to(m); int bonus = 0; // 中心兵推进 if (piece == PAWN) { if ((Center16Mask >> to) & 1ULL) bonus += 1200; int f = file_of(from); if (f==3 || f==4) bonus += 800; // d/e 兵 } // 轻子开发 if (piece == KNIGHT || piece == BISHOP) { int homeRank = (us==WHITE?0:7); if (rank_of(from)==homeRank && rank_of(to)!=homeRank) bonus += 900; } return bonus; } // 剃刀:小深度且形势差则直接进入 QS static bool razor_prune(const Board& b, int depth, int alpha) { if (!OPT.Razoring) return false; if (depth > 2) return false; if (b.square_attacked(b.kingSq[b.side], b.side^1)) return false; int eval = Eval::evaluate(b); int margin = 120 * depth; return eval + margin <= alpha; } // 内部迭代加深(无 ttMove 时帮助找到一个好首着) static uint32_t internal_iterative_deepening(Board& b, int depth, int ply) { if (!OPT.IID) return 0; int d = depth - 2; if (d < 1) return 0; uint32_t pvTable[MAX_PLY][MAX_PLY] = {}; int pvLen[MAX_PLY] = {}; int alpha=-INF, beta=INF; int score = 0; score = Quiescence(b, alpha, beta, ply); // 给个初值防止空 (void)score; // 小搜索 Stats.seldepth = 0; (void)Search; // 前置声明避免警告 // 为了简单直接用正常 Search,不专门实现子版本 return 0; } static int Search(Board& b, int depth, int alpha, int beta, int ply, bool doNull, uint32_t pvTable[MAX_PLY][MAX_PLY], int pvLen[MAX_PLY]); // 前置 // 根部/非根通用搜索体 static int Search(Board& b, int depth, int alpha, int beta, int ply, bool doNull, uint32_t pvTable[MAX_PLY][MAX_PLY], int pvLen[MAX_PLY]) { Stats.nodes++; if (ply > Stats.seldepth) Stats.seldepth = ply; if (TM.time_up()) return Eval::evaluate(b); bool inCheck = b.square_attacked(b.kingSq[b.side], b.side^1); if (depth <= 0) return Quiescence(b, alpha, beta, ply); if (b.halfmove >= 100) return (b.side==WHITE? OPT.Contempt : -OPT.Contempt); if (is_threefold(b)) return (b.side==WHITE? OPT.Contempt : -OPT.Contempt); if (insufficient_material(b)) return 0; bool pvNode = (beta - alpha > 1); bool dangerNode = inCheck || is_hanging_major(b, b.side); bool found=false; TTEntry* tte = TT.probe(b.key, found); uint32_t ttMove = 0; if (found && tte->depth >= depth) { int score = score_from_tt(tte->score, ply); // 根节点不直接用 TT 返回 if (ply > 0) { if (tte->flag == 0) return score; if (tte->flag == 1 && score >= beta) return score; if (tte->flag == 2 && score <= alpha) return score; } ttMove = tte->move; } int staticEval = (found ? tte->eval : Eval::evaluate(b)); // 剃刀尝试 if (!pvNode && !dangerNode && razor_prune(b, depth, alpha)) { int qs = Quiescence(b, alpha-1, alpha, ply); if (qs <= alpha) return qs; } // ProbCut if (!pvNode && !dangerNode && !inCheck && OPT.ProbCut) { if (try_probcut(b, depth, beta, ply)) return beta; } // 反向 futility(危险节点不做) if (!pvNode && !dangerNode && !inCheck && depth <= 2) { int rbeta = alpha - 110*depth; if (staticEval <= rbeta) { int qs = Quiescence(b, rbeta, rbeta+1, ply); if (qs <= rbeta) return qs; } } // Null move(谨慎) if (doNull && !pvNode && !dangerNode && !inCheck && depth >= 3 && allow_null(b) && staticEval >= beta + 120) { State &st = b.st[b.sp++]; st.key = b.key; st.castling=b.castling; st.ep=b.ep; st.halfmove=b.halfmove; st.captured=-1; st.move=0; st.fullmove=b.fullmove; if (b.ep!=-1) b.key ^= ZEP[file_of(b.ep)]; b.ep = -1; b.side ^= 1; b.key ^= ZSide; // 可变 R(深度大时多减) int R = 2 + (depth >= 5 ? 1 : 0) + (depth >= 8 ? 1 : 0); int score = -Search(b, depth - 1 - R, -beta, -beta+1, ply+1, false, pvTable, pvLen); b.side ^= 1; b.key ^= ZSide; if (b.ep!=-1) b.key ^= ZEP[file_of(b.ep)]; b.ep = st.ep; if (b.ep!=-1) b.key ^= ZEP[file_of(b.ep)]; b.castling = st.castling; b.halfmove = st.halfmove; b.fullmove = st.fullmove; b.sp--; if (score >= beta) return score; } MoveList list; gen_moves(b, list, false); // 排序:TT > captures(+少兑子 & 吃史) > CounterMove > Killers > castle > history (+ 开局偏置) + 随机抖动 for (int i=0;i<list.size;i++){ uint32_t m = list.mv[i].m; int score = 0; if ((int)m == (int)ttMove) score = 1<<30; else if (m_captured(m)!=-1) { int cap = m_captured(m); int pc = m_piece(m)%6; score = (1<<29) + SEE_Value[cap] - (SEE_Value[m_piece(m)]/10); score += trade_bias_for_sorting(b, m); score += CaptureHistory[b.side][pc][m_to(m)]/4; } else if (m == CounterMove[m_from(b.st[b.sp-1].move)][m_to(b.st[b.sp-1].move)]) score = (1<<28)-1; // 反击 else if (m == Killers1[ply]) score = (1<<28); else if (m == Killers2[ply]) score = (1<<28)-2; else if (m_castle(m)) score = (1<<28)-3; // 易位优先于普通 history else score = History[b.side][m_from(m)][m_to(m)]; // 降低“安静的非易位王走子”优先级 if (!inCheck && (m_piece(m) % 6) == KING && m_captured(m) == -1 && !m_castle(m) && m_promo(m) == -1) { score -= (1 << 27); } // 开局微偏置(中心兵推进/轻子开发) score += opening_bias_score(b, m); // 随机抖动 score += rand_jitter(); list.mv[i].score = score; } sort(list.mv, list.mv + list.size, [](const MoveEntry& a, const MoveEntry& b){ return a.score > b.score; }); int origAlpha = alpha; int bestScore = -INF; uint32_t bestMove = 0; pvLen[ply] = 0; int legalMoves=0; for (int i=0;i<list.size;i++){ uint32_t m = list.mv[i].m; // 仅把“吃子/升变”视为战术步;易位算安静步 bool isTactical = (m_captured(m)!=-1) || (m_promo(m)!=-1); // 叶子 futility(危险节点不剪) if (!pvNode && !dangerNode && !inCheck && depth == 1 && !isTactical) { if (staticEval + 90 <= alpha) continue; } // LMP(危险节点不做) if (!pvNode && !dangerNode && !inCheck && depth <= 3 && !isTactical && i > 6 + 2*depth) continue; // SEE 负收益吃子剪枝(更谨慎) if (!pvNode && !dangerNode && !inCheck && m_captured(m)!=-1 && depth <= 5 && !see_ge(b, m, -10)) continue; if (!make_move(b, m)) continue; legalMoves++; bool nextInCheck = b.square_attacked(b.kingSq[b.side], b.side^1); int ext = nextInCheck ? 1 : 0; int newDepth = depth - 1 + ext; int score; // LMR 更保守 if (!pvNode && !dangerNode && !inCheck && !isTactical && depth >= 3 && !nextInCheck) { int r = 1 + (depth>=6) + (i>=8); score = -Search(b, newDepth - r, -alpha-1, -alpha, ply+1, true, pvTable, pvLen); } else { score = -Search(b, newDepth, -alpha-1, -alpha, ply+1, true, pvTable, pvLen); } if (score > alpha && score < beta) { score = -Search(b, newDepth, -beta, -alpha, ply+1, true, pvTable, pvLen); } unmake_move(b); if (score > bestScore) { bestScore = score; bestMove = m; pvTable[ply][0] = m; for (int k=0;k<pvLen[ply+1];k++) pvTable[ply][k+1] = pvTable[ply+1][k]; pvLen[ply] = pvLen[ply+1] + 1; } if (score > alpha) { alpha = score; if (m_captured(m)==-1 && !m_castle(m)) { History[b.side][m_from(m)][m_to(m)] += depth * depth; // 反击表:记录对上一手的回应 if (b.sp>=1) { uint32_t prev = b.st[b.sp-1].move; CounterMove[m_from(prev)][m_to(prev)] = m; } if (Killers1[ply] != m) { Killers2[ply] = Killers1[ply]; Killers1[ply] = m; } } else if (m_captured(m)!=-1) { int pc = m_piece(m)%6; CaptureHistory[b.side][pc][m_to(m)] += 32 + depth*2; } if (alpha >= beta) { break; } } } if (legalMoves==0) { if (inCheck) return mated_in(ply); return (b.halfmove>=100 || is_threefold(b)) ? (b.side==WHITE? OPT.Contempt : -OPT.Contempt) : 0; } // TT 写入(更细的替换:若槽位 key 不同,优先保留深度更深且新鲜的) if (tte) { bool replace = true; if (tte->key != b.key && tte->move!=0 && tte->age==TT.age && tte->depth > depth) replace = false; if (replace) { tte->key = b.key; tte->move = bestMove; tte->depth = depth; tte->eval = staticEval; tte->age = TT.age; if (bestScore <= origAlpha) tte->flag = 2; else if (bestScore >= beta) tte->flag = 1; else tte->flag = 0; tte->score = (int16_t)score_to_tt(bestScore, ply); } } return bestScore; } // 复原 PV static int get_pv_line(Board& b, uint32_t pv[MAX_PLY]) { int cnt=0; Board copy = b; for (int i=0;i<MAX_PLY;i++){ bool found=false; TTEntry* e = TT.probe(copy.key, found); if (!found || e->move==0) break; uint32_t m = e->move; if (!make_move(copy, m)) break; pv[cnt++] = m; } return cnt; } static string move_to_uci(uint32_t m) { int from = m_from(m), to = m_to(m); char s[8]; s[0] = 'a' + file_of(from); s[1] = '1' + rank_of(from); s[2] = 'a' + file_of(to); s[3] = '1' + rank_of(to); int promo = m_promo(m); if (promo!=-1) { s[4] = "nbrq"[ (promo==KNIGHT)?0 : (promo==BISHOP)?1 : (promo==ROOK)?2 : 3 ]; s[5] = 0; } else { s[4] = 0; } return string(s); } static uint32_t parse_move_uci(Board& b, const string& str) { if (str.size()<4) return 0; int ff = str[0]-'a'; int fr = str[1]-'1'; int tf = str[2]-'a'; int tr = str[3]-'1'; int from = sq(ff,fr), to = sq(tf,tr); int pc = b.board[from]; if (pc == NO_PIECE) return 0; int captured = b.board[to]; bool ep=false, castle=false, dpp=false; int promoType = -1; if ((pc%6)==KING && ((from==sq(4,0) && (to==sq(6,0)||to==sq(2,0))) || (from==sq(4,7) && (to==sq(6,7)||to==sq(2,7))))) { castle = true; } if ((pc%6)==PAWN && to == b.ep && b.ep!=-1 && captured==-1) { ep = true; captured = Board::code(b.side^1, PAWN); } if ((pc%6)==PAWN && abs(tr-fr)==2) dpp = true; if (str.size()>=5) { char c = (char)tolower((unsigned char)str[4]); if (c=='q') promoType = QUEEN; else if (c=='r') promoType = ROOK; else if (c=='b') promoType = BISHOP; else if (c=='n') promoType = KNIGHT; } return enc_move(from,to,pc,captured,promoType,ep,castle,dpp); } // ----------------------------- 开局库(内置微型) ----------------------------- struct BookEntry { const char* fen; // 只含棋子布局/行棋方/易位/吃过路(不含半回合/全回合) const char* moves[8]; // 备选着法(uci),末尾用 nullptr int weights[8]; // 权重(与 moves 对齐),末尾 0 }; static const BookEntry MINI_BOOK[] = { // 起始局面 {"rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq -", {"e2e4","d2d4","g1f3","c2c4",nullptr}, {40,30,20,10,0}}, // e4 e5 {"rnbqkbnr/pppp1ppp/8/4p3/4P3/8/PPPP1PPP/RNBQKBNR w KQkq -", {"g1f3","f1c4","d2d4",nullptr}, {40,30,20,0}}, // d4 d5 {"rnbqkbnr/ppp1pppp/8/3p4/3P4/8/PPP1PPPP/RNBQKBNR w KQkq -", {"c2c4","g1f3","c1f4",nullptr}, {40,30,20,0}}, // Nf3 Nf6 {"rnbqkb1r/pppppppp/5n2/8/8/5N2/PPPPPPPP/RNBQKB1R w KQkq -", {"c2c4","d2d4","g2g3",nullptr}, {35,35,30,0}}, // c4 e5 {"rnbqkbnr/pppppppp/8/4p3/2P5/8/PP1PPPPP/RNBQKBNR w KQkq -", {"g1f3","d2d4",nullptr}, {60,40,0}}, }; static string fen_core(const Board& b) { // 生成 FEN 的前 4 项 string s; for (int r=7;r>=0;--r){ int empty=0; for (int f=0;f<8;f++){ int sqr = sq(f,r); int code = b.board[sqr]; if (code==NO_PIECE) { empty++; continue; } if (empty){ s += char('0'+empty); empty=0; } int color = code/6, pt = code%6; char c='?'; switch(pt){ case PAWN:c='p';break; case KNIGHT:c='n';break; case BISHOP:c='b';break; case ROOK:c='r';break; case QUEEN:c='q';break; case KING:c='k';break; } if (color==WHITE) c = toupper(c); s += c; } if (empty) s += char('0'+empty); if (r) s += '/'; } s += ' '; s += (b.side==WHITE? 'w':'b'); s += ' '; string cs; if (b.castling & 1) cs+='K'; if (b.castling & 2) cs+='Q'; if (b.castling & 4) cs+='k'; if (b.castling & 8) cs+='q'; s += (cs.empty()? "-" : cs); s += ' '; if (b.ep!=-1) { string ep; ep += char('a'+file_of(b.ep)); ep += char('1'+rank_of(b.ep)); s += ep; } else s += '-'; return s; } static bool probe_book(const Board& b, uint32_t& outMove) { if (!OPT.UseBook) return false; string key = fen_core(b); for (const auto& e : MINI_BOOK) { if (key == e.fen) { // 选一个 vector<pair<string,int>> cand; for (int i=0; e.moves[i]!=nullptr; ++i) cand.push_back({e.moves[i], e.weights[i]}); if (cand.empty()) return false; if (OPT.BookRandom) { // 权重随机 int sum=0; for (auto &p : cand) sum += p.second; if (sum<=0) sum = (int)cand.size(); uint64_t r = RJ.next(); int x = (int)(r % sum); for (auto &p : cand) { if (x < p.second) { uint32_t m = parse_move_uci(const_cast<Board&>(b), p.first); if (m && make_move(const_cast<Board&>(b), m)) { unmake_move(const_cast<Board&>(b)); outMove = m; return true; } // 若解析失败则尝试下一个 break; } x -= p.second; } } // 最佳(第一个) for (auto &p : cand) { uint32_t m = parse_move_uci(const_cast<Board&>(b), p.first); if (m && make_move(const_cast<Board&>(b), m)) { unmake_move(const_cast<Board&>(b)); outMove = m; return true; } } return false; } } return false; } // ----------------------------- 根搜索(MultiPV 支持) ----------------------------- struct RootResult { uint32_t move; int score; vector<uint32_t> pv; }; static void print_info_line_ex(int depth, int seldepth, int score, uint64_t timeMs, uint64_t nodes, uint64_t nps, int multipv, const vector<uint32_t>& pv) { cout << "info depth " << depth << " seldepth " << seldepth << " multipv " << multipv << " score "; if (score >= MATE_IN_MAX_PLY) { int mateIn = (MATE_SCORE - score + 1) / 2; cout << "mate " << mateIn; } else if (score <= -MATE_IN_MAX_PLY) { int mateIn = (MATE_SCORE + score + 1) / 2; cout << "mate " << -mateIn; } else { cout << "cp " << score; } cout << " time " << timeMs << " nodes " << nodes << " nps " << nps << " hashfull " << TT.hashfull() << " pv"; for (auto m : pv) cout << " " << move_to_uci(m); cout << endl; cout.flush(); } static vector<RootResult> RootSearch(Board& b, int depth) { // 根搜索:PVS + MultiPV vector<RootResult> results; MoveList list; gen_moves(b, list, false); // 预排序:使用与 Search 相近的启发 for (int i=0;i<list.size;i++){ uint32_t m = list.mv[i].m; int score = 0; bool found=false; TTEntry* tte = TT.probe(b.key, found); uint32_t ttMove = (found? tte->move : 0); if ((int)m == (int)ttMove) score = 1<<30; else if (m_captured(m)!=-1) { int cap = m_captured(m); score = (1<<29) + SEE_Value[cap] - (SEE_Value[m_piece(m)]/10) + trade_bias_for_sorting(b,m); } else if (m == Killers1[0]) score = (1<<28); else if (m_castle(m)) score = (1<<28)-1; else score = History[b.side][m_from(m)][m_to(m)]; score += opening_bias_score(b, m); score += rand_jitter(); list.mv[i].score = score; } sort(list.mv, list.mv+list.size, [](const MoveEntry& a,const MoveEntry& b){return a.score>b.score;}); int alpha = -INF, beta = INF; int multipv = max(1, OPT.MultiPV); multipv = min(multipv, max(1, list.size)); vector<uint32_t> excluded; excluded.reserve(multipv); for (int mpv=1; mpv<=multipv; ++mpv){ int bestScore = -INF; uint32_t bestMove = 0; vector<uint32_t> bestPV; // PVS 根循环 int searched=0; for (int i=0;i<list.size;i++){ uint32_t m = list.mv[i].m; // 已被更高 multipv 选中过的走子跳过 if (find(excluded.begin(), excluded.end(), m) != excluded.end()) continue; if (!make_move(b, m)) continue; searched++; uint32_t pvTable[MAX_PLY][MAX_PLY] = {}; int pvLen[MAX_PLY] = {}; bool nextInCheck = b.square_attacked(b.kingSq[b.side], b.side^1); int ext = nextInCheck ? 1 : 0; int newDepth = depth - 1 + ext; int score; if (searched==1) { score = -Search(b, newDepth, -beta, -alpha, 1, true, pvTable, pvLen); } else { // PVS 窄窗再全窗 score = -Search(b, newDepth, -alpha-1, -alpha, 1, true, pvTable, pvLen); if (score > alpha) { score = -Search(b, newDepth, -beta, -alpha, 1, true, pvTable, pvLen); } } unmake_move(b); if (score > bestScore) { bestScore = score; bestMove = m; bestPV.clear(); bestPV.push_back(m); for (int k=0;k<pvLen[0];k++) bestPV.push_back(pvTable[0][k]); } if (score > alpha) { alpha = score; } if (TM.time_up()) break; } if (bestMove==0){ // 回退:选择首个合法 for (int i=0;i<list.size;i++){ uint32_t m = list.mv[i].m; if (find(excluded.begin(), excluded.end(), m) != excluded.end()) continue; if (make_move(b, m)) { unmake_move(b); bestMove=m; bestPV={m}; break; } } } if (bestMove) excluded.push_back(bestMove); results.push_back(RootResult{bestMove, bestScore, bestPV}); // 输出每条 PV print_info_line_ex(depth, Stats.seldepth, bestScore, Stats.elapsed_ms(), Stats.nodes, Stats.nps(), mpv, bestPV); if (TM.time_up() || TM.soft_time_up()) break; } return results; } // ----------------------------- UCI 输出/迭代加深 ----------------------------- static uint32_t LastBestMoveRoot = 0; static void print_info_line(int depth, int score, const vector<uint32_t>& pv) { cout << "info depth " << depth << " seldepth " << Stats.seldepth << " score "; if (score >= MATE_IN_MAX_PLY) { int mateIn = (MATE_SCORE - score + 1) / 2; cout << "mate " << mateIn; } else if (score <= -MATE_IN_MAX_PLY) { int mateIn = (MATE_SCORE + score + 1) / 2; cout << "mate " << -mateIn; } else { cout << "cp " << score; } cout << " time " << Stats.elapsed_ms() << " nodes " << Stats.nodes << " nps " << Stats.nps() << " hashfull " << TT.hashfull() << " pv"; for (auto m : pv) cout << " " << move_to_uci(m); cout << endl; cout.flush(); } static uint32_t iterative_deepening(Board& b, const Limits& lim) { Stats.reset(); TM.setup(b, lim); TT.age++; memset(History, 0, sizeof(History)); memset(Killers1, 0, sizeof(Killers1)); memset(Killers2, 0, sizeof(Killers2)); memset(CounterMove, 0, sizeof(CounterMove)); memset(CaptureHistory, 0, sizeof(CaptureHistory)); uint32_t bestMove = 0; int bestScore = -INF; int startDepth = 1; int maxDepth = min(lim.depth, MAX_PLY-2); int aspiration = 16; int prevScore = Eval::evaluate(b); // 开局库尝试(仅在极早期,且允许) if (b.fullmove <= 10 && OPT.UseBook) { uint32_t bk; if (probe_book(b, bk)) { LastBestMoveRoot = bk; // 打一行信息 cout << "info string book move " << move_to_uci(bk) << endl; } } for (int depth = startDepth; depth <= maxDepth; ++depth) { if (TM.time_up()) break; if (OPT.MultiPV > 1) { // 使用根搜索 + MultiPV auto res = RootSearch(b, depth); if (res.empty()) break; // 取第一条作为 best bestMove = res[0].move; bestScore = res[0].score; // 软停止控制 if (TM.soft_time_up()) break; continue; } int alpha = max(-INF, prevScore - aspiration); int beta = min(+INF, prevScore + aspiration); int score; while (true) { uint32_t pvTable[MAX_PLY][MAX_PLY] = {}; int pvLen[MAX_PLY] = {}; Stats.seldepth = 0; score = Search(b, depth, alpha, beta, 0, true, pvTable, pvLen); if (TM.time_up()) break; if (score <= alpha) { alpha = max(-INF, score - aspiration - 8); aspiration = min(300, aspiration<<1); continue; } if (score >= beta) { beta = min(+INF, score + aspiration + 8); aspiration = min(300, aspiration<<1); continue; } // 组装 PV vector<uint32_t> pv; for (int i=0;i<pvLen[0];i++) pv.push_back(pvTable[0][i]); if (pv.empty()) { uint32_t line[MAX_PLY]; int l = get_pv_line(b, line); for (int i=0;i<l;i++) pv.push_back(line[i]); } // 选择 bestmove:优先 pv[0],否则校验 TT/合法走子 uint32_t curBest = 0; if (!pv.empty()) { if (make_move(b, pv[0])) { unmake_move(b); curBest = pv[0]; } } if (curBest == 0) { bool ttFound=false; TTEntry* e = TT.probe(b.key, ttFound); if (ttFound && e->move && make_move(b, e->move)) { unmake_move(b); curBest = e->move; } } if (curBest == 0) { MoveList list; gen_moves(b, list, false); for (int i=0;i<list.size;i++){ if (make_move(b, list.mv[i].m)) { curBest = list.mv[i].m; unmake_move(b); break; } } } if (curBest) LastBestMoveRoot = curBest; bestMove = curBest? curBest : bestMove; bestScore = score; print_info_line(depth, score, pv); break; } if (TM.time_up()) break; if (lim.movetime > 0) { if (Stats.elapsed_ms() >= (uint64_t)lim.movetime) break; } else { if (TM.soft_time_up()) break; } } if (bestMove == 0) { // 回退策略:用上次根最佳,或任一合法 if (LastBestMoveRoot && make_move(b, LastBestMoveRoot)) { unmake_move(b); bestMove = LastBestMoveRoot; } else { MoveList list; gen_moves(b, list, false); for (int i=0;i<list.size;i++){ if (make_move(b, list.mv[i].m)) { bestMove = list.mv[i].m; unmake_move(b); break; } } } } return bestMove; } // ----------------------------- UCI ----------------------------- static void uci_id() { cout << "id name Chtholly-Engine 2.0" << endl; cout << "id author LLM" << endl; cout << "option name Hash type spin default 128 min 1 max 4096" << endl; cout << "option name Randomness type spin default 0 min 0 max 100000" << endl; cout << "option name RandomSeed type spin default 0 min 0 max 4294967295" << endl; cout << "option name Clear Hash type button" << endl; cout << "option name MultiPV type spin default 1 min 1 max 10" << endl; cout << "option name Contempt type spin default 0 min -200 max 200" << endl; cout << "option name MoveOverhead type spin default 20 min 0 max 500" << endl; cout << "option name UseBook type check default true" << endl; cout << "option name BookRandom type check default true" << endl; cout << "option name QChecks type check default true" << endl; cout << "option name ProbCut type check default true" << endl; cout << "option name Razoring type check default true" << endl; cout << "option name IID type check default true" << endl; // Chess960 预留 cout << "option name UCI_Chess960 type check default false" << endl; cout << "uciok" << endl; cout.flush(); } static void uci_isready() { cout << "readyok" << endl << flush; } static void uci_ucinewgame() { TT.clear(); // 若随机幅度>0且未固定种子,每盘重播种(时间种子) if (RC.jitter > 0 && RC.seed == 0) RC.seeded = false; } static void setoption(const string& name, const string& value) { if (name == "Hash") { int MB = stoi(value); TT.resizeMB(MB); } else if (name == "Randomness") { int A = stoi(value); RC.jitter = max(0, min(100000, A)); } else if (name == "RandomSeed") { uint64_t s = 0; try { s = stoull(value); } catch(...) { s = 0; } RC.seed = s; RJ.seed(RC.seed ? RC.seed : (uint64_t)chrono::steady_clock::now().time_since_epoch().count()); RC.seeded = true; } else if (name == "Clear Hash") { TT.clear(); } else if (name == "MultiPV") { int v = stoi(value); OPT.MultiPV = max(1, min(10, v)); } else if (name == "Contempt") { int v = stoi(value); OPT.Contempt = max(-200, min(200, v)); } else if (name == "MoveOverhead") { int v = stoi(value); OPT.MoveOverhead = max(0, min(500, v)); } else if (name == "UseBook") { OPT.UseBook = (value=="true"||value=="True"||value=="1"); } else if (name == "BookRandom") { OPT.BookRandom = (value=="true"||value=="True"||value=="1"); } else if (name == "QChecks") { OPT.QChecks = (value=="true"||value=="True"||value=="1"); } else if (name == "ProbCut") { OPT.ProbCut = (value=="true"||value=="True"||value=="1"); } else if (name == "Razoring") { OPT.Razoring = (value=="true"||value=="True"||value=="1"); } else if (name == "IID") { OPT.IID = (value=="true"||value=="True"||value=="1"); } else if (name == "UCI_Chess960") { OPT.Chess960 = (value=="true"||value=="True"||value=="1"); } } static void uci_position(Board& b, istringstream& iss) { string token; if (!(iss >> token)) return; if (token == "startpos") { b.set_startpos(); if (iss >> token) { if (token == "moves") { while (iss >> token) { uint32_t m = parse_move_uci(b, token); if (m==0 || !make_move(b, m)) { MoveList list; gen_moves(b, list, false); bool done=false; for (int i=0;i<list.size;i++) { if (move_to_uci(list.mv[i].m) == token) { if (make_move(b, list.mv[i].m)) { done=true; break; } } } (void)done; } } } } } else if (token == "fen") { string fen, tmp; vector<string> parts; for (int i=0;i<6 && (iss >> tmp); ++i) parts.push_back(tmp); fen = ""; for (int i=0;i<(int)parts.size();i++){ if (i) fen += " "; fen += parts[i]; } if (parts.size()<4) return; b.set_fen(fen); if (iss >> token) { if (token == "moves") { while (iss >> token) { uint32_t m = parse_move_uci(b, token); if (m==0 || !make_move(b, m)) { MoveList list; gen_moves(b, list, false); for (int i=0;i<list.size;i++) { if (move_to_uci(list.mv[i].m) == token) { if (make_move(b, list.mv[i].m)) { break; } } } } } } } } } static void uci_go(Board& b, istringstream& iss) { Limits lim; string token; while (iss >> token) { if (token == "wtime") iss >> lim.wtime; else if (token == "btime") iss >> lim.btime; else if (token == "winc") iss >> lim.winc; else if (token == "binc") iss >> lim.binc; else if (token == "movestogo") iss >> lim.movestogo; else if (token == "movetime") iss >> lim.movetime; else if (token == "depth") iss >> lim.depth; else if (token == "nodes") { uint64_t nd; iss >> nd; lim.nodes = nd; } else if (token == "ponder") lim.ponder = true; else if (token == "infinite") { lim.movetime = -1; } } uint32_t best = iterative_deepening(b, lim); cout << "bestmove " << move_to_uci(best) << endl; cout.flush(); } // ----------------------------- 简易调试命令(perft) ----------------------------- static uint64_t perft(Board& b, int depth) { if (depth==0) return 1; MoveList list; gen_moves(b, list, false); uint64_t nodes=0; for (int i=0;i<list.size;i++){ uint32_t m = list.mv[i].m; if (!make_move(b, m)) continue; nodes += perft(b, depth-1); unmake_move(b); } return nodes; } // ----------------------------- 主程序 ----------------------------- int main() { ios::sync_with_stdio(false); cin.tie(nullptr); init_zobrist(); init_attack_tables(); init_castle_masks(); TT.resizeMB(128); Board board; board.set_startpos(); string line; while (std::getline(cin, line)) { if (line.empty()) continue; istringstream iss(line); string cmd; iss >> cmd; if (cmd == "uci") uci_id(); else if (cmd == "isready") uci_isready(); else if (cmd == "ucinewgame") uci_ucinewgame(); else if (cmd == "position") uci_position(board, iss); else if (cmd == "go") uci_go(board, iss); else if (cmd == "perft") { int d=0; iss>>d; if (d<=0) { cout<<"info string perft depth must >0\n"; cout.flush(); continue; } auto t0=chrono::steady_clock::now(); uint64_t n = perft(board, d); auto t1=chrono::steady_clock::now(); uint64_t ms = (uint64_t)chrono::duration_cast<chrono::milliseconds>(t1-t0).count(); if (!ms) ms=1; cout<<"info string perft nodes "<<n<<" time "<<ms<<"ms nps "<<(n*1000ULL/ms)<<"\n"; cout.flush(); } else if (cmd == "setoption") { string name, tmp, value; iss >> tmp; // name iss >> tmp; // actual name name = tmp; while (iss >> tmp && tmp!="value") name += " " + tmp; getline(iss, value); while (!value.empty() && isspace((unsigned char)value.front())) value.erase(value.begin()); while (!value.empty() && isspace((unsigned char)value.back())) value.pop_back(); setoption(name, value); } else if (cmd == "stop") { // 优先使用上一次根节点的最佳着作为回退 uint32_t best = 0; if (LastBestMoveRoot && make_move(board, LastBestMoveRoot)) { unmake_move(board); best = LastBestMoveRoot; } else { uint32_t pv[MAX_PLY]; int l = get_pv_line(board, pv); if (l>0 && make_move(board, pv[0])) { unmake_move(board); best = pv[0]; } else { MoveList list; gen_moves(board, list, false); for (int i=0;i<list.size;i++){ if (make_move(board, list.mv[i].m)) { best = list.mv[i].m; unmake_move(board); break; } } } } cout << "bestmove " << move_to_uci(best) << endl; cout.flush(); } else if (cmd == "ponderhit") { // 简单处理:无特别逻辑 } else if (cmd == "quit") break; } return 0; }

分享这个问答