PST-only Evaluation for MinimalChess 0.4

Discussion of chess software programming and technical issues.

Moderators: hgm, Rebel, chrisw

Mike Sherwin
Posts: 860
Joined: Fri Aug 21, 2020 1:25 am
Location: Planet Earth, Sol system
Full name: Michael J Sherwin

Re: PST-only Evaluation for MinimalChess 0.4

Post by Mike Sherwin »

lithander wrote: Wed Apr 21, 2021 12:47 pm
Mike Sherwin wrote: Mon Apr 19, 2021 6:34 pm A simple dynamic pst maker is not difficult to write. And you can put your current chess knowledge into it. Here is the dynamic pst maker for RomiChess. I wrote it while I was learning how to program.
Correct me if I'm wrong but don't most classical chess programs have an evaluation that considers dynamic properties such as mobility or pawn structure etc? If they use static PSTs then it's only one of many components that factor into the evaluation, right?

So it seems to me that your creation of PSTs dynamically filling in these information is basically achieving the same result as having this kind of hand crafted, complex evaluation function? Of course storing these values in PSTs is maybe more efficient because it makes your evaluation function cheaper. All these dynamic properties of a position maybe don't change enough between individual moves that you have to recompute the PSTs all the time? When exactly *do* you compute the PSTs in your search, though?

I'm not trying to argue, just to understand your approach better.
The PSTs in RomiChess are calculated at the root and after the first couple ply to incorporate the most immediate change to the position. You covered just about everything in your question except for one aspect. The PSTs can contain virtually unlimited chess knowledge at almost no cost in time.
Edit: RomiChess also has simple endleaf pawn evaluation and a pawn hash.
User avatar
lithander
Posts: 880
Joined: Sun Dec 27, 2020 2:40 am
Location: Bremen, Germany
Full name: Thomas Jahn

Re: PST-only Evaluation for MinimalChess 0.4

Post by lithander »

The computer is running non-stop for a week now and my results are... well... confusing.

A first very promising set of tables that gained 35 ELO over Pesto in self-play played weaker against other engines. Meanwhile I have also managed to find tables that do the opposite: A significant improvement of 30 ELO over Rustic 2 and a smaller improvement against other engines still, but weaker in self-play and against Bit-Genie 2.

If no general improvement can be found this makes my task so much harder: To make a decision on what to use for MMC 0.4 I would have to test against a huge roster of engines. And because a few ELO make all the difference here I already run between 2000 and 4000 tests per engine per PST-candidate. So this is extremely time consuming...

The way the tuner optimizes the values is by minimizing the mean-squared-error of the evaluation of all the positions in the dataset against their stored game result. So the tables are optimized to predict as accurately as possible if something is a winning position or losing position. Sadly this seems to correlate only loosely with actual performance of play. And it also varies a lot on what dataset you use. Evaluate the original pesto against the original zurichess dataset and it get's pretty great scores (0.246044, smaller is better), evaluate it against the bigger, newer version (v7) and it scores quite badly (0.265496). I can easily produce PSTs that score better on that dataset (0.261839), just that they almost never play better than Pesto and if they do this is (as said above) only true for a subset of opponents and not so for others.

So it's not clear what data-set to chose to start with. It's not true that minimizing the mean-squared-error always improves the PSTs in actual play. And if you find an improvement against one engine it's usually playing worse against at least a few other engines.

Tuning helps a ton if you're starting out with handwritten PSTs or material values only but if you compare your results against something like Pesto, that already comes close to represent an optimal PST table, all I can do is produce "different, good" results, not clearly better ones.

