Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
63 changes: 59 additions & 4 deletions src/eval.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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(){
Expand Down Expand Up @@ -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.
Expand All @@ -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){
Expand All @@ -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];
Expand All @@ -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){
Expand All @@ -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];
Expand All @@ -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
Expand Down
7 changes: 7 additions & 0 deletions src/eval.h
Original file line number Diff line number Diff line change
Expand Up @@ -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();

Expand Down
14 changes: 13 additions & 1 deletion src/tuner.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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");
}

Expand Down
Loading
Loading