Merge official-stockfish/master
authorFabian Fichter <ianfab@users.noreply.github.com>
Thu, 3 Sep 2020 21:43:11 +0000 (23:43 +0200)
committerFabian Fichter <ianfab@users.noreply.github.com>
Thu, 3 Sep 2020 21:43:11 +0000 (23:43 +0200)
1  2 
src/evaluate.cpp

@@@ -35,8 -34,8 +35,8 @@@ namespace Trace 
  
    enum Tracing { NO_TRACE, TRACE };
  
 -  enum Term { // The first 8 entries are reserved for PieceType
 -    MATERIAL = 8, IMBALANCE, MOBILITY, THREAT, PASSED, SPACE, WINNABLE, TOTAL, TERM_NB
 +  enum Term { // The first PIECE_TYPE_NB entries are reserved for PieceType
-     MATERIAL = PIECE_TYPE_NB, IMBALANCE, MOBILITY, THREAT, PASSED, SPACE, INITIATIVE, VARIANT, TOTAL, TERM_NB
++    MATERIAL = PIECE_TYPE_NB, IMBALANCE, MOBILITY, THREAT, PASSED, SPACE, VARIANT, WINNABLE, TOTAL, TERM_NB
    };
  
    Score scores[TERM_NB][COLOR_NB];
@@@ -183,9 -173,7 +183,8 @@@ namespace 
      template<Color Us> Score threats() const;
      template<Color Us> Score passed() const;
      template<Color Us> Score space() const;
 +    template<Color Us> Score variant() const;
-     ScaleFactor scale_factor(Value eg) const;
-     Score initiative(Score score) const;
+     Value winnable(Score score) const;
  
      const Position& pos;
      Material::Entry* me;
    }
  
  
 +  // Evaluation::variant() computes variant-specific evaluation bonuses for a given side.
 +
 +  template<Tracing T> template<Color Us>
 +  Score Evaluation<T>::variant() const {
 +
 +    constexpr Color Them = ~Us;
 +    constexpr Direction Down = pawn_push(Them);
 +
 +    Score score = SCORE_ZERO;
 +
 +    // Capture the flag
 +    if (pos.capture_the_flag(Us))
 +    {
 +        PieceType ptCtf = pos.capture_the_flag_piece();
 +        Bitboard ctfPieces = pos.pieces(Us, ptCtf);
 +        Bitboard ctfTargets = pos.capture_the_flag(Us) & pos.board_bb();
 +        Bitboard onHold = 0;
 +        Bitboard onHold2 = 0;
 +        Bitboard processed = 0;
 +        Bitboard blocked = pos.pieces(Us, PAWN) | attackedBy[Them][ALL_PIECES];
 +        Bitboard doubleBlocked =  attackedBy2[Them]
 +                                | (pos.pieces(Us, PAWN) & (shift<Down>(pos.pieces()) | attackedBy[Them][ALL_PIECES]))
 +                                | (pos.pieces(Them) & pe->pawn_attacks(Them))
 +                                | (pawn_attacks_bb<Them>(pos.pieces(Them, PAWN) & pe->pawn_attacks(Them)));
 +        Bitboard inaccessible = pos.pieces(Us, PAWN) & shift<Down>(pos.pieces(Them, PAWN));
 +        // Traverse all paths of the CTF pieces to the CTF targets.
 +        // Put squares that are attacked or occupied on hold for one iteration.
 +        for (int dist = 0; (ctfPieces || onHold || onHold2) && (ctfTargets & ~processed); dist++)
 +        {
 +            int wins = popcount(ctfTargets & ctfPieces);
 +            if (wins)
 +                score += make_score(4000, 4000) * wins / (wins + dist * dist);
 +            Bitboard current = ctfPieces & ~ctfTargets;
 +            processed |= ctfPieces;
 +            ctfPieces = onHold & ~processed;
 +            onHold = onHold2 & ~processed;
 +            onHold2 = 0;
 +            while (current)
 +            {
 +                Square s = pop_lsb(&current);
 +                Bitboard attacks = (  (PseudoAttacks[Us][ptCtf][s] & pos.pieces())
 +                                    | (PseudoMoves[Us][ptCtf][s] & ~pos.pieces())) & ~processed & pos.board_bb();
 +                ctfPieces |= attacks & ~blocked;
 +                onHold |= attacks & ~doubleBlocked;
 +                onHold2 |= attacks & ~inaccessible;
 +            }
 +        }
 +    }
 +
 +    // nCheck
 +    if (pos.check_counting())
 +    {
 +        int remainingChecks = pos.checks_remaining(Us);
 +        assert(remainingChecks > 0);
 +        score += make_score(3600, 1000) / (remainingChecks * remainingChecks);
 +    }
 +
 +    // Extinction
 +    if (pos.extinction_value() != VALUE_NONE)
 +    {
 +        for (PieceType pt : pos.extinction_piece_types())
 +            if (pt != ALL_PIECES)
 +            {
 +                int denom = std::max(pos.count(Us, pt) - pos.extinction_piece_count(), 1);
 +                if (pos.count(Them, pt) >= pos.extinction_opponent_piece_count() || pos.two_boards())
 +                    score += make_score(1000000 / (500 + PieceValue[MG][pt]),
 +                                        1000000 / (500 + PieceValue[EG][pt])) / (denom * denom)
 +                            * (pos.extinction_value() / VALUE_MATE);
 +            }
 +            else if (pos.extinction_value() == VALUE_MATE)
 +                score += make_score(pos.non_pawn_material(Us), pos.non_pawn_material(Us)) / pos.count<ALL_PIECES>(Us);
 +    }
 +
 +    // Connect-n
 +    if (pos.connect_n() > 0)
 +    {
 +        for (Direction d : {NORTH, NORTH_EAST, EAST, SOUTH_EAST})
 +        {
 +            // Find sufficiently large gaps
 +            Bitboard b = pos.board_bb() & ~pos.pieces(Them);
 +            for (int i = 1; i < pos.connect_n(); i++)
 +                b &= shift(d, b);
 +            // Count number of pieces per gap
 +            while (b)
 +            {
 +                Square s = pop_lsb(&b);
 +                int c = 0;
 +                for (int j = 0; j < pos.connect_n(); j++)
 +                    if (pos.pieces(Us) & (s - j * d))
 +                        c++;
 +                score += make_score(200, 200)  * c / (pos.connect_n() - c) / (pos.connect_n() - c);
 +            }
 +        }
 +    }
 +
 +    // Potential piece flips
 +    if (pos.flip_enclosed_pieces())
 +    {
 +        // Stable pieces
 +        if (pos.flip_enclosed_pieces() == REVERSI)
 +        {
 +            Bitboard edges = (FileABB | file_bb(pos.max_file()) | Rank1BB | rank_bb(pos.max_rank())) & pos.board_bb();
 +            Bitboard edgePieces = pos.pieces(Us) & edges;
 +            while (edgePieces)
 +            {
 +                Bitboard connectedEdge = attacks_bb(Us, ROOK, pop_lsb(&edgePieces), ~(pos.pieces(Us) & edges)) & edges;
 +                if (!more_than_one(connectedEdge & ~pos.pieces(Us)))
 +                    score += make_score(300, 300);
 +                else if (!(connectedEdge & ~pos.pieces()))
 +                    score += make_score(200, 200);
 +            }
 +        }
 +
 +        // Unstable
 +        Bitboard unstable = 0;
 +        Bitboard drops = pos.drop_region(Them, IMMOBILE_PIECE);
 +        while (drops)
 +        {
 +            Square s = pop_lsb(&drops);
 +            if (pos.flip_enclosed_pieces() == REVERSI)
 +            {
 +                Bitboard b = attacks_bb(Them, QUEEN, s, ~pos.pieces(Us)) & ~PseudoAttacks[Them][KING][s] & pos.pieces(Them);
 +                while(b)
 +                    unstable |= between_bb(s, pop_lsb(&b));
 +            }
 +            else
 +                unstable |= PseudoAttacks[Them][KING][s] & pos.pieces(Us);
 +        }
 +        score -= make_score(200, 200) * popcount(unstable);
 +    }
 +
 +    if (T)
 +        Trace::add(VARIANT, Us, score);
 +
 +    return score;
 +  }
 +
 +