(My wife suggested I should just mix the one that was great against MMC Pesto with the other one and create a unicorn from my two one-trick ponies and I'm almost desperate enough to give it a try. :lol:)
Minimal Chess (simple, open source, C#) - Youtube & Github
Leorik (competitive, in active development, C#) - Github & Lichess
User avatar
lithander
Posts: 880
Joined: Sun Dec 27, 2020 2:40 am
Location: Bremen, Germany
Full name: Thomas Jahn

Re: PST-only Evaluation for MinimalChess 0.4

Post by lithander »

I finished the test runs for the latest set of PSTs (mentioned in the last post, used by MMC 0.3.6.2 Salsa here) at 5s+0.5s and computed the ratings with ordo.

Code: Select all

   # PLAYER                        :  RATING  POINTS  PLAYED   (%)
   1 dumber-1.2                    :  1942.8  2171.5    4000    54
   2 MinimalChess 0.3.6.0 Pesto    :  1915.2  5005.5    8726    57
   3 MinimalChess 0.3.6.2 Salsa    :  1910.2  4609.5    8245    56
   4 Rustic 2                      :  1815.0  1456.5    4000    36
   5 Bit-Genie_2                   :  1789.6  1483.0    4481    33
Against this very limited set of opponents it seems like I found the "Salsa" PSTs to be only 5 ELO weaker on average. Maybe that's good enough to serve as a replacement for the borrowed PeSTO tables. That was the goal after all. So am I done?

I don't really feel like I have exhausted the possibilities here. Because if you look at the head to head statistics...

Code: Select all

MinimalChess 0.3.6.0 Pesto 1915.2 :   8726 (+4179,=1653,-2894),  57.4 %
   vs.                               :  games (    +,    =,    -),   (%)
   dumber-1.2                        :   2000 (  685,  396,  919),  44.1
   MinimalChess 0.3.6.2 Salsa        :   2245 (  845,  607,  793),  51.2
   Rustic 2                          :   2000 ( 1017,  362,  621),  59.9
   Bit-Genie_2                       :   2481 ( 1632,  288,  561),  71.6

Code: Select all

MinimalChess 0.3.6.2 Salsa 1910.2 :   8245 (+3761,=1697,-2787),  55.9 %
   vs.                               :  games (    +,    =,    -),   (%)
   dumber-1.2                        :   2000 (  696,  499,  805),  47.3
   MinimalChess 0.3.6.0 Pesto        :   2245 (  793,  607,  845),  48.8
   Rustic 2                          :   2000 ( 1175,  341,  484),  67.3
   Bit-Genie_2                       :   2000 ( 1097,  250,  653),  61.1
...the "Salsa" version seems to have completely different strengths and weaknesses than the PeSTO tables. Results against individual engines fluctuate by much more than 5 ELO. Anchoring against Bit-Genie_2 and disregarding the self-play results Pesto would appear 80 ELO stronger than Salsa. Anchoring against Rustic 2 and disregarding the self-play results Pesto appears 50 ELO weaker than Salsa.

So I'm still hoping for a set of PSTs that are more consistent between engines. That play a more standard opening (starting with moving the queens or kings pawn 2 squares up instead of getting the two knights out first) and can guide my engine to win 2B vs lone King endgames, which PeSTO does but Salsa doesnt.

I had automatically assumed that the newer and larger data-set ("quiet-labeled.v7.epd", 1428000 positions) was the better one but will now focus my attempts on the data-set that was likely used to train Pesto ("quiet-labeled.epd", 725000 positions). If there are certain imbalances in the positions of the dataset these will emerge in the PSTs you tune with them - at least I found no way to prevent that without nasty hacks or even hand-edditing certain values.

Bye the way: Does anyone have a suggestion of another 1800-2000 ELO engine that I could include that play well on fast time controls and is (ideally) in active development by someone active in this forum?
Minimal Chess (simple, open source, C#) - Youtube & Github
Leorik (competitive, in active development, C#) - Github & Lichess
Mike Sherwin
Posts: 860
Joined: Fri Aug 21, 2020 1:25 am
Location: Planet Earth, Sol system
Full name: Michael J Sherwin

Re: PST-only Evaluation for MinimalChess 0.4

Post by Mike Sherwin »

lithander wrote: Sun Apr 25, 2021 3:16 am Bye the way: Does anyone have a suggestion of another 1800-2000 ELO engine that I could include that play well on fast time controls and is (ideally) in active development by someone active in this forum?
:lol: I may know of one that is almost ready. What time limit do you use? Is it 5 seconds per game and half a second increment per move? I'll have to add that. And 3 fold repetition needs to be added. And I want to add dynamic PST's because PESTO is overdone. And then Bricabrac should be a minimal engine somewhere between 1800 and 2000. Right now with just PESTO mg (because tapered is broken) Bricabrac is testing at a little more than 1800.
User avatar
lithander
Posts: 880
Joined: Sun Dec 27, 2020 2:40 am
Location: Bremen, Germany
Full name: Thomas Jahn

Re: PST-only Evaluation for MinimalChess 0.4

Post by lithander »

Mike Sherwin wrote: Sun Apr 25, 2021 6:34 am :lol: I may know of one that is almost ready.
Sure! Let me know when it's ready! And it would be great if you could provide me with a pre-compiled Windows executable!
Minimal Chess (simple, open source, C#) - Youtube & Github
Leorik (competitive, in active development, C#) - Github & Lichess
Uri Blass
Posts: 10267
Joined: Thu Mar 09, 2006 12:37 am
Location: Tel-Aviv Israel

Re: PST-only Evaluation for MinimalChess 0.4

Post by Uri Blass »

I think that we see what you can achieve with PST-only evaluation and the interesting question is what rating it is possible to achieve with different types of evaluations:

1)mainly material evaluation
It means that evaluation basically has 2 numbers:
a)material evaluation of 1,3,3,5,9(I know it is probably bad but I have a reason for it and the only change that I allow is changing 1,3,3,5,9 to 1,4,4,6,10 or other natural numbers)

b)positional evaluation that is always lower than 0.5
The reason is that I can see based on the evaluation if the engine has material advantage of 1 pawn(evaluation more than 0.5 and less than 1.5) or evaluations of 2 pawns(evaluation more than 1.5 and less than 2.5)

If I use something like 1,3.3,3.4 for pawn knight and bishop then with a possible positional evaluation of 0.4 I do not know the material advantage
because +0.4 may be 4 pawns for a knight+0.3 positional and also knight and 0.1 positional for 3 pawns.

The positional evaluation can be complicated but it has limitation not to be high.

2)only mobility-piece evaluation except value of pieces and no piece square table.
The value of a piece is defined only by the type of the piece and the number of squares the piece control.
Mike Sherwin
Posts: 860
Joined: Fri Aug 21, 2020 1:25 am
Location: Planet Earth, Sol system
Full name: Michael J Sherwin

Re: PST-only Evaluation for MinimalChess 0.4

Post by Mike Sherwin »

lithander wrote: Sun Apr 25, 2021 11:40 am
Mike Sherwin wrote: Sun Apr 25, 2021 6:34 am :lol: I may know of one that is almost ready.
Sure! Let me know when it's ready! And it would be great if you could provide me with a pre-compiled Windows executable!
Halfway there. Got tapered PeSTO working correctly. And got all time controls working well. Decided to keep PeSTO for now as a reference like everyone else. And halfway through a match at a TC of 5 sec + 0.5 sec with TSCP it is Bricabrac +31 -7 =12 for +182. And some of those 12 draws would be wins if 3-fold detection was added. Don't really know why TSCP is being so dominated at this time control? Is it just because tapering is working now? All games are ending normally and TSCP is reaching decent depths.

Anyway, I'm now adding hash keys for 3-fold detection and later for TT and pawn scoring. If everything goes well maybe Bric will be ready either today or tomorrow! :D
Mike Sherwin
Posts: 860
Joined: Fri Aug 21, 2020 1:25 am
Location: Planet Earth, Sol system
Full name: Michael J Sherwin

Re: PST-only Evaluation for MinimalChess 0.4

Post by Mike Sherwin »

Uri Blass wrote: Sun Apr 25, 2021 6:57 pm I think that we see what you can achieve with PST-only evaluation and the interesting question is what rating it is possible to achieve with different types of evaluations:

1)mainly material evaluation
It means that evaluation basically has 2 numbers:
a)material evaluation of 1,3,3,5,9(I know it is probably bad but I have a reason for it and the only change that I allow is changing 1,3,3,5,9 to 1,4,4,6,10 or other natural numbers)

b)positional evaluation that is always lower than 0.5
The reason is that I can see based on the evaluation if the engine has material advantage of 1 pawn(evaluation more than 0.5 and less than 1.5) or evaluations of 2 pawns(evaluation more than 1.5 and less than 2.5)

