Set engine strength using Elo value

Remove the Strength preferences setting.

Add a new left drawer menu item to set the current engine strength in
terms of Elo. Supported for all engines that implement the standard
UCI options UCI_LimitStrength and UCI_Elo.

Elo settings are remembered individually for each engine.
This commit is contained in:
Peter Osterlund 2020-04-19 22:13:33 +02:00
parent 33dfafd18c
commit 7c75c83859
32 changed files with 409 additions and 232 deletions

View File

@ -48,6 +48,7 @@ import org.petero.droidfish.activities.util.PGNFile;
import org.petero.droidfish.activities.util.PGNFile.GameInfo;
import org.petero.droidfish.activities.Preferences;
import org.petero.droidfish.book.BookOptions;
import org.petero.droidfish.engine.DroidComputerPlayer.EloData;
import org.petero.droidfish.engine.EngineUtil;
import org.petero.droidfish.engine.UCIOptions;
import org.petero.droidfish.gamelogic.DroidChessController;
@ -136,6 +137,7 @@ import android.view.View.OnTouchListener;
import android.view.WindowManager;
import android.webkit.WebView;
import android.widget.ArrayAdapter;
import android.widget.CheckBox;
import android.widget.EditText;
import android.widget.ImageButton;
import android.widget.ImageView.ScaleType;
@ -1094,8 +1096,7 @@ public class DroidFish extends Activity
mEcoHints = getIntSetting("ecoHints", ECO_HINTS_AUTO);
String engine = settings.getString("engine", "stockfish");
int strength = settings.getInt("strength", 1000);
setEngineStrength(engine, strength);
setEngine(engine);
mPonderMode = settings.getBoolean("ponderMode", false);
if (!mPonderMode)
@ -1308,16 +1309,16 @@ public class DroidFish extends Activity
getWindow().clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
}
private void setEngineStrength(String engine, int strength) {
private void setEngine(String engine) {
if (!storageAvailable()) {
if (!"stockfish".equals(engine) && !"cuckoochess".equals(engine))
engine = "stockfish";
}
ctrl.setEngineStrength(engine, strength);
setEngineTitle(engine, strength);
ctrl.setEngine(engine);
setEngineTitle(engine, ctrl.eloData().getEloToUse());
}
private void setEngineTitle(String engine, int strength) {
private void setEngineTitle(String engine, int elo) {
String eName = "";
if (EngineUtil.isOpenExchangeEngine(engine)) {
String engineFileName = new File(engine).getName();
@ -1336,10 +1337,10 @@ public class DroidFish extends Activity
eName = getString("cuckoochess".equals(engine) ?
R.string.cuckoochess_engine :
R.string.stockfish_engine);
boolean analysis = (ctrl != null) && ctrl.analysisMode();
if ((strength < 1000) && !analysis)
eName = String.format(Locale.US, "%s: %d%%", eName, strength / 10);
}
if (ctrl != null && !ctrl.analysisMode())
if (elo != Integer.MAX_VALUE)
eName = String.format(Locale.US, "%s: %d", eName, elo);
engineTitleText.setText(eName);
}
@ -1363,10 +1364,9 @@ public class DroidFish extends Activity
}
@Override
public void updateEngineTitle() {
public void updateEngineTitle(int elo) {
String engine = settings.getString("engine", "stockfish");
int strength = settings.getInt("strength", 1000);
setEngineTitle(engine, strength);
setEngineTitle(engine, elo);
}
@Override
@ -1433,31 +1433,34 @@ public class DroidFish extends Activity
}
private class DrawerItem {
int id;
int itemId; // Item string resource id
DrawerItemId id;
private int resId; // Item string resource id
DrawerItem(int id, int itemId) {
DrawerItem(DrawerItemId id, int resId) {
this.id = id;
this.itemId = itemId;
this.resId = resId;
}
@Override
public String toString() {
return getString(itemId);
return getString(resId);
}
}
static private final int ITEM_NEW_GAME = 0;
static private final int ITEM_EDIT_BOARD = 1;
static private final int ITEM_SETTINGS = 2;
static private final int ITEM_FILE_MENU = 3;
static private final int ITEM_RESIGN = 4;
static private final int ITEM_FORCE_MOVE = 5;
static private final int ITEM_DRAW = 6;
static private final int ITEM_SELECT_BOOK = 7;
static private final int ITEM_MANAGE_ENGINES = 8;
static private final int ITEM_SET_COLOR_THEME = 9;
static private final int ITEM_ABOUT = 10;
private enum DrawerItemId {
NEW_GAME,
SET_STRENGTH,
EDIT_BOARD,
SETTINGS,
FILE_MENU,
RESIGN,
FORCE_MOVE,
DRAW,
SELECT_BOOK,
MANAGE_ENGINES,
SET_COLOR_THEME,
ABOUT,
}
/** Initialize the drawer part of the user interface. */
private void initDrawers() {
@ -1466,14 +1469,15 @@ public class DroidFish extends Activity
rightDrawer = findViewById(R.id.right_drawer);
final DrawerItem[] leftItems = new DrawerItem[] {
new DrawerItem(ITEM_NEW_GAME, R.string.option_new_game),
new DrawerItem(ITEM_EDIT_BOARD, R.string.option_edit_board),
new DrawerItem(ITEM_FILE_MENU, R.string.option_file),
new DrawerItem(ITEM_SELECT_BOOK, R.string.option_select_book),
new DrawerItem(ITEM_MANAGE_ENGINES, R.string.option_manage_engines),
new DrawerItem(ITEM_SET_COLOR_THEME, R.string.option_color_theme),
new DrawerItem(ITEM_SETTINGS, R.string.option_settings),
new DrawerItem(ITEM_ABOUT, R.string.option_about)
new DrawerItem(DrawerItemId.NEW_GAME, R.string.option_new_game),
new DrawerItem(DrawerItemId.SET_STRENGTH, R.string.set_engine_strength),
new DrawerItem(DrawerItemId.EDIT_BOARD, R.string.option_edit_board),
new DrawerItem(DrawerItemId.FILE_MENU, R.string.option_file),
new DrawerItem(DrawerItemId.SELECT_BOOK, R.string.option_select_book),
new DrawerItem(DrawerItemId.MANAGE_ENGINES, R.string.option_manage_engines),
new DrawerItem(DrawerItemId.SET_COLOR_THEME, R.string.option_color_theme),
new DrawerItem(DrawerItemId.SETTINGS, R.string.option_settings),
new DrawerItem(DrawerItemId.ABOUT, R.string.option_about),
};
leftDrawer.setAdapter(new ArrayAdapter<>(this,
R.layout.drawer_list_item,
@ -1484,9 +1488,9 @@ public class DroidFish extends Activity
});
final DrawerItem[] rightItems = new DrawerItem[] {
new DrawerItem(ITEM_RESIGN, R.string.option_resign_game),
new DrawerItem(ITEM_FORCE_MOVE, R.string.option_force_computer_move),
new DrawerItem(ITEM_DRAW, R.string.option_draw)
new DrawerItem(DrawerItemId.RESIGN, R.string.option_resign_game),
new DrawerItem(DrawerItemId.FORCE_MOVE, R.string.option_force_computer_move),
new DrawerItem(DrawerItemId.DRAW, R.string.option_draw),
};
rightDrawer.setAdapter(new ArrayAdapter<>(this,
R.layout.drawer_list_item,
@ -1504,7 +1508,7 @@ public class DroidFish extends Activity
}
/** React to a selection in the left/right drawers. */
private void handleDrawerSelection(int itemId) {
private void handleDrawerSelection(DrawerItemId id) {
drawerLayout.closeDrawer(Gravity.LEFT);
drawerLayout.closeDrawer(Gravity.RIGHT);
leftDrawer.clearChoices();
@ -1512,30 +1516,33 @@ public class DroidFish extends Activity
setAutoMode(AutoMode.OFF);
switch (itemId) {
case ITEM_NEW_GAME:
switch (id) {
case NEW_GAME:
showDialog(NEW_GAME_DIALOG);
break;
case ITEM_EDIT_BOARD:
case SET_STRENGTH:
reShowDialog(SET_STRENGTH_DIALOG);
break;
case EDIT_BOARD:
startEditBoard(ctrl.getFEN());
break;
case ITEM_SETTINGS: {
case SETTINGS: {
Intent i = new Intent(DroidFish.this, Preferences.class);
startActivityForResult(i, RESULT_SETTINGS);
break;
}
case ITEM_FILE_MENU:
case FILE_MENU:
if (storageAvailable())
reShowDialog(FILE_MENU_DIALOG);
break;
case ITEM_RESIGN:
case RESIGN:
if (ctrl.humansTurn())
ctrl.resignGame();
break;
case ITEM_FORCE_MOVE:
case FORCE_MOVE:
ctrl.stopSearch();
break;
case ITEM_DRAW:
case DRAW:
if (ctrl.humansTurn()) {
if (ctrl.claimDrawIfPossible())
ctrl.stopPonder();
@ -1543,20 +1550,20 @@ public class DroidFish extends Activity
DroidFishApp.toast(R.string.offer_draw, Toast.LENGTH_SHORT);
}
break;
case ITEM_SELECT_BOOK:
case SELECT_BOOK:
if (storageAvailable())
reShowDialog(SELECT_BOOK_DIALOG);
break;
case ITEM_MANAGE_ENGINES:
case MANAGE_ENGINES:
if (storageAvailable())
reShowDialog(MANAGE_ENGINES_DIALOG);
else
reShowDialog(SELECT_ENGINE_DIALOG_NOMANAGE);
break;
case ITEM_SET_COLOR_THEME:
case SET_COLOR_THEME:
showDialog(SET_COLOR_THEME_DIALOG);
break;
case ITEM_ABOUT:
case ABOUT:
showDialog(ABOUT_DIALOG);
break;
}
@ -2025,6 +2032,7 @@ public class DroidFish extends Activity
static private final int DELETE_NETWORK_ENGINE_DIALOG = 25;
static private final int CLIPBOARD_DIALOG = 26;
static private final int SELECT_FEN_FILE_DIALOG = 27;
static private final int SET_STRENGTH_DIALOG = 28;
/** Remove and show a dialog. */
void reShowDialog(int id) {
@ -2036,6 +2044,7 @@ public class DroidFish extends Activity
protected Dialog onCreateDialog(int id) {
switch (id) {
case NEW_GAME_DIALOG: return newGameDialog();
case SET_STRENGTH_DIALOG: return setStrengthDialog();
case PROMOTE_DIALOG: return promoteDialog();
case BOARD_MENU_DIALOG: return boardMenuDialog();
case FILE_MENU_DIALOG: return fileMenuDialog();
@ -2076,6 +2085,106 @@ public class DroidFish extends Activity
return builder.create();
}
private Dialog setStrengthDialog() {
EloStrengthSetter m = new EloStrengthSetter();
return m.getDialog();
}
/** Handle user interface to set engine strength. */
private class EloStrengthSetter {
private final EloData eloData = ctrl.eloData();
private CheckBox checkBox;
private TextView eloLabel;
private EditText editTxt;
private SeekBar seekBar;
private int progressToElo(int p) {
return eloData.minElo + p;
}
private int eloToProgress(int elo) {
return elo - eloData.minElo;
}
private void updateText(int elo) {
String txt = Integer.valueOf(elo).toString();
if (!txt.equals(editTxt.getText().toString())) {
editTxt.setText(txt);
editTxt.setSelection(txt.length());
}
}
private void updateEnabledState(boolean enabled) {
eloLabel.setEnabled(enabled);
editTxt.setEnabled(enabled);
seekBar.setEnabled(enabled);
}
public Dialog getDialog() {
if (!eloData.canChangeStrength()) {
DroidFishApp.toast(R.string.engine_cannot_reduce_strength, Toast.LENGTH_LONG);
return null;
}
AlertDialog.Builder builder = new AlertDialog.Builder(DroidFish.this);
builder.setTitle(R.string.set_engine_strength);
View content = View.inflate(DroidFish.this, R.layout.set_strength, null);
builder.setView(content);
checkBox = content.findViewById(R.id.strength_checkbox);
eloLabel = content.findViewById(R.id.strength_elolabel);
editTxt = content.findViewById(R.id.strength_edittext);
seekBar = content.findViewById(R.id.strength_seekbar);
checkBox.setChecked(eloData.limitStrength);
seekBar.setMax(eloToProgress(eloData.maxElo));
seekBar.setProgress(eloToProgress(eloData.elo));
updateText(eloData.elo);
updateEnabledState(eloData.limitStrength);
checkBox.setOnCheckedChangeListener((button, isChecked) -> {
updateEnabledState(isChecked);
});
seekBar.setOnSeekBarChangeListener(new OnSeekBarChangeListener() {
@Override
public void onStopTrackingTouch(SeekBar seekBar) { }
@Override
public void onStartTrackingTouch(SeekBar seekBar) { }
@Override
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
updateText(progressToElo(progress));
}
});
editTxt.addTextChangedListener(new TextWatcher() {
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
String txt = editTxt.getText().toString();
try {
int elo = Integer.parseInt(txt);
int p = eloToProgress(elo);
if (p != seekBar.getProgress())
seekBar.setProgress(p);
updateText(progressToElo(p));
} catch (NumberFormatException ignore) {
}
}
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) { }
@Override
public void afterTextChanged(Editable s) { }
});
builder.setNegativeButton(R.string.cancel, null);
builder.setPositiveButton(android.R.string.ok, (dialog, which) -> {
boolean limitStrength = checkBox.isChecked();
int elo = progressToElo(seekBar.getProgress());
ctrl.setStrength(limitStrength, elo);
});
return builder.create();
}
}
private void startNewGame(int type) {
if (type != 2) {
int gameModeType = (type == 0) ? GameMode.PLAYER_WHITE : GameMode.PLAYER_BLACK;
@ -2091,7 +2200,7 @@ public class DroidFish extends Activity
ctrl.newGame(gameMode, tcData);
ctrl.startGame();
setBoardFlip(true);
updateEngineTitle();
updateEngineTitle(ctrl.eloData().getEloToUse()); // Game mode affects Elo setting
}
private Dialog promoteDialog() {
@ -2502,9 +2611,8 @@ public class DroidFish extends Activity
editor.putString("engine", engine);
editor.apply();
dialog.dismiss();
int strength = settings.getInt("strength", 1000);
setEngineOptions(false);
setEngineStrength(engine, strength);
setEngine(engine);
});
builder.setOnCancelListener(dialog -> {
if (!abortOnCancel)
@ -2845,8 +2953,7 @@ public class DroidFish extends Activity
int numPV = this.numPV;
final int maxPV = ctrl.maxPV();
if (gameMode.analysisMode()) {
numPV = Math.min(numPV, maxPV);
numPV = Math.max(numPV, 1);
numPV = Math.min(Math.max(numPV, 1), maxPV);
if (maxPV > 1) {
lst.add(getString(R.string.num_variations)); actions.add(MULTIPV_SET);
}
@ -2988,11 +3095,9 @@ public class DroidFish extends Activity
seekBar.setOnSeekBarChangeListener(new OnSeekBarChangeListener() {
@Override
public void onStopTrackingTouch(SeekBar seekBar) {
}
public void onStopTrackingTouch(SeekBar seekBar) { }
@Override
public void onStartTrackingTouch(SeekBar seekBar) {
}
public void onStartTrackingTouch(SeekBar seekBar) { }
@Override
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
int nPV = progressToNumPV(progress, maxPV);
@ -3014,11 +3119,9 @@ public class DroidFish extends Activity
}
}
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
}
public void beforeTextChanged(CharSequence s, int start, int count, int after) { }
@Override
public void afterTextChanged(Editable s) {
}
public void afterTextChanged(Editable s) { }
});
builder.setNegativeButton(R.string.cancel, null);
@ -3349,9 +3452,8 @@ public class DroidFish extends Activity
editor.putString("engine", engine);
editor.apply();
dialog.dismiss();
int strength = settings.getInt("strength", 1000);
setEngineOptions(false);
setEngineStrength(engine, strength);
setEngine(engine);
}
dialog.cancel();
reShowDialog(NETWORK_ENGINE_DIALOG);
@ -3553,22 +3655,22 @@ public class DroidFish extends Activity
@Override
public void reportInvalidMove(Move m) {
String msg = String.format(Locale.US, "%s %s-%s",
getString(R.string.invalid_move),
TextIO.squareToString(m.from), TextIO.squareToString(m.to));
getString(R.string.invalid_move),
TextIO.squareToString(m.from), TextIO.squareToString(m.to));
DroidFishApp.toast(msg, Toast.LENGTH_SHORT);
}
@Override
public void reportEngineName(String engine) {
String msg = String.format(Locale.US, "%s: %s",
getString(R.string.engine), engine);
getString(R.string.engine), engine);
DroidFishApp.toast(msg, Toast.LENGTH_SHORT);
}
@Override
public void reportEngineError(String errMsg) {
String msg = String.format(Locale.US, "%s: %s",
getString(R.string.engine_error), errMsg);
getString(R.string.engine_error), errMsg);
DroidFishApp.toast(msg, Toast.LENGTH_LONG);
}

View File

@ -86,7 +86,7 @@ public interface GUIInterface {
void setRemainingTime(int wTime, int bTime, int nextUpdate);
/** Update engine title text. */
void updateEngineTitle();
void updateEngineTitle(int elo);
/** Update title with the material difference. */
void updateMaterialDifferenceTitle(Util.MaterialDiff diff);

View File

@ -91,9 +91,7 @@ public class SeekBarPreference extends Preference implements OnSeekBarChangeList
builder.setView(selectPercentageBinding.getRoot());
String title = "";
String key = getKey();
if (key.equals("strength")) {
title = getContext().getString(R.string.edit_strength);
} else if (key.equals("bookRandom")) {
if (key.equals("bookRandom")) {
title = getContext().getString(R.string.edit_randomization);
}
builder.setTitle(title);

View File

@ -109,7 +109,7 @@ public class DroidComputerPlayer {
int movesToGo; // Number of moves to next time control
String engine; // Engine name (identifier)
int strength; // Engine strength setting (0 - 1000)
int elo; // Engine UCI_Elo setting, or Integer.MAX_VALUE for full strength
int numPV; // Number of PV lines to compute
boolean ponderEnabled; // True if pondering enabled, for engine time management
@ -145,14 +145,14 @@ public class DroidComputerPlayer {
* @param ponderEnabled True if pondering is enabled in the GUI. Can affect time management.
* @param ponderMove Move to ponder, or null for non-ponder search.
* @param engine Chess engine to use for searching.
* @param strength Engine strength setting.
* @param elo Engine Elo strength setting.
*/
public static SearchRequest searchRequest(int id, long now,
Position prevPos, ArrayList<Move> mList,
Position currPos, boolean drawOffer,
int wTime, int bTime, int wInc, int bInc, int movesToGo,
boolean ponderEnabled, Move ponderMove,
String engine, int strength) {
String engine, int elo) {
SearchRequest sr = new SearchRequest();
sr.searchId = id;
sr.startTime = now;
@ -168,7 +168,7 @@ public class DroidComputerPlayer {
sr.bInc = bInc;
sr.movesToGo = movesToGo;
sr.engine = engine;
sr.strength = strength;
sr.elo = elo;
sr.numPV = 1;
sr.ponderEnabled = ponderEnabled;
sr.ponderMove = ponderMove;
@ -204,7 +204,7 @@ public class DroidComputerPlayer {
sr.isAnalyze = true;
sr.wTime = sr.bTime = sr.wInc = sr.bInc = sr.movesToGo = 0;
sr.engine = engine;
sr.strength = 1000;
sr.elo = Integer.MAX_VALUE;
sr.numPV = numPV;
sr.ponderEnabled = false;
sr.ponderMove = null;
@ -310,6 +310,56 @@ public class DroidComputerPlayer {
}
}
public static class EloData {
public boolean limitStrength = false; // True if engine strength reduction is enabled
public int elo = 0; // Current strength setting
public int minElo = 0; // Smallest possible Elo value
public int maxElo = 0; // Largest possible Elo value
/** Return true if engine is able to change the playing strength. */
public boolean canChangeStrength() {
return minElo < maxElo;
}
/** Get current Elo setting.
* Return MAX_VALUE if reduced strength not enabled or not supported. */
public int getEloToUse() {
if (canChangeStrength() && limitStrength)
return elo;
return Integer.MAX_VALUE;
}
}
/** Return engine Elo strength data. */
public synchronized EloData getEloData() {
EloData ret = new EloData();
UCIEngine uci = uciEngine;
if (uci != null) {
UCIOptions opts = uci.getUCIOptions();
UCIOptions.OptionBase lsOpt = opts.getOption("UCI_LimitStrength");
UCIOptions.OptionBase eloOpt = opts.getOption("UCI_Elo");
if (lsOpt instanceof UCIOptions.CheckOption &&
eloOpt instanceof UCIOptions.SpinOption) {
ret.limitStrength = ((UCIOptions.CheckOption)lsOpt).value;
UCIOptions.SpinOption eloSpin = (UCIOptions.SpinOption)eloOpt;
ret.elo = eloSpin.value;
ret.minElo = eloSpin.minValue;
ret.maxElo = eloSpin.maxValue;
}
}
return ret;
}
/** Set engine UCI strength parameters. */
public void setStrength(int elo) {
Map<String,String> opts = new TreeMap<>();
boolean limitStrength = elo != Integer.MAX_VALUE;
opts.put("UCI_LimitStrength", limitStrength ? "true" : "false");
if (limitStrength)
opts.put("UCI_Elo", String.valueOf(elo));
setEngineUCIOptions(opts);
}
/** Return all book moves, both as a formatted string and as a list of moves. */
public final Pair<String, ArrayList<Move>> getBookHints(Position pos, boolean localized) {
return book.getAllBookMoves(pos, localized);
@ -571,7 +621,7 @@ public class DroidComputerPlayer {
// Set strength and MultiPV parameters
clearInfo();
uciEngine.setStrength(searchRequest.strength);
uciEngine.setEloStrength(searchRequest.elo);
if (maxPV > 1) {
int num = Math.min(maxPV, searchRequest.numPV);
uciEngine.setOption("MultiPV", num);
@ -721,6 +771,7 @@ public class DroidComputerPlayer {
uci.writeLineToEngine("ucinewgame");
uci.writeLineToEngine("isready");
engineState.setState(MainState.WAIT_READY);
listener.notifyEngineInitialized();
}
break;
}

View File

@ -236,10 +236,6 @@ public class ExternalEngine extends UCIEngineBase {
return true;
}
@Override
public void setStrength(int strength) {
}
@Override
public String readLineFromEngine(int timeoutMillis) {
String ret = inLines.readLine(timeoutMillis);

View File

@ -46,9 +46,9 @@ public class InternalStockFish extends ExternalEngine {
}
@Override
protected boolean configurableOption(String name) {
protected boolean editableOption(String name) {
name = name.toLowerCase(Locale.US);
if (!super.configurableOption(name))
if (!super.editableOption(name))
return false;
if (name.equals("skill level") || name.equals("write debug log") ||
name.equals("write search log"))
@ -56,11 +56,6 @@ public class InternalStockFish extends ExternalEngine {
return true;
}
@Override
public final void setStrength(int strength) {
setOption("Skill Level", strength/50);
}
private long readCheckSum(File f) {
try (InputStream is = new FileInputStream(f);
DataInputStream dis = new DataInputStream(is)) {

View File

@ -228,10 +228,6 @@ public class NetworkEngine extends UCIEngineBase {
return true;
}
@Override
public void setStrength(int strength) {
}
@Override
public String readLineFromEngine(int timeoutMillis) {
String ret = engineToGui.readLine(timeoutMillis);

View File

@ -67,8 +67,9 @@ public interface UCIEngine {
/** Write a line to the engine. \n will be added automatically. */
void writeLineToEngine(String data);
/** Set the engine strength, allowed values 0 - 1000. */
void setStrength(int strength);
/** Temporarily set the engine Elo strength to use for the next search.
* Integer.MAX_VALUE means full strength. */
void setEloStrength(int elo);
/** Set an engine integer option. */
void setOption(String name, int value);

View File

@ -27,6 +27,7 @@ import java.util.Arrays;
import java.util.Locale;
import java.util.Map;
import java.util.Properties;
import java.util.TreeMap;
import org.petero.droidfish.EngineOptions;
import org.petero.droidfish.engine.cuckoochess.CuckooChessEngine;
@ -80,14 +81,15 @@ public abstract class UCIEngineBase implements UCIEngine {
iniOptions.load(is);
} catch (IOException ignore) {
}
Map<String,String> opts = new TreeMap<>();
for (Map.Entry<Object,Object> ent : iniOptions.entrySet()) {
if (ent.getKey() instanceof String && ent.getValue() instanceof String) {
String key = ((String)ent.getKey()).toLowerCase(Locale.US);
String value = (String)ent.getValue();
if (configurableOption(key))
setOption(key, value);
opts.put(key, value);
}
}
setUCIOptions(opts);
}
@Override
@ -125,12 +127,11 @@ public abstract class UCIEngineBase implements UCIEngine {
/** Get engine UCI options file. */
protected abstract File getOptionsFile();
/** Return true if the UCI option can be changed by the user. */
protected boolean configurableOption(String name) {
/** Return true if the UCI option can be edited in the "Engine Options" dialog. */
protected boolean editableOption(String name) {
name = name.toLowerCase(Locale.US);
if (name.startsWith("uci_")) {
String[] allowed = { "uci_limitstrength", "uci_elo" };
return Arrays.asList(allowed).contains(name);
return false;
} else {
String[] ignored = { "hash", "ponder", "multipv",
"gaviotatbpath", "syzygypath" };
@ -138,6 +139,17 @@ public abstract class UCIEngineBase implements UCIEngine {
}
}
/** Return true if the UCI option can be modified by the user, either directly
* from the "Engine Options" dialog or indirectly, for example from the
* "Set Engine Strength" dialog. */
private boolean configurableOption(String name) {
if (editableOption(name))
return true;
name = name.toLowerCase(Locale.US);
String[] configurable = { "uci_limitstrength", "uci_elo" };
return Arrays.asList(configurable).contains(name);
}
@Override
public void shutDown() {
if (processAlive) {
@ -221,7 +233,7 @@ public abstract class UCIEngineBase implements UCIEngine {
int maxV = Integer.parseInt(maxVal);
if (minV <= defV && defV <= maxV)
option = new UCIOptions.SpinOption(name, minV, maxV, defV);
} catch (NumberFormatException ex) {
} catch (NumberFormatException ignore) {
}
}
} else if (type.equals("combo")) {
@ -241,8 +253,7 @@ public abstract class UCIEngineBase implements UCIEngine {
}
if (option != null) {
if (!configurableOption(name))
option.visible = false;
option.visible = editableOption(name);
options.addOption(option);
}
return option;
@ -253,6 +264,21 @@ public abstract class UCIEngineBase implements UCIEngine {
return options.contains(optName);
}
@Override
public final void setEloStrength(int elo) {
String lsName = "UCI_LimitStrength";
boolean limit = elo != Integer.MAX_VALUE;
UCIOptions.OptionBase o = options.getOption(lsName);
if (o instanceof UCIOptions.CheckOption) {
// Don't use setOption() since this value reflects current search parameters,
// not user specified strength settings, so should not be saved in .ini file.
writeLineToEngine(String.format(Locale.US, "setoption name %s value %s",
lsName, limit ? "true" : "false"));
}
if (limit)
setOption("UCI_Elo", elo);
}
@Override
public final void setOption(String name, int value) {
setOption(name, String.format(Locale.US, "%d", value));

View File

@ -41,7 +41,7 @@ public class UCIOptions implements Serializable, Cloneable {
private static final long serialVersionUID = 1L;
public String name;
public Type type;
public boolean visible = true;
public boolean visible = true; // True if visible in "Engine Options" dialog
@Override
public OptionBase clone() throws CloneNotSupportedException {

View File

@ -74,9 +74,9 @@ public class CuckooChessEngine extends UCIEngineBase {
}
@Override
protected boolean configurableOption(String name) {
protected boolean editableOption(String name) {
name = name.toLowerCase(Locale.US);
if (!super.configurableOption(name))
if (!super.editableOption(name))
return false;
if (name.equals("strength"))
return false;
@ -88,11 +88,6 @@ public class CuckooChessEngine extends UCIEngineBase {
return true;
}
@Override
public final void setStrength(int strength) {
setOption("strength", strength);
}
private void mainLoop(LocalPipe is, LocalPipe os) {
String line;
while ((line = is.readLine()) != null) {

View File

@ -41,6 +41,7 @@ import org.petero.droidfish.book.BookOptions;
import org.petero.droidfish.book.EcoDb;
import org.petero.droidfish.engine.DroidComputerPlayer;
import org.petero.droidfish.engine.UCIOptions;
import org.petero.droidfish.engine.DroidComputerPlayer.EloData;
import org.petero.droidfish.engine.DroidComputerPlayer.SearchRequest;
import org.petero.droidfish.engine.DroidComputerPlayer.SearchType;
import org.petero.droidfish.gamelogic.Game.CommentInfo;
@ -60,7 +61,6 @@ public class DroidChessController {
private PGNOptions pgnOptions;
private String engine = "";
private int strength = 1000;
private int numPV = 1;
private SearchListener listener;
@ -146,13 +146,15 @@ public class DroidChessController {
if (!gameMode.playerWhite() || !gameMode.playerBlack())
setPlayerNames(game); // If computer player involved, set player names
updateGameMode();
abortSearch();
updateComputeThreads();
gui.updateEngineTitle();
updateGUI();
gui.updateEngineTitle(getEloToUse()); // Game mode affects Elo setting
restartSearch();
}
}
private int getEloToUse() {
return eloData().getEloToUse();
}
public final GameMode getGameMode() {
return gameMode;
}
@ -179,30 +181,49 @@ public class DroidChessController {
engineOptions = options;
if (computerPlayer != null)
computerPlayer.setEngineOptions(engineOptions);
if (restart && (game != null)) {
abortSearch();
updateComputeThreads();
updateGUI();
}
if (restart)
restartSearch();
}
}
/** Set engine and engine strength. Restart computer thinking if appropriate.
* @param engine Name of engine.
* @param strength Engine strength, 0 - 1000. */
public final synchronized void setEngineStrength(String engine, int strength) {
boolean newEngine = !engine.equals(this.engine);
if (newEngine || (strength != this.strength)) {
this.engine = engine;
this.strength = strength;
if (game != null) {
abortSearch();
updateComputeThreads();
updateGUI();
}
private void restartSearch() {
if (game != null) {
abortSearch();
updateComputeThreads();
updateGUI();
}
}
/** Set engine. Restart computer thinking if appropriate. */
public final synchronized void setEngine(String engine) {
if (!engine.equals(this.engine)) {
this.engine = engine;
restartSearch();
}
}
/** Set engine strength. Restart computer thinking if appropriate. */
public final synchronized void setStrength(boolean limitStrength, int elo) {
EloData d = eloData();
int oldElo = d.getEloToUse();
d.limitStrength = limitStrength;
d.elo = elo;
int newElo = d.getEloToUse();
if (oldElo != newElo) {
if (computerPlayer != null)
computerPlayer.setStrength(newElo);
restartSearch();
gui.updateEngineTitle(newElo);
}
}
/** Return engine Elo strength data. */
public final synchronized EloData eloData() {
if (computerPlayer == null)
return new EloData();
return computerPlayer.getEloData();
}
/** Set engine UCI options. */
public final synchronized void setEngineUCIOptions(Map<String,String> uciOptions) {
if (computerPlayer != null)
@ -233,8 +254,7 @@ public class DroidChessController {
DataInputStream dis = new DataInputStream(bais)) {
game.readFromStream(dis, version);
game.tree.translateMoves();
} catch (IOException ignore) {
} catch (ChessParseError ignore) {
} catch (IOException|ChessParseError ignore) {
}
}
@ -383,9 +403,7 @@ public class DroidChessController {
if (humansTurn()) {
int varNo = game.tree.addMove("--", "", 0, "", "");
game.tree.goForward(varNo);
abortSearch();
updateComputeThreads();
updateGUI();
restartSearch();
gui.setSelection(-1);
}
}
@ -596,11 +614,8 @@ public class DroidChessController {
clampedNumPV = Math.max(clampedNumPV, 1);
boolean modified = clampedNumPV != this.numPV;
this.numPV = numPV;
if (modified) {
abortSearch();
updateComputeThreads();
updateGUI();
}
if (modified)
restartSearch();
}
/** Request computer player to make a move immediately. */
@ -757,7 +772,7 @@ public class DroidChessController {
} else if (currTime < 999950) {
statStrTmp.append(String.format(Locale.US, " t:%.1f", currTime / 1000.0));
} else {
statStrTmp.append(String.format(Locale.US, " t:%d", (int)((currTime + 500) / 1000)));
statStrTmp.append(String.format(Locale.US, " t:%d", (currTime + 500) / 1000));
}
statStrTmp.append(" n:");
appendWithPrefix(statStrTmp, currNodes);
@ -900,18 +915,25 @@ public class DroidChessController {
}
@Override
public void notifySearchResult(final int id, final String cmd, final Move ponder) {
public void notifySearchResult(int id, String cmd, Move ponder) {
new Thread(() -> gui.runOnUIThread(() -> makeComputerMove(id, cmd, ponder))).start();
}
@Override
public void notifyEngineName(final String engineName) {
public void notifyEngineName(String engineName) {
gui.runOnUIThread(() -> {
updatePlayerNames(engineName);
gui.reportEngineName(engineName);
});
}
@Override
public void notifyEngineInitialized() {
gui.runOnUIThread(() -> {
gui.updateEngineTitle(eloData().getEloToUse());
});
}
@Override
public void reportEngineError(final String errMsg) {
gui.runOnUIThread(() -> gui.reportEngineError(errMsg));
@ -998,7 +1020,7 @@ public class DroidChessController {
game.haveDrawOffer(),
wTime, bTime, wInc, bInc, movesToGo,
gui.ponderMode(), fPonderMove,
engine, strength);
engine, getEloToUse());
computerPlayer.queueSearchRequest(sr);
} else {
computerPlayer.queueStartEngine(searchId, engine);
@ -1033,8 +1055,9 @@ public class DroidChessController {
String engine = "Computer";
if (computerPlayer != null) {
engine = computerPlayer.getEngineName();
if (strength < 1000)
engine += String.format(Locale.US, " (%.1f%%)", strength * 0.1);
int elo = getEloToUse();
if (elo != Integer.MAX_VALUE)
engine += String.format(Locale.US, " (%d)", elo);
}
String player = gui.playerName();
String white = gameMode.playerWhite() ? player : engine;
@ -1045,8 +1068,9 @@ public class DroidChessController {
private synchronized void updatePlayerNames(String engineName) {
if (game != null) {
if (strength < 1000)
engineName += String.format(Locale.US, " (%.1f%%)", strength * 0.1);
int elo = getEloToUse();
if (elo != Integer.MAX_VALUE)
engineName += String.format(Locale.US, " (%d)", elo);
String white = gameMode.playerWhite() ? game.tree.white : engineName;
String black = gameMode.playerBlack() ? game.tree.black : engineName;
game.tree.setPlayerNames(white, black);

View File

@ -84,4 +84,7 @@ public interface SearchListener {
/** Report engine error. */
void reportEngineError(String errMsg);
/** Report that engine has been initialized. */
void notifyEngineInitialized();
}

View File

@ -0,0 +1,39 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<CheckBox
android:id="@+id/strength_checkbox"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginTop="10dp"
android:text="@string/limit_strength"/>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal">
<TextView
android:id="@+id/strength_elolabel"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="5dp"
android:text="Elo:" />
<EditText
android:id="@+id/strength_edittext"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1.0"
android:inputType="number" />
</LinearLayout>
<SeekBar
android:id="@+id/strength_seekbar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="10dp" />
</LinearLayout>

View File

@ -44,6 +44,7 @@
android:gravity="center"
android:textSize="14sp"
android:tag="title"
android:ellipsize="middle"
android:text="@string/app_name" />
<TextView

View File

@ -50,7 +50,6 @@
<string name="tools_menu">Прылады</string>
<string name="load_save_menu">Загрузіць/Захаваць партыю</string>
<string name="goto_move">Зрабіць ход</string>
<string name="edit_strength">Змяніць ровень складанасці</string>
<string name="edit_randomization">Змяніць рандамізацыю (выпадковасць)</string>
<string name="invalid_number_format">Няправільны фармат ліку</string>
<string name="side_to_move">Задаць кірунак ходу</string>
@ -273,8 +272,6 @@
<string name="prefs_playerNameFlip_title">Паварочваць дошку: гулец</string>
<string name="prefs_playerNameFlip_summary">Скарыстаць імя гульца для аўтаматычнага павароту дошкі</string>
<string name="prefs_engine_settings">Налады рухавічка</string>
<string name="prefs_strength_title">Сіла гульні</string>
<string name="prefs_strength_summary">Падтрымваецца толькі ўнутранымі рухавічкамі. Рэжым аналізу заўсёды скарыстае поўную сілу.</string>
<string name="prefs_ponderMode_title">Разважанне</string>
<string name="prefs_ponderMode_summary">Дазволіць рухавічку абдумванне падчас чакання ходу апанента. Падтрымваецца большасцю рухавічкоў.</string>
<string name="prefs_hash_title">Хэш-табліца</string>

View File

@ -50,7 +50,6 @@ Um gegebenenfalls Strom zu sparen, ist es empfehlenswert, dass Sie diese Paramet
<string name="tools_menu">Hilfsmittel</string>
<string name="load_save_menu">Partie laden/speichern</string>
<string name="goto_move">Gehe zu Zug</string>
<string name="edit_strength">Spielstärke ändern</string>
<string name="edit_randomization">Randomisierung ändern</string>
<string name="invalid_number_format">Ungültiges Zahlenformat</string>
<string name="side_to_move">Seite am Zug</string>
@ -273,8 +272,6 @@ Um gegebenenfalls Strom zu sparen, ist es empfehlenswert, dass Sie diese Paramet
<string name="prefs_playerNameFlip_title">Spielfeld nach Namen drehen</string>
<string name="prefs_playerNameFlip_summary">Spielfeld in Abhängigkeit vom Spielernamen drehen</string>
<string name="prefs_engine_settings">Engine-Einstellungen</string>
<string name="prefs_strength_title">Spielstärke</string>
<string name="prefs_strength_summary">Wird nur von internen Engines unterstützt. Im Analyse-Modus wird stets die volle Stärke verwendet.</string>
<string name="prefs_ponderMode_title">Vorausberechnung</string>
<string name="prefs_ponderMode_summary">Vorausberechnung von Zügen durch die Engine, wenn der Spieler am Zug ist (von den meisten Engines unterstützt)</string>
<string name="prefs_hash_title">Hash-Tabelle</string>

View File

@ -50,7 +50,6 @@ Si está usted utilizando la batería, se recomienda que cambie los ajustes para
<string name="tools_menu">Herramientas</string>
<string name="load_save_menu">Cargar/Guardar Partida</string>
<string name="goto_move">Ir a la jugada</string>
<string name="edit_strength">Editar Fuerza de juego</string>
<string name="edit_randomization">Editar Aleatoriedad</string>
<string name="invalid_number_format"> Formato de número no válido</string>
<string name="side_to_move">Turno de juego</string>
@ -273,8 +272,6 @@ Si está usted utilizando la batería, se recomienda que cambie los ajustes para
<string name="prefs_playerNameFlip_title">Invertir tablero : Jugador</string>
<string name="prefs_playerNameFlip_summary">Utilizar el nombre del jugador para invertir el tablero automáticamente</string>
<string name="prefs_engine_settings">Ajustes del Motor/Programa</string>
<string name="prefs_strength_title">Fuerza/Nivel de Juego</string>
<string name="prefs_strength_summary">Soportado únicamente por los motores internos. El modo análisis siempre utiliza fuerza máxima.</string>
<string name="prefs_ponderMode_title">Pensar siempre</string>
<string name="prefs_ponderMode_summary">Dejar calcular al motor mientras espera la jugada del oponente. Lo incorporan la mayoría de los motores.</string>
<string name="prefs_hash_title">Tablas Hash</string>

View File

@ -50,7 +50,6 @@ Lorsque que vous êtes sur batterie, il est recommandé de changer les paramètr
<string name="tools_menu">Outils</string>
<string name="load_save_menu">Charger/Sauvegarder une partie</string>
<string name="goto_move">Aller au coup</string>
<string name="edit_strength">Modifier le niveau</string>
<string name="edit_randomization">Modifier la randomisation</string>
<string name="invalid_number_format">Format du nombre non valide</string>
<string name="side_to_move">Camp au trait</string>
@ -273,8 +272,6 @@ Lorsque que vous êtes sur batterie, il est recommandé de changer les paramètr
<string name="prefs_playerNameFlip_title">Disposition de l\'échiquier: Joueur</string>
<string name="prefs_playerNameFlip_summary">Utiliser le nom du joueur pour disposer automatiquement l\'échiquier</string>
<string name="prefs_engine_settings">Paramètres du module</string>
<string name="prefs_strength_title">Niveau de jeu</string>
<string name="prefs_strength_summary">Supporté uniquement par les modules internes. Le mode Analyse utilisera toujours le niveau maximal.</string>
<string name="prefs_ponderMode_title">Réflexion permanente</string>
<string name="prefs_ponderMode_summary">Laisser le module actif lors du coup de l\'opposant. Supporté par la plupart des modules.</string>
<string name="prefs_hash_title">Table de hachage</string>

View File

@ -50,7 +50,6 @@ Se l\'alimentazione è a batteria, è consigliabile modificare le impostazioni p
<string name="tools_menu">Strumenti</string>
<string name="load_save_menu">Apri/Salva partita</string>
<string name="goto_move">Vai alla mossa</string>
<string name="edit_strength">Imposta la Forza del motore</string>
<string name="edit_randomization">Modifica la casualizzazione</string>
<string name="invalid_number_format">Formato del numero non valido</string>
<string name="side_to_move">Lato che muove</string>
@ -273,8 +272,6 @@ Se l\'alimentazione è a batteria, è consigliabile modificare le impostazioni p
<string name="prefs_playerNameFlip_title">Gira la scacchiera: Giocatore</string>
<string name="prefs_playerNameFlip_summary">Usa il nome del giocatore per girare la scacchiera automaticamente</string>
<string name="prefs_engine_settings">Impostazioni del motore</string>
<string name="prefs_strength_title">Forza di gioco</string>
<string name="prefs_strength_summary">Supportato solo dai motori interni. La modalità di analisi utilizza sempre la massima forza.</string>
<string name="prefs_ponderMode_title">Sto riflettendo</string>
<string name="prefs_ponderMode_summary">Lascia che il motore rifletta nell\'attesa della mossa dell\'avversario. Supportato dalla maggior parte dei motori.</string>
<string name="prefs_hash_title">Tabella hash</string>

View File

@ -54,7 +54,6 @@ DroidFish는 백그라운드에 실행 중인 상태에서 다음과 같이 설
<string name="tools_menu">도구</string>
<string name="load_save_menu">게임 저장/불러오기</string>
<string name="goto_move">이동으로 가기</string>
<string name="edit_strength">강도 조정</string>
<string name="edit_randomization">랜덤화 편집</string>
<string name="invalid_number_format">잘못된 숫자 형식</string>
<string name="side_to_move">이동할 사이드</string>
@ -277,8 +276,6 @@ DroidFish는 백그라운드에 실행 중인 상태에서 다음과 같이 설
<string name="prefs_playerNameFlip_title">플립 보드 : 플레이어</string>
<string name="prefs_playerNameFlip_summary">자동 플립 보드를 위해 플레이어 이름을 사용합니다</string>
<string name="prefs_engine_settings">엔진 설정</string>
<string name="prefs_strength_title">플레이 강도</string>
<string name="prefs_strength_summary">내장 엔진에서만 지원합니다. 분석 모드에서는 항상 최고 강도를 사용합니다.</string>
<string name="prefs_ponderMode_title">숙고하기</string>
<string name="prefs_ponderMode_summary">상대방의 이동을 기다리는 동안 엔진이 생각하도록 합니다. 대부분의 엔진에 의해 지원됩니다.</string>
<string name="prefs_hash_title">해시 테이블</string>

View File

@ -50,7 +50,6 @@ Als uw telefoon op batterij werkt is het aan te raden om deze instellingen te wi
<string name="tools_menu">Tools</string>
<string name="load_save_menu">Laad/Bewaar Partij</string>
<string name="goto_move">Ga naar zet</string>
<string name="edit_strength">Speelsterkte bewerken</string>
<string name="edit_randomization">Willekeurigheid bewerken</string>
<string name="invalid_number_format">Ongeldig nummer formaat</string>
<string name="side_to_move">Speler aan zet</string>
@ -273,8 +272,6 @@ Als uw telefoon op batterij werkt is het aan te raden om deze instellingen te wi
<string name="prefs_playerNameFlip_title">Draai bord:Speler</string>
<string name="prefs_playerNameFlip_summary">Draai bord automatisch op spelers naam</string>
<string name="prefs_engine_settings">Engine instellingen</string>
<string name="prefs_strength_title">Speel sterkte</string>
<string name="prefs_strength_summary">Alleen ondersteund door interne engines. Analyse gebruikt altijd de hoogste denksterkte.</string>
<string name="prefs_ponderMode_title">Peinzen</string>
<string name="prefs_ponderMode_summary">Laat de engine verder denken tijdens wachten op de zet van de tegenstander.</string>
<string name="prefs_hash_title">Hashtabel</string>

View File

@ -50,7 +50,6 @@ Jeśli pracujesz na baterii, zalecana jest zmiana ustawień, w celu oszczędzani
<string name="tools_menu">Narzędzia</string>
<string name="load_save_menu">Załaduj/zapisz partię</string>
<string name="goto_move">Idź do posunięcia</string>
<string name="edit_strength">Edytuj siłę gry</string>
<string name="edit_randomization">Zmień losowość</string>
<string name="invalid_number_format">Niepoprawny format liczby</string>
<string name="side_to_move">Strona na posunięciu</string>
@ -273,8 +272,6 @@ Jeśli pracujesz na baterii, zalecana jest zmiana ustawień, w celu oszczędzani
<string name="prefs_playerNameFlip_title">Obracanie szachownicy: gracz</string>
<string name="prefs_playerNameFlip_summary">Używaj nazwy gracza, żeby obracać szachownicę automatycznie</string>
<string name="prefs_engine_settings">Ustawienia silnika</string>
<string name="prefs_strength_title">Siła gry</string>
<string name="prefs_strength_summary">Wspierane wyłącznie przez lokalne silniki szachowe. Tryb analizy zawsze używa maksymalnej siły gry.</string>
<string name="prefs_ponderMode_title">Namysł</string>
<string name="prefs_ponderMode_summary">Silnik szachowy będzie liczył pozycję czekając na posunięcie przeciwnika. Wspierane przez większość silników.</string>
<string name="prefs_hash_title">Tablica haszująca</string>

View File

@ -50,7 +50,6 @@ Se você está usando somente a bateria, recomenda-se que você mude as configur
<string name="tools_menu">Ferramentas</string>
<string name="load_save_menu">Carregar/Salvar partida</string>
<string name="goto_move">Ir para o lance</string>
<string name="edit_strength">Alterar nível</string>
<string name="edit_randomization">Alterar aleatoriedade</string>
<string name="invalid_number_format">Formato de número inválido</string>
<string name="side_to_move">Lado a mover</string>
@ -273,8 +272,6 @@ Se você está usando somente a bateria, recomenda-se que você mude as configur
<string name="prefs_playerNameFlip_title">Inverter tabuleiro: jogador</string>
<string name="prefs_playerNameFlip_summary">Usar o nome do jogador para virar o tabuleiro automaticamente</string>
<string name="prefs_engine_settings">Configurações do software de Xadrez</string>
<string name="prefs_strength_title">Nível de jogo</string>
<string name="prefs_strength_summary">Somente suportado pelo software interno. Modo análise sempre usa força total.</string>
<string name="prefs_ponderMode_title">Análise constante</string>
<string name="prefs_ponderMode_summary">Deixar o software analisando enquanto espera o lance do oponente. Suportado pela maioria dos softwares.</string>
<string name="prefs_hash_title">Tabela hash</string>

View File

@ -50,7 +50,6 @@
<string name="tools_menu">Инструменты</string>
<string name="load_save_menu">Загрузить/Сохранить партию</string>
<string name="goto_move">Сделать ход</string>
<string name="edit_strength">Изменить уровень сложности</string>
<string name="edit_randomization">Изменить рандомизацию (случайность)</string>
<string name="invalid_number_format">Неправильный формат числа</string>
<string name="side_to_move">Задать направление хода</string>
@ -273,8 +272,6 @@
<string name="prefs_playerNameFlip_title">Поворачивать доску: игрок</string>
<string name="prefs_playerNameFlip_summary">Использовать имя игрока для автоматического поворота доски</string>
<string name="prefs_engine_settings">Настройки движка</string>
<string name="prefs_strength_title">Сила игры</string>
<string name="prefs_strength_summary">Поддерживается только внутренними движками. Режим анализа всегда использует полную силу.</string>
<string name="prefs_ponderMode_title">Размышление</string>
<string name="prefs_ponderMode_summary">Позволить движку обдумывание во время ожидания хода оппонента. Поддерживается большинством движков.</string>
<string name="prefs_hash_title">Хэш-таблица</string>

View File

@ -50,7 +50,6 @@ Pil gücüyle çalışıyorsanız, pil gücünden tasarruf etmek için ayarları
<string name="tools_menu">Araçlar</string>
<string name="load_save_menu">Oyun Yükle/Kaydet</string>
<string name="goto_move">Harekete git</string>
<string name="edit_strength">Gücü ayarla</string>
<string name="edit_randomization">Rasgeleliği ayarla</string>
<string name="invalid_number_format">Geçersiz sayı formatı</string>
<string name="side_to_move">Hareket tarafı</string>
@ -273,8 +272,6 @@ Pil gücüyle çalışıyorsanız, pil gücünden tasarruf etmek için ayarları
<string name="prefs_playerNameFlip_title">Tahtayı Dödür: Oyuncu</string>
<string name="prefs_playerNameFlip_summary">Tahtayı döndürmek için Oyuncu Adını kullan</string>
<string name="prefs_engine_settings">Motor Ayarları</string>
<string name="prefs_strength_title">Oynama zorluğu</string>
<string name="prefs_strength_summary">Sadece dahili motorlar destekler. Analiz modu daima tam gücü kullanır.</string>
<string name="prefs_ponderMode_title">Düşünme</string>
<string name="prefs_ponderMode_summary">Rakibini beklerken bilgisayarın düşünmesine izin ver. Çoğu motor destekler.</string>
<string name="prefs_hash_title">Hash Tablosu</string>

View File

@ -50,7 +50,6 @@
<string name="tools_menu">Інструменти</string>
<string name="load_save_menu">Завантажити/Зберегти партію</string>
<string name="goto_move">Зробити хід</string>
<string name="edit_strength">Змінити рівень складності</string>
<string name="edit_randomization">Змінити рандомізацію (випадковість)</string>
<string name="invalid_number_format">Неправильний формат числа</string>
<string name="side_to_move">Задати напрямок ходу</string>
@ -273,8 +272,6 @@
<string name="prefs_playerNameFlip_title">Повертати дошку: гравець</string>
<string name="prefs_playerNameFlip_summary">Використовувати ім\'я гравця для автоматичного повороту дошки</string>
<string name="prefs_engine_settings">Налаштування рушія</string>
<string name="prefs_strength_title">Сила ігри</string>
<string name="prefs_strength_summary">Підтримується тільки внутрішніми рушіями. Режим аналізу завжди використовує повну силу.</string>
<string name="prefs_ponderMode_title">Розважання</string>
<string name="prefs_ponderMode_summary">Дозволити рушію обдумування під час очікування ходу опонента. Підтримується більшістю рушіїв.</string>
<string name="prefs_hash_title">Геш-таблиця</string>

View File

@ -50,7 +50,6 @@
<string name="tools_menu">工具</string>
<string name="load_save_menu">加载/保存对局</string>
<string name="goto_move">转到着法</string>
<string name="edit_strength">编辑棋力</string>
<string name="edit_randomization">编辑随机性</string>
<string name="invalid_number_format">编号格式无效</string>
<string name="side_to_move">轮走棋方</string>
@ -273,8 +272,6 @@
<string name="prefs_playerNameFlip_title">翻转棋盘: 棋手</string>
<string name="prefs_playerNameFlip_summary">以棋手名字自动翻转棋盘</string>
<string name="prefs_engine_settings">引擎设置</string>
<string name="prefs_strength_title">对弈棋力</string>
<string name="prefs_strength_summary">支持内置引擎。分析模式总是使用最强棋力</string>
<string name="prefs_ponderMode_title">思考</string>
<string name="prefs_ponderMode_summary">引擎在等待对手走棋时思考。支持大多数引擎</string>
<string name="prefs_hash_title">哈希表</string>

View File

@ -50,7 +50,9 @@ If you are running on battery power, it is recommended that you change settings
<string name="tools_menu">Tools</string>
<string name="load_save_menu">Load/Save Game</string>
<string name="goto_move">Goto move</string>
<string name="edit_strength">Edit Strength</string>
<string name="limit_strength">Limit Strength</string>
<string name="set_engine_strength">Set Engine Strength</string>
<string name="engine_cannot_reduce_strength">Engine cannot reduce playing strength</string>
<string name="edit_randomization">Edit Randomization</string>
<string name="invalid_number_format">Invalid number format</string>
<string name="side_to_move">Side to Move</string>
@ -273,8 +275,6 @@ If you are running on battery power, it is recommended that you change settings
<string name="prefs_playerNameFlip_title">Flip Board: Player</string>
<string name="prefs_playerNameFlip_summary">Use Player Name to flip board automatically</string>
<string name="prefs_engine_settings">Engine Settings</string>
<string name="prefs_strength_title">Playing Strength</string>
<string name="prefs_strength_summary">Only supported by internal engines. Analysis mode always uses full strength.</string>
<string name="prefs_ponderMode_title">Pondering</string>
<string name="prefs_ponderMode_summary">Let engine think while waiting for opponent\'s move. Supported by most engines.</string>
<string name="prefs_hash_title">Hash Table</string>

View File

@ -30,12 +30,6 @@
</PreferenceCategory>
<PreferenceCategory
android:title="@string/prefs_engine_settings">
<org.petero.droidfish.activities.util.SeekBarPreference
android:key="strength"
android:title="@string/prefs_strength_title"
android:summary="@string/prefs_strength_summary"
android:defaultValue="1000">
</org.petero.droidfish.activities.util.SeekBarPreference>
<CheckBoxPreference
android:key="ponderMode"
android:title="@string/prefs_ponderMode_title"

View File

@ -123,6 +123,29 @@ light bulb image) that toggles analysis mode. When analysis mode is disabled the
game mode that was used before analysis mode was enabled is restored.
# Playing strength
For engines that can reduce their playing strength using the UCI_LimitStrength
and UCI_Elo options, it is possible to specify the engine playing strength by
opening the *Left drawer menu* and selecting *Set Engine Strength*. Both
built-in engines (*Stockfish* and *CuckooChess*) can reduce their playing
strength.
The available Elo range can be different for different engines. If *Stockfish*
is playing too strong even on the lowest setting, consider switching to the
*CuckooChess* engine which is able to play at a much weaker level. At the lowest
setting, *CuckooChess* plays random legal moves so it should be usable also for
an absolute beginner.
The selected playing strength is shown in the title bar after the engine name.
Playing strength changes take effect the next time the engine starts to think
about a move.
The playing strength setting is only used in game playing mode. When the engine
is in analysis mode, full strength is always used.
# The move list text area
The move list keeps a record of moves played during a game and during analysis.
@ -737,34 +760,6 @@ what to do if a player runs out of time.
Time control changes take effect when a new game is started.
## Playing strength
For built-in engines (*Stockfish* and *CuckooChess*), it is possible to specify
the playing strength in the *Engine Settings* section. The strength is specified
as a percentage between 0% and 100%. You can specify the value either by
dragging the slider, or by tapping the value and entering a new value in the
dialog box.
For *Stockfish* the actual strength used is an integer value between 0 and 20,
so the specified percentage value is divided by 5 and rounded down to an
integer. Even at 0 strength *Stockfish* plays at a level that may be too high
for a beginner.
For *CuckooChess* the specified percentage value is used without rounding and a
smaller value causes the engine to make mistakes more often and to make bigger
mistakes. At 0% strength the engine plays random legal moves so it should be
usable also for an absolute beginner.
Playing strength changes take effect the next time the engine starts to think
about a move.
The playing strength setting is only used in game playing mode. When the engine
is in analysis mode, full strength is always used.
**Note!** For non built-in engines, the *Playing Strength* setting has no
effect. It may however be possible to set the engine strength by modifying some
of the engine UCI options.
## Hash table size
Use *Left drawer menu* -> *Settings* -> *Engine Settings* -> *Hash Table* to

Binary file not shown.