mdoege / PyTuroChamp

Python implementations of early chess engines including TUROCHAMP
https://mdoege.github.io/PyTuroChamp/
14 stars 4 forks source link

some unclear options #4

Closed tissatussa closed 1 year ago

tissatussa commented 1 year ago

lately i examined your PyTuroChamp code (again), trying to set optimal options regarding time management : it seems your python code does not have this, so setting a MAXPLIES higher then 3 can result -in some positions- in a thinking time of more than 60 seconds per move, which often makes the engine run out of time when playing in CuteChess -- i use 15 minutes per player per game and i QPLIES = MAXPLIES + 6 (=9) .. the addition of 6 is given in your original script, so i left it that way.

i'm familiar with Python and i can read and adjust your script .. i'm thinking about developing a script part which does simple time management : create some 'dynamic' [#] MAXPLIES, depending on the time left (say for 30 moves) and the amount of ply done : i imagine when enough time is left, the script can do (1) more ply. But first i wanted to test several option settings and get a better understanding of your script. I succeeded to run the multi version, using ptc_worker.py (i'm on Linux Xubuntu 22.04).

i have a question about the value of the PSTAB option : your script uses '0.5', but the UCI options list shows a max value 1024 !? And setting '0.5' in the CuteChess settings pane is impossible, only integers are allowed there !?

last thing for now : what does PDEAD do ? i see PDEAD = 1, with info version of dead position eval but what does that mean ? And which other values can be used ?

mdoege commented 1 year ago

I think PDEAD does nothing anymore. It is left over from an earlier PTC version which had two different ways to compute the isdead() function.

PSTAB: Which engine? For Bare and Newt, the UCI value is divided by ten https://github.com/mdoege/PyTuroChamp/blob/bc761fa02bcef8d3d52f08a3b724a4bf68e4f3f3/ptc_xboard.py#L310 For PTC, I have used PSTAB=0 for a long time. I really only introduced it to help PTC a little in the beginning, but of course TUROCHAMP does not use a piece-square table, so it is best turned off I think. Some of the other engines may benefit from a little PSTAB influence though.

For time management, I have experimented with iterative deepening in newt.py. I think it kind of worked, but it was not a good fit for the fixed depth TUROCHAMP search, so I left it out. Have you looked at the Go TUROCHAMP version? https://github.com/herohde/morlock I think that one has time management.

tissatussa commented 1 year ago

Have you looked at the Go TUROCHAMP version? https://github.com/herohde/morlock I think that one has time management.

yes, but it's in GO, and i'm not familiar with that language .. so, if i want to adjust code, i will stick to Python for now ..

..of course TUROCHAMP does not use a piece-square table, so it is best turned off I think..

i quickly tested Bare, Newt, and the others, but for the moment i just stick to PTC, especially while using the multi thread version.

i like the PST idea .. and also the percentage influence it can have on the eval .. this way i think i have room for testing several values (sets) .. i don't know if i succeed, i could log values to know more .. ofcourse the whole concept is rather simple, the engine won't reach 1500 rating i guess, but that's OK for me .. building some own simple time management logic seems just a challenge.

thanks for all your info.

mdoege commented 1 year ago

Actually Go is not such a bad language I think. Its main drawback from my point of view is its lack of exceptions and therefore incredibly annoying error handling. But I don't think this is a big issue for a chess engine. How many errors can that run into?

Go and Nim are some of the most sane alternatives to Python IMO. The other modern languages like Rust just suck...

tissatussa commented 1 year ago

Although a discussion about languages is beside the subject of this Issue, i want to comment on that .. i'm an old-school programmer, i started with assembler and Basic .. later i moved to Pascal and Delphi (by Borland, to create GUIs), and a little Perl, and later JS, which i know rather well now .. then i discovered Python and i like its setup and syntax, it's simple and logical and its code is beautiful IMO .. it seems since a few years pyInstaller is stable and works very well to create a binary from Python code, eg. i can create a binary of any PTC configuration, which runs fine in CuteChess, however its size is big : 182 Mb ! Nevermind, nowadays that's not a real issue .. They say C and C++ are best, esp. for chess engine programming, their binaries are small and very fast, but i know just a little C(++), enough to adjust some found errors in code, but to me this language is difficult because of the pointer constructions and tricky variable type casting, to put it simply .. i even stumbled upon the language 'D', which seems very nice, but not used often .. i like to learn an use a language with many users, so i can easilyy find code and answers to questions in forums .. i'd like to learn a new language .. JS mostly requires Node, which is OK to me, but JS is not conveniant for most users that way, when using it to write a non-web program .. do you know the neat little JS script 'P4wn', originally a chess engine in only 5 Kb ! See https://p4wn.sourceforge.net/ .. i'd like to rewrite that script to create a binary which can run in CuteChess with UCI (i found an UCI adaptor for that, but it's only for Windows) .. Nim seems related to Python, so for me it might be a good choice .. GO seems very good indeed, but to me it's "still Google", so maybe avoid it ? Btw. why does Rust (and other languages?) 'suck' in your opinion ? More and more (chess engine) programmers seem to use it.

I have respect for your honest opinions.

mdoege commented 1 year ago

And I started with Locomotive Basic on the Amstrad CPC. I suppose Python is my modern Basic. :-)