If I use something like 1,3.3,3.4 for pawn knight and bishop then with a possible positional evaluation of 0.4 I do not know the material advantage
because +0.4 may be 4 pawns for a knight+0.3 positional and also knight and 0.1 positional for 3 pawns.

The positional evaluation can be complicated but it has limitation not to be high.

2)only mobility-piece evaluation except value of pieces and no piece square table.
The value of a piece is defined only by the type of the piece and the number of squares the piece control.
A test that I did with GNUChess4.0 was to test if each piece reached a certain level in the evaluation and if it did not then 10 was subtracted from its positional score otherwise 10 was added to its positional score. Fritz 5 at the time accused GNUChess of being Kasparov.
Mike Sherwin
Posts: 860
Joined: Fri Aug 21, 2020 1:25 am
Location: Planet Earth, Sol system
Full name: Michael J Sherwin

Re: PST-only Evaluation for MinimalChess 0.4

Post by Mike Sherwin »

Mike Sherwin wrote: Sun Apr 25, 2021 8:06 pm
lithander wrote: Sun Apr 25, 2021 11:40 am
Mike Sherwin wrote: Sun Apr 25, 2021 6:34 am :lol: I may know of one that is almost ready.
Sure! Let me know when it's ready! And it would be great if you could provide me with a pre-compiled Windows executable!
Halfway there. Got tapered PeSTO working correctly. And got all time controls working well. Decided to keep PeSTO for now as a reference like everyone else. And halfway through a match at a TC of 5 sec + 0.5 sec with TSCP it is Bricabrac +31 -7 =12 for +182. And some of those 12 draws would be wins if 3-fold detection was added. Don't really know why TSCP is being so dominated at this time control? Is it just because tapering is working now? All games are ending normally and TSCP is reaching decent depths.

