diff --git a/src/history.c b/src/history.c index 28442cda..c68538de 100644 --- a/src/history.c +++ b/src/history.c @@ -23,12 +23,12 @@ #include "move.h" #include "util.h" -int y9 = 3; +int y9 = 4; int z0 = 4; -int z1 = 120; -int z2 = -120; -int z3 = 1896; +int z1 = 164; +int z2 = -113; +int z3 = 1729; void UpdateHistories(SearchStack* ss, ThreadData* thread, @@ -94,9 +94,23 @@ void UpdateHistories(SearchStack* ss, } } -void UpdatePawnCorrection(int raw, int real, Board* board, ThreadData* thread) { - const int16_t correction = Min(30000, Max(-30000, (real - raw) * PAWN_CORRECTION_GRAIN)); +void UpdatePawnCorrection(int raw, int real, int depth, Board* board, ThreadData* thread) { + const int16_t correction = Min(30000, Max(-30000, (real - raw) * CORRECTION_GRAIN)); const int idx = (board->pawnZobrist & PAWN_CORRECTION_MASK); + const int saveDepth = Min(16, depth); - thread->pawnCorrection[idx] = (thread->pawnCorrection[idx] * 255 + correction) / 256; + thread->pawnCorrection[idx] = (thread->pawnCorrection[idx] * (256 - saveDepth) + correction * saveDepth) / 256; +} + +void UpdateContCorrection(int raw, int real, int depth, SearchStack* ss, ThreadData* thread) { + const Move m1 = (ss - 1)->move; + const Move m2 = (ss - 2)->move; + + if (m1 && m2) { + const int16_t correction = Min(30000, Max(-30000, (real - raw) * CORRECTION_GRAIN)); + const int saveDepth = Min(16, depth); + + int16_t* contCorrection = &thread->contCorrection[Moving(m1)][To(m1)][Moving(m2)][To(m2)]; + *contCorrection = (*contCorrection * (256 - saveDepth) + correction * saveDepth) / 256; + } } diff --git a/src/history.h b/src/history.h index 1cd29915..fcff7954 100644 --- a/src/history.h +++ b/src/history.h @@ -82,7 +82,18 @@ INLINE void UpdateCH(SearchStack* ss, Move move, int16_t bonus) { } INLINE int GetPawnCorrection(Board* board, ThreadData* thread) { - return thread->pawnCorrection[board->pawnZobrist & PAWN_CORRECTION_MASK] / PAWN_CORRECTION_GRAIN; + return thread->pawnCorrection[board->pawnZobrist & PAWN_CORRECTION_MASK] / CORRECTION_GRAIN; +} + +INLINE int GetContCorrection(SearchStack* ss, ThreadData* thread) { + const Move m1 = (ss - 1)->move; + const Move m2 = (ss - 2)->move; + + if (m1 && m2) { + return thread->contCorrection[Moving(m1)][To(m1)][Moving(m2)][To(m2)] / CORRECTION_GRAIN; + } else { + return 0; + } } void UpdateHistories(SearchStack* ss, @@ -94,6 +105,7 @@ void UpdateHistories(SearchStack* ss, Move captures[], int nC); -void UpdatePawnCorrection(int raw, int real, Board* board, ThreadData* thread); +void UpdatePawnCorrection(int raw, int real, int depth, Board* board, ThreadData* thread); +void UpdateContCorrection(int raw, int real, int depth, SearchStack* ss, ThreadData* thread); #endif \ No newline at end of file diff --git a/src/makefile b/src/makefile index cd53c4c2..4c007dcc 100644 --- a/src/makefile +++ b/src/makefile @@ -3,7 +3,7 @@ EXE = berserk SRC = attacks.c bench.c berserk.c bits.c board.c eval.c history.c move.c movegen.c movepick.c perft.c random.c \ search.c see.c tb.c thread.c transposition.c uci.c util.c zobrist.c nn/accumulator.c nn/evaluate.c pyrrhic/tbprobe.c CC = clang -VERSION = 20240324 +VERSION = 20241119 MAIN_NETWORK = berserk-d43206fe90e4.nn EVALFILE = $(MAIN_NETWORK) DEFS = -DVERSION=\"$(VERSION)\" -DEVALFILE=\"$(EVALFILE)\" -DNDEBUG @@ -114,18 +114,21 @@ download-network: echo "Downloaded network $(EVALFILE) and verified"; \ else \ echo "Downloaded network $(EVALFILE) failed validation"; \ + exit 1; \ fi; \ elif hash sha256sum 2>/dev/null; then \ if [ "$(EVALFILE)" = "berserk-"`sha256sum $(EVALFILE) | cut -c1-12`".nn" ]; then \ echo "Downloaded network $(EVALFILE) and verified"; \ else \ echo "Downloaded network $(EVALFILE) failed validation"; \ + exit 1; \ fi; \ else \ echo "Downloaded network $(EVALFILE), but unable to verify"; \ fi; \ else \ - echo "Unable to downlaod network: $(EVALFILE)"; \ + echo "Unable to download network: $(EVALFILE)"; \ + exit 1; \ fi; \ elif test -f "$(EVALFILE)"; then \ echo "Using network: $(EVALFILE)"; \ diff --git a/src/search.c b/src/search.c index 936a15a5..b9dee573 100644 --- a/src/search.c +++ b/src/search.c @@ -41,74 +41,87 @@ #include "util.h" #include "zobrist.h" -float a0 = 2.1872; -float a1 = 0.2487; +float a0 = 2.0385; +float a1 = 0.2429; -float b0 = 1.2973; -float b1 = 0.3772; +float b0 = 1.3050; +float b1 = 0.3503; -float c0 = 2.7002; -float c1 = 0.9448; +float c0 = 2.1885; +float c1 = 0.9911; -float d0 = 14.9419; +float d0 = 15.2703; -float e0 = 103.9379; +float e0 = 94.0617; -float f0 = 1.3658; -float f1 = 0.0482; +int e1 = 200; -float g0 = 0.0995; -float g1 = 0.0286; +float f0 = 1.3110; +float f1 = 0.0533; + +float g0 = 0.1127; +float g1 = 0.0262; float g2 = 0.0261; -float g3 = 0.4843; -float g4 = 1.4498; +float g3 = 0.5028; +float g4 = 1.6561; + +float h0 = 0.5630; +float h1 = 2.2669; +float h2 = 0.4499; -float h0 = 0.5464; -float h1 = 2.1394; -float h2 = 0.4393; +int i0 = 9; +int i1 = 70; +int i2 = 118; +int i3 = 11800; +int i4 = 25; -int i0 = 8; -int i1 = 67; -int i2 = 112; -int i3 = 12525; +int i9 = 4; -int v0 = 6; -int v1 = 252; +int v0 = 5; +int v1 = 214; int k0 = 4; int k1 = 4; -int k2 = 342; +int k2 = 385; int k3 = 10; int k4 = 4; -int l0 = 197; -int l1 = 5; +int l0 = 172; +int l1 = 6; -int m0 = 7; -int m1 = 2658; +int m0 = 5; +int m1 = 2788; int n0 = 10; -int n1 = 87; +int n1 = 81; int n2 = 46; int o0 = 6; int o1 = 5; -int o2 = 17; +int o2 = 14; int o3 = 6; -int o4 = 50; +int o4 = 48; -int p0 = 8; +int p0 = 8192; +int p1 = 1024; +int p2 = 2048; +int p3 = 1024; +int p4 = 2048; +int p5 = 1024; +int p6 = 1024; +int p7 = 1024; +int p8 = 1024; -int q0 = 76; +int q0 = 69; int r0 = 11; -int s0 = 78; +int s0 = 77; -int t0 = 60; +int t0 = 63; int u0 = 9; -int u1 = 16; +int u1 = 17; // arrays to store these pruning cutoffs at specific depths int LMR[MAX_SEARCH_PLY][64]; @@ -118,7 +131,7 @@ int STATIC_PRUNE[2][MAX_SEARCH_PLY]; void InitPruningAndReductionTables() { for (int depth = 1; depth < MAX_SEARCH_PLY; depth++) for (int moves = 1; moves < 64; moves++) - LMR[depth][moves] = log(depth) * log(moves) / a0 + a1; + LMR[depth][moves] = (log(depth) * log(moves) / a0 + a1) * 1024; LMR[0][0] = LMR[0][1] = LMR[1][0] = 0; @@ -537,7 +550,7 @@ int Negamax(int alpha, int beta, int depth, int cutnode, ThreadData* thread, PV* rawEval = ttEval; if (rawEval == EVAL_UNKNOWN) rawEval = Evaluate(board, thread); - eval = ss->staticEval = ClampEval(rawEval + GetPawnCorrection(board, thread) / 2); + eval = ss->staticEval = ClampEval(rawEval + GetPawnCorrection(board, thread) / 2 + GetContCorrection(ss, thread)); // correct eval on fmr eval = AdjustEvalOnFMR(board, eval); @@ -546,7 +559,7 @@ int Negamax(int alpha, int beta, int depth, int cutnode, ThreadData* thread, PV* eval = ttScore; } else if (!ss->skip) { rawEval = Evaluate(board, thread); - eval = ss->staticEval = ClampEval(rawEval + GetPawnCorrection(board, thread) / 2); + eval = ss->staticEval = ClampEval(rawEval + GetPawnCorrection(board, thread) / 2 + GetContCorrection(ss, thread)); // correct eval on fmr eval = AdjustEvalOnFMR(board, eval); @@ -573,18 +586,19 @@ int Negamax(int alpha, int beta, int depth, int cutnode, ThreadData* thread, PV* // IIR by Ed Schroder // http://talkchess.com/forum3/viewtopic.php?f=7&t=74769&sid=64085e3396554f0fba414404445b3120 if (!(ss->skip || inCheck)) { - if ((isPV || cutnode) && depth >= 4 && !hashMove) + if ((isPV || cutnode) && depth >= i9 && !hashMove) depth--; } MovePicker mp; if (!isPV && !inCheck) { const int opponentHasEasyCapture = !!OpponentsEasyCaptures(board); + const int opponentDeclining = ss->staticEval + (ss - 1)->staticEval > 1; // Reverse Futility Pruning // i.e. the static eval is so far above beta we prune if (depth <= i0 && !ss->skip && eval < TB_WIN_BOUND && eval >= beta && - eval - i1 * depth + i2 * (improving && !opponentHasEasyCapture) >= beta && + eval - i1 * depth + i2 * (improving && !opponentHasEasyCapture) + i4 * opponentDeclining >= beta && (!hashMove || GetHistory(ss, thread, hashMove) > i3)) return (eval + beta) / 2; @@ -684,15 +698,15 @@ int Negamax(int alpha, int beta, int depth, int cutnode, ThreadData* thread, PV* int history = GetHistory(ss, thread, move); int R = LMR[Min(depth, 63)][Min(legalMoves, 63)]; - R -= p0 * history / 65536; // adjust reduction based on historical score - R += (IsCap(hashMove) || IsPromo(hashMove)); // increase reduction if hash move is noisy + R -= p0 * history / 65536; // adjust reduction based on historical score + R += p1 * (IsCap(hashMove) || IsPromo(hashMove)); // increase reduction if hash move is noisy if (bestScore > -TB_WIN_BOUND) { if (!isRoot && legalMoves >= LMP[improving][depth]) skipQuiets = 1; if (!IsCap(move) && PromoPT(move) != QUEEN) { - int lmrDepth = Max(1, depth - R); + int lmrDepth = Max(1, depth - R / 1024); if (!killerOrCounter && lmrDepth < m0 && history < -m1 * (depth - 1)) { skipQuiets = 1; @@ -774,32 +788,32 @@ int Negamax(int alpha, int beta, int depth, int cutnode, ThreadData* thread, PV* if (depth > 1 && legalMoves > 1 && !(isPV && IsCap(move))) { // increase reduction on non-pv if (!ttPv) - R += 2; + R += p2; // increase reduction if our eval is declining if (!improving) - R++; + R += p3; // reduce these special quiets less if (killerOrCounter) - R -= 2; + R -= p4; // move GAVE check if (board->checkers) - R--; + R -= p5; // Reduce more on expected cut nodes // idea from komodo/sf, explained by Don Daily here // https://talkchess.com/forum3/viewtopic.php?f=7&t=47577&start=10#p519741 // and https://www.chessprogramming.org/Node_Types if (cutnode) - R += 1 + !IsCap(move); + R += p6 + p7 * !IsCap(move); if (ttDepth >= depth) - R--; + R -= p8; // prevent dropping into QS, extending, or reducing all extensions - R = Min(newDepth, Max(R, 1)); + R = Min(newDepth, Max(R / 1024, 1)); int lmrDepth = newDepth - R; score = -Negamax(-alpha - 1, -alpha, lmrDepth, 1, thread, &childPv, ss + 1); @@ -885,8 +899,10 @@ int Negamax(int alpha, int beta, int depth, int cutnode, ThreadData* thread, PV* if (!ss->skip && !(isRoot && thread->multiPV > 0)) TTPut(tt, board->zobrist, depth, bestScore, bound, bestMove, ss->ply, rawEval, ttPv); - if (!inCheck && !IsCap(bestMove) && (bound & (bestScore >= rawEval ? BOUND_LOWER : BOUND_UPPER))) - UpdatePawnCorrection(rawEval, bestScore, board, thread); + if (!inCheck && !IsCap(bestMove) && (bound & (bestScore >= rawEval ? BOUND_LOWER : BOUND_UPPER))) { + UpdatePawnCorrection(rawEval, bestScore, depth, board, thread); + UpdateContCorrection(rawEval, bestScore, depth, ss, thread); + } return bestScore; } @@ -941,7 +957,7 @@ int Quiesce(int alpha, int beta, int depth, ThreadData* thread, SearchStack* ss) rawEval = ttEval; if (rawEval == EVAL_UNKNOWN) rawEval = Evaluate(board, thread); - eval = ss->staticEval = ClampEval(rawEval + GetPawnCorrection(board, thread) / 2); + eval = ss->staticEval = ClampEval(rawEval + GetPawnCorrection(board, thread) / 2 + GetContCorrection(ss, thread)); // correct eval on fmr eval = AdjustEvalOnFMR(board, eval); @@ -950,7 +966,7 @@ int Quiesce(int alpha, int beta, int depth, ThreadData* thread, SearchStack* ss) eval = ttScore; } else { rawEval = Evaluate(board, thread); - eval = ss->staticEval = ClampEval(rawEval + GetPawnCorrection(board, thread) / 2); + eval = ss->staticEval = ClampEval(rawEval + GetPawnCorrection(board, thread) / 2 + GetContCorrection(ss, thread)); // correct eval on fmr eval = AdjustEvalOnFMR(board, eval); @@ -1134,6 +1150,7 @@ void SearchClearThread(ThreadData* thread) { memset(&thread->ch, 0, sizeof(thread->ch)); memset(&thread->caph, 0, sizeof(thread->caph)); memset(&thread->pawnCorrection, 0, sizeof(thread->pawnCorrection)); + memset(&thread->contCorrection, 0, sizeof(thread->contCorrection)); thread->board.accumulators = thread->accumulators; thread->previousScore = UNKNOWN; diff --git a/src/types.h b/src/types.h index c441f839..3f0fc0b7 100644 --- a/src/types.h +++ b/src/types.h @@ -38,7 +38,8 @@ #define ALIGN_ON 64 #define ALIGN __attribute__((aligned(ALIGN_ON))) -#define PAWN_CORRECTION_GRAIN 256 +#define CORRECTION_GRAIN 256 + #define PAWN_CORRECTION_SIZE 131072 #define PAWN_CORRECTION_MASK (PAWN_CORRECTION_SIZE - 1) @@ -198,6 +199,7 @@ struct ThreadData { int16_t caph[12][64][2][7]; // capture history (piece - to - defeneded - captured_type) int16_t pawnCorrection[PAWN_CORRECTION_SIZE]; + int16_t contCorrection[12][64][12][64]; int action, calls; pthread_t nativeThread; diff --git a/src/uci.c b/src/uci.c index 6f7872e3..faff97e6 100644 --- a/src/uci.c +++ b/src/uci.c @@ -54,6 +54,8 @@ extern float d0; extern float e0; +extern int e1; + extern float f0; extern float f1; @@ -71,6 +73,9 @@ extern int i0; extern int i1; extern int i2; extern int i3; +extern int i4; + +extern int i9; extern int v0; extern int v1; @@ -98,6 +103,14 @@ extern int o3; extern int o4; extern int p0; +extern int p1; +extern int p2; +extern int p3; +extern int p4; +extern int p5; +extern int p6; +extern int p7; +extern int p8; extern int q0; @@ -117,11 +130,11 @@ extern int z1; extern int z2; extern int z3; -float w0 = 0.3784; -float w1 = 0.0570; +float w0 = 0.4193; +float w1 = 0.0575; -float x0 = 0.7776; -float x1 = 5.8320; +float x0 = 0.9221; +float x1 = 5.9280; int MOVE_OVERHEAD = 50; int MULTI_PV = 1; @@ -136,8 +149,8 @@ SearchParams Limits; // this repo: https://github.com/vondele/WLD_model // Third order polynomial fit of Berserk data -const double as[4] = {-5.83465749, 46.43599644, -58.49798392, 172.62328616}; -const double bs[4] = {-7.95320845, 48.50833438, -66.34647240, 56.29169197}; +const double as[4] = {-2.02923586, 16.87641200, -27.06230207, 182.53858835}; +const double bs[4] = {-6.15230497, 42.37548361, -80.19006222, 77.75994970}; // win% as permilli given score and ply int WRModel(Score s, int ply) { @@ -354,6 +367,7 @@ void PrintUCIOptions() { printf("option name c1 type string\n"); printf("option name d0 type string\n"); printf("option name e0 type string\n"); + printf("option name e1 type string\n"); printf("option name f0 type string\n"); printf("option name f1 type string\n"); printf("option name g0 type string\n"); @@ -368,6 +382,8 @@ void PrintUCIOptions() { printf("option name i1 type string\n"); printf("option name i2 type string\n"); printf("option name i3 type string\n"); + printf("option name i4 type string\n"); + printf("option name i9 type string\n"); printf("option name v0 type string\n"); printf("option name v1 type string\n"); printf("option name k0 type string\n"); @@ -388,6 +404,14 @@ void PrintUCIOptions() { printf("option name o3 type string\n"); printf("option name o4 type string\n"); printf("option name p0 type string\n"); + printf("option name p1 type string\n"); + printf("option name p2 type string\n"); + printf("option name p3 type string\n"); + printf("option name p4 type string\n"); + printf("option name p5 type string\n"); + printf("option name p6 type string\n"); + printf("option name p7 type string\n"); + printf("option name p8 type string\n"); printf("option name q0 type string\n"); printf("option name r0 type string\n"); printf("option name s0 type string\n"); @@ -589,6 +613,7 @@ void UCILoop() { FO(c1) FO(d0) FO(e0) + IO(e1) FO(f0) FO(f1) FO(g0) @@ -603,6 +628,8 @@ void UCILoop() { IO(i1) IO(i2) IO(i3) + IO(i4) + IO(i9) IO(v0) IO(v1) IO(k0) @@ -623,6 +650,14 @@ void UCILoop() { IO(o3) IO(o4) IO(p0) + IO(p1) + IO(p2) + IO(p3) + IO(p4) + IO(p5) + IO(p6) + IO(p7) + IO(p8) IO(q0) IO(r0) IO(s0) @@ -639,7 +674,7 @@ void UCILoop() { IO(z2) IO(z3) - else printf("Unknown command: %s \n", in); + // else printf("Unknown command: %s \n", in); InitPruningAndReductionTables(); } diff --git a/src/uci.h b/src/uci.h index 11599351..854071c8 100644 --- a/src/uci.h +++ b/src/uci.h @@ -25,7 +25,7 @@ extern int CONTEMPT; extern SearchParams Limits; // Normalization of a score to 50% WR at 100cp -#define Normalize(s) ((s) / 1.54) +#define Normalize(s) ((s) / 1.70) int WRModel(Score s, int ply);