Yes, Go is still from Google. And I think Hello World in Go is 3.5 MB compiled or so, which is pretty ridiculous when comparing it to the C64 or Amiga, etc. But as a souped up, slightly dumbed down, modern version of C, that is at the same time still a small language (unlike C++), I mostly like it. It is boring, but in a good way. And it has easy parallelism, unlike Python. So I think Nim and Go are good companion languages for Python, because they excel in ares where Python is lacking (parallelism, compiling to a reasonably small native binary, running the code in a web browser without the need for WASM, etc.) With PyPy and other tricks, Python can achieve native performance on a modern CPU with lots of RAM (for some programs at least), but especially on a slower CPU with less RAM compiled languages still make a lot of sense.

Rust is far too security-focused to interest me. Maybe for network-facing programs it makes sense, but it also requires a lot of programmer effort to understand lifetimes and all that. And in the past there have been many programming languages that promised better security and reliability, but in the end the security issues were just shifted to somewhere else in code, all the while programmer effort goes up 10x. Of course writing pure C and doing manual memory management is probably not a good idea when dealing with the network. But that just means the language needs to have a garbage collector, not necessarily the added complexity of lifetimes. A chess engine is unlikely to get hacked, so I don't see the point of Rust there.

D is mostly hyped by its creator, and nobody else.

Yes, I know p4wn, I already had the code on my drive. Sounds like an interesting project...

tissatussa commented 1 year ago

i want to give an example of adjusting code while compiling gives an error :

in the GitHub Issue https://github.com/tonyOreglia/zenFresh/issues/1 i once asked the author advice about some C(++) construct, but i never got an answer .. to solve the problem, i also asked some StackOverflow question (which is hidden now) :

how can i solve this C++ array code error https://stackoverflow.com/questions/75659063/how-can-i-solve-this-c-array-code-error

here, some insight is given : it seems the author used some "GNU extension", not part of C .. this is all rather specific to me, i didn't find a solution, eg. can i install a GNU extension ? When i try to solve problems like this i learn a lot .. eg. often such compile errors are caused by the code being mainly for Windows, or even 32-bit, which seems too old for modern compilers, so it seems -- i'm on 64-bit Linux.

tissatussa commented 1 year ago

..p4wn [..] Sounds like an interesting project..

Yes, it plays very fast and rather good .. in fact it's amazing .. you should read the background story, how it all happened : years ago there was a coding challenge to write an entertaining web-script under 5 Kb (! total, html & css & JS) and someone received a price for this chess script .. many programmers had thought about creating such script, but they concluded it was impossible under 5Kb .. so, it's amazing .. later the script was called 'P4wn' and hosted at SourceForge - and still is .. at first it was highly scrambled / minified, to reach 5 Kb, but some people reconstructed it into readable code. I wonder how strong this engine will be when applying higher depths .. it must hold some slick rules, like Turing supplied us !?

tissatussa commented 1 year ago

regarding the current Issue, can you explain the following, am i wrong to think some code in pyturochamp.py needs to be adjusted ? :

def getpos(b):
    "Get positional-play value for a board"
    ppv = 0
    if not len(list(b.legal_moves)) and b.is_checkmate():
        if b.turn == c.WHITE:
            ppv = -1000
        else:
            ppv =  1000
    for i in b.piece_map().keys():
        m = b.piece_at(i)
        if m and m.color == COMPC:
            mm = m.piece_type
            if mm == c.KING and (
              len(b.pieces(c.PAWN, COMPC)) + len(b.pieces(c.PAWN, PLAYC)) ) <= 8:   # endgame is different
                mm = 8                              #   for the King
            if COMPC == c.WHITE:
                j, k = i // 8, i % 8
                ppv += PSTAB * pst[mm][8 * (7 - j) + k] / 100
            else:
                ppv += PSTAB * pst[mm][i]               / 100