Anyway, I'm now adding hash keys for 3-fold detection and later for TT and pawn scoring. If everything goes well maybe Bric will be ready either today or tomorrow! :D
Bricabrac - Tscp181 : 74.5/100 63-14-23 (11111010=1101=1111=1==10=11==01=1=1011=1110=11111111=1011111=011110=11111==01011011111=1=11==0==1111) 75% +191
Bric is going to be looking for a fight! :lol:
User avatar
lithander
Posts: 880
Joined: Sun Dec 27, 2020 2:40 am
Location: Bremen, Germany
Full name: Thomas Jahn

Re: PST-only Evaluation for MinimalChess 0.4

Post by lithander »

A worthy rival for Pesto has been found!

I found a new set of PST values ("Chili") that causes MinimalChess to play significantly better than Salsa (+36 ELO) and even appears stronger than Pesto (+21 ELO) in my tests! :o :D 8-)

Code: Select all

   
   2 MinimalChess 0.3.6.3 Chili    :  1939.0  7123.5   12000    59
   3 MinimalChess 0.3.6.0 Pesto    :  1918.2  7177.0   12726    56
   4 MinimalChess 0.3.6.2 Salsa    :  1903.1  6549.5   12245    53
Here are the head-to-head details:

Code: Select all

MinimalChess 0.3.6.3 Chili 1939.0    :  12000 (+5860,=2527,-3613),  59.4 %
   vs.                               :  games (    +,    =,    -),   (%)
   dumber-1.2                        :   2000 (  806,  368,  826),  49.5 
   MinimalChess 0.3.6.0 Pesto        :   2000 (  818,  505,  677),  53.5
   MinimalChess 0.3.6.2 Salsa        :   2000 (  865,  589,  546),  58.0
   WukongJS 1.5                      :   2000 (  977,  434,  589),  59.7
   Rustic 2                          :   2000 ( 1141,  375,  484),  66.4
   Bit-Genie_2                       :   2000 ( 1253,  256,  491),  69.0 

