Looking for uci engines with piece value options shown

Discussion of anything and everything relating to chess playing software and machines.

Moderators: hgm, Rebel, chrisw

User avatar
xr_a_y
Posts: 1871
Joined: Sat Nov 25, 2017 2:28 pm
Location: France

Re: Looking for uci engines with piece value options shown

Post by xr_a_y »

For its worth, here is how I try to do it ... very basic PSO ...

batch of 100 games at 1+0.01 tc

I will converge ... but take your time ...

Code: Select all

#!/usr/bin/python
# -*- coding: utf-8 -*-

from subprocess import Popen, PIPE
import sys
import platform
import random
import math
import multiprocessing
from joblib import Parallel, delayed
import pyswarms as ps
from random import randrange
from pyswarms.utils.plotters import plot_cost_history
from random import randrange
import matplotlib.pyplot as plt
import numpy as np
from termcolor import colored

threads = 8
ngames = 100

if platform.system() == 'Windows':
   cutechess_cli_path = "\"C:\Program Files (x86)\cutechess\cutechess-cli.exe\""
else:
   cutechess_cli_path = "/ssd/cutechess/projects/cli/cutechess-cli"

engine = 'conf=minic_dev_uci'

engine_param_cmd = ' option.{name}={value}'

opponents = [ 'conf=minic_dev_uci' ]

options = '-each tc=1+0.01 -openings file=/ssd/Minic/Book_and_Test/OpeningBook/Hert500.pgn format=pgn order=random plies=24 -draw movenumber=80 movecount=5 score=5 -resign movecount=5 score=600 -pgnout out.pgn'

params = [
 
  ["PawnValueMG"  , 0 , 2000, 500 ],
  ["PawnValueEG"  , 0 , 2000, 500 ],
  ["KnightValueMG", 0 , 2000, 500 ],
  ["KnightValueEG", 0 , 2000, 500 ],
  ["BishopValueMG", 0 , 2000, 500 ],
  ["BishopValueEG", 0 , 2000, 500 ],
  ["RookValueMG"  , 0 , 2000, 500 ],
  ["RookValueEG"  , 0 , 2000, 500 ],
  ["QueenValueMG" , 0 , 2000, 500 ],
  ["QueenValueEG" , 0 , 2000, 500 ],
  
]

def run_one(i,fcp,scp,ngames):

   if i % 2 != 0:
       fcp, scp = scp, fcp

   cutechess_args = '-engine %s -engine %s %s' % (fcp, scp, options)
   command = '%s %s' % (cutechess_cli_path, cutechess_args)

   print("Running game {}/{}   \r".format(i+1,ngames), end='')
         
   process = Popen(command, shell = True, stdout = PIPE)
   output = process.communicate()[0]
   if process.returncode != 0:
       sys.stderr.write('failed to execute command: %s\n' % command)
       return None

   result = -1
   for line in output.decode("utf-8").splitlines():
       if line.startswith('Finished game'):
           if line.find(": 1-0") != -1:
               result = i % 2
           elif line.find(": 0-1") != -1:
               result = (i % 2) ^ 1
           elif line.find(": 1/2-1/2") != -1:
               result = 2
           else:
               sys.stderr.write('the game did not terminate properly\n')
               return None
           break

   if result == 0:
       return 1
   elif result == 1:
       return -1
   elif result == 2:
       return 0    

def run(ngames,threads,fcp,scp):

    results = Parallel(n_jobs=threads)(delayed(run_one)(i,fcp,scp,ngames) for i in range(ngames))

    l = list(results) 
    draws = l.count(0)
    wins = l.count(1)
    losses = l.count(-1)
    mu = max(0.00001,wins + draws/2)/ngames # avoid div by zero
    elo = -math.log(1.0/mu-1.0)*400.0/math.log(10.0);
    los = .5 + .5 * math.erf((wins-losses)/math.sqrt(2.0*(wins+losses)));

    print("\nwins   {}".format(wins))
    print("draws  {}".format(draws))
    print("losses {}".format(losses))
    print(colored("elo    {}".format(elo), 'red' if elo<0 else 'green' if elo > 15 else 'yellow'))
    print("LOS    {}".format(los))

    return sum(results),los,elo

def obj_func_one(x):
   
    print("\nCurrent params {}".format([int(xx) for xx in x]))
   
    # configure engine
    fcp = engine
    scp = opponents[random.getrandbits(16) % len(opponents)]    
    
    for i in range(len(params)):
       option = engine_param_cmd.format(name = params[i][0], value = int(x[i]))
       fcp += option    
    
    return ngames - run(ngames,threads,fcp,scp)[0]

def obj_func_many(x):
    res = [None]*len(x)
    for k in range(len(x)):
       res[k] = obj_func_one(x[k])
    return res