first i see two lines like ppv = (-)100, then i see two lines ppv += PSTAB * pst[m][ .. : as i stated your UCI option shows '1024' max for PSTAB, not '1', so i can set '512', not '0.5' .. the pst[mm] values are around 0 - 60 .. so, when using 512 instead of 0.5, shouldn't PSTAB be divided by 1024 here ?

mdoege commented 1 year ago

The 1024 maximum is pretty arbitrary I think, I don't think you want a huge value like that.

The default value of PSTAB in PTC used to be 2, so I would experiment with something like that...

Here is the commit that changed the default from 2 to 0: https://github.com/mdoege/PyTuroChamp/commit/aa43b46e7e5931472d17a368a0215bf888d77c42

tissatussa commented 1 year ago

thanks for the explanations .. this GitHub Issue gave me some insight how to program the Turing Rules .. he didn't use PST's, but indeed we can add these with an "influence factor", PSTAB = 0 meaning no influence : i like that setup because it's simple.

my question "unclear options" goes with understanding the whole concept .. these days i set myself the task of creating my own full implementation of the Turing rules, just to practice my programming skills and to see if my calculations are same as yours, using test positions .. my results should be the same, at first when MAXPLIES = 1 but also with higher max-plies, although at this moment it's not clear to me how to make the score counting recursive to achive that .. i guess it's done with the famous "mini-max" procedure, am i right ? I could try to decipher your code, but can you tell me the mini-max algorithm in simple words, and/or how you implemented MAXPLIES > 1 ? Btw. maybe it's better to start a new Issue for such question, and close this Issue ?

mdoege commented 1 year ago

I think it's okay to continue talking in this thread.

By the way, I originally introduced PSTAB into PTC because I was conducting test games (PTC vs. Sunfish) and PTC was playing badly. So I thought a little bit of PSTAB would improve things, especially openings, and it did. But later I found that my Turing heuristic had a subtle bug that had existed since the first commit. When I fixed the bug (https://github.com/mdoege/PyTuroChamp/commit/4a0cfa087f0cb66791940c77cca87a37b05dc0d9), play improved, so I decided that PSTAB=0 was alright now. But of course I've kept the option in case somebody wanted to experiment with it...

For learning about chess programming, evaluation, heuristics, tree search, etc. I would recommend to read the Chess Programming Wiki, e.g. https://www.chessprogramming.org/Minimax

Of course the wiki will also cover the massive strides that chess programs have made since Turing. Because in his time, even alpha-beta was not fully understood yet, so engines tended to be more selective and pick the most likely moves, e.g. like the Bernstein program. But early Mephisto computers in the 1980s were also highly selective...

tissatussa commented 1 year ago

..read the Chess Programming Wiki, e.g. https://www.chessprogramming.org/Minimax ..

indeed, this page has it all .. i should study it.

at first i found https://en.chessbase.com/post/reconstructing-turing-s-paper-machine which is my reference for the Turing Rules .. at this moment i implemented all Rules except rule 5 "Castling" .. but when testing the starting position, this is not relevant .. 1.e3 seems 'best move' but my calculation shows two different moves with higher and equal value .. am i mistaken ? Why ?

this top-5 table makes it easy to confirm (rule 5 column missing but 0) :

move  Black   White  : [1]    [2]   [3] [4]  [6]  [7]
------------------------------------------------------
g1f3  8.400  10.228  : 2.828   5     0   0   2.4   0   : sqrt( 8) + 5   + 0 - 0 + 2.4 + 0
b1c3  8.400  10.228  : 2.828   5     0   0   2.4   0   : sqrt( 8) + 5   + 0 - 0 + 2.4 + 0
e2e3  8.400  10.042  : 3.742   4     1   1   2.3   0   : sqrt(14) + 4   + 1 - 1 + 2.3 + 0
g1h3  8.400   9.349  : 2.449   4.5   0   0   2.4   0   : sqrt( 6) + 4.5 + 0 - 0 + 2.4 + 0
b1a3  8.400   9.349  : 2.449   4.5   0   0   2.4   0   : sqrt( 6) + 4.5 + 0 - 0 + 2.4 + 0

so, 1.e3 is 3rd choice !? i also calculated those values by hand, and i see no error .. can you count with me ? This table can help. Note: i substract the value of Rule 4, as indicated, but maybe when adding it, the move 1.e3 becomes first with 12.042 points ?

For convenience, here are the Rules :

[1] Mobility.
    For the Q,R,B,N, add the square root of the number of moves the piece can make; count each capture as two moves

[2] Piece safety.
    For the R,B,N, add 1.0 point if it is defended, and 1.5 points if it is defended at least twice.

[3] King mobility.
    For the K, the same as (1) except for castling moves

[4] King safety.
    For the K, deduct (subtract) points for its vulnerability as follows:
    assume that a Queen of the same colour is on the King's square;
    calculate its mobility, and then subtract this value from the score.

[5] Castling.
    Add 1.0 point for the possibility of still being able to castle on a later move if a King or Rook move is being considered;
    add another point if castling can take place on the next move;
    finally add one more point for actually castling.

[6] Pawn credit.
    Add 0.2 point for each rank advanced, and 0.3 point for being defended by a non-Pawn.

[7] Mates and checks.
    Add 1.0 point for the threat of mate and 0.5 point for a check.
mdoege commented 1 year ago

Yes, e3 should be number 1.

You can just create a small script to run PTC in the chess start position, like this:

import pyturochamp as p
import chess

b = chess.Board("rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1")
print(p.getmove(b))

and run it to see how PTC scores each possible computer move:

(1/20) g1h3 21.2 0.00
(2/20) g1f3 22.2 0.00
(3/20) b1c3 22.2 0.00
(4/20) b1a3 21.2 0.00
(5/20) h2h3 20.2 0.00
(6/20) g2g3 20.7 0.00
(7/20) f2f3 18.5 0.00
(8/20) e2e3 23.8 0.00
(9/20) d2d3 22.1 0.00
(10/20) c2c3 20.9 0.00
(11/20) b2b3 20.7 0.00
(12/20) a2a3 20.2 0.00
(13/20) h2h4 21.2 0.00
(14/20) g2g4 20.9 0.00
(15/20) f2f4 18.8 0.00
(16/20) e2e4 23.6 0.00
(17/20) d2d4 22.7 0.00
(18/20) c2c4 21.2 0.00
(19/20) b2b4 20.9 0.00
(20/20) a2a4 21.2 0.00
info depth 2 seldepth 8 score cp 0 time 49 nodes 420 pv e2e3
(23.800000000000004, ['e2e3'])

Yes, e2e3 scores highest...

tissatussa commented 1 year ago

mmm .. that's all done by your script .. but what about a simple calculation by hand, as i suggested ? How is your e2e3 value 23.8 calculated ? How do the Rule values differ from my table ?

the info line shows depth 2 while i just do 1 ply ..

regarding seldepth 8 : i guess this is the QPLIES = MAXPLIES (2) + 6 ? the ChessBase article i mentioned tells about ..quiescence search ("captures had to be followed up at least to the point where no further capture was immediately possible"), as applied by Turing in the Glennie game.. and your QPLIES refers to that, am i right ? Did Turing really mean to do max 6 plies capturing, if possible ? In some positions this will be many nodes ! And it's not clear to me how the outcome of such multi-capture-sequence is added to the score .. at some point we read ..the quiescent search reveals that 15.Rg1 Bxf3 16.Qxf3 Qxa6 loses a pawn for White.. that's all !? How does PTC use the quiescent search outcome ?

tissatussa commented 1 year ago

i appreciate your feedback and help! I must be making some basic thinking errors while trying to understand those Turing Rules in practice .. i'll try to make things as clear as possible for you, not to bother ..

mdoege commented 1 year ago

The Turing heuristic (first column in my output) is only applied to the very next computer ply!

So the fact that my engine also does a tree search for the material evaluation is unimportant for comparison to your implementation. Also, the material search is all zero (second column in my output) anyway. So the Turing heuristic alone (23.8 for e3 and 23.6 for e4) decides the best move.

Quiescence plies are just like normal plies: At the final node you get a material evaluation.

Turing was pretty vague on the number of quiescence plies he wanted to do. So I simply picked a number for QPLIES that is high enough to reproduce the Glennie game somewhat decently. I.e. QPLIES > 6 will not do a much better job to reproduce that game, but it will make the engine a lot slower.

That is one of the advantages of the Go TUROCHAMP implementation: I think they even have an unbounded quiescence search! But for my much slower Python version, I had to set some limit or the time per move could skyrocket in a complex board position.

tissatussa commented 1 year ago

exactly, well said .. i think i understand all of it, but i must study and test more .. yes, i know that Morlock Go engine, i'm testing it while using different ply settings : change that value in the code and compile .. such engines run fine in CuteChess and reach depth 6 easily, but often not deeper .. indeed i read about the unbounded quiescence on their page, it has no UCI option for Qsearch.

and thanks for the small script to run PTC .. i can use it for reference .. I'll keep you posted, OK ?

mdoege commented 1 year ago

And when you play a tournament between PTC and Morlock with different openings in Cute Chess, Morlock is definitely better, maybe 200 or 300 Elo or so (I forgot the exact value), so the unbounded search definitely improves the game.

But when comparing Morlock's moves to the Glennie game, I don't think it does much better than PTC the last time I checked. So the Go version is definitely stronger but not necessarily more historically accurate. And somehow I doubt an unbounded search would have been feasible on 1950s/1960s computer hardware in a reasonable time per move. So I think this is a choice between what Turing wished his chess engine would do and what would have been realistically possible.

The selective search is probably the main reason that most TUROCHAMP implementations produce different moves. I know of a JavaScript version (https://github.com/ankushChatterjee/turochamp) that almost exactly reproduces the moves from my Python and Nim engines, but I assume that is because the author looked at my code. But independent TUROCHAMP implementations disagree a lot in their preferred moves...

tissatussa commented 1 year ago

FYI: i was familiar with a chess engine called Tucano, see https://github.com/alcides-schulz/Tucano and lately i discovered that its source also includes some Perft functions, also special ones which give more detailed numbers of the nodes of a FEN position at several plies, like amount of captures, checks, castlings, promotions and more .. i know a few other programmers who created such Perft, but this one is nicely coded and these days i was able to adjust the C code (not C++) to fit my idea : using its 'PerftX' function in a Python3 subprocess to catch stdout and use that output to generate this data table of any FEN position, to be sorted on any data column :

1k3b1r/ppp3pp/5p2/3r4/1P2q3/1QBbPN2/P2P1PPP/2R1K2R w K - 5 16

next-fen-table-sort

[ table sorted on '1.valid' (by default), second key '0.move' ]

it's a console application and works very fast : regarding all (valid) moves the next FEN positions are displayed with their many property values, which can be sorted on any column by terminal input - also sub-sorting (2nd 3rd key etc.) can be done by (my fixed) assignment of the column relations .. the process to calculate all data of 3 plies usually takes less than 10 seconds on my machine .. the 4 columns left of the FEN string are 'regular' TuroChamp values .. (ofcourse) the script is in beta stage, i did not yet set proper names for all columns, but the abbrevations D, N, C, E, O, P, K, M mean Depth Nodes Captures E.p. Castles Promotions Checks Mates, generated by the Tucano source.

i don't know yet how to use such perf data to evaluate a position, but it was a challenge to write the script : these day i learned a lot about C, i think the Tucano code is written very clearly. To explore this alternative (?) idea, maybe i can discover some data patterns .. if not, it's interesting for me to create table sorting like this .. at a later stage i can insert other data, eg. columns which display data which is a combination of other data columns - i guess this is how HCE Hand-Crafted-Eval works.

for a long time a have such ideas, being a chess player and a programmer .. i would really like to know your thoughts about using Perft data for chess evaluations : in fact, absolutely, such data reflects the future of a position, so could any 'new logic' be deduced ? For this, the game of chess is perfect : we can study the data of mate-in-N positions, they have a fixed eval : mate ends the game.

mdoege commented 1 year ago

I had heard about perft on some chess forum, but until today I had no idea what it was. But https://www.chessprogramming.org/Perft brought me up to speed quickly. :-)

I have not used perft for my own chess engines because I trust python-chess to find all legal moves and the Sunfish move generator to find all pseudo-legal moves. Of course there might still be bugs in move generation, but I have not tested it...

And I don't know how useful perft is outside of debugging the move generator. Robert Hyatt in the wiki article seems to think it is only a debugging tool.

Looking at my code vs Morlock again, I noticed that the selective plies in Morlock are done closer to the Turing text. Morlock uses bitboards (and probably a lot of other performance tricks), so they can do this. My main issue with Turing's approach was that checking whether a piece is en prise is computationally a bit expensive, especially in a slower language like Python or in a compiled language like Nim that uses ASCII strings for the board representation.

So my selective plies in Python/Nim work like this: Check whether the position is dead (is in check?, can a recapture be made?). If not dead, follow all capture (and maybe checking) moves to the next ply. So that would be an example where my versions cut some corners to improve search speed. Although I think the higher Elo of Morlock is more about its unbounded search depth and not these minor differences in selective rules. Higher search depth simply means more Elo...