Simplified Stockfish Evaluation

Discussion of chess software programming and technical issues.

Moderators: hgm, Dann Corbit, Harvey Williamson

User avatar
maksimKorzh
Posts: 771
Joined: Sat Sep 08, 2018 5:37 pm
Location: Ukraine
Full name: Maksim Korzh

Simplified Stockfish Evaluation

Post by maksimKorzh »

Hi guys

Today I've made an experiment:
1. Extracted material/PST/game phase material margin values from https://hxim.github.io/Stockfish-Evaluation-Guide/
2. Removed scaling and everything apart the pure tapered evaluation
3. Played 100 games versus https://www.chessprogramming.org/Simpli ... n_Function at 0s+100ms timecontrol (depth 5-6)

Here're the results where 1.1 is simplified eval and 1.3a is stockfish simplified eval:

Code: Select all

-----------------WukongJS 1.1-----------------
WukongJS 1.1 - WukongJS 1.3a : 56,5/100 45-32-23 (===00111000=11101001100=1=11011=00=01001001110=1111101=0=111=0=00=11111100=1=00=01111=011=1=11=001==)  57%   +49
-----------------WukongJS 1.3a-----------------
WukongJS 1.3a - WukongJS 1.1 : 43,5/100 32-45-23 (===11000111=00010110011=0=00100=11=10110110001=0000010=1=000=1=11=00000011=0=11=10000=100=0=00=110==)  44%   -42
Despite the fact of the lower win rate the games actually won by SF eval looks like a positional masterpiece,
while lost games are mostly forced mate occurred due to unfortunate openings.

Here's the PGN:
https://github.com/maksimKorzh/wukongJS ... lified.pgn

Here's SF's material + PST + tapered eval implementation in javascript (since my engine is written in JS):

Code: Select all