-   // Evaluation::initiative() computes the initiative correction value
-   // for the position. It is a second order bonus/malus based on the
+   // Evaluation::winnable() adjusts the mg and eg score components based on the
    // known attacking/defending status of the players.
+   // A single value is derived from the mg and eg values and returned.
  
    template<Tracing T>
-   Score Evaluation<T>::initiative(Score score) const {
+   Value Evaluation<T>::winnable(Score score) const {
  
 -    int outflanking =  distance<File>(pos.square<KING>(WHITE), pos.square<KING>(BLACK))
 -                     - distance<Rank>(pos.square<KING>(WHITE), pos.square<KING>(BLACK));
 +    // No initiative bonus for extinction variants
-     if (pos.extinction_value() != VALUE_NONE || pos.captures_to_hand() || pos.connect_n())
-       return SCORE_ZERO;
++    int complexity = 0;
++    if (pos.extinction_value() == VALUE_NONE && !pos.captures_to_hand() && !pos.connect_n())
++    {
 +    int outflanking = !pos.count<KING>(WHITE) || !pos.count<KING>(BLACK) ? 0
 +                     :  distance<File>(pos.square<KING>(WHITE), pos.square<KING>(BLACK))
 +                      - distance<Rank>(pos.square<KING>(WHITE), pos.square<KING>(BLACK));
  
      bool pawnsOnBothFlanks =   (pos.pieces(PAWN) & QueenSide)
                              && (pos.pieces(PAWN) & KingSide);
  
      bool almostUnwinnable =   outflanking < 0
 +                           && pos.stalemate_value() == VALUE_DRAW
                             && !pawnsOnBothFlanks;
  
 -    bool infiltration = rank_of(pos.square<KING>(WHITE)) > RANK_4
 -                     || rank_of(pos.square<KING>(BLACK)) < RANK_5;
 +    bool infiltration =   (pos.count<KING>(WHITE) && rank_of(pos.square<KING>(WHITE)) > RANK_4)
 +                       || (pos.count<KING>(BLACK) && rank_of(pos.square<KING>(BLACK)) < RANK_5);
  
      // Compute the initiative bonus for the attacking side
--    int complexity =   9 * pe->passed_count()
++    complexity =       9 * pe->passed_count()
                      + 12 * pos.count<PAWN>()
 +                    + 15 * pos.count<SOLDIER>()
                      +  9 * outflanking
                      + 21 * pawnsOnBothFlanks
                      + 24 * infiltration
                      - 43 * almostUnwinnable
                      -  2 * pos.rule50_count()
                      -110 ;
++    }
  
      Value mg = mg_value(score);
      Value eg = eg_value(score);
                  sf = 22 + 3 * pos.count<ALL_PIECES>(strongSide);
          }
          else
 -            sf = std::min(sf, 36 + 7 * pos.count<PAWN>(strongSide));
 +            sf = std::min(sf, 36 + 7 * (pos.count<PAWN>(strongSide) + pos.count<SOLDIER>(strongSide)));
      }
  
-     return ScaleFactor(sf);
+     // Interpolate between the middlegame and (scaled by 'sf') endgame score
+     v =  mg * int(me->game_phase())
+        + eg * int(PHASE_MIDGAME - me->game_phase()) * ScaleFactor(sf) / SCALE_FACTOR_NORMAL;
+     v /= PHASE_MIDGAME;
+     if (T)
+     {
+         Trace::add(WINNABLE, make_score(u, eg * ScaleFactor(sf) / SCALE_FACTOR_NORMAL - eg_value(score)));
+         Trace::add(TOTAL, make_score(mg, eg * ScaleFactor(sf) / SCALE_FACTOR_NORMAL));
+     }
+     return Value(v);
    }
  
  
      score +=  king<   WHITE>() - king<   BLACK>()
              + threats<WHITE>() - threats<BLACK>()
              + passed< WHITE>() - passed< BLACK>()
 -            + space<  WHITE>() - space<  BLACK>();
 +            + space<  WHITE>() - space<  BLACK>()
 +            + variant<WHITE>() - variant<BLACK>();
  
-     score += initiative(score);
-     // Interpolate between a middlegame and a (scaled by 'sf') endgame score
-     ScaleFactor sf = scale_factor(eg_value(score));
-     v =  mg_value(score) * int(me->game_phase())
-        + eg_value(score) * int(PHASE_MIDGAME - me->game_phase()) * sf / SCALE_FACTOR_NORMAL;
-     v /= PHASE_MIDGAME;
+     // Derive single value from mg and eg parts of score
+     v = winnable(score);
  
      // In case of tracing add all remaining individual evaluation terms
      if (T)
@@@ -1285,8 -905,7 +1282,8 @@@ std::string Eval::trace(const Position
       << "     Threats | " << Term(THREAT)
       << "      Passed | " << Term(PASSED)
       << "       Space | " << Term(SPACE)
-      << "  Initiative | " << Term(INITIATIVE)
 +     << "     Variant | " << Term(VARIANT)
+      << "    Winnable | " << Term(WINNABLE)
       << " ------------+-------------+-------------+------------\n"
       << "       Total | " << Term(TOTAL);