PST-only Evaluation for MinimalChess 0.4

Discussion of chess software programming and technical issues.

Moderators: hgm, Rebel, chrisw

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 »

mar wrote: Thu Apr 15, 2021 8:11 pm I tried to replace my texel-tuned HCE with PeSTO and immediately lost ~200 elo, which means (considering my engine is slightly below PeSTO in CCRL) that PeSTO's search is at least 200 elo better than mine.
I don't know what HCE means. :oops: Hand-Crafted-Evaluation? Highly-Complex-Evaluation? I suppose you mean you have a costly but precise evaluation that combines PSTs with other evaluation terms. For example it looks at pawn structure, mobility and just has a lot of "chess knowledge" implemented to evaluate the board?
Ronald wrote: Thu Apr 15, 2021 9:00 pm If I remember correctly I used the "quiet-labeled.epd" created by the Zurichess developper for the tuning proces.
Training only on quiet positions is really clever. I think I found them and have started to use that data for my own training. However there are different versions! I assume you used the smallest one? Because it seems like I managed to improve the PeSTO tables by tuning them on the v7 set.
Ronald wrote: Thu Apr 15, 2021 9:00 pm The Texel tuning process will normally find a local minimum of the error, so there are multiple tuning results possible depending on initial values/sequence of PST values. These different results will normally also result in different "strength". So for getting "optimal" results, you will probably have to play with initial values of the PST's and or sequence of elements.
I'm currently trying to grow PSTs from the default values 100, 300, 300, 500, 900 for both midgame and endgame as initial values. There's no randomness involved but my tuner is semi-automatic: you can interact with it using a command line interface and tune the tables step by step. For example I can chose to only tune the MG or EG table (no interpolation) on a subset of the training set. Or only improve a value if the mean-squared-error changes beyond a specific threshold. I can also dump to a file edit per hand and reload. It gives me a lot of control but I don't know what I'm even doing :wink: so there's a lot of trial and error and it takes a long time... but it's kinda fun. If I stumble upon noteworthy results I'll post them here!
Minimal Chess (simple, open source, C#) - Youtube & Github
Leorik (competitive, in active development, C#) - Github & Lichess
User avatar
hgm
Posts: 27789
Joined: Fri Mar 10, 2006 10:06 am
Location: Amsterdam
Full name: H G Muller

Re: PST-only Evaluation for MinimalChess 0.4

Post by hgm »

HCE usually stands for hand-crafted evaluation. But I don' think that excludes Texel tuning, as long as the terms you tune were picked by hand. So it is used mainly in contrast with neural networks.
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'm finally seeing progress with my tuning attempts. The new PSTs are initialized with material values only (all the tables are filled with 100, 300, 300, 500, 900 etc) and fully tuned they look totally different than PeSTOs. I've made a quick 2000 games test against a previous version with PeSTO tables (tc=5+0.5 and book moves) and they performed really well.

Code: Select all

Score of MinimalChess Tabasco vs MinimalChess PeSTO: 855 - 649 - 496  [0.551] 2000
...      MinimalChess Tabasco playing White: 444 - 294 - 262  [0.575] 1000
...      MinimalChess Tabasco playing Black: 411 - 355 - 234  [0.528] 1000
...      White vs Black: 799 - 705 - 496  [0.523] 2000
Elo difference: 35.9 +/- 13.2, LOS: 100.0 %, DrawRatio: 24.8 %
It's not perfect, though. For example without book openings the new tables cause the engine to prefer playing the two knights first before getting any pawns out. At this point I have no idea how to change something like that without editing the tables manually.
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 18, 2021 10:42 pm I'm finally seeing progress with my tuning attempts. The new PSTs are initialized with material values only (all the tables are filled with 100, 300, 300, 500, 900 etc) and fully tuned they look totally different than PeSTOs. I've made a quick 2000 games test against a previous version with PeSTO tables (tc=5+0.5 and book moves) and they performed really well.

Code: Select all

Score of MinimalChess Tabasco vs MinimalChess PeSTO: 855 - 649 - 496  [0.551] 2000
...      MinimalChess Tabasco playing White: 444 - 294 - 262  [0.575] 1000
...      MinimalChess Tabasco playing Black: 411 - 355 - 234  [0.528] 1000
...      White vs Black: 799 - 705 - 496  [0.523] 2000
Elo difference: 35.9 +/- 13.2, LOS: 100.0 %, DrawRatio: 24.8 %
It's not perfect, though. For example without book openings the new tables cause the engine to prefer playing the two knights first before getting any pawns out. At this point I have no idea how to change something like that without editing the tables manually.
Very nice! :) But, I am happier than you are because I just squished another bug!! :D And I'm on my way to post about it. :P

Edit: :D :) :| :( :cry: Now you are happier.
User avatar
mvanthoor
Posts: 1784
Joined: Wed Jul 03, 2019 4:42 pm
Location: Netherlands
Full name: Marcel Vanthoor

Re: PST-only Evaluation for MinimalChess 0.4

Post by mvanthoor »

lithander wrote: Sun Apr 18, 2021 10:42 pm I'm finally seeing progress with my tuning attempts. The new PSTs are initialized with material values only (all the tables are filled with 100, 300, 300, 500, 900 etc) and fully tuned they look totally different than PeSTOs. I've made a quick 2000 games test against a previous version with PeSTO tables (tc=5+0.5 and book moves) and they performed really well.

Code: Select all

Score of MinimalChess Tabasco vs MinimalChess PeSTO: 855 - 649 - 496  [0.551] 2000
...      MinimalChess Tabasco playing White: 444 - 294 - 262  [0.575] 1000
...      MinimalChess Tabasco playing Black: 411 - 355 - 234  [0.528] 1000
...      White vs Black: 799 - 705 - 496  [0.523] 2000
Elo difference: 35.9 +/- 13.2, LOS: 100.0 %, DrawRatio: 24.8 %
It's not perfect, though. For example without book openings the new tables cause the engine to prefer playing the two knights first before getting any pawns out. At this point I have no idea how to change something like that without editing the tables manually.
It is 36 Elo stronger in self-play, than Minimalchess Pesto? :o

I need to get some time for chess programming again, so I can implement the four basic features I still have lying around, and then get to work on that tuner and tapering. I'm getting jealous here.
Author of Rustic, an engine written in Rust.
Releases | Code | Docs | Progress | CCRL
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 »

mvanthoor wrote: Mon Apr 19, 2021 11:12 am It is 36 Elo stronger in self-play, than Minimalchess Pesto? :o
Yes. But by the nature of my test I might just have selected for a one-trick pony that can beat the version with Pesto weights in 5s+0.5s timecontrol and nothing else. I threw a dozen attempts away for failing at that criteria until I hit one that didn't.

To really make sure that the new weights are a general improvement takes a lot more time, even, than tuning the weights and that kept my computer busy during the full weekend, already. So I won't have a definitive answer for some time. Need the cores free for work related things, during the day.
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 »

Hi Thomas,

A serious question, putting aside the minimal aspect for now, why walk down a path that is trodden upon already? A new path would be to write a dynamic pst maker that is tuned by your tuning method.
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 18, 2021 11:43 pm Edit: :D :) :| :( :cry: Now you are happier.
Preliminary tests against real engines (outside selfplay) seem to suggest that my self-tuned tables are resulting in weaker play then PeSTO's. So it's not unlikely that you're the happier one, again! ;)
Mike Sherwin wrote: Mon Apr 19, 2021 5:40 pm A serious question, putting aside the minimal aspect for now, why walk down a path that is trodden upon already? A new path would be to write a dynamic pst maker that is tuned by your tuning method.
I'm only doing chess programming since a few months. I don't want to be the fool who thinks he can improve upon traditional methods before first understanding the status quo of the field. For a first engine I think my goal to write a 2000 ELO engine in 500 lines of idiomatic C# code is more fruitful - it's a didactic engine meant for one user in particular: me.

I'm also a novice chess player (1000 ELO maybe?) and so the idea of tuning my own PSTs intrigues me because it's basically encoding chess knowledge that I myself don't have yet. But if it's layed out before me I discover "new" things about chess. (Why are the rooks squares in the 7th rank more valuable? Ah... to pick up the pawns. Why do people move the B and G pawns early but by only one square? Ah... because the square the block is really valuable for a bishop)

To sum it up: It's more fun to me to make steady progress on modest goals than aiming for the moon with no real hope of reaching it. And a hobby should be fun, right?^^
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: Mon Apr 19, 2021 6:07 pm
Mike Sherwin wrote: Sun Apr 18, 2021 11:43 pm Edit: :D :) :| :( :cry: Now you are happier.
Preliminary tests against real engines (outside selfplay) seem to suggest that my self-tuned tables are resulting in weaker play then PeSTO's. So it's not unlikely that you're the happier one, again! ;)
Mike Sherwin wrote: Mon Apr 19, 2021 5:40 pm A serious question, putting aside the minimal aspect for now, why walk down a path that is trodden upon already? A new path would be to write a dynamic pst maker that is tuned by your tuning method.
I'm only doing chess programming since a few months. I don't want to be the fool who thinks he can improve upon traditional methods before first understanding the status quo of the field. For a first engine I think my goal to write a 2000 ELO engine in 500 lines of idiomatic C# code is more fruitful - it's a didactic engine meant for one user in particular: me.

I'm also a novice chess player (1000 ELO maybe?) and so the idea of tuning my own PSTs intrigues me because it's basically encoding chess knowledge that I myself don't have yet. But if it's layed out before me I discover "new" things about chess. (Why are the rooks squares in the 7th rank more valuable? Ah... to pick up the pawns. Why do people move the B and G pawns early but by only one square? Ah... because the square the block is really valuable for a bishop)

To sum it up: It's more fun to me to make steady progress on modest goals than aiming for the moon with no real hope of reaching it. And a hobby should be fun, right?^^
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.

Code: Select all

void InitEval()
{
  s32 sq;
  s32 sum;
  s32 first;
  s32 row;
  s32 col;
  s32 g, n;
  s32 cenRow;
  s32 cenCol;
  u64 moves;
  u64 capts;
  u64 trgts;
  u64 aPieces;

  aPieces = wPieces | bPieces;

  for(sq = A1; sq <= H8; sq++)
  {
    row = Row(sq);
    col = Col(sq);
    if(row <= ROW_4) cenRow = row;
    else             cenRow = 7 - row;
    if(col <= COL_D) cenCol = col;
    else             cenCol = 7 - col;

    // Pawn Tables
    wPawnTbl[sq] = 0;
    if(sq > H1 && sq < A8) {
      if(row == ROW_2)
        wPawnTbl[sq] = 12;
      else
        wPawnTbl[sq] = (cenCol * cenCol + row * row); }

    bPawnTbl[sq] = 0;
    if(sq < A8 && sq > H1) {
      if(row == ROW_7)
        bPawnTbl[sq] = 12;
      else
        bPawnTbl[sq] = (cenCol * cenCol + (7 - row) * (7 - row)); }

    if(!(wPassed[sq] & bPawns)) wPawnTbl[sq] += (20 * row);;
    if(!(bPassed[sq] & wPawns)) bPawnTbl[sq] += (20 * (7 - row));

    wPawnTbl[sq] += ((ply * row) / 32);
    bPawnTbl[sq] += ((ply * (7 - row)) / 32);

    if(~wPawns & setBit[sq]) {
      g = evalHistTblg[WP][sq]; n = evalHistTbln[WP][sq];
      wPawnTbl[sq] += (n > 99 ? g/n/2 : 0);
    }

    if(~bPawns & setBit[sq]) {
      g = evalHistTblg[BP][sq]; n = evalHistTbln[BP][sq];
      bPawnTbl[sq] += (n > 99 ? g/n/2 : 0);
    }

    
    // Knight Tables
    moves = knight[sq] & ~aPieces;
    trgts = knight[sq] & (bRooks | bQueens | bKings);
    wKnightTbl[sq] = PopCnt(moves) * 4 + PopCnt(trgts) * 8;
    wKnightTbl[sq] += ((cenRow + cenRow) * (cenCol + cenCol) + row * 3);
    if(cenCol == 0) wKnightTbl[sq] -= 20;
    g = evalHistTblg[WN][sq]; n = evalHistTbln[WN][sq];
    wKnightTbl[sq] += (n > 99 ? g/n/2 : 0);

    moves = knight[sq] & ~aPieces;
    trgts = knight[sq] & (wRooks | wQueens | wKings);
    bKnightTbl[sq] = PopCnt(moves) * 4 + PopCnt(trgts) * 8;
    bKnightTbl[sq] += ((cenRow + cenRow) * (cenCol + cenCol) + (7 - row) * 3);
    if(cenCol == 0) bKnightTbl[sq] -= 20;
    g = evalHistTblg[BN][sq]; n = evalHistTbln[BN][sq];
    bKnightTbl[sq] += (n > 99 ? g/n/2 : 0);

    // Bishop Tables
    first = FirstBit(ray7p[sq] & aPieces);
    moves = ray7p[sq] & below[first];
    capts = setBit[first];
    first = FirstBit(ray9p[sq] & aPieces);
    moves |= ray9p[sq] & below[first];
    capts |= setBit[first];
    first = LastBit(ray7m[sq] & aPieces);
    moves |= ray7m[sq] & above[first];
    capts |= setBit[first];
    first = LastBit(ray9m[sq] & aPieces);
    moves |= ray9m[sq] & above[first];
    capts |= setBit[first];

    trgts = capts & (bRooks | bQueens | bKings);
    wBishopTbl[sq] = PopCnt(moves) * 3 + PopCnt(trgts) * 8;
    wBishopTbl[sq] += ((cenRow + cenRow) * (cenCol + cenCol) + row * 2);
    if(cenRow == 0) wBishopTbl[sq] -= 10;
    if(moves & (D4bit | E4bit | D5bit | E5bit)) wBishopTbl[sq] += 10;
    g = evalHistTblg[WB][sq]; n = evalHistTbln[WB][sq];
    wBishopTbl[sq] += (n > 99 ? g/n/2 : 0);

    trgts = capts & (wRooks | wQueens | wKings);
    bBishopTbl[sq] = PopCnt(moves) * 3 + PopCnt(trgts) * 8;
    bBishopTbl[sq] += ((cenRow + cenRow) * (cenCol + cenCol) + (7 - row) * 2);
    if(cenRow == 0) bBishopTbl[sq] -= 10;
    if(moves & (D4bit | E4bit | D5bit | E5bit)) bBishopTbl[sq] += 10;
    g = evalHistTblg[BB][sq]; n = evalHistTbln[BB][sq];
    bBishopTbl[sq] += (n > 99 ? g/n/2 : 0);

    // Rook Tables
    first = FirstBit(ray8p[sq] & aPieces);
    moves = ray8p[sq] & below[first];
    capts = setBit[first];
    first = FirstBit(ray1p[sq] & aPieces);
    moves |= ray1p[sq] & below[first];
    capts |= setBit[first];
    first = LastBit(ray8m[sq] & aPieces);
    moves |= ray8m[sq] & above[first];
    capts |= setBit[first];
    first = LastBit(ray1m[sq] & aPieces);
    moves |= ray1m[sq] & above[first];
    capts |= setBit[first];

    trgts = capts & (bQueens | bKings);
    wRookTbl[sq] = PopCnt(moves) * 2 + PopCnt(trgts) * 4;
    if(row < ROW_3) wRookTbl[sq] += (cenCol * 2);
    if(!(openFile[Col(sq)] & wPawns)) wRookTbl[sq] += (40 - row * 2);
    if(!(openFile[Col(sq)] & bPawns)) wRookTbl[sq] += (20 - row * 2);
    wRookTbl[sq] /= 2;
    g = evalHistTblg[WR][sq]; n = evalHistTbln[WR][sq];
    wRookTbl[sq] += (n > 99 ? g/n/2 : 0);

    trgts = capts & (wQueens | wKings);
    bRookTbl[sq] = PopCnt(moves) * 2 + PopCnt(trgts) * 4;
    if(row > ROW_6) bRookTbl[sq] += (cenCol * 2);
    if(!(openFile[Col(sq)] & bPawns)) bRookTbl[sq] += (40 - (7 - row) * 2);
    if(!(openFile[Col(sq)] & wPawns)) bRookTbl[sq] += (20 - (7 - row) * 2);
    bRookTbl[sq] /= 2;
    g = evalHistTblg[BR][sq]; n = evalHistTbln[BR][sq];
    bRookTbl[sq] += (n > 99 ? g/n/2 : 0);

    // Queen Tables
    first = FirstBit(ray8p[sq] & aPieces);
    moves = ray8p[sq] & below[first];
    capts = setBit[first];
    first = FirstBit(ray1p[sq] & aPieces);
    moves |= ray1p[sq] & below[first];
    capts |= setBit[first];
    first = LastBit(ray8m[sq] & aPieces);
    moves |= ray8m[sq] & above[first];
    capts |= setBit[first];
    first = LastBit(ray1m[sq] & aPieces);
    moves |= ray1m[sq] & above[first];
    capts |= setBit[first];
    first = FirstBit(ray7p[sq] & aPieces);
    moves |= ray7p[sq] & below[first];
    capts |= setBit[first];
    first = FirstBit(ray9p[sq] & aPieces);
    moves |= ray9p[sq] & below[first];
    capts |= setBit[first];
    first = LastBit(ray7m[sq] & aPieces);
    moves |= ray7m[sq] & above[first];
    capts |= setBit[first];
    first = LastBit(ray9m[sq] & aPieces);
    moves |= ray9m[sq] & above[first];
    capts |= setBit[first];

    trgts = capts & (bPieces);
    wQueenTbl[sq] = PopCnt(moves) * 2 + PopCnt(trgts) * 4;
    wQueenTbl[sq] += ((cenRow + cenRow) * (cenCol + cenCol) + row * 4);
    if(cenRow == 0) wQueenTbl[sq] -= 10;
    if(moves & (D4bit | E4bit | D5bit | E5bit)) wQueenTbl[sq] += 20;
    wQueenTbl[sq] /= 2;
    g = evalHistTblg[WQ][sq]; n = evalHistTbln[WQ][sq];
    wQueenTbl[sq] += (n > 99 ? g/n/2 : 0);

    trgts = capts & (wPieces);
    bQueenTbl[sq] = PopCnt(moves) * 2 + PopCnt(trgts) * 4;
    bQueenTbl[sq] += ((cenRow + cenRow) * (cenCol + cenCol) + (7 - row) * 4);
    if(cenRow == 0) bQueenTbl[sq] -= 10;
    if(moves & (D4bit | E4bit | D5bit | E5bit)) bQueenTbl[sq] += 20;
    bQueenTbl[sq] /= 2;
    g = evalHistTblg[BQ][sq]; n = evalHistTbln[BQ][sq];
    bQueenTbl[sq] += (n > 99 ? g/n/2 : 0);

    // King Tables
    sum = (4200 - bMat) >> 6;
    wKingTbl[sq] = ((cenRow + cenCol) * sum);
    g = evalHistTblg[WK][sq]; n = evalHistTbln[WK][sq];
    wKingTbl[sq] += (n > 99 ? g/n/2 : 0);

    sum = (4200 - wMat) >> 6;
    bKingTbl[sq] = ((cenRow + cenCol) * sum);
    g = evalHistTblg[BK][sq]; n = evalHistTbln[BK][sq];
    bKingTbl[sq] += (n > 99 ? g/n/2 : 0);

    if(!(wBlocker[sq] & bPawns) && (wFrontal[sq] & bPawns))
    {
      if(ray8p[sq] & below[FirstBit(ray8p[sq] & bPawns)] & wPawns)
      {
        wQueenTbl[sq] += 4;
        wRookTbl[sq] -= 4;
        wBishopTbl[sq] += (8 + row);
        wKnightTbl[sq] += (12 + row);
        wKingTbl[sq] += (10 + row);
      }else
      {
        wQueenTbl[sq] +=8;
        wRookTbl[sq] += 6;
        wBishopTbl[sq] += (8 + row * 2);
        wKnightTbl[sq] += (12 + row * 2);
        wKingTbl[sq] += (20 + row * 4);
      }
    }

    if(!(bBlocker[sq] & wPawns) && (bFrontal[sq] & wPawns))
    {
      if(ray8m[sq] & above[LastBit(ray8m[sq] & wPawns)] & bPawns)
      {
        bQueenTbl[sq] += 4;
        bRookTbl[sq] -= 4;
        bBishopTbl[sq] += (8 + (7 - row));
        bKnightTbl[sq] += (12 + (7 - row));
        bKingTbl[sq] += (10 + (7 - row));
      }else
      {
        bQueenTbl[sq] += 8;
        bRookTbl[sq] += 6;
        bBishopTbl[sq] += (8 + (7 - row) * 2);
        bKnightTbl[sq] += (12 + (7 - row) * 2);
        bKingTbl[sq] += (20 + (7 - row) * 4);
      }
    }

    if(row == ROW_7)
    {
      wRookTbl[sq] += 40;
      wRookTbl[sq + 8] += 30;
    }

    if(row == ROW_2)
    {
      bRookTbl[sq] += 40;
      bRookTbl[sq - 8] += 30;
    }
  }

  // Blocked Pawn Penalties
  if(wPawns & D2bit)
  {
    wBishopTbl[D3] -= 30;
    wKnightTbl[D3] -= 24;
    wRookTbl[D3] -= 16;
    wQueenTbl[D3] -= 12;
  }

  if(wPawns & E2bit)
  {
    wBishopTbl[E3] -= 30;
    wKnightTbl[E3] -= 24;
    wRookTbl[E3] -= 16;
    wQueenTbl[E3] -= 12;
  }

  if(bPawns & D7bit)
  {
    bBishopTbl[D6] -= 30;
    bKnightTbl[D6] -= 24;
    bRookTbl[D6] -= 16;
    bQueenTbl[D6] -= 12;
  }

  if(bPawns & E7bit)
  {
    bBishopTbl[E6] -= 30;
    bKnightTbl[E6] -= 24;
    bRookTbl[E6] -= 16;
    bQueenTbl[E6] -= 12;
  }

  // Keep Pawns at Home if King is on same side
  if(bMat > 4600) {
    if(wKings & (E1bit | F1bit | G1bit | H1bit)) {
      wPawnTbl[F2] = 20;
      wPawnTbl[G2] = 20;
      wPawnTbl[H2] = 20;
      wPawnTbl[F3] = 16;
      wPawnTbl[G3] = 16;
      wPawnTbl[H3] = 16; }

    if(wKings & (E1bit | D1bit | C1bit | B1bit | A1bit)) {
      wPawnTbl[C2] = 20;
      wPawnTbl[B2] = 20;
      wPawnTbl[A2] = 20;
      wPawnTbl[C3] = 16;
      wPawnTbl[B3] = 16;
      wPawnTbl[A3] = 16; } }

  if(wMat > 4600) {
    if(bKings & (E8bit | F8bit | G8bit | H8bit)) {
      bPawnTbl[F7] = 20;
      bPawnTbl[G7] = 20;
      bPawnTbl[H7] = 20;
      bPawnTbl[F6] = 16;
      bPawnTbl[G6] = 16;
      bPawnTbl[H6] = 16; }

    if(bKings & (E8bit | D8bit | C8bit | B8bit | A8bit)) {
      bPawnTbl[C7] = 10;
      bPawnTbl[B7] = 20;
      bPawnTbl[A7] = 20;
      bPawnTbl[C6] = 16;
      bPawnTbl[B6] = 16;
      bPawnTbl[A6] = 16; } }

  // Penalty if piece blocks bishop 
  if(wBishops & F1bit) {
    wQueenTbl[E2] -= 40;
    wQueenTbl[D3] -= 36;
    wRookTbl[E2] -= 30;
    wRookTbl[D3] -= 24;
    if(wPawns & G2bit)   {
      wKnightTbl[E2] -= 20;
      if(wPawns & (E2bit | D3bit)) {
        wPawnTbl[G3] += 20;
        wBishopTbl[G2] += 20;
        wPawnTbl[E2] -= 10; } } }

  if(wBishops & C1bit) {
    wQueenTbl[D2] -= 40;
    wQueenTbl[E3] -= 36;
    wRookTbl[D2] -= 30;
    wRookTbl[E3] -= 24;
    if(wPawns & B2bit)   {
      wKnightTbl[D2] -= 10;
      if(wPawns & (D2bit | E3bit)) {
        wPawnTbl[B3] += 20;
        wBishopTbl[B2] += 20;
        wPawnTbl[D2] -= 10; } } }

  if(bBishops & F8bit) {
    bQueenTbl[E7] -= 40;
    bQueenTbl[D6] -= 36;
    bRookTbl[E7] -= 30;
    bRookTbl[D6] -= 24;
    if(bPawns & G7bit)   {
      bKnightTbl[E7] -= 20;
      if(bPawns & (E7bit | D6bit)) {
        bPawnTbl[G6] += 20;
        bBishopTbl[G7] += 20;
        bPawnTbl[E7] -= 10; } } }

  if(bBishops & C8bit) {
    bQueenTbl[D7] -= 40;
    bQueenTbl[E6] -= 36;
    bRookTbl[D7] -= 30;
    bRookTbl[E6] -= 24;
    if(bPawns & B7bit)   {
      bKnightTbl[D7] -= 10;
      if(bPawns & (D7bit | E6bit)) {
        bPawnTbl[B6] += 20;
        bBishopTbl[B7] += 20;
        bPawnTbl[D7] -= 10; } } }

  // Penalty for not Castleing
  wKingTbl[H1] = wKingTbl[G1];
  if(wIndexs & WCASKbit) {
    wKingTbl[F1] = wKingTbl[E1] - 60;
    wKingTbl[G1] -= 60;
    wKingTbl[D1] = wKingTbl[E1] - 60;
    wKingTbl[C1] -= 60;
    wKingTbl[B1] -= 60; }
  wKingTbl[A1] = wKingTbl[B1];

  bKingTbl[H8] = bKingTbl[G8];
  if(bIndexs & BCASKbit) {
    bKingTbl[F8] = bKingTbl[E8] - 60;
    bKingTbl[G8] -= 60; 
    bKingTbl[D8] = bKingTbl[E8] - 60;
    bKingTbl[C8] -= 60;
    bKingTbl[B8] -= 60; }
  bKingTbl[A8] = bKingTbl[B8];

  // Penalty for not moving center pawns
  if(wPawns & (E2bit | D2bit)) {
    wPawnTbl[E2] -= 16;
    wPawnTbl[E3] -= 8;
    wPawnTbl[D2] -= 16;
    wPawnTbl[D3] -= 8; 
    if(bPawns & E5bit)
      wPawnTbl[E4] += 30;
    if(bPawns & D5bit)
      wPawnTbl[D4] += 30; }

  if(bPawns & (E7bit | D7bit)) {
    bPawnTbl[E7] -= 16;
    bPawnTbl[E6] -= 8;
    bPawnTbl[D7] -= 16;
    bPawnTbl[D6] -= 8;
    if(wPawns & E4bit)
      bPawnTbl[E5] += 30;
    if(wPawns & D4bit)
      bPawnTbl[D5] += 30; }

  // Encourage development of minor Pieces
  if(ply < 20) { 
    wKnightTbl[G1] -= 24;
    wKnightTbl[B1] -= 24;
    wBishopTbl[F1] -= 20;
    wBishopTbl[C1] -= 20;
    bKnightTbl[G8] -= 24;
    bKnightTbl[B8] -= 24;
    bBishopTbl[F8] -= 20;
    bBishopTbl[C8] -= 20; }

  // Encourage C2 and C7 Pawns to move
  if((wPawns & C2bit) && !(wKings & (D1bit | C1bit | B1bit | A1bit))) {
    if(wPawns & (E2bit | E3bit)) {
      wKnightTbl[C3] -= 20;
      wKnightTbl[D2] += 10;
      wPawnTbl[D2] -= 20;
      wPawnTbl[D3] -= 20;
      wPawnTbl[C2] -= 10;
      wPawnTbl[C3] += 10;
      wPawnTbl[C4] += 40; }
    else
    if((wPawns & D4bit) && (bPawns & (D5bit | D6bit | D7bit))) {
      wKnightTbl[C3] -= 20; 
      if(wPawns & (B2bit | B3bit)) {
        wPawnTbl[B3] += 20;
        wPawnTbl[C3] += 10;
        wPawnTbl[C4] += 40; }
      else {
        wPawnTbl[C2] -= 10;
        wPawnTbl[C3] += 30; } }
    else
    if((wPawns & E4bit) && (bPawns & (E5bit | C5bit)) &&
       (wPawns & (D2bit | D3bit))) {
      wPawnTbl[C3] += 30;
      wPawnTbl[D4] += 20;
      wKnightTbl[C3] -= 20;
      wKnightTbl[D2] += 20; } } 

  if((bPawns & C7bit) && !(bKings & (D8bit | C8bit | B8bit | A8bit))) {
    if(bPawns & (E7bit | E6bit)) {
      bKnightTbl[C6] -= 20;
      bKnightTbl[D7] += 20;
      bPawnTbl[D7] -= 20;
      bPawnTbl[D6] -= 20;
      bPawnTbl[C7] -= 6;
      bPawnTbl[C6] += 6;
      bPawnTbl[C5] += 40; }
    else
    if((bPawns & D5bit) && (wPawns & (D4bit | D3bit | D2bit))) {
      bKnightTbl[C6] -= 20;
      bKnightTbl[D7] += 10;
      if(bPawns & (B7bit | B6bit)) {
        bPawnTbl[B6] += 20;
        bPawnTbl[C6] += 10;
        bPawnTbl[C5] += 40; }
      else {
        bPawnTbl[C7] -= 10;
        bPawnTbl[C6] += 20; } }
    else
    if((bPawns & E5bit) && (wPawns & (E4bit | C4bit)) &&
       (bPawns & (D7bit | D6bit))) {
      bPawnTbl[C6] += 20;
      bPawnTbl[D5] += 20;
      bKnightTbl[C6] -= 20;
      bKnightTbl[D7] += 20; } } 
  
  // Discourage Pawn Moves from row 4 to row 5 releasing tension
  wPawnTbl[C5] = wPawnTbl[C4] - 24;
  wPawnTbl[D5] = wPawnTbl[D4] - 20;
  wPawnTbl[E5] = wPawnTbl[E4] - 20;
  bPawnTbl[C4] = bPawnTbl[C5] - 24;
  bPawnTbl[D4] = bPawnTbl[D5] - 20;
  bPawnTbl[E4] = bPawnTbl[E5] - 20;

  // Discourage King Moves into corner when not needed
  wKingTbl[A1] = wKingTbl[B1] - 10;
  wKingTbl[H1] = wKingTbl[G1] - 10;
  bKingTbl[A8] = bKingTbl[B8] - 10;
  bKingTbl[H8] = bKingTbl[G8] - 10;
}
Since I put my chess knowledge into it, it plays a lot like me. 8-) But, tares me to shreds. When it plays b2 or g2 most of the time it is because the bishop has more mobility or other goodness on that square and not just because it cannot find a square with a static higher value to land on that may not be as good. And Romi has its fans just because it is an engine that they feel plays a more human like game! My next dynamic pst maker will have values in the milli-pawn range so that they can be tuned by an automated tuning method. I'm not trying to talk you into anything. I'm just presenting an option that could make you even happier. :D
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: 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.
Minimal Chess (simple, open source, C#) - Youtube & Github
Leorik (competitive, in active development, C#) - Github & Lichess