/****************************\
   ============================
   
            EVALUATION

   ============================              
  \****************************/
  
  /*
      Following material weights and PST values are taken from
         https://hxim.github.io/Stockfish-Evaluation-Guide/
  */
  
  // material score
  const materialWeights = [
    // opening material score
    [0, 124, 781, 825, 1276, 2538, 0, -124, -781, -825, -1276, -2538, 0],
    
    // endgame material score
    [0, 206, 854, 915, 1380, 2682, 0, -206, -854, -915, -1380, -2682, 0]
  ];

  const openingPhaseScore = 15258;
  const endgamePhaseScore = 3915;
  const opening = 0, endgame = 1, middlegame = 2;
  const PAWN = 0, KNIGHT = 1, BISHOP = 2, ROOK = 3, QUEEN = 4, KING = 5;

  // piece-square tables
  const pst = [
    [
      //pawn
      [
          0,   0,   0,   0,   0,   0,   0,   0,   o, o, o, o, o, o, o, o,
         -7,   7,  -3, -13,   5, -16,  10,  -8,   o, o, o, o, o, o, o, o,
          5, -12,  -7,  22,  -8,  -5, -15,  -8,   o, o, o, o, o, o, o, o,
         13,   0, -13,   1,  11,  -2, -13,   5,   o, o, o, o, o, o, o, o,
         -4, -23,   6,  20,  40,  17,   4,  -8,   o, o, o, o, o, o, o, o,
         -9, -15,  11,  15,  32,  22,   5, -22,   o, o, o, o, o, o, o, o,
          3,   3,  10,  19,  16,  19,   7,  -5,   o, o, o, o, o, o, o, o,
          0,   0,   0,   0,   0,   0,   0,   0,   o, o, o, o, o, o, o, o
      ],
      
      // knight
      [
       -201, -83, -56, -26, -26, -56, -83,-201,   o, o, o, o, o, o, o, o,
        -67, -27,   4,  37,  37,   4, -27, -67,   o, o, o, o, o, o, o, o,
         -9,  22,  58,  53,  53,  58,  22,  -9,   o, o, o, o, o, o, o, o,
        -34,  13,  44,  51,  51,  44,  13, -34,   o, o, o, o, o, o, o, o,
        -35,   8,  40,  49,  49,  40,   8, -35,   o, o, o, o, o, o, o, o,
        -61, -17,   6,  12,  12,   6, -17, -61,   o, o, o, o, o, o, o, o,
        -77, -41, -27, -15, -15, -27, -41, -77,   o, o, o, o, o, o, o, o,
       -175, -92, -74, -73, -73, -74, -92,-175,   o, o, o, o, o, o, o, o
      ],
      
      // bishop
      [
        -48,   1, -14, -23, -23, -14,   1, -48,   o, o, o, o, o, o, o, o,
        -17, -14,   5,   0,   0,   5, -14, -17,   o, o, o, o, o, o, o, o,
        -16,   6,   1,  11,  11,   1,   6, -16,   o, o, o, o, o, o, o, o,
        -12,  29,  22,  31,  31,  22,  29, -12,   o, o, o, o, o, o, o, o,
         -5,  11,  25,  39,  39,  25,  11,  -5,   o, o, o, o, o, o, o, o,
         -7,  21,  -5,  17,  17,  -5,  21,  -7,   o, o, o, o, o, o, o, o,
        -15,   8,  19,   4,   4,  19,   8, -15,   o, o, o, o, o, o, o, o,
        -53,  -5,  -8, -23, -23,  -8,  -5, -53,   o, o, o, o, o, o, o, o
      ],
      
      // rook
      [
        -17, -19,  -1,   9,   9,  -1, -19, -17,   o, o, o, o, o, o, o, o
         -2,  12,  16,  18,  18,  16,  12,  -2,   o, o, o, o, o, o, o, o,
        -22,  -2,   6,  12,  12,   6,  -2, -22,   o, o, o, o, o, o, o, o,
        -27, -15,  -4,   3,   3,  -4, -15, -27,   o, o, o, o, o, o, o, o,
        -13,  -5,  -4,  -6,  -6,  -4,  -5, -13,   o, o, o, o, o, o, o, o,
        -25, -11,  -1,   3,   3,  -1, -11, -25,   o, o, o, o, o, o, o, o,
        -21, -13,  -8,   6,   6,  -8, -13, -21,   o, o, o, o, o, o, o, o,
        -31, -20, -14,  -5,  -5, -14, -20, -31,   o, o, o, o, o, o, o, o
      ],
      
      // queen
      [
         -2,  -2,   1,  -2,  -2,   1,  -2,  -2,   o, o, o, o, o, o, o, o,
         -5,   6,  10,   8,   8,  10,   6,  -5,   o, o, o, o, o, o, o, o,
         -4,  10,   6,   8,   8,   6,  10,  -4,   o, o, o, o, o, o, o, o,
          0,  14,  12,   5,   5,  12,  14,   0,   o, o, o, o, o, o, o, o,
          4,   5,   9,   8,   8,   9,   5,   4,   o, o, o, o, o, o, o, o,
         -3,   6,  13,   7,   7,  13,   6,  -3,   o, o, o, o, o, o, o, o,
         -3,   5,   8,  12,  12,   8,   5,  -3,   o, o, o, o, o, o, o, o,
          3,  -5,  -5,   4,   4,  -5,  -5,   3,   o, o, o, o, o, o, o, o
      ],
      
      // king
      [
         59,  89,  45,  -1,  -1,  45,  89,  59,   o, o, o, o, o, o, o, o,
         88, 120,  65,  33,  33,  65, 120,  88,   o, o, o, o, o, o, o, o,
        123, 145,  81,  31,  31,  81, 145, 123,   o, o, o, o, o, o, o, o,
        154, 179, 105,  70,  70, 105, 179, 154,   o, o, o, o, o, o, o, o,
        164, 190, 138,  98,  98, 138, 190, 164,   o, o, o, o, o, o, o, o,
        195, 258, 169, 120, 120, 169, 258, 195,   o, o, o, o, o, o, o, o,
        278, 303, 234, 179, 179, 234, 303, 278,   o, o, o, o, o, o, o, o,
        271, 327, 271, 198, 198, 271, 327, 271,   o, o, o, o, o, o, o, o
      ],
    ],
    
    // Endgame positional piece scores //
    [
      //pawn
      [
          0,   0,   0,   0,   0,   0,   0,   0,   o, o, o, o, o, o, o, o,
          0, -11,  12,  21,  25,  19,   4,   7,   o, o, o, o, o, o, o, o,
         28,  20,  21,  28,  30,   7,   6,  13,   o, o, o, o, o, o, o, o,
         10,   5,   4,  -5,  -5,  -5,  14,   9,   o, o, o, o, o, o, o, o,
          6,  -2,  -8,  -4, -13, -12, -10,  -9,   o, o, o, o, o, o, o, o,
        -10, -10, -10,   4,   4,   3,  -6,  -4,   o, o, o, o, o, o, o, o,
        -10,  -6,  10,   0,  14,   7,  -5, -19,   o, o, o, o, o, o, o, o,
          0,   0,   0,   0,   0,   0,   0,   0,   o, o, o, o, o, o, o, o
      ],
      
      // knight
      [
       -100, -88, -56, -17, -17, -56, -88,-100,   o, o, o, o, o, o, o, o,
        -69, -50, -51,  12,  12, -51, -50, -69,   o, o, o, o, o, o, o, o,
        -51, -44, -16,  17,  17, -16, -44, -51,   o, o, o, o, o, o, o, o,
        -45, -16,   9,  39,  39,   9, -16, -45,   o, o, o, o, o, o, o, o,
        -35,  -2,  13,  28,  28,  13,  -2, -35,   o, o, o, o, o, o, o, o,
        -40, -27,  -8,  29,  29,  -8, -27, -40,   o, o, o, o, o, o, o, o,
        -67, -54, -18,   8,   8, -18, -54, -67,   o, o, o, o, o, o, o, o,
        -96, -65, -49, -21, -21, -49, -65, -96,   o, o, o, o, o, o, o, o
      ],
      
      // bishop
      [
        -46, -42, -37, -24, -24, -37, -42, -46,   o, o, o, o, o, o, o, o,
        -31, -20,  -1,   1,   1,  -1, -20, -31,   o, o, o, o, o, o, o, o,
        -30,   6,   4,   6,   6,   4,   6, -30,   o, o, o, o, o, o, o, o,
        -17,  -1, -14,  15,  15, -14,  -1, -17,   o, o, o, o, o, o, o, o,
        -20,  -6,   0,  17,  17,   0,  -6, -20,   o, o, o, o, o, o, o, o,
        -16,  -1,  -2,  10,  10,  -2,  -1, -16,   o, o, o, o, o, o, o, o,
        -37, -13, -17,   1,   1, -17, -13, -37,   o, o, o, o, o, o, o, o,
        -57, -30, -37, -12, -12, -37, -30, -57,   o, o, o, o, o, o, o, o
      ],
      
      // rook
      [
         18,   0,  19,  13,  13,  19,   0,  18,   o, o, o, o, o, o, o, o,
          4,   5,  20,  -5,  -5,  20,   5,   4,   o, o, o, o, o, o, o, o,
          6,   1,  -7,  10,  10,  -7,   1,   6,   o, o, o, o, o, o, o, o,
         -5,   8,   7,  -6,  -6,   7,   8,  -5,   o, o, o, o, o, o, o, o,
         -6,   1,  -9,   7,   7,  -9,   1,  -6,   o, o, o, o, o, o, o, o,
          6,  -8,  -2,  -6,  -6,  -2,  -8,   6,   o, o, o, o, o, o, o, o,
        -12,  -9,  -1,  -2,  -2,  -1,  -9, -12,   o, o, o, o, o, o, o, o,
         -9, -13, -10,  -9,  -9, -10, -13,  -9,   o, o, o, o, o, o, o, o
      ],
      
      // queen
      [
        -75, -52, -43, -36, -36, -43, -52, -75,   o, o, o, o, o, o, o, o,
        -50, -27, -24,  -8,  -8, -24, -27, -50,   o, o, o, o, o, o, o, o,
        -38, -18, -12,   1,   1, -12, -18, -38,   o, o, o, o, o, o, o, o,
        -29,  -6,   9,  21,  21,   9,  -6, -29,   o, o, o, o, o, o, o, o,
        -23,  -3,  13,  24,  24,  13,  -3, -23,   o, o, o, o, o, o, o, o,
        -39, -18,  -9,   3,   3,  -9, -18, -39,   o, o, o, o, o, o, o, o,
        -55, -31, -22,  -4,  -4, -22, -31, -55,   o, o, o, o, o, o, o, o,
        -69, -57, -47,- 26,  26, -47, -57, -69,   o, o, o, o, o, o, o, o
      ],
      
      // king
      [
         11,  59,  73,  78,  78,  73,  59,  11,   o, o, o, o, o, o, o, o,
         47, 121, 116, 131, 131, 116, 121,  47,   o, o, o, o, o, o, o, o,
         92, 172, 184, 191, 191, 184, 172,  92,   o, o, o, o, o, o, o, o,
         96, 166, 199, 199, 199, 199, 166,  96,   o, o, o, o, o, o, o, o,
        103, 156, 172, 172, 172, 172, 156, 103,   o, o, o, o, o, o, o, o,
         88, 130, 169, 175, 175, 169, 130,  88,   o, o, o, o, o, o, o, o,
         53, 100, 133, 135, 135, 133, 100,  53,   o, o, o, o, o, o, o, o,
          1,  45,  85,  76,  76,  85,  45,   1,   o, o, o, o, o, o, o, o
      ]
    ]
  ];

  // mirror positional score tables for opposite side
  const mirrorSquare= [
    a1, b1, c1, d1, e1, f1, g1, h1,   o, o, o, o, o, o, o, o,
    a2, b2, c2, d2, e2, f2, g2, h2,   o, o, o, o, o, o, o, o,
    a3, b3, c3, d3, e3, f3, g3, h3,   o, o, o, o, o, o, o, o,
    a4, b4, c4, d4, e4, f4, g4, h4,   o, o, o, o, o, o, o, o,
    a5, b5, c5, d5, e5, f5, g5, h5,   o, o, o, o, o, o, o, o,
    a6, b6, c6, d6, e6, f6, g6, h6,   o, o, o, o, o, o, o, o,
    a7, b7, c7, d7, e7, f7, g7, h7,   o, o, o, o, o, o, o, o,
    a8, b8, c8, d8, e8, f8, g8, h8,   o, o, o, o, o, o, o, o
  ];
  
  // insufficient material detection
  function isMaterialDraw() {
    if(pieceList[P] == 0 && pieceList[p] == 0) {
      if (pieceList[R] == 0 && pieceList[r] == 0 && pieceList[Q] == 0 && pieceList[q] == 0) {
        if (pieceList[B] == 0 && pieceList[b] == 0) {
          if (pieceList[N] < 3 && pieceList[n] < 3)
            return 1;
      } else if (pieceList[N] == 0 && pieceList[n] == 0) {
        if (Math.abs(pieceList[B] - pieceList[b]) < 2)
          return 1;
      } else if ((pieceList[N] < 3 && pieceList[B] == 0) || (pieceList[B] == 1 && pieceList[N] == 0)) {
        if ((pieceList[n] < 3 && pieceList[b] == 0) || (pieceList[b] == 1 && pieceList[n] == 0))
          return 1;
        }
      } else if (pieceList[Q] == 0 && pieceList[q] == 0) {
        if (pieceList[R] == 1 && pieceList[r] == 1) {
          if ((pieceList[N] + pieceList[B]) < 2 && (pieceList[n] + pieceList[b]) < 2) return 1;
        } else if (pieceList[R] == 1 && pieceList[r] == 0) {        
          if ((pieceList[N] + pieceList[B] == 0) &&
            (((pieceList[n] + pieceList[b]) == 1) || 
             ((pieceList[n] + pieceList[b]) == 2)))
            return 1;
        } else if (pieceList[r] == 1 && pieceList[R] == 0) {
          if ((pieceList[n] + pieceList[b] == 0) &&
            (((pieceList[N] + pieceList[B]) == 1) ||
             ((pieceList[N] + pieceList[B]) == 2)))
            return 1;
        }
      }
    }
    
    return 0;
  }
  
  // get game phase
  function getGamePhaseScore() {
    let gamePhaseScore = 0;

    for (let piece = N; piece <= Q; piece++) gamePhaseScore += pieceList[piece] * materialWeights[opening][piece];
    for (let piece = n; piece <= q; piece++) gamePhaseScore += pieceList[piece] * -materialWeights[opening][piece];

    return gamePhaseScore;
  }
  
  // static evaluation
  function evaluate() {
    if (isMaterialDraw()) return 0;
    
    let gamePhaseScore = getGamePhaseScore();
    let gamePhase = -1;
    
    if (gamePhaseScore > openingPhaseScore) gamePhase = opening;
    else if (gamePhaseScore < endgamePhaseScore) gamePhase = endgame;
    else gamePhase = middlegame;

    let score = 0;
    var scoreOpening = 0;
    var scoreEndgame = 0;
    
    for (let piece = P; piece <= k; piece++) {
      for (pieceIndex = 0; pieceIndex < pieceList[piece]; pieceIndex++) {
        let square = pieceList.pieces[piece * 10 + pieceIndex];

        // evaluate material
        scoreOpening += materialWeights[opening][piece];
        scoreEndgame += materialWeights[endgame][piece];

        // positional score
        switch (piece) {
          case P:
            scoreOpening += pst[opening][PAWN][square];
            scoreEndgame += pst[endgame][PAWN][square];
            break;

          case N:
            scoreOpening += pst[opening][KNIGHT][square];
            scoreEndgame += pst[endgame][KNIGHT][square];
            break;

          case B:
            scoreOpening += pst[opening][BISHOP][square];
            scoreEndgame += pst[endgame][BISHOP][square];
            break;

          case R:
            scoreOpening += pst[opening][ROOK][square];
            scoreEndgame += pst[endgame][ROOK][square];
            break;

          case Q:
            scoreOpening += pst[opening][QUEEN][square];
            scoreEndgame += pst[endgame][QUEEN][square];
            break;

          case K:
            scoreOpening += pst[opening][KING][square];
            scoreEndgame += pst[endgame][KING][square];
            break;

          case p:
            scoreOpening -= pst[opening][PAWN][mirrorSquare[square]];
            scoreEndgame -= pst[endgame][PAWN][mirrorSquare[square]];
            break;

          case n:
            scoreOpening -= pst[opening][KNIGHT][mirrorSquare[square]];
            scoreEndgame -= pst[endgame][KNIGHT][mirrorSquare[square]];
            break;

          case b:
            scoreOpening -= pst[opening][BISHOP][mirrorSquare[square]];
            scoreEndgame -= pst[endgame][BISHOP][mirrorSquare[square]];
            break;

          case r:
            scoreOpening -= pst[opening][ROOK][mirrorSquare[square]];
            scoreEndgame -= pst[endgame][ROOK][mirrorSquare[square]];
            break;
          
          case q:
            scoreOpening -= pst[opening][QUEEN][mirrorSquare[square]];
            scoreEndgame -= pst[endgame][QUEEN][mirrorSquare[square]];
            break;

          case k:
            scoreOpening -= pst[opening][KING][mirrorSquare[square]];
            scoreEndgame -= pst[endgame][KING][mirrorSquare[square]];
            break;
        }
      }
    }

    // interpolate score in the middlegame
    if (gamePhase == middlegame)
        score = (
            scoreOpening * gamePhaseScore +
            scoreEndgame * (openingPhaseScore - gamePhaseScore)
        ) / openingPhaseScore;
    else if (gamePhase == opening) score = scoreOpening;
    else if (gamePhase == endgame) score = scoreEndgame;
    
    score = Math.round(score * (100 - fifty) / 100);
    return (side == white) ? score: -score;
  }