MinimalChess 0.3.6.0 Pesto 1918.2    :  12726 (+5887,=2580,-4259),  56.4 %
   vs.                               :  games (    +,    =,    -),   (%) 
   dumber-1.2                        :   2000 (  685,  396,  919),  44.1
   MinimalChess 0.3.6.3 Chili        :   2000 (  677,  505,  818),  46.5
   MinimalChess 0.3.6.2 Salsa        :   2245 (  845,  607,  793),  51.2
   WukongJS 1.5                      :   2000 ( 1031,  422,  547),  62.1 
   Rustic 2                          :   2000 ( 1017,  362,  621),  59.9
   Bit-Genie_2                       :   2481 ( 1632,  288,  561),  71.6

MinimalChess 0.3.6.2 Salsa 1903.1    :  12245 (+5182,=2735,-4328),  53.5 %
   vs.                               :  games (    +,    =,    -),   (%) 
   dumber-1.2                        :   2000 (  696,  499,  805),  47.3
   MinimalChess 0.3.6.3 Chili        :   2000 (  546,  589,  865),  42.0 
   MinimalChess 0.3.6.0 Pesto        :   2245 (  793,  607,  845),  48.8
   WukongJS 1.5                      :   2000 (  875,  449,  676),  55.0 
   Rustic 2                          :   2000 ( 1175,  341,  484),  67.3
   Bit-Genie_2                       :   2000 ( 1097,  250,  653),  61.1
I really like these "Chili" PSTs. Pesto plays really strong against Bit-Genie_2 (71.6%) but surprisingly weak against dumber-1.2 (44.1%) whereas the spread with the Chili tables compared to the same opponents is much smaller. (49.5% to 69.0%) It plays decent openings even without an opening book and it manages to convert won endgames as far as I can see!

Here are the tables and code demonstrating how to use them:

Code: Select all

 
public static int LostValue => -9999;

static readonly int[] PhaseValues = new int[6] { 0, 155, 305, 405, 1050, 0 };

static readonly int Midgame = 5255;
static readonly int Endgame = 435;

