diff --git a/src/eval.cpp b/src/eval.cpp index cfdfe84..32e785d 100644 --- a/src/eval.cpp +++ b/src/eval.cpp @@ -31,8 +31,16 @@ Eval::Score Eval::mobilidade_bispo[14]; Eval::Score Eval::mobilidade_torre[15]; Eval::Score Eval::mobilidade_dama[28]; +Eval::Score Eval::ks_weight_c, Eval::ks_weight_b, Eval::ks_weight_t, Eval::ks_weight_d; + int peao_ala_da_dama[LADOS],peao_ala_do_rei[LADOS]; +// King safety zone: 9 squares around each square (king + 8 neighbors). +// Built once at startup; eval-time we look up the enemy king's zone and +// AND each attacker's bitboard against it. File-scope (not exposed in +// eval.h) because no other TU needs it. +static Bitboard::u64 king_zone[CASAS_DO_TABULEIRO]; + void Eval::init_eval_tables(){ // Phase 1 of the tapered-eval rollout still has mg = eg, so the packed // score per square just duplicates the legacy single value into both @@ -85,6 +93,20 @@ void Eval::init_eval_tables(){ for (int i = 0; i < 28; i++){ mobilidade_dama[i] = make_score(Values::mobilidade_dama_mg[i], Values::mobilidade_dama_eg[i]); } + + // King zone = king attack mask | the king square itself (the 3x3 block + // around each square). One precomputed bitboard per square so eval-time + // is one load + one AND per attacker. + for (int x = 0; x < CASAS_DO_TABULEIRO; x++){ + king_zone[x] = Gen::bit_moves_rei[x] | Bitboard::mask[x]; + } + + // King safety weights — mg in low 16, eg=0 in high 16. Tuner only + // adjusts the mg half. + ks_weight_c = make_score(KS_WEIGHT_C, 0); + ks_weight_b = make_score(KS_WEIGHT_B, 0); + ks_weight_t = make_score(KS_WEIGHT_T, 0); + ks_weight_d = make_score(KS_WEIGHT_D, 0); } int Eval::fase(){ @@ -168,6 +190,24 @@ int Eval::avaliar(){ Bitboard::u64 t1; int casa; + // King zones for both sides. When iterating side L's pieces, we count + // attacks landing in zona_inimiga[l] — the zone around the OTHER king, + // i.e. the squares L is threatening. + const int rei_branco_sq = Bitboard::bitscan(Bitboard::bit_pieces[BRANCAS][R]); + const int rei_preto_sq = Bitboard::bitscan(Bitboard::bit_pieces[PRETAS][R]); + const Bitboard::u64 zona_inimiga[LADOS] = { + king_zone[rei_preto_sq], + king_zone[rei_branco_sq] + }; + + // Snapshot KS weights once. They're stored as Score objects (so the + // tuner can mutate them at runtime), but only the mg half is meaningful. + // Extracting once per eval avoids redundant unpacks in the hot loop. + const int kw_c = mg_score(ks_weight_c); + const int kw_b = mg_score(ks_weight_b); + const int kw_t = mg_score(ks_weight_t); + const int kw_d = mg_score(ks_weight_d); + for (int l = 0; l < LADOS; l++){ // Mobility uses popcount of attack squares that aren't own pieces. @@ -176,6 +216,8 @@ int Eval::avaliar(){ // recognize that bishop/rook mobility matters more in the endgame // where boards are open, while knight mobility is roughly uniform. const Bitboard::u64 nao_proprios = ~Bitboard::bit_lados[l]; + const Bitboard::u64 zona = zona_inimiga[l]; + int pressao_rei = 0; t1 = Bitboard::bit_pieces[l][P]; while (t1){ @@ -193,7 +235,9 @@ int Eval::avaliar(){ t1 &= Bitboard::not_mask[casa]; score[l] += score_casas[l][C][casa]; - score[l] += mobilidade_cavalo[Bitboard::popcount(Gen::bit_moves_cavalo[casa] & nao_proprios)]; + const Bitboard::u64 att = Gen::bit_moves_cavalo[casa]; + score[l] += mobilidade_cavalo[Bitboard::popcount(att & nao_proprios)]; + pressao_rei += Bitboard::popcount(att & zona) * kw_c; } t1 = Bitboard::bit_pieces[l][B]; @@ -202,7 +246,9 @@ int Eval::avaliar(){ t1 &= Bitboard::not_mask[casa]; score[l] += score_casas[l][B][casa]; - score[l] += mobilidade_bispo[Bitboard::popcount(Gen::atacantes_bispo(casa) & nao_proprios)]; + const Bitboard::u64 att = Gen::atacantes_bispo(casa); + score[l] += mobilidade_bispo[Bitboard::popcount(att & nao_proprios)]; + pressao_rei += Bitboard::popcount(att & zona) * kw_b; } if (Bitboard::popcount(Bitboard::bit_pieces[l][B]) >= 2){ @@ -217,7 +263,9 @@ int Eval::avaliar(){ score[l] += score_casas[l][T][casa]; const int torre_b = avaliar_torre(l, casa); score[l] += make_score(torre_b, torre_b); - score[l] += mobilidade_torre[Bitboard::popcount(Gen::atacantes_torre(casa) & nao_proprios)]; + const Bitboard::u64 att = Gen::atacantes_torre(casa); + score[l] += mobilidade_torre[Bitboard::popcount(att & nao_proprios)]; + pressao_rei += Bitboard::popcount(att & zona) * kw_t; } t1 = Bitboard::bit_pieces[l][D]; @@ -226,8 +274,15 @@ int Eval::avaliar(){ t1 &= Bitboard::not_mask[casa]; score[l] += score_casas[l][D][casa]; - score[l] += mobilidade_dama[Bitboard::popcount((Gen::atacantes_bispo(casa) | Gen::atacantes_torre(casa)) & nao_proprios)]; + const Bitboard::u64 att = Gen::atacantes_bispo(casa) | Gen::atacantes_torre(casa); + score[l] += mobilidade_dama[Bitboard::popcount(att & nao_proprios)]; + pressao_rei += Bitboard::popcount(att & zona) * kw_d; } + + // Quadratic king-safety bonus, mg-only. The squaring is what makes + // the term care about *multiple* attackers — a lone knight near the + // enemy king is uninteresting, but two attackers compound. + score[l] += make_score((pressao_rei * pressao_rei) / KS_SCALE, 0); } // Pawn shield is a midgame-only concept (king wants to stay tucked diff --git a/src/eval.h b/src/eval.h index ff2fb6a..d7f42e5 100644 --- a/src/eval.h +++ b/src/eval.h @@ -64,6 +64,13 @@ namespace Eval{ extern Score mobilidade_torre[15]; extern Score mobilidade_dama[28]; + // King safety weights, stored as mg-only packed Scores (eg half always + // zero). Tuned via the standard Score-tuning machinery in tuner.cpp; + // eg stays 0 because king-safety pressure is a midgame concept and the + // phase taper alone provides the endgame fade. Initial values come from + // Values::KS_WEIGHT_* and get assigned in init_eval_tables. + extern Score ks_weight_c, ks_weight_b, ks_weight_t, ks_weight_d; + void init_eval_tables(); void atualizar_materiais(); diff --git a/src/tuner.cpp b/src/tuner.cpp index f6dd6e3..717e2cd 100644 --- a/src/tuner.cpp +++ b/src/tuner.cpp @@ -250,7 +250,14 @@ static void register_params() { for (int i = 0; i < 15; i++){ params.push_back({&Eval::mobilidade_torre[i], NULL, true}); params.push_back({&Eval::mobilidade_torre[i], NULL, false}); } for (int i = 0; i < 28; i++){ params.push_back({&Eval::mobilidade_dama[i], NULL, true}); params.push_back({&Eval::mobilidade_dama[i], NULL, false}); } - fprintf(stderr, "tuner: registered %zu parameters (PSTs + mobility, mg+eg halves)\n", params.size()); + // King safety weights: mg-only (eg half permanently 0, not registered), + // side-independent (no mirror). + params.push_back({&Eval::ks_weight_c, NULL, true}); + params.push_back({&Eval::ks_weight_b, NULL, true}); + params.push_back({&Eval::ks_weight_t, NULL, true}); + params.push_back({&Eval::ks_weight_d, NULL, true}); + + fprintf(stderr, "tuner: registered %zu parameters (PSTs + mobility + king safety, mg+eg halves)\n", params.size()); } // Apply delta (typically ±1) to one half of the parameter, mirroring to @@ -385,6 +392,11 @@ static void print_tuned_values() { print_mobility_array("mobilidade_dama_mg", Eval::mobilidade_dama, 28, true); print_mobility_array("mobilidade_dama_eg", Eval::mobilidade_dama, 28, false); + fprintf(stdout, "\t#define KS_WEIGHT_C %d\n", Eval::mg_score(Eval::ks_weight_c)); + fprintf(stdout, "\t#define KS_WEIGHT_B %d\n", Eval::mg_score(Eval::ks_weight_b)); + fprintf(stdout, "\t#define KS_WEIGHT_T %d\n", Eval::mg_score(Eval::ks_weight_t)); + fprintf(stdout, "\t#define KS_WEIGHT_D %d\n\n", Eval::mg_score(Eval::ks_weight_d)); + fprintf(stdout, "// ==== end tuned values ====\n"); } diff --git a/src/values.h b/src/values.h index 86fae56..2a52456 100644 --- a/src/values.h +++ b/src/values.h @@ -16,17 +16,16 @@ namespace Values{ #define VALOR_DAMA 900 #define VALOR_REI 10000 - // PeSTO tapered piece values — mg and eg material baked into score_casas. - #define VALOR_PEAO_MG 82 - #define VALOR_PEAO_EG 94 - #define VALOR_CAVALO_MG 337 - #define VALOR_CAVALO_EG 281 - #define VALOR_BISPO_MG 365 - #define VALOR_BISPO_EG 297 - #define VALOR_TORRE_MG 477 - #define VALOR_TORRE_EG 512 - #define VALOR_DAMA_MG 1025 - #define VALOR_DAMA_EG 936 + #define VALOR_PEAO_MG 0 + #define VALOR_PEAO_EG 0 + #define VALOR_CAVALO_MG 0 + #define VALOR_CAVALO_EG 0 + #define VALOR_BISPO_MG 0 + #define VALOR_BISPO_EG 0 + #define VALOR_TORRE_MG 0 + #define VALOR_TORRE_EG 0 + #define VALOR_DAMA_MG 0 + #define VALOR_DAMA_EG 0 const int pieces_valor[6] = { @@ -44,6 +43,21 @@ namespace Values{ #define BISHOP_PAIR_MG 15 #define BISHOP_PAIR_EG 45 + // King safety. For each side L we count L's attacks landing in the + // 9-square zone around the enemy king (king + 8 neighbors), weighted + // per attacker type. The accumulated "pressao" gets a quadratic mg + // bonus: bonus = pressao² / KS_SCALE. Quadratic so the second/third + // attacker matters disproportionately more than the first (one piece + // near the king is fine, three pieces is mate). Mg-only because heavy + // pieces come off in the endgame and the king becomes an active piece, + // where this term would invert in motivation. Initial weights are + // SF-classical-era literature defaults — Texel tuner adjusts them. + #define KS_WEIGHT_C 20 + #define KS_WEIGHT_B 21 + #define KS_WEIGHT_T 41 + #define KS_WEIGHT_D 81 + #define KS_SCALE 256 + #define PHASE_PEAO 0 #define PHASE_CAVALO 1 #define PHASE_BISPO 1 @@ -60,30 +74,30 @@ namespace Values{ // at the top. First-pass literature values; will be Texel-tuned later. // Knight max attack-count = 8, bishop = 13, rook = 14, queen = 27. const int mobilidade_cavalo_mg[9] = { - -40, -15, -3, 0, 3, 6, 9, 11, 13 + -38, -13, -1, 2, 5, 7, 7, 9, 11 }; const int mobilidade_cavalo_eg[9] = { - -35, -12, -3, 0, 3, 6, 9, 11, 13 + -33, -10, -1, 2, 5, 8, 11, 13, 15 }; const int mobilidade_bispo_mg[14] = { - -25, -10, -2, 0, 3, 5, 7, 9, 10, 11, 12, 12, 12, 12 + -23, -11, -4, -2, 1, 3, 9, 9, 8, 9, 10, 11, 13, 14 }; const int mobilidade_bispo_eg[14] = { - -30, -15, -3, 3, 8, 11, 14, 17, 19, 21, 23, 24, 25, 26 + -28, -17, -5, 1, 6, 9, 12, 19, 21, 23, 23, 25, 23, 24 }; const int mobilidade_torre_mg[15] = { - -25, -10, -3, 0, 2, 4, 6, 7, 8, 9, 10, 10, 10, 10, 10 + -23, -8, -2, -2, 0, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8 }; const int mobilidade_torre_eg[15] = { - -35, -15, -5, 0, 5, 10, 15, 18, 21, 23, 25, 27, 28, 29, 30 + -33, -13, -6, -1, 7, 12, 17, 20, 22, 25, 27, 26, 26, 27, 28 }; const int mobilidade_dama_mg[28] = { - -15, -8, -2, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, - 11, 12, 13, 14, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15 + -13, -6, -1, 1, 2, 0, 2, 2, 3, 7, 9, 10, 11, 12, + 13, 14, 15, 16, 17, 17, 16, 17, 16, 13, 15, 13, 13, 17 }; const int mobilidade_dama_eg[28] = { - -20, -12, -4, 0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, - 22, 24, 26, 28, 29, 30, 31, 32, 33, 34, 34, 34, 34, 34 + -22, -10, -6, 0, 0, 2, 4, 6, 8, 10, 16, 18, 20, 22, + 24, 26, 28, 30, 31, 32, 33, 34, 35, 35, 32, 32, 32, 36 }; @@ -244,108 +258,108 @@ namespace Values{ }}; const int peao_score_mg[64] = { - 0, 0, 0, 0, 0, 0, 0, 0, - -35, -1, -20, -23, -15, 24, 38, -22, - -26, -4, -4, -10, 3, 3, 33, -12, - -27, -2, -5, 12, 17, 6, 10, -25, - -14, 13, 6, 21, 23, 12, 17, -23, - -6, 7, 26, 31, 65, 56, 25, -20, - 98, 134, 61, 95, 68, 126, 34, -11, - 0, 0, 0, 0, 0, 0, 0, 0 + 82, 82, 82, 82, 82, 82, 82, 82, + 49, 79, 64, 61, 67, 104, 118, 62, + 58, 79, 77, 74, 87, 87, 113, 72, + 57, 82, 79, 92, 97, 90, 94, 59, + 67, 93, 86, 101, 107, 96, 98, 61, + 74, 87, 106, 111, 145, 139, 105, 60, + 182, 218, 145, 179, 152, 210, 114, 73, + 82, 82, 82, 82, 82, 82, 82, 82 }; const int peao_score_eg[64] = { - 0, 0, 0, 0, 0, 0, 0, 0, - 13, 8, 8, 10, 13, 0, 2, -7, - 4, 7, -6, 1, 0, -5, -1, -8, - 13, 9, -3, -7, -7, -8, 3, -1, - 32, 24, 13, 5, -2, 4, 17, 17, - 94, 100, 85, 67, 56, 53, 82, 84, - 178, 173, 158, 134, 147, 132, 165, 187, - 0, 0, 0, 0, 0, 0, 0, 0 + 94, 94, 94, 94, 94, 94, 94, 94, + 109, 100, 104, 106, 109, 96, 94, 89, + 100, 100, 90, 97, 96, 91, 91, 88, + 109, 105, 93, 89, 89, 88, 99, 95, + 128, 117, 109, 97, 94, 100, 113, 113, + 186, 192, 177, 159, 148, 145, 174, 176, + 274, 265, 254, 230, 241, 225, 257, 279, + 94, 94, 94, 94, 94, 94, 94, 94 }; const int cavalo_score_mg[64] = { - -105, -21, -58, -33, -17, -28, -19, -23, - -29, -53, -12, -3, -1, 18, -14, -19, - -23, -9, 12, 10, 19, 17, 25, -16, - -13, 4, 16, 13, 28, 19, 21, -8, - -9, 17, 19, 53, 37, 69, 18, 22, - -47, 60, 37, 65, 84, 129, 73, 44, - -73, -41, 72, 36, 23, 62, 7, -17, - -167, -89, -34, -49, 61, -97, -15, -107 + 230, 318, 281, 306, 322, 311, 320, 312, + 308, 286, 327, 336, 338, 353, 325, 320, + 316, 330, 347, 349, 358, 352, 360, 323, + 326, 343, 355, 352, 367, 355, 360, 331, + 330, 356, 358, 388, 372, 404, 357, 361, + 292, 395, 376, 400, 423, 468, 409, 383, + 266, 298, 407, 375, 362, 401, 342, 322, + 172, 250, 302, 290, 400, 242, 324, 231 }; const int cavalo_score_eg[64] = { - -29, -51, -23, -15, -22, -18, -50, -64, - -42, -20, -10, -5, -2, -20, -23, -44, - -23, -3, -1, 15, 10, -3, -20, -22, - -18, -6, 16, 25, 16, 17, 4, -18, - -17, 3, 22, 22, 22, 11, 8, -18, - -24, -20, 10, 9, -1, -9, -19, -41, - -25, -8, -25, -2, -9, -25, -24, -52, - -58, -38, -13, -28, -31, -27, -63, -99 + 250, 232, 260, 268, 261, 265, 233, 216, + 241, 263, 269, 278, 281, 259, 260, 239, + 256, 280, 278, 298, 293, 276, 263, 257, + 265, 277, 299, 308, 299, 300, 287, 265, + 266, 286, 305, 305, 305, 294, 291, 265, + 259, 263, 293, 292, 282, 274, 264, 242, + 258, 275, 254, 281, 274, 257, 259, 231, + 225, 245, 270, 255, 252, 256, 220, 184 }; const int bispo_score_mg[64] = { - -33, -3, -14, -21, -13, -12, -39, -21, - 4, 15, 16, 0, 7, 21, 33, 1, - 0, 15, 15, 15, 14, 27, 18, 10, - -6, 13, 13, 26, 34, 12, 10, 4, - -4, 5, 19, 50, 37, 37, 7, -2, - -16, 37, 43, 40, 35, 50, 37, -2, - -26, 16, -18, -13, 30, 59, 18, -47, - -29, 4, -82, -37, -25, -42, 7, -8 + 334, 364, 353, 346, 350, 354, 328, 346, + 367, 382, 379, 363, 370, 384, 396, 366, + 367, 378, 378, 378, 377, 390, 381, 377, + 357, 378, 376, 389, 399, 375, 377, 371, + 359, 372, 382, 417, 400, 400, 374, 365, + 351, 400, 406, 403, 402, 417, 404, 365, + 338, 379, 349, 354, 393, 422, 381, 320, + 338, 371, 285, 330, 338, 321, 374, 359 }; const int bispo_score_eg[64] = { - -23, -9, -23, -5, -9, -16, -5, -17, - -14, -18, -7, -1, 4, -9, -15, -27, - -12, -3, 8, 10, 13, 3, -7, -15, - -6, 3, 13, 19, 7, 10, -3, -9, - -3, 9, 12, 9, 14, 10, 3, 2, - 2, -8, 0, -1, -2, 6, 0, 4, - -8, -4, 7, -12, -3, -13, -4, -14, - -14, -21, -11, -8, -7, -9, -17, -24 + 276, 288, 276, 290, 286, 279, 290, 282, + 281, 277, 288, 294, 299, 286, 280, 268, + 283, 292, 303, 305, 308, 298, 288, 284, + 289, 300, 308, 314, 306, 305, 296, 286, + 293, 308, 307, 308, 313, 308, 302, 301, + 299, 291, 296, 298, 297, 305, 299, 303, + 291, 293, 304, 287, 293, 286, 291, 285, + 285, 278, 288, 291, 292, 290, 282, 275 }; const int torre_score_mg[64] = { - -19, -13, 1, 17, 16, 7, -37, -26, - -44, -16, -20, -9, -1, 11, -6, -71, - -45, -25, -16, -17, 3, 0, -5, -33, - -36, -26, -12, -1, 9, -7, 6, -23, - -24, -11, 7, 26, 24, 35, -8, -20, - -5, 19, 26, 36, 17, 45, 61, 16, - 27, 32, 58, 62, 80, 67, 26, 44, - 32, 42, 32, 51, 63, 9, 31, 43 + 460, 466, 476, 492, 491, 486, 442, 449, + 431, 459, 455, 466, 474, 486, 473, 408, + 434, 454, 459, 458, 478, 475, 474, 446, + 439, 453, 463, 474, 484, 472, 485, 456, + 455, 468, 482, 503, 499, 512, 471, 459, + 470, 498, 501, 511, 496, 524, 540, 495, + 502, 507, 533, 537, 555, 546, 505, 523, + 511, 518, 507, 526, 538, 488, 510, 522 }; const int torre_score_eg[64] = { - -9, 2, 3, -1, -5, -13, 4, -20, - -6, -6, 0, 2, -9, -9, -11, -3, - -4, 0, -5, -1, -7, -12, -8, -16, - 3, 5, 8, 4, -5, -6, -8, -11, - 4, 3, 13, 1, 2, 1, -1, 2, - 7, 7, 7, 5, 4, -3, -5, -3, - 11, 13, 13, 11, -3, 3, 8, 3, - 13, 10, 18, 15, 12, 12, 8, 5 + 505, 515, 513, 509, 505, 501, 518, 494, + 504, 504, 510, 512, 501, 501, 499, 511, + 506, 510, 505, 509, 503, 498, 506, 498, + 514, 519, 518, 514, 505, 508, 506, 503, + 518, 517, 523, 514, 515, 515, 513, 516, + 521, 518, 518, 515, 518, 511, 509, 511, + 525, 525, 523, 521, 510, 514, 522, 517, + 524, 524, 528, 525, 522, 526, 522, 519 }; const int dama_score_mg[64] = { - -1, -18, -9, 10, -15, -25, -31, -50, - -35, -8, 11, 2, 8, 15, -3, 1, - -14, 2, -11, -2, -5, 2, 14, 5, - -9, -26, -9, -10, -2, -4, 3, -3, - -27, -27, -16, -16, -1, 17, -2, 1, - -13, -17, 7, 8, 29, 56, 47, 57, - -24, -39, -5, 1, -16, 57, 28, 54, - -28, 0, 29, 12, 59, 44, 43, 45 + 1022, 1009, 1018, 1035, 1012, 1000, 992, 977, + 992, 1019, 1034, 1025, 1031, 1038, 1024, 1026, + 1009, 1026, 1016, 1021, 1018, 1025, 1040, 1030, + 1014, 1001, 1014, 1013, 1021, 1023, 1030, 1024, + 1000, 1000, 1007, 1009, 1022, 1044, 1025, 1028, + 1012, 1007, 1030, 1031, 1054, 1083, 1074, 1084, + 1003, 984, 1018, 1024, 1011, 1084, 1055, 1081, + 999, 1027, 1056, 1039, 1084, 1071, 1070, 1072 }; const int dama_score_eg[64] = { - -33, -28, -22, -43, -5, -32, -20, -41, - -22, -23, -30, -16, -16, -23, -36, -32, - -16, -27, 15, 6, 9, 17, 10, 5, - -18, 28, 19, 47, 31, 34, 39, 23, - 3, 22, 24, 45, 57, 40, 57, 36, - -20, 6, 9, 49, 47, 35, 19, 9, - -17, 20, 32, 41, 58, 25, 30, 0, - -9, 22, 22, 27, 27, 19, 10, 20 + 901, 910, 912, 894, 929, 902, 914, 897, + 916, 915, 904, 918, 918, 911, 901, 902, + 918, 911, 951, 940, 943, 951, 944, 939, + 918, 966, 953, 982, 969, 972, 977, 961, + 941, 960, 958, 983, 995, 978, 995, 974, + 918, 944, 943, 983, 985, 973, 957, 947, + 921, 958, 968, 979, 996, 963, 968, 938, + 929, 960, 960, 965, 965, 957, 948, 958 }; const int rei_score_mg[64] = {