mirror of
https://github.com/peterosterlund2/droidfish.git
synced 2025-12-20 04:32:17 +01:00
DroidFish: Added support for Gaviota endgame tablebases.
This commit is contained in:
@@ -53,6 +53,17 @@ public class ChessBoard extends View {
|
||||
|
||||
List<Move> moveHints;
|
||||
|
||||
/** Decoration for a square. Currently the only possible decoration is a number. */
|
||||
public final static class SquareDecoration {
|
||||
int sq;
|
||||
int number;
|
||||
public SquareDecoration(int sq, int number) {
|
||||
this.sq = sq;
|
||||
this.number = number;
|
||||
}
|
||||
}
|
||||
private ArrayList<SquareDecoration> decorations;
|
||||
|
||||
protected Paint darkPaint;
|
||||
protected Paint brightPaint;
|
||||
private Paint selectedSquarePaint;
|
||||
@@ -60,6 +71,7 @@ public class ChessBoard extends View {
|
||||
private Paint whitePiecePaint;
|
||||
private Paint blackPiecePaint;
|
||||
private Paint labelPaint;
|
||||
private Paint decorationPaint;
|
||||
private ArrayList<Paint> moveMarkPaint;
|
||||
|
||||
public ChessBoard(Context context, AttributeSet attrs) {
|
||||
@@ -93,6 +105,9 @@ public class ChessBoard extends View {
|
||||
|
||||
labelPaint = new Paint();
|
||||
labelPaint.setAntiAlias(true);
|
||||
|
||||
decorationPaint = new Paint();
|
||||
decorationPaint.setAntiAlias(true);
|
||||
|
||||
moveMarkPaint = new ArrayList<Paint>();
|
||||
for (int i = 0; i < 6; i++) {
|
||||
@@ -122,6 +137,7 @@ public class ChessBoard extends View {
|
||||
whitePiecePaint.setColor(ct.getColor(ColorTheme.BRIGHT_PIECE));
|
||||
blackPiecePaint.setColor(ct.getColor(ColorTheme.DARK_PIECE));
|
||||
labelPaint.setColor(ct.getColor(ColorTheme.SQUARE_LABEL));
|
||||
decorationPaint.setColor(ct.getColor(ColorTheme.DECORATION));
|
||||
for (int i = 0; i < 6; i++)
|
||||
moveMarkPaint.get(i).setColor(ct.getColor(ColorTheme.ARROW_0 + i));
|
||||
|
||||
@@ -130,7 +146,7 @@ public class ChessBoard extends View {
|
||||
|
||||
private Handler handlerTimer = new Handler();
|
||||
|
||||
final class AnimInfo {
|
||||
private final class AnimInfo {
|
||||
AnimInfo() { startTime = -1; }
|
||||
boolean paused;
|
||||
long posHash; // Position the animation is valid for
|
||||
@@ -183,7 +199,7 @@ public class ChessBoard extends View {
|
||||
drawPiece(canvas, xCrd, yCrd, piece);
|
||||
}
|
||||
}
|
||||
AnimInfo anim = new AnimInfo();
|
||||
private AnimInfo anim = new AnimInfo();
|
||||
|
||||
/**
|
||||
* Set up move animation. The animation will start the next time setPosition is called.
|
||||
@@ -191,7 +207,7 @@ public class ChessBoard extends View {
|
||||
* @param move The move leading to the target position.
|
||||
* @param forward True if forward direction, false for undo move.
|
||||
*/
|
||||
public void setAnimMove(Position sourcePos, Move move, boolean forward) {
|
||||
public final void setAnimMove(Position sourcePos, Move move, boolean forward) {
|
||||
anim.startTime = -1;
|
||||
anim.paused = true; // Animation starts at next position update
|
||||
if (forward) {
|
||||
@@ -366,6 +382,7 @@ public class ChessBoard extends View {
|
||||
blackPiecePaint.setTextSize(sqSize);
|
||||
whitePiecePaint.setTextSize(sqSize);
|
||||
labelPaint.setTextSize(sqSize/4.0f);
|
||||
decorationPaint.setTextSize(sqSize/3.0f);
|
||||
computeOrigin(width, height);
|
||||
for (int x = 0; x < 8; x++) {
|
||||
for (int y = 0; y < 8; y++) {
|
||||
@@ -406,8 +423,10 @@ public class ChessBoard extends View {
|
||||
cursorSquarePaint.setStrokeWidth(sqSize/(float)16);
|
||||
canvas.drawRect(x0, y0, x0 + sqSize, y0 + sqSize, cursorSquarePaint);
|
||||
}
|
||||
if (!animActive)
|
||||
if (!animActive) {
|
||||
drawMoveHints(canvas);
|
||||
drawDecorations(canvas);
|
||||
}
|
||||
|
||||
anim.draw(canvas);
|
||||
// long t1 = System.currentTimeMillis();
|
||||
@@ -511,8 +530,7 @@ public class ChessBoard extends View {
|
||||
|
||||
private final void drawLabel(Canvas canvas, int xCrd, int yCrd, boolean right,
|
||||
boolean bottom, char c) {
|
||||
String s = "";
|
||||
s += c;
|
||||
String s = Character.toString(c);
|
||||
if (labelBounds == null) {
|
||||
labelBounds = new Rect();
|
||||
labelPaint.getTextBounds("f", 0, 1, labelBounds);
|
||||
@@ -556,7 +574,7 @@ public class ChessBoard extends View {
|
||||
return sq;
|
||||
}
|
||||
|
||||
final private boolean myColor(int piece) {
|
||||
private final boolean myColor(int piece) {
|
||||
return (piece != Piece.EMPTY) && (Piece.isWhite(piece) == pos.whiteMove);
|
||||
}
|
||||
|
||||
@@ -667,4 +685,48 @@ public class ChessBoard extends View {
|
||||
invalidate();
|
||||
}
|
||||
}
|
||||
|
||||
public final void setSquareDecorations(ArrayList<SquareDecoration> decorations) {
|
||||
boolean equal = false;
|
||||
if ((this.decorations == null) || (decorations == null)) {
|
||||
equal = this.decorations == decorations;
|
||||
} else {
|
||||
equal = this.decorations.equals(decorations);
|
||||
}
|
||||
if (!equal) {
|
||||
this.decorations = decorations;
|
||||
invalidate();
|
||||
}
|
||||
}
|
||||
|
||||
private final void drawDecorations(Canvas canvas) {
|
||||
if (decorations == null)
|
||||
return;
|
||||
for (SquareDecoration sd : decorations) {
|
||||
int sq = sd.sq;
|
||||
if ((sd.sq < 0) || (sd.sq >= 64))
|
||||
continue;
|
||||
int xCrd = getXCrd(Position.getX(sq));
|
||||
int yCrd = getYCrd(Position.getY(sq));
|
||||
|
||||
int num = sd.number;
|
||||
String s;
|
||||
if (num > 0)
|
||||
s = "+" + String.valueOf(num);
|
||||
else if (num < 0)
|
||||
s = String.valueOf(num);
|
||||
else
|
||||
s = "0";
|
||||
|
||||
Rect bounds = new Rect();
|
||||
decorationPaint.getTextBounds(s, 0, s.length(), bounds);
|
||||
xCrd += (sqSize - (bounds.left + bounds.right)) / 2;
|
||||
yCrd += (sqSize - (bounds.top + bounds.bottom)) / 2;
|
||||
canvas.drawText(s, xCrd, yCrd, decorationPaint);
|
||||
}
|
||||
}
|
||||
|
||||
public final int getSelectedSquare() {
|
||||
return selectedSquare;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -46,13 +46,14 @@ public class ColorTheme {
|
||||
final static int ARROW_4 = 11;
|
||||
final static int ARROW_5 = 12;
|
||||
final static int SQUARE_LABEL = 13;
|
||||
private final static int numColors = 14;
|
||||
final static int DECORATION = 14;
|
||||
private final static int numColors = 15;
|
||||
|
||||
private int colorTable[] = new int[numColors];
|
||||
|
||||
private static final String[] prefNames = {
|
||||
"darkSquare", "brightSquare", "selectedSquare", "cursorSquare", "darkPiece", "brightPiece", "currentMove",
|
||||
"arrow0", "arrow1", "arrow2", "arrow3", "arrow4", "arrow5", "squareLabel"
|
||||
"arrow0", "arrow1", "arrow2", "arrow3", "arrow4", "arrow5", "squareLabel", "decoration"
|
||||
};
|
||||
private static final String prefPrefix = "color_";
|
||||
|
||||
@@ -61,19 +62,23 @@ public class ColorTheme {
|
||||
private final static String themeColors[][] = {
|
||||
{
|
||||
"#FF808080", "#FFBEBE5A", "#FFFF0000", "#FF00FF00", "#FF000000", "#FFFFFFFF", "#FF888888",
|
||||
"#A01F1FFF", "#A0FF1F1F", "#501F1FFF", "#50FF1F1F", "#1E1F1FFF", "#28FF1F1F", "#FFFF0000"
|
||||
"#A01F1FFF", "#A0FF1F1F", "#501F1FFF", "#50FF1F1F", "#1E1F1FFF", "#28FF1F1F", "#FFFF0000",
|
||||
"#FF9F9F66"
|
||||
},
|
||||
{
|
||||
"#FF77A26D", "#FFC8C365", "#FFFFFF00", "#FF00FF00", "#FF202020", "#FFFFFFCC", "#FF6B9262",
|
||||
"#A01F1FFF", "#A0FF1F1F", "#501F1FFF", "#50FF1F1F", "#1E1F1FFF", "#28FF1F1F", "#FFFF0000"
|
||||
"#A01F1FFF", "#A0FF1F1F", "#501F1FFF", "#50FF1F1F", "#1E1F1FFF", "#28FF1F1F", "#FFFF0000",
|
||||
"#FF808080"
|
||||
},
|
||||
{
|
||||
"#FF83A5D2", "#FFFFFFFA", "#FF3232D1", "#FF5F5FFD", "#FF282828", "#FFF0F0F0", "#FF3333FF",
|
||||
"#A01F1FFF", "#A01FFF1F", "#501F1FFF", "#501FFF1F", "#1E1F1FFF", "#281FFF1F", "#FFFF0000"
|
||||
"#A01F1FFF", "#A01FFF1F", "#501F1FFF", "#501FFF1F", "#1E1F1FFF", "#281FFF1F", "#FFFF0000",
|
||||
"#FF808080"
|
||||
},
|
||||
{
|
||||
"#FF666666", "#FFDDDDDD", "#FFFF0000", "#FF0000FF", "#FF000000", "#FFFFFFFF", "#FF888888",
|
||||
"#A01F1FFF", "#A0FF1F1F", "#501F1FFF", "#50FF1F1F", "#1E1F1FFF", "#28FF1F1F", "#FFFF0000"
|
||||
"#A01F1FFF", "#A0FF1F1F", "#501F1FFF", "#50FF1F1F", "#1E1F1FFF", "#28FF1F1F", "#FFFF0000",
|
||||
"#FF909090"
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -28,6 +28,7 @@ import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.TreeMap;
|
||||
|
||||
import org.petero.droidfish.ChessBoard.SquareDecoration;
|
||||
import org.petero.droidfish.activities.CPUWarning;
|
||||
import org.petero.droidfish.activities.EditBoard;
|
||||
import org.petero.droidfish.activities.EditPGNLoad;
|
||||
@@ -39,10 +40,12 @@ import org.petero.droidfish.engine.EngineUtil;
|
||||
import org.petero.droidfish.gamelogic.DroidChessController;
|
||||
import org.petero.droidfish.gamelogic.ChessParseError;
|
||||
import org.petero.droidfish.gamelogic.Move;
|
||||
import org.petero.droidfish.gamelogic.Pair;
|
||||
import org.petero.droidfish.gamelogic.Position;
|
||||
import org.petero.droidfish.gamelogic.TextIO;
|
||||
import org.petero.droidfish.gamelogic.PgnToken;
|
||||
import org.petero.droidfish.gamelogic.GameTree.Node;
|
||||
import org.petero.droidfish.gtb.Probe;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.app.AlertDialog;
|
||||
@@ -163,8 +166,10 @@ public class DroidFish extends Activity implements GUIInterface {
|
||||
private final static String bookDir = "DroidFish";
|
||||
private final static String pgnDir = "DroidFish" + File.separator + "pgn";
|
||||
private final static String engineDir = "DroidFish" + File.separator + "uci";
|
||||
private final static String gtbDefaultDir = "DroidFish" + File.separator + "gtb";
|
||||
private BookOptions bookOptions = new BookOptions();
|
||||
private PGNOptions pgnOptions = new PGNOptions();
|
||||
private EGTBOptions egtbOptions = new EGTBOptions();
|
||||
|
||||
private long lastVisibleMillis; // Time when GUI became invisible. 0 if currently visible.
|
||||
private long lastComputationMillis; // Time when engine last showed that it was computing.
|
||||
@@ -235,6 +240,7 @@ public class DroidFish extends Activity implements GUIInterface {
|
||||
new File(extDir + sep + bookDir).mkdirs();
|
||||
new File(extDir + sep + pgnDir).mkdirs();
|
||||
new File(extDir + sep + engineDir).mkdirs();
|
||||
new File(extDir + sep + gtbDefaultDir).mkdirs();
|
||||
}
|
||||
|
||||
private String getPgnIntent() {
|
||||
@@ -404,6 +410,7 @@ public class DroidFish extends Activity implements GUIInterface {
|
||||
Move m = cb.mousePressed(sq);
|
||||
if (m != null)
|
||||
ctrl.makeHumanMove(m);
|
||||
setEgtbHints(cb.getSelectedSquare());
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -416,9 +423,9 @@ public class DroidFish extends Activity implements GUIInterface {
|
||||
public void onTrackballEvent(MotionEvent event) {
|
||||
if (ctrl.humansTurn()) {
|
||||
Move m = cb.handleTrackballEvent(event);
|
||||
if (m != null) {
|
||||
if (m != null)
|
||||
ctrl.makeHumanMove(m);
|
||||
}
|
||||
setEgtbHints(cb.getSelectedSquare());
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -609,6 +616,20 @@ public class DroidFish extends Activity implements GUIInterface {
|
||||
bookOptions.random = (settings.getInt("bookRandom", 500) - 500) * (3.0 / 500);
|
||||
setBookOptions();
|
||||
|
||||
egtbOptions.hints = settings.getBoolean("tbHints", false);
|
||||
egtbOptions.hintsEdit = settings.getBoolean("tbHintsEdit", false);
|
||||
egtbOptions.rootProbe = settings.getBoolean("tbRootProbe", false);
|
||||
egtbOptions.engineProbe = settings.getBoolean("tbEngineProbe", true);
|
||||
String gtbPath = settings.getString("gtbPath", "");
|
||||
if (gtbPath.length() == 0) {
|
||||
File extDir = Environment.getExternalStorageDirectory();
|
||||
String sep = File.separator;
|
||||
gtbPath = extDir.getAbsolutePath() + sep + gtbDefaultDir;
|
||||
}
|
||||
egtbOptions.gtbPath = gtbPath;
|
||||
setEgtbOptions();
|
||||
setEgtbHints(cb.getSelectedSquare());
|
||||
|
||||
updateThinkingInfo();
|
||||
|
||||
pgnOptions.view.variations = settings.getBoolean("viewVariations", true);
|
||||
@@ -679,6 +700,29 @@ public class DroidFish extends Activity implements GUIInterface {
|
||||
ctrl.setBookOptions(options);
|
||||
}
|
||||
|
||||
private final void setEgtbOptions() {
|
||||
ctrl.setEgtbOptions(new EGTBOptions(egtbOptions));
|
||||
}
|
||||
|
||||
private final void setEgtbHints(int sq) {
|
||||
if (!egtbOptions.hints || (sq < 0)) {
|
||||
cb.setSquareDecorations(null);
|
||||
return;
|
||||
}
|
||||
|
||||
Probe gtbProbe = Probe.getInstance();
|
||||
ArrayList<Pair<Integer, Integer>> x = gtbProbe.movePieceProbe(cb.pos, sq);
|
||||
if (x == null) {
|
||||
cb.setSquareDecorations(null);
|
||||
return;
|
||||
}
|
||||
|
||||
ArrayList<SquareDecoration> sd = new ArrayList<SquareDecoration>();
|
||||
for (Pair<Integer,Integer> p : x)
|
||||
sd.add(new SquareDecoration(p.first, p.second));
|
||||
cb.setSquareDecorations(sd);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onCreateOptionsMenu(Menu menu) {
|
||||
getMenuInflater().inflate(R.menu.options_menu, menu);
|
||||
@@ -841,6 +885,7 @@ public class DroidFish extends Activity implements GUIInterface {
|
||||
@Override
|
||||
public void setSelection(int sq) {
|
||||
cb.setSelection(sq);
|
||||
setEgtbHints(sq);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -940,12 +985,13 @@ public class DroidFish extends Activity implements GUIInterface {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setPosition(Position pos, String variantInfo, List<Move> variantMoves) {
|
||||
public void setPosition(Position pos, String variantInfo, ArrayList<Move> variantMoves) {
|
||||
variantStr = variantInfo;
|
||||
this.variantMoves = variantMoves;
|
||||
cb.setPosition(pos);
|
||||
setBoardFlip();
|
||||
updateThinkingInfo();
|
||||
setEgtbHints(cb.getSelectedSquare());
|
||||
}
|
||||
|
||||
private String thinkingStr1 = "";
|
||||
@@ -953,12 +999,12 @@ public class DroidFish extends Activity implements GUIInterface {
|
||||
private String bookInfoStr = "";
|
||||
private String variantStr = "";
|
||||
private ArrayList<ArrayList<Move>> pvMoves = new ArrayList<ArrayList<Move>>();
|
||||
private List<Move> bookMoves = null;
|
||||
private List<Move> variantMoves = null;
|
||||
private ArrayList<Move> bookMoves = null;
|
||||
private ArrayList<Move> variantMoves = null;
|
||||
|
||||
@Override
|
||||
public void setThinkingInfo(String pvStr, String statStr, String bookInfo,
|
||||
ArrayList<ArrayList<Move>> pvMoves, List<Move> bookMoves) {
|
||||
ArrayList<ArrayList<Move>> pvMoves, ArrayList<Move> bookMoves) {
|
||||
thinkingStr1 = pvStr;
|
||||
thinkingStr2 = statStr;
|
||||
bookInfoStr = bookInfo;
|
||||
|
||||
44
DroidFish/src/org/petero/droidfish/EGTBOptions.java
Normal file
44
DroidFish/src/org/petero/droidfish/EGTBOptions.java
Normal file
@@ -0,0 +1,44 @@
|
||||
package org.petero.droidfish;
|
||||
|
||||
/** Endgame tablebase probing options. */
|
||||
public final class EGTBOptions {
|
||||
public boolean hints; // Hints when playing/analyzing
|
||||
public boolean hintsEdit; // Hints in "edit board" mode
|
||||
public boolean rootProbe; // Only search optimal moves at root
|
||||
public boolean engineProbe; // Let engine use EGTB
|
||||
public String gtbPath; // GTB directory path
|
||||
|
||||
public EGTBOptions() {
|
||||
hints = false;
|
||||
hintsEdit = false;
|
||||
rootProbe = false;
|
||||
engineProbe = false;
|
||||
gtbPath = "";
|
||||
}
|
||||
|
||||
public EGTBOptions(EGTBOptions other) {
|
||||
hints = other.hints;
|
||||
hintsEdit = other.hintsEdit;
|
||||
rootProbe = other.rootProbe;
|
||||
engineProbe = other.engineProbe;
|
||||
gtbPath = other.gtbPath;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if ((o == null) || (o.getClass() != this.getClass()))
|
||||
return false;
|
||||
EGTBOptions other = (EGTBOptions)o;
|
||||
|
||||
return ((hints == other.hints) &&
|
||||
(hintsEdit == other.hintsEdit) &&
|
||||
(rootProbe == other.rootProbe) &&
|
||||
(engineProbe == other.engineProbe) &&
|
||||
gtbPath.equals(other.gtbPath));
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
@@ -19,7 +19,6 @@
|
||||
package org.petero.droidfish;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.petero.droidfish.gamelogic.Game;
|
||||
import org.petero.droidfish.gamelogic.Move;
|
||||
@@ -31,7 +30,7 @@ import android.content.Context;
|
||||
public interface GUIInterface {
|
||||
|
||||
/** Update the displayed board position. */
|
||||
public void setPosition(Position pos, String variantInfo, List<Move> variantMoves);
|
||||
public void setPosition(Position pos, String variantInfo, ArrayList<Move> variantMoves);
|
||||
|
||||
/** Mark square sq as selected. Set to -1 to clear selection. */
|
||||
public void setSelection(int sq);
|
||||
@@ -55,7 +54,7 @@ public interface GUIInterface {
|
||||
|
||||
/** Update the computer thinking information. */
|
||||
public void setThinkingInfo(String pvStr, String statStr, String bookInfo,
|
||||
ArrayList<ArrayList<Move>> pvMoves, List<Move> bookMoves);
|
||||
ArrayList<ArrayList<Move>> pvMoves, ArrayList<Move> bookMoves);
|
||||
|
||||
/** Ask what to promote a pawn to. Should call reportPromotePiece() when done. */
|
||||
public void requestPromotePiece();
|
||||
|
||||
@@ -58,7 +58,7 @@ public class ChessBoardEdit extends ChessBoard {
|
||||
y0 = (height - (sqSize * 10 + gap)) / 2;
|
||||
}
|
||||
|
||||
int extraPieces(int x, int y) {
|
||||
private final int extraPieces(int x, int y) {
|
||||
if (y == -1) { // White pieces
|
||||
switch (x) {
|
||||
case 0: return Piece.WKING;
|
||||
|
||||
@@ -18,21 +18,28 @@
|
||||
|
||||
package org.petero.droidfish.activities;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
import org.petero.droidfish.ChessBoard;
|
||||
import org.petero.droidfish.R;
|
||||
import org.petero.droidfish.ChessBoard.SquareDecoration;
|
||||
import org.petero.droidfish.gamelogic.ChessParseError;
|
||||
import org.petero.droidfish.gamelogic.Move;
|
||||
import org.petero.droidfish.gamelogic.Pair;
|
||||
import org.petero.droidfish.gamelogic.Piece;
|
||||
import org.petero.droidfish.gamelogic.Position;
|
||||
import org.petero.droidfish.gamelogic.TextIO;
|
||||
import org.petero.droidfish.gtb.Probe;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.app.AlertDialog;
|
||||
import android.app.Dialog;
|
||||
import android.content.DialogInterface;
|
||||
import android.content.Intent;
|
||||
import android.content.SharedPreferences;
|
||||
import android.content.res.Configuration;
|
||||
import android.os.Bundle;
|
||||
import android.preference.PreferenceManager;
|
||||
import android.text.ClipboardManager;
|
||||
import android.view.GestureDetector;
|
||||
import android.view.KeyEvent;
|
||||
@@ -53,12 +60,17 @@ public class EditBoard extends Activity {
|
||||
private Button okButton;
|
||||
private Button cancelButton;
|
||||
|
||||
boolean egtbHints;
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
initUI();
|
||||
|
||||
SharedPreferences settings = PreferenceManager.getDefaultSharedPreferences(this);
|
||||
egtbHints = settings.getBoolean("tbHintsEdit", false);
|
||||
|
||||
Intent i = getIntent();
|
||||
Position pos;
|
||||
try {
|
||||
@@ -78,7 +90,7 @@ public class EditBoard extends Activity {
|
||||
cb.cursorY = oldCB.cursorY;
|
||||
cb.cursorVisible = oldCB.cursorVisible;
|
||||
cb.setPosition(oldCB.pos);
|
||||
cb.setSelection(oldCB.selectedSquare);
|
||||
setSelection(oldCB.selectedSquare);
|
||||
status.setText(statusStr);
|
||||
}
|
||||
|
||||
@@ -128,6 +140,7 @@ public class EditBoard extends Activity {
|
||||
Move m = cb.mousePressed(sq);
|
||||
if (m != null)
|
||||
doMove(m);
|
||||
setEgtbHints(cb.getSelectedSquare());
|
||||
}
|
||||
});
|
||||
cb.setOnTouchListener(new OnTouchListener() {
|
||||
@@ -138,9 +151,9 @@ public class EditBoard extends Activity {
|
||||
cb.setOnTrackballListener(new ChessBoard.OnTrackballListener() {
|
||||
public void onTrackballEvent(MotionEvent event) {
|
||||
Move m = cb.handleTrackballEvent(event);
|
||||
if (m != null) {
|
||||
if (m != null)
|
||||
doMove(m);
|
||||
}
|
||||
setEgtbHints(cb.getSelectedSquare());
|
||||
}
|
||||
});
|
||||
cb.setOnLongClickListener(new OnLongClickListener() {
|
||||
@@ -152,10 +165,34 @@ public class EditBoard extends Activity {
|
||||
});
|
||||
}
|
||||
|
||||
private final void setSelection(int sq) {
|
||||
cb.setSelection(sq);
|
||||
setEgtbHints(sq);
|
||||
}
|
||||
|
||||
private final void setEgtbHints(int sq) {
|
||||
if (!egtbHints || (sq < 0)) {
|
||||
cb.setSquareDecorations(null);
|
||||
return;
|
||||
}
|
||||
|
||||
Probe gtbProbe = Probe.getInstance();
|
||||
ArrayList<Pair<Integer, Integer>> x = gtbProbe.relocatePieceProbe(cb.pos, sq);
|
||||
if (x == null) {
|
||||
cb.setSquareDecorations(null);
|
||||
return;
|
||||
}
|
||||
|
||||
ArrayList<SquareDecoration> sd = new ArrayList<SquareDecoration>();
|
||||
for (Pair<Integer,Integer> p : x)
|
||||
sd.add(new SquareDecoration(p.first, p.second));
|
||||
cb.setSquareDecorations(sd);
|
||||
}
|
||||
|
||||
private void doMove(Move m) {
|
||||
if (m.to < 0) {
|
||||
if ((m.from < 0) || (cb.pos.getPiece(m.from) == Piece.EMPTY)) {
|
||||
cb.setSelection(m.to);
|
||||
setSelection(m.to);
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -172,9 +209,9 @@ public class EditBoard extends Activity {
|
||||
pos.setPiece(m.from, Piece.EMPTY);
|
||||
cb.setPosition(pos);
|
||||
if (m.from >= 0)
|
||||
cb.setSelection(-1);
|
||||
setSelection(-1);
|
||||
else
|
||||
cb.setSelection(m.from);
|
||||
setSelection(m.from);
|
||||
checkValid();
|
||||
}
|
||||
|
||||
@@ -263,13 +300,13 @@ public class EditBoard extends Activity {
|
||||
switch (item) {
|
||||
case 0: // Edit side to move
|
||||
showDialog(SIDE_DIALOG);
|
||||
cb.setSelection(-1);
|
||||
setSelection(-1);
|
||||
checkValid();
|
||||
break;
|
||||
case 1: { // Clear board
|
||||
Position pos = new Position();
|
||||
cb.setPosition(pos);
|
||||
cb.setSelection(-1);
|
||||
setSelection(-1);
|
||||
checkValid();
|
||||
break;
|
||||
}
|
||||
@@ -277,7 +314,7 @@ public class EditBoard extends Activity {
|
||||
try {
|
||||
Position pos = TextIO.readFEN(TextIO.startPosFEN);
|
||||
cb.setPosition(pos);
|
||||
cb.setSelection(-1);
|
||||
setSelection(-1);
|
||||
checkValid();
|
||||
} catch (ChessParseError e) {
|
||||
}
|
||||
@@ -286,19 +323,19 @@ public class EditBoard extends Activity {
|
||||
case 3: // Edit castling flags
|
||||
removeDialog(CASTLE_DIALOG);
|
||||
showDialog(CASTLE_DIALOG);
|
||||
cb.setSelection(-1);
|
||||
setSelection(-1);
|
||||
checkValid();
|
||||
break;
|
||||
case 4: // Edit en passant file
|
||||
removeDialog(EP_DIALOG);
|
||||
showDialog(EP_DIALOG);
|
||||
cb.setSelection(-1);
|
||||
setSelection(-1);
|
||||
checkValid();
|
||||
break;
|
||||
case 5: // Edit move counters
|
||||
removeDialog(MOVCNT_DIALOG);
|
||||
showDialog(MOVCNT_DIALOG);
|
||||
cb.setSelection(-1);
|
||||
setSelection(-1);
|
||||
checkValid();
|
||||
break;
|
||||
case 6: { // Copy position
|
||||
@@ -306,7 +343,7 @@ public class EditBoard extends Activity {
|
||||
String fen = TextIO.toFEN(cb.pos) + "\n";
|
||||
ClipboardManager clipboard = (ClipboardManager)getSystemService(CLIPBOARD_SERVICE);
|
||||
clipboard.setText(fen);
|
||||
cb.setSelection(-1);
|
||||
setSelection(-1);
|
||||
break;
|
||||
}
|
||||
case 7: { // Paste position
|
||||
@@ -321,7 +358,7 @@ public class EditBoard extends Activity {
|
||||
cb.setPosition(e.pos);
|
||||
Toast.makeText(getApplicationContext(), getParseErrString(e), Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
cb.setSelection(-1);
|
||||
setSelection(-1);
|
||||
checkValid();
|
||||
}
|
||||
break;
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
package org.petero.droidfish.book;
|
||||
|
||||
/** Settings controlling opening book usage */
|
||||
public class BookOptions {
|
||||
public final class BookOptions {
|
||||
public String filename = "";
|
||||
|
||||
public int maxLength = 1000000;
|
||||
|
||||
@@ -23,6 +23,7 @@ import java.io.FileReader;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
|
||||
import org.petero.droidfish.EGTBOptions;
|
||||
import org.petero.droidfish.book.BookOptions;
|
||||
import org.petero.droidfish.book.DroidBook;
|
||||
import org.petero.droidfish.gamelogic.Move;
|
||||
@@ -33,6 +34,8 @@ import org.petero.droidfish.gamelogic.SearchListener;
|
||||
import org.petero.droidfish.gamelogic.TextIO;
|
||||
import org.petero.droidfish.gamelogic.UndoInfo;
|
||||
import org.petero.droidfish.gamelogic.SearchListener.PvInfo;
|
||||
import org.petero.droidfish.gtb.Probe;
|
||||
import org.petero.droidfish.gtb.Probe.ProbeResult;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
@@ -45,6 +48,8 @@ public class DroidComputerPlayer {
|
||||
private final Context context;
|
||||
private final SearchListener listener;
|
||||
private final DroidBook book;
|
||||
private EGTBOptions egtbOptions;
|
||||
private final Probe gtbProbe;
|
||||
|
||||
/** Set when "ucinewgame" needs to be sent. */
|
||||
private boolean newGame = false;
|
||||
@@ -118,6 +123,7 @@ public class DroidComputerPlayer {
|
||||
|
||||
long[] posHashList; // For draw decision after completed search
|
||||
int posHashListSize; // For draw decision after completed search
|
||||
ArrayList<Move> searchMoves; // Moves to search, or null to search all moves
|
||||
|
||||
/**
|
||||
* Create a request to start an engine.
|
||||
@@ -238,6 +244,8 @@ public class DroidComputerPlayer {
|
||||
this.context = context;
|
||||
this.listener = listener;
|
||||
book = DroidBook.getInstance();
|
||||
egtbOptions = new EGTBOptions();
|
||||
gtbProbe = Probe.getInstance();
|
||||
engineState = new EngineState();
|
||||
searchRequest = null;
|
||||
}
|
||||
@@ -264,12 +272,21 @@ public class DroidComputerPlayer {
|
||||
public final void setBookOptions(BookOptions options) {
|
||||
book.setOptions(options);
|
||||
}
|
||||
|
||||
public final void setEgtbOptions(EGTBOptions options) {
|
||||
egtbOptions = options;
|
||||
gtbProbe.setPath(options.gtbPath);
|
||||
}
|
||||
|
||||
/** Return all book moves, both as a formatted string and as a list of moves. */
|
||||
public final Pair<String, ArrayList<Move>> getBookHints(Position pos) {
|
||||
return book.getAllBookMoves(pos);
|
||||
}
|
||||
|
||||
public final ProbeResult egtbProbe(Position pos) {
|
||||
return gtbProbe.probeHard(pos);
|
||||
}
|
||||
|
||||
/** Get engine reported name. */
|
||||
public final synchronized String getEngineName() {
|
||||
return engineName;
|
||||
@@ -319,6 +336,22 @@ public class DroidComputerPlayer {
|
||||
handleQueue();
|
||||
}
|
||||
|
||||
/** Decide what moves to search. Filters out non-optimal moves if tablebases are used. */
|
||||
private final ArrayList<Move> movesToSearch(SearchRequest sr) {
|
||||
ArrayList<Move> moves = null;
|
||||
if (egtbOptions.rootProbe) {
|
||||
moves = gtbProbe.findOptimal(sr.currPos);
|
||||
}
|
||||
if (moves != null) {
|
||||
sr.searchMoves = moves;
|
||||
} else {
|
||||
moves = new MoveGen().pseudoLegalMoves(sr.currPos);
|
||||
moves = MoveGen.removeIllegal(sr.currPos, moves);
|
||||
sr.searchMoves = null;
|
||||
}
|
||||
return moves;
|
||||
}
|
||||
|
||||
/**
|
||||
* Start a search. Search result is returned to the search listener object.
|
||||
* The result can be a valid move string, in which case the move is played
|
||||
@@ -352,9 +385,8 @@ public class DroidComputerPlayer {
|
||||
}
|
||||
}
|
||||
|
||||
// If only one legal move, play it without searching
|
||||
ArrayList<Move> moves = new MoveGen().pseudoLegalMoves(sr.currPos);
|
||||
moves = MoveGen.removeIllegal(sr.currPos, moves);
|
||||
// If only one move to search, play it without searching
|
||||
ArrayList<Move> moves = movesToSearch(sr);
|
||||
if (moves.size() == 0) {
|
||||
listener.notifySearchResult(sr.searchId, "", null); // User set up a position where computer has no valid moves.
|
||||
return;
|
||||
@@ -381,8 +413,7 @@ public class DroidComputerPlayer {
|
||||
stopSearch();
|
||||
|
||||
// If no legal moves, there is nothing to analyze
|
||||
ArrayList<Move> moves = new MoveGen().pseudoLegalMoves(sr.currPos);
|
||||
moves = MoveGen.removeIllegal(sr.currPos, moves);
|
||||
ArrayList<Move> moves = movesToSearch(sr);
|
||||
if (moves.size() == 0)
|
||||
return;
|
||||
|
||||
@@ -485,7 +516,7 @@ public class DroidComputerPlayer {
|
||||
|
||||
engineState.searchId = searchRequest.searchId;
|
||||
|
||||
// Reduce remaining time there was an engine delay
|
||||
// Reduce remaining time if there was an engine delay
|
||||
if (isSearch) {
|
||||
long now = System.currentTimeMillis();
|
||||
int delay = (int)(now - searchRequest.startTime);
|
||||
@@ -530,6 +561,13 @@ public class DroidComputerPlayer {
|
||||
goStr.append(String.format(" movestogo %d", sr.movesToGo));
|
||||
if (sr.ponderMove != null)
|
||||
goStr.append(" ponder");
|
||||
if (sr.searchMoves != null) {
|
||||
goStr.append(" searchmoves");
|
||||
for (Move m : sr.searchMoves) {
|
||||
goStr.append(' ');
|
||||
goStr.append(TextIO.moveToUCIString(m));
|
||||
}
|
||||
}
|
||||
uciEngine.writeLineToEngine(goStr.toString());
|
||||
engineState.setState((sr.ponderMove == null) ? MainState.SEARCH : MainState.PONDER);
|
||||
} else { // Analyze
|
||||
@@ -547,7 +585,16 @@ public class DroidComputerPlayer {
|
||||
uciEngine.writeLineToEngine(posStr.toString());
|
||||
uciEngine.setOption("UCI_AnalyseMode", true);
|
||||
uciEngine.setOption("Threads", sr.engineThreads > 0 ? sr.engineThreads : numCPUs);
|
||||
uciEngine.writeLineToEngine("go infinite");
|
||||
StringBuilder goStr = new StringBuilder(96);
|
||||
goStr.append("go infinite");
|
||||
if (sr.searchMoves != null) {
|
||||
goStr.append(" searchmoves");
|
||||
for (Move m : sr.searchMoves) {
|
||||
goStr.append(' ');
|
||||
goStr.append(TextIO.moveToUCIString(m));
|
||||
}
|
||||
}
|
||||
uciEngine.writeLineToEngine(goStr.toString());
|
||||
engineState.setState(MainState.ANALYZE);
|
||||
}
|
||||
}
|
||||
@@ -624,7 +671,7 @@ public class DroidComputerPlayer {
|
||||
switch (engineState.state) {
|
||||
case READ_OPTIONS: {
|
||||
if (readUCIOption(uci, s)) {
|
||||
uci.initOptions();
|
||||
uci.initOptions(egtbOptions);
|
||||
uci.writeLineToEngine("ucinewgame");
|
||||
uci.writeLineToEngine("isready");
|
||||
engineState.setState(MainState.WAIT_READY);
|
||||
|
||||
@@ -27,6 +27,7 @@ import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.nio.channels.FileChannel;
|
||||
|
||||
import org.petero.droidfish.EGTBOptions;
|
||||
import org.petero.droidfish.R;
|
||||
import android.content.Context;
|
||||
|
||||
@@ -160,9 +161,13 @@ public class ExternalEngine extends UCIEngineBase {
|
||||
|
||||
/** @inheritDoc */
|
||||
@Override
|
||||
public void initOptions() {
|
||||
super.initOptions();
|
||||
public void initOptions(EGTBOptions egtbOptions) {
|
||||
super.initOptions(egtbOptions);
|
||||
setOption("Hash", 16);
|
||||
if (egtbOptions.engineProbe) {
|
||||
setOption("GaviotaTbPath", egtbOptions.gtbPath);
|
||||
setOption("GaviotaTbCache", 8);
|
||||
}
|
||||
}
|
||||
|
||||
/** @inheritDoc */
|
||||
|
||||
@@ -18,6 +18,8 @@
|
||||
|
||||
package org.petero.droidfish.engine;
|
||||
|
||||
import org.petero.droidfish.EGTBOptions;
|
||||
|
||||
public interface UCIEngine {
|
||||
|
||||
/** For reporting engine error messages. */
|
||||
@@ -30,7 +32,7 @@ public interface UCIEngine {
|
||||
public void initialize();
|
||||
|
||||
/** Initialize default options. */
|
||||
public void initOptions();
|
||||
public void initOptions(EGTBOptions egtbOptions);
|
||||
|
||||
/** Shut down engine. */
|
||||
public void shutDown();
|
||||
|
||||
@@ -21,6 +21,7 @@ package org.petero.droidfish.engine;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
|
||||
import org.petero.droidfish.EGTBOptions;
|
||||
import org.petero.droidfish.engine.cuckoochess.CuckooChessEngine;
|
||||
|
||||
import android.content.Context;
|
||||
@@ -61,7 +62,7 @@ public abstract class UCIEngineBase implements UCIEngine {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initOptions() {
|
||||
public void initOptions(EGTBOptions egtbOptions) {
|
||||
isUCI = true;
|
||||
}
|
||||
|
||||
|
||||
@@ -25,6 +25,7 @@ import chess.Position;
|
||||
import chess.TextIO;
|
||||
import java.util.ArrayList;
|
||||
|
||||
import org.petero.droidfish.EGTBOptions;
|
||||
import org.petero.droidfish.engine.LocalPipe;
|
||||
import org.petero.droidfish.engine.UCIEngineBase;
|
||||
|
||||
@@ -73,8 +74,8 @@ public class CuckooChessEngine extends UCIEngineBase {
|
||||
|
||||
/** @inheritDoc */
|
||||
@Override
|
||||
public final void initOptions() {
|
||||
super.initOptions();
|
||||
public final void initOptions(EGTBOptions egtbOptions) {
|
||||
super.initOptions(egtbOptions);
|
||||
}
|
||||
|
||||
/** @inheritDoc */
|
||||
|
||||
@@ -22,6 +22,7 @@ import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.petero.droidfish.EGTBOptions;
|
||||
import org.petero.droidfish.GUIInterface;
|
||||
import org.petero.droidfish.GameMode;
|
||||
import org.petero.droidfish.PGNOptions;
|
||||
@@ -40,6 +41,7 @@ public class DroidChessController {
|
||||
private DroidComputerPlayer computerPlayer = null;
|
||||
private PgnToken.PgnTokenReceiver gameTextListener = null;
|
||||
private BookOptions bookOptions = new BookOptions();
|
||||
private EGTBOptions egtbOptions = new EGTBOptions();
|
||||
private Game game = null;
|
||||
private Move ponderMove = null;
|
||||
private GUIInterface gui;
|
||||
@@ -81,6 +83,7 @@ public class DroidChessController {
|
||||
if (computerPlayer == null) {
|
||||
computerPlayer = new DroidComputerPlayer(gui.getContext(), listener);
|
||||
computerPlayer.setBookOptions(bookOptions);
|
||||
computerPlayer.setEgtbOptions(egtbOptions);
|
||||
}
|
||||
computerPlayer.queueStartEngine(searchId, engine);
|
||||
searchId++;
|
||||
@@ -139,6 +142,14 @@ public class DroidChessController {
|
||||
}
|
||||
}
|
||||
|
||||
public final synchronized void setEgtbOptions(EGTBOptions options) {
|
||||
if (!egtbOptions.equals(options)) {
|
||||
egtbOptions = options;
|
||||
if (computerPlayer != null)
|
||||
computerPlayer.setEgtbOptions(egtbOptions);
|
||||
}
|
||||
}
|
||||
|
||||
/** Set engine and engine strength. Restart computer thinking if appropriate.
|
||||
* @param engine Name of engine.
|
||||
* @param strength Engine strength, 0 - 1000. */
|
||||
@@ -560,7 +571,7 @@ public class DroidChessController {
|
||||
|
||||
private boolean whiteMove = true;
|
||||
private String bookInfo = "";
|
||||
private List<Move> bookMoves = null;
|
||||
private ArrayList<Move> bookMoves = null;
|
||||
|
||||
private Move ponderMove = null;
|
||||
private ArrayList<PvInfo> pvInfoV = new ArrayList<PvInfo>();
|
||||
@@ -676,7 +687,7 @@ public class DroidChessController {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void notifyBookInfo(int id, String bookInfo, List<Move> moveList) {
|
||||
public void notifyBookInfo(int id, String bookInfo, ArrayList<Move> moveList) {
|
||||
this.bookInfo = bookInfo;
|
||||
bookMoves = moveList;
|
||||
setSearchInfo(id);
|
||||
@@ -944,7 +955,7 @@ public class DroidChessController {
|
||||
}
|
||||
|
||||
private final synchronized void setThinkingInfo(int id, ArrayList<ArrayList<Move>> pvMoves, String pvStr,
|
||||
String statStr, String bookInfo, List<Move> bookMoves) {
|
||||
String statStr, String bookInfo, ArrayList<Move> bookMoves) {
|
||||
if (id == searchId)
|
||||
gui.setThinkingInfo(pvStr, statStr, bookInfo, pvMoves, bookMoves);
|
||||
}
|
||||
|
||||
@@ -674,10 +674,10 @@ public class GameTree {
|
||||
}
|
||||
|
||||
/** List of possible continuation moves. */
|
||||
public final List<Move> variations() {
|
||||
public final ArrayList<Move> variations() {
|
||||
if (currentNode.verifyChildren(currentPos))
|
||||
updateListener();
|
||||
List<Move> ret = new ArrayList<Move>();
|
||||
ArrayList<Move> ret = new ArrayList<Move>();
|
||||
for (Node child : currentNode.children)
|
||||
ret.add(child.move);
|
||||
return ret;
|
||||
|
||||
@@ -19,7 +19,6 @@
|
||||
package org.petero.droidfish.gamelogic;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
|
||||
/**
|
||||
@@ -83,7 +82,7 @@ public interface SearchListener {
|
||||
public void notifyStats(int id, int nodes, int nps, int time);
|
||||
|
||||
/** Report opening book information. */
|
||||
public void notifyBookInfo(int id, String bookInfo, List<Move> moveList);
|
||||
public void notifyBookInfo(int id, String bookInfo, ArrayList<Move> moveList);
|
||||
|
||||
/** Report move (or command, such as "resign") played by the engine. */
|
||||
public void notifySearchResult(int id, String cmd, Move ponder);
|
||||
|
||||
83
DroidFish/src/org/petero/droidfish/gtb/GtbProbe.java
Normal file
83
DroidFish/src/org/petero/droidfish/gtb/GtbProbe.java
Normal file
@@ -0,0 +1,83 @@
|
||||
/*
|
||||
GtbProbe - Java interface to Gaviota endgame tablebases.
|
||||
Copyright (C) 2011-2012 Peter Österlund, peterosterlund2@gmail.com
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.petero.droidfish.gtb;
|
||||
|
||||
/** Interface to native gtb probing code. */
|
||||
class GtbProbe {
|
||||
static {
|
||||
System.loadLibrary("gtb");
|
||||
}
|
||||
|
||||
private String currTbPath = "";
|
||||
|
||||
GtbProbe() {
|
||||
}
|
||||
|
||||
final synchronized void setPath(String tbPath) {
|
||||
if (!currTbPath.equals(tbPath)) {
|
||||
currTbPath = tbPath;
|
||||
init(tbPath);
|
||||
}
|
||||
}
|
||||
|
||||
final static int NOPIECE = 0;
|
||||
final static int PAWN = 1;
|
||||
final static int KNIGHT = 2;
|
||||
final static int BISHOP = 3;
|
||||
final static int ROOK = 4;
|
||||
final static int QUEEN = 5;
|
||||
final static int KING = 6;
|
||||
|
||||
final static int NOSQUARE = 64;
|
||||
|
||||
// Castle masks
|
||||
final static int H1_CASTLE = 8;
|
||||
final static int A1_CASTLE = 4;
|
||||
final static int H8_CASTLE = 2;
|
||||
final static int A8_CASTLE = 1;
|
||||
|
||||
// tbinfo values
|
||||
final static int DRAW = 0;
|
||||
final static int WMATE = 1;
|
||||
final static int BMATE = 2;
|
||||
final static int FORBID = 3;
|
||||
final static int UNKNOWN = 7;
|
||||
|
||||
/**
|
||||
* Probe table bases.
|
||||
* @param wtm True if white to move.
|
||||
* @param epSq En passant square, or NOSQUARE.
|
||||
* @param castleMask Castle mask.
|
||||
* @param whiteSquares Array of squares occupied by white pieces, terminated with NOSQUARE.
|
||||
* @param blackSquares Array of squares occupied by black pieces, terminated with NOSQUARE.
|
||||
* @param whitePieces Array of white pieces, terminated with NOPIECE.
|
||||
* @param blackPieces Array of black pieces, terminated with NOPIECE.
|
||||
* @param result Two element array. Set to [tbinfo, plies].
|
||||
* @return True if success.
|
||||
*/
|
||||
public final native boolean probeHard(boolean wtm, int epSq,
|
||||
int castleMask,
|
||||
int[] whiteSquares,
|
||||
int[] blackSquares,
|
||||
byte[] whitePieces,
|
||||
byte[] blackPieces,
|
||||
int[] result);
|
||||
|
||||
private final native static boolean init(String tbPath);
|
||||
}
|
||||
278
DroidFish/src/org/petero/droidfish/gtb/Probe.java
Normal file
278
DroidFish/src/org/petero/droidfish/gtb/Probe.java
Normal file
@@ -0,0 +1,278 @@
|
||||
/*
|
||||
GtbCuckoo - Interface to Gaviota endgame tablebases.
|
||||
Copyright (C) 2011-2012 Peter Österlund, peterosterlund2@gmail.com
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.petero.droidfish.gtb;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
import org.petero.droidfish.gamelogic.Move;
|
||||
import org.petero.droidfish.gamelogic.MoveGen;
|
||||
import org.petero.droidfish.gamelogic.Pair;
|
||||
import org.petero.droidfish.gamelogic.Piece;
|
||||
import org.petero.droidfish.gamelogic.Position;
|
||||
import org.petero.droidfish.gamelogic.UndoInfo;
|
||||
|
||||
/** Interface between Position class and GTB probing code. */
|
||||
public class Probe {
|
||||
private final GtbProbe gtb;
|
||||
private final int whiteSquares[];
|
||||
private final int blackSquares[];
|
||||
private final byte whitePieces[];
|
||||
private final byte blackPieces[];
|
||||
|
||||
private static final Probe INSTANCE = new Probe();
|
||||
|
||||
/** Get singleton instance. */
|
||||
public static Probe getInstance() {
|
||||
return INSTANCE;
|
||||
}
|
||||
|
||||
/** Constructor. */
|
||||
private Probe() {
|
||||
gtb = new GtbProbe();
|
||||
whiteSquares = new int[65];
|
||||
blackSquares = new int[65];
|
||||
whitePieces = new byte[65];
|
||||
blackPieces = new byte[65];
|
||||
}
|
||||
|
||||
public void setPath(String tbPath) {
|
||||
gtb.setPath(tbPath);
|
||||
}
|
||||
|
||||
public static final class ProbeResult {
|
||||
public final static int DRAW = 0;
|
||||
public final static int WMATE = 1;
|
||||
public final static int BMATE = 2;
|
||||
public final static int UNKNOWN = 3;
|
||||
|
||||
public int result;
|
||||
public int movesToMate; // Full moves to mate, or 0 if DRAW or UNKNOWN.
|
||||
}
|
||||
|
||||
/**
|
||||
* Probe table bases.
|
||||
* @param pos The position to probe.
|
||||
* @param result Two element array. Set to [tbinfo, plies].
|
||||
* @return True if success.
|
||||
*/
|
||||
public final ProbeResult probeHard(Position pos) {
|
||||
int castleMask = 0;
|
||||
if (pos.a1Castle()) castleMask |= GtbProbe.A1_CASTLE;
|
||||
if (pos.h1Castle()) castleMask |= GtbProbe.H1_CASTLE;
|
||||
if (pos.a8Castle()) castleMask |= GtbProbe.A8_CASTLE;
|
||||
if (pos.h8Castle()) castleMask |= GtbProbe.H8_CASTLE;
|
||||
|
||||
int nWhite = 0;
|
||||
int nBlack = 0;
|
||||
for (int sq = 0; sq < 64; sq++) {
|
||||
int p = pos.getPiece(sq);
|
||||
switch (p) {
|
||||
case Piece.WKING:
|
||||
whiteSquares[nWhite] = sq;
|
||||
whitePieces[nWhite++] = GtbProbe.KING;
|
||||
break;
|
||||
case Piece.WQUEEN:
|
||||
whiteSquares[nWhite] = sq;
|
||||
whitePieces[nWhite++] = GtbProbe.QUEEN;
|
||||
break;
|
||||
case Piece.WROOK:
|
||||
whiteSquares[nWhite] = sq;
|
||||
whitePieces[nWhite++] = GtbProbe.ROOK;
|
||||
break;
|
||||
case Piece.WBISHOP:
|
||||
whiteSquares[nWhite] = sq;
|
||||
whitePieces[nWhite++] = GtbProbe.BISHOP;
|
||||
break;
|
||||
case Piece.WKNIGHT:
|
||||
whiteSquares[nWhite] = sq;
|
||||
whitePieces[nWhite++] = GtbProbe.KNIGHT;
|
||||
break;
|
||||
case Piece.WPAWN:
|
||||
whiteSquares[nWhite] = sq;
|
||||
whitePieces[nWhite++] = GtbProbe.PAWN;
|
||||
break;
|
||||
|
||||
case Piece.BKING:
|
||||
blackSquares[nBlack] = sq;
|
||||
blackPieces[nBlack++] = GtbProbe.KING;
|
||||
break;
|
||||
case Piece.BQUEEN:
|
||||
blackSquares[nBlack] = sq;
|
||||
blackPieces[nBlack++] = GtbProbe.QUEEN;
|
||||
break;
|
||||
case Piece.BROOK:
|
||||
blackSquares[nBlack] = sq;
|
||||
blackPieces[nBlack++] = GtbProbe.ROOK;
|
||||
break;
|
||||
case Piece.BBISHOP:
|
||||
blackSquares[nBlack] = sq;
|
||||
blackPieces[nBlack++] = GtbProbe.BISHOP;
|
||||
break;
|
||||
case Piece.BKNIGHT:
|
||||
blackSquares[nBlack] = sq;
|
||||
blackPieces[nBlack++] = GtbProbe.KNIGHT;
|
||||
break;
|
||||
case Piece.BPAWN:
|
||||
blackSquares[nBlack] = sq;
|
||||
blackPieces[nBlack++] = GtbProbe.PAWN;
|
||||
break;
|
||||
}
|
||||
}
|
||||
whiteSquares[nWhite] = GtbProbe.NOSQUARE;
|
||||
blackSquares[nBlack] = GtbProbe.NOSQUARE;
|
||||
whitePieces[nWhite] = GtbProbe.NOPIECE;
|
||||
blackPieces[nBlack] = GtbProbe.NOPIECE;
|
||||
int epSquare = pos.getEpSquare();
|
||||
if (epSquare == -1)
|
||||
epSquare = GtbProbe.NOSQUARE;
|
||||
|
||||
int[] result = new int[2];
|
||||
boolean res = gtb.probeHard(pos.whiteMove, epSquare, castleMask,
|
||||
whiteSquares, blackSquares, whitePieces, blackPieces,
|
||||
result);
|
||||
ProbeResult ret = new ProbeResult();
|
||||
if (res) {
|
||||
switch (result[0]) {
|
||||
case GtbProbe.DRAW:
|
||||
ret.result = ProbeResult.DRAW;
|
||||
ret.movesToMate = 0;
|
||||
break;
|
||||
case GtbProbe.WMATE:
|
||||
ret.result = ProbeResult.WMATE;
|
||||
ret.movesToMate = (result[1] + 1) / 2;
|
||||
break;
|
||||
case GtbProbe.BMATE:
|
||||
ret.result = ProbeResult.BMATE;
|
||||
ret.movesToMate = (result[1] + 1) / 2;
|
||||
break;
|
||||
default:
|
||||
ret.result = ProbeResult.UNKNOWN;
|
||||
ret.movesToMate = 0;
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
ret.result = ProbeResult.UNKNOWN;
|
||||
ret.movesToMate = 0;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/** Return a list of all legal moves that are not known to be non-optimal.
|
||||
* Returns null if no legal move could be excluded. */
|
||||
public final ArrayList<Move> findOptimal(Position pos) {
|
||||
ArrayList<Move> moveList = new MoveGen().pseudoLegalMoves(pos);
|
||||
moveList = MoveGen.removeIllegal(pos, moveList);
|
||||
ArrayList<Move> optimalMoves = new ArrayList<Move>();
|
||||
ArrayList<Move> unknownMoves = new ArrayList<Move>();
|
||||
final int MATE0 = 100000;
|
||||
int bestScore = -1000000;
|
||||
UndoInfo ui = new UndoInfo();
|
||||
for (Move m : moveList) {
|
||||
pos.makeMove(m, ui);
|
||||
ProbeResult res = probeHard(pos);
|
||||
pos.unMakeMove(m, ui);
|
||||
if (res.result == ProbeResult.UNKNOWN) {
|
||||
unknownMoves.add(m);
|
||||
} else {
|
||||
int wScore;
|
||||
if (res.result == ProbeResult.WMATE)
|
||||
wScore = MATE0 - res.movesToMate;
|
||||
else if (res.result == ProbeResult.BMATE)
|
||||
wScore = -(MATE0 - res.movesToMate);
|
||||
else
|
||||
wScore = 0;
|
||||
int score = pos.whiteMove ? wScore : -wScore;
|
||||
if (score > bestScore) {
|
||||
optimalMoves.clear();
|
||||
optimalMoves.add(m);
|
||||
bestScore = score;
|
||||
} else if (score == bestScore) {
|
||||
optimalMoves.add(m);
|
||||
} else {
|
||||
// Ignore move
|
||||
}
|
||||
}
|
||||
}
|
||||
for (Move m : unknownMoves)
|
||||
optimalMoves.add(m);
|
||||
return (optimalMoves.size() < moveList.size()) ? optimalMoves : null;
|
||||
}
|
||||
|
||||
/** For a given position and from square, return EGTB information
|
||||
* about all legal destination squares. Return null if no information available. */
|
||||
public final ArrayList<Pair<Integer,Integer>> movePieceProbe(Position pos, int fromSq) {
|
||||
int p = pos.getPiece(fromSq);
|
||||
if ((p == Piece.EMPTY) || (pos.whiteMove != Piece.isWhite(p)))
|
||||
return null;
|
||||
ArrayList<Pair<Integer,Integer>> ret = new ArrayList<Pair<Integer,Integer>>();
|
||||
|
||||
ArrayList<Move> moveList = new MoveGen().pseudoLegalMoves(pos);
|
||||
moveList = MoveGen.removeIllegal(pos, moveList);
|
||||
UndoInfo ui = new UndoInfo();
|
||||
for (Move m : moveList) {
|
||||
if (m.from != fromSq)
|
||||
continue;
|
||||
pos.makeMove(m, ui);
|
||||
ProbeResult res = probeHard(pos);
|
||||
pos.unMakeMove(m, ui);
|
||||
if (res.result == ProbeResult.UNKNOWN)
|
||||
continue;
|
||||
int score = 0;
|
||||
if (res.result == ProbeResult.WMATE) {
|
||||
score = pos.whiteMove ? res.movesToMate + 1 : -res.movesToMate;
|
||||
} else if (res.result == ProbeResult.BMATE) {
|
||||
score = pos.whiteMove ? -res.movesToMate : res.movesToMate + 1;
|
||||
}
|
||||
ret.add(new Pair<Integer,Integer>(m.to, score));
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/** For a given position and from square, return EGTB information
|
||||
* about all legal alternative positions for the piece on from square.
|
||||
* Return null if no information is available. */
|
||||
public final ArrayList<Pair<Integer, Integer>> relocatePieceProbe(Position pos, int fromSq) {
|
||||
int p = pos.getPiece(fromSq);
|
||||
if (p == Piece.EMPTY)
|
||||
return null;
|
||||
boolean isPawn = (Piece.makeWhite(p) == Piece.WPAWN);
|
||||
ArrayList<Pair<Integer,Integer>> ret = new ArrayList<Pair<Integer,Integer>>();
|
||||
for (int sq = 0; sq < 64; sq++) {
|
||||
if ((sq != fromSq) && (pos.getPiece(sq) != Piece.EMPTY))
|
||||
continue;
|
||||
if (isPawn && ((sq < 8) || (sq >= 56)))
|
||||
continue;
|
||||
pos.setPiece(fromSq, Piece.EMPTY);
|
||||
pos.setPiece(sq, p);
|
||||
ProbeResult res = probeHard(pos);
|
||||
pos.setPiece(sq, Piece.EMPTY);
|
||||
pos.setPiece(fromSq, p);
|
||||
if (res.result == ProbeResult.UNKNOWN)
|
||||
continue;
|
||||
int score = 0;
|
||||
if (res.result == ProbeResult.WMATE) {
|
||||
score = res.movesToMate;
|
||||
} else if (res.result == ProbeResult.BMATE) {
|
||||
score = -res.movesToMate;
|
||||
}
|
||||
ret.add(new Pair<Integer,Integer>(sq, score));
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user