static readonly int[,] MidgameTables = new int[6, 64]{
{  //PAWN MG
    100,   100,   100,   100,   100,   100,   100,   100,
    173,   227,   144,   189,   182,   228,   124,    75,
    82,    94,   111,   115,   152,   150,   109,    68,
    65,    95,    87,   102,   104,    92,    98,    59,
    52,    80,    77,    92,    96,    86,    90,    55,
    55,    79,    77,    70,    83,    82,   114,    69,
    45,    80,    61,    56,    64,   102,   116,    59,
    100,   100,   100,   100,   100,   100,   100,   100,
},
{  //KNIGHT MG
    106,   248,   280,   272,   326,   196,   291,   178,
    224,   255,   369,   322,   319,   360,   307,   289,
    254,   365,   342,   368,   387,   423,   380,   344,
    296,   322,   322,   357,   343,   378,   324,   325,
    292,   313,   320,   317,   333,   326,   326,   299,
    283,   298,   317,   317,   326,   323,   329,   290,
    277,   254,   294,   303,   304,   325,   295,   290,
    214,   284,   251,   273,   289,   281,   288,   278,
},
{  //BISHOP MG
    275,   319,   255,   290,   292,   289,   316,   307,
    301,   344,   312,   314,   354,   382,   347,   287,
    316,   357,   377,   366,   355,   374,   364,   334,
    330,   337,   350,   378,   370,   373,   339,   330,
    327,   349,   345,   357,   365,   344,   341,   336,
    333,   349,   345,   347,   344,   359,   347,   340,
    339,   346,   348,   331,   340,   352,   363,   335,
    304,   331,   318,   315,   321,   321,   301,   308,
},
{  //ROOK MG
    504,   520,   493,   517,   519,   493,   489,   500,
    501,   506,   538,   535,   545,   538,   491,   507,
    477,   503,   506,   506,   495,   523,   529,   488,
    452,   473,   485,   509,   501,   512,   475,   460,
    445,   456,   472,   477,   483,   472,   490,   457,
    437,   461,   465,   460,   479,   473,   475,   446,
    435,   467,   461,   468,   476,   487,   475,   409,
    460,   466,   480,   488,   489,   476,   447,   454,
},
{  //QUEEN MG
    854,   910,   921,   913,   955,   939,   928,   926,
    881,   867,   910,   921,   898,   961,   936,   948,
    894,   896,   916,   921,   939,   969,   954,   960,
    879,   880,   898,   894,   907,   925,   904,   904,
    897,   881,   897,   896,   904,   904,   910,   905,
    890,   908,   898,   903,   901,   908,   917,   911,
    872,   893,   917,   906,   912,   919,   903,   908,
    903,   888,   897,   915,   891,   879,   875,   858,
},
{  //KING MG
    -6,    46,    33,    22,   -26,   -14,    14,    12,
    23,    11,    22,    20,    11,     5,   -10,   -25,
    23,    22,    30,     6,    10,    29,    28,    -4,
    -1,    -4,     5,   -12,   -11,   -11,    -5,   -38,
   -15,     8,   -18,   -40,   -53,   -36,   -27,   -46,
     6,     1,   -37,   -73,   -80,   -54,   -20,   -21,
    12,    11,   -10,   -74,   -52,   -20,    16,    18,
   -12,    44,    19,   -66,     6,   -29,    32,    24,
}
};

static int[,] EndgameTables = new int[6, 64]{
{  //PAWN EG
    100,   100,   100,   100,   100,   100,   100,   100,
    269,   260,   245,   222,   230,   218,   253,   274,
    183,   190,   174,   158,   145,   141,   172,   173,
    124,   117,   106,    98,    92,    97,   110,   108,
    106,   102,    89,    86,    87,    85,    96,    91,
    96,    100,    85,    94,    92,    89,    92,    84,
    106,   102,    99,   101,   103,    93,    96,    86,
    100,   100,   100,   100,   100,   100,   100,   100,
},
{  //KNIGHT EG
    225,   237,   268,   249,   258,   250,   220,   190,
    254,   273,   265,   283,   276,   261,   261,   234,
    257,   265,   293,   292,   281,   279,   264,   246,
    265,   284,   303,   302,   302,   293,   286,   266,
    265,   274,   296,   305,   297,   297,   286,   263,
    261,   277,   281,   294,   290,   280,   265,   260,
    245,   260,   271,   276,   278,   262,   258,   239,
    246,   236,   258,   264,   261,   262,   238,   225,
},
{  //BISHOP EG
    287,   278,   286,   287,   292,   287,   286,   277,
    290,   295,   304,   286,   296,   288,   294,   284,
    298,   292,   297,   298,   300,   304,   299,   297,
    295,   307,   308,   308,   309,   306,   298,   298,
    291,   298,   309,   314,   304,   307,   295,   289,
    285,   296,   305,   307,   310,   300,   294,   285,
    283,   285,   292,   298,   302,   289,   288,   271,
    275,   290,   279,   291,   289,   283,   290,   284,
},
{  //ROOK EG
    510,   506,   516,   514,   512,   507,   506,   505,
    508,   510,   507,   509,   497,   501,   506,   504,
    502,   503,   504,   504,   500,   493,   494,   495,
    501,   498,   509,   497,   499,   497,   494,   499,
    498,   501,   503,   500,   494,   491,   488,   486,
    492,   495,   491,   496,   489,   486,   488,   482,
    491,   489,   495,   498,   490,   488,   485,   493,
    491,   499,   500,   500,   497,   491,   497,   480,
},
{  //QUEEN EG
    919,   932,   941,   942,   940,   933,   924,   941,
    895,   935,   936,   947,   971,   932,   932,   907,
    895,   913,   917,   958,   957,   935,   927,   909,
    920,   935,   927,   955,   971,   950,   970,   943,
    892,   945,   934,   963,   948,   944,   948,   931,
    906,   887,   924,   916,   922,   926,   923,   916,
    899,   899,   881,   898,   900,   888,   880,   878,
    885,   890,   885,   869,   910,   886,   900,   878,
},
{  //KING EG
    -71,   -40,   -23,   -25,   -12,    12,     2,   -14,
    -13,    13,     6,    10,    11,    29,    19,    11,
      3,    14,    16,    10,    13,    35,    37,     8,
    -13,    16,    19,    22,    19,    27,    22,     1,
    -24,    -7,    16,    21,    24,    19,     6,   -14,
    -24,    -7,    10,    20,    24,    17,     7,   -11,
    -31,   -13,     3,    11,    12,     3,    -7,   -21,
    -56,   -39,   -25,   -11,   -28,   -14,   -29,   -51,
}};