def PSO():
    # Set-up hyperparameters
    options = {'c1': 0.5, 'c2': 0.3, 'w':0.9}
    # Call instance of PSO
    optimizer = ps.single.GlobalBestPSO(n_particles=15, dimensions=len(params), options=options, bounds=([params[i][1] for i in range(len(params))], [params[i][2] for i in range(len(params))]))
    # Perform optimization
    best_cost, best_pos = optimizer.optimize(obj_func_many, iters=1000)
    plot_cost_history(optimizer.cost_history)
    plt.show()    

def main(argv = None):
    if argv is None:
        argv = sys.argv[1:]
    PSO()

if __name__ == "__main__":
    sys.exit(main())
Look at this very dummy example how PSO can be effective even with 100% noisy function

Code: Select all

#!/usr/bin/python
# -*- coding: utf-8 -*-

from subprocess import Popen, PIPE
import sys
import platform
import random
import math
import pyswarms as ps
from pyswarms.utils.plotters import plot_cost_history
from random import randrange
import matplotlib.pyplot as plt


# parameters to optimize
params = [
  ["staticNullMoveMaxDepth0", 0, 15, 6],
  ["staticNullMoveMaxDepth1", 0, 15, 6],
  ["staticNullMoveDepthCoeff0", 0, 300, 80],
  ["staticNullMoveDepthCoeff1", 0, 300, 80],
  ["staticNullMoveDepthInit0", 0, 300, 0],
  ["staticNullMoveDepthInit1", 0, 300, 0],
]

def obj_func_one_fake(x):
    return sum([ (x[i]-params[i][2]/2)*(x[i]-params[i][2]/2) + randrange(-100,1000)*params[i][2]/100 for i in range(len(x))])

def obj_func_many(x):
    res = [None]*len(x)
    for k in range(len(x)):
       res[k] = obj_func_one_fake(x[k])
    return res

def PSO():
    # Set-up hyperparameters
    options = {'c1': 0.5, 'c2': 0.3, 'w':0.9}
    # Call instance of PSO
    optimizer = ps.single.GlobalBestPSO(n_particles=15, dimensions=len(params), options=options, bounds=([params[i][1] for i in range(len(params))], [params[i][2] for i in range(len(params))]))
    # Perform optimization
    best_cost, best_pos = optimizer.optimize(obj_func_many, iters=1500)

    plot_cost_history(optimizer.cost_history)
    plt.show()

def main(argv = None):
    if argv is None:
        argv = sys.argv[1:]
    PSO()

if __name__ == "__main__":
    sys.exit(main())
Damir
Posts: 2801
Joined: Mon Feb 11, 2008 3:53 pm
Location: Denmark
Full name: Damir Desevac

Re: Looking for uci engines with piece value options shown

Post by Damir »

xr_a_y wrote: Sat Jun 27, 2020 7:35 pm
BrendanJNorman wrote: Sat Jun 27, 2020 6:02 pm
xr_a_y wrote: Sat Jun 27, 2020 12:36 pm
xr_a_y wrote: Sat Jun 27, 2020 11:02 am
xr_a_y wrote: Sat Jun 27, 2020 9:27 am
Ferdy wrote: Sat Jun 27, 2020 1:45 am
xr_a_y wrote: Fri Jun 26, 2020 5:24 pm I'd be glad to release a Minic version for that if you're interesting
I am interested, preferably an option name without space. Also allow the min/max to have more distance from default value.

Example:
option name KnightValue type spin default 300 min 100 max 500
You can use Minic 2.38 unofficial release. All pieces can go from 0 to 2000.
Here : https://github.com/tryingsomestuff/Mini ... ter/Minic2

If that work, please send me your optimized setting ! :wink:
Oups mistake let me patch that. Wont be long
Done, you can now indeed take 2.38...
I messed around a bit with Minic. I like it a lot.

The playing style and move/plan choices are unusual, but strong. Sort of like how Thinker used to be.

I wonder how many eval settings you could possibly open up (King Safety, Outposts, Pawn Structure etc) and if you could implement a NPS limit in UCI options for creating weaker personalities.

A lot of us would appreciate another interesting engine to spar against.
I can open all search and eval settings if someone is interesting ... but that will be a lot of parameters. That's why I have chosen to expose only those "style parameters" in the 2.37 release (and pieces values for this experience in 2.38). Let me know if you wanna play with something. More or less everything here (https://github.com/tryingsomestuff/Mini ... Config.cpp) and there (https://github.com/tryingsomestuff/Mini ... Config.cpp) can become a GUI parameter ...

About weaker personality, there is already UCI_LimitStrength / UCI_Elo available or my own "Level" option. Those are using a limited depth search, disable some eval feature and add randomness in move choice.
I would really much love to see all search and eval setting in your engine. If you can make publicly avilable it would be great