No4b
Posts: 105
Joined: Thu Jun 18, 2020 3:21 pm
Location: Moscow
Full name: Alexander Litov

Re: Simplified Stockfish Evaluation

Post by No4b »

Great stuff! Keep up your work! =)
maksimKorzh wrote: Fri Jan 01, 2021 6:57 pm Despite the fact of the lower win rate the games actually won by SF eval looks like a positional masterpiece,
while lost games are mostly forced mate occurred due to unfortunate openings.
I wouldnt say that the winrate is necessarily lower. You need at least couple thousands games to get any reliable result from testing.
100 games test will give you more than +- 50 elo error margin, so it quite possible that version with new eval is better.
User avatar
maksimKorzh
Posts: 771
Joined: Sat Sep 08, 2018 5:37 pm
Location: Ukraine
Full name: Maksim Korzh

Re: Simplified Stockfish Evaluation

Post by maksimKorzh »

No4b wrote: Fri Jan 01, 2021 11:28 pm Great stuff! Keep up your work! =)
maksimKorzh wrote: Fri Jan 01, 2021 6:57 pm Despite the fact of the lower win rate the games actually won by SF eval looks like a positional masterpiece,
while lost games are mostly forced mate occurred due to unfortunate openings.
I wouldnt say that the winrate is necessarily lower. You need at least couple thousands games to get any reliable result from testing.
100 games test will give you more than +- 50 elo error margin, so it quite possible that version with new eval is better.
I just meant that PeSTO values are around 70 Elo better even within 100 games.
Anyway, now I'm now implementing pawn eval - I take bonuses and penalties from SF but implement on my own with regards to engine's ecosystem.