private static int PieceTableIndex(Piece piece) => ((int)piece >> 2) - 1;

//a black piece has 2nd bit set, white has not. Square ^ 56 flips the file, so that tables work for black
private static int SquareTableIndex(int square, Piece piece) => square ^ (28 * ((int)piece & 2));

public static int Evaluate(Board board)
{
    int midGame = 0;
    int endGame = 0;
    int phase = 0;
    for (int i = 0; i < 64; i++)
    {
        Piece piece = board[i];
        if (piece == Piece.None)
            continue;

        Color color = Pieces.GetColor(piece);
        int pieceIndex = PieceTableIndex(piece);
        int squareIndex = SquareTableIndex(i, piece);
        phase += PhaseValues[pieceIndex];
        midGame += (int)color * MidgameTables[pieceIndex, squareIndex];
        endGame += (int)color * EndgameTables[pieceIndex, squareIndex];
    }

    double factor = Linstep(Endgame, Midgame, phase);
    double score = factor * midGame + (1 - factor) * endGame;
    return (int)score;
}

public static double Linstep(double edge0, double edge1, double v)
{
    return Math.Min(1, Math.Max(0, (v - edge0) / (edge1 - edge0)));
}

/*
Mean squared error of 'chillipepper003' evaluating 725000 positions from "quiet-labeled.epd" 
    105609| +0.396856 | 100% Midgame
     28134| +0.309630 |
     26512| +0.306764 |
     28655| +0.310108 |
     35035| +0.273508 |
     41092| +0.247912 |
     54940| +0.232401 |
     92300| +0.198270 |
    116183| +0.174543 |
    196540| +0.203038 | 100% Endgame
------------------------
            | +0.246433  
*/
The above code and tables basically work like this:

Iterate over each square of the board and compute a midGame, endGame and phase value for that particular position. The midGame score is calulated by looking up the value of each encountered piece from the midgame tables and summing them. The endGame score does the same but looks up the values from the endgame tables. The phase value doesn't care about the color or square a piece is found on. Each piece is worth a certain amount of phase points taken from the PhaseValues array.
Now, to reach the final score you mix endGame and midGame score together based on the phase value. Everything above 5255 phase points is 100% midGame, everything below 435 phase points is 100% endGame. And everything in between gets linearly interpolated.

I also like how the values of each piece all average pretty much around the traditional values (1, 3, 3, 5, 9) because it makes it much easier for me to reason about what I see in the tables! :)
Mike Sherwin wrote: Sun Apr 25, 2021 6:34 am Decided to keep PeSTO for now as a reference like everyone else.
Feel free to give my above tables a try if you rather want to leave the well trodden path! :) My engine is published under the MIT license and the only condition that license imposes is to add a License and Copyright Notice.
Minimal Chess (simple, open source, C#) - Youtube & Github
Leorik (competitive, in active development, C#) - Github & Lichess