mirror of
https://github.com/peterosterlund2/droidfish.git
synced 2024-11-27 06:10:28 +01:00
Remove unneeded code from buildSrc.
This commit is contained in:
parent
1a533ca1fb
commit
de36444c9f
|
@ -130,14 +130,13 @@ public class Book {
|
|||
return null;
|
||||
}
|
||||
|
||||
MoveGen.MoveList legalMoves = new MoveGen().pseudoLegalMoves(pos);
|
||||
MoveGen.removeIllegal(pos, legalMoves);
|
||||
ArrayList<Move> legalMoves = MoveGen.instance.legalMoves(pos);
|
||||
int sum = 0;
|
||||
for (int i = 0; i < bookMoves.size(); i++) {
|
||||
BookEntry be = bookMoves.get(i);
|
||||
boolean contains = false;
|
||||
for (int mi = 0; mi < legalMoves.size; mi++)
|
||||
if (legalMoves.m[mi].equals(be.move)) {
|
||||
for (Move m : legalMoves)
|
||||
if (m.equals(be.move)) {
|
||||
contains = true;
|
||||
break;
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
CuckooChess - A java chess program.
|
||||
DroidFish - An Android chess program.
|
||||
Copyright (C) 2011 Peter Österlund, peterosterlund2@gmail.com
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
|
@ -21,9 +21,28 @@ package chess;
|
|||
/** Exception class to represent parse errors in FEN or algebraic notation. */
|
||||
public class ChessParseError extends Exception {
|
||||
private static final long serialVersionUID = -6051856171275301175L;
|
||||
public ChessParseError() {
|
||||
}
|
||||
|
||||
public Position pos;
|
||||
public int resourceId = -1;
|
||||
|
||||
public ChessParseError(String msg) {
|
||||
super(msg);
|
||||
pos = null;
|
||||
}
|
||||
public ChessParseError(String msg, Position pos) {
|
||||
super(msg);
|
||||
this.pos = pos;
|
||||
}
|
||||
|
||||
public ChessParseError(int resourceId) {
|
||||
super("");
|
||||
pos = null;
|
||||
this.resourceId = resourceId;
|
||||
}
|
||||
|
||||
public ChessParseError(int resourceId, Position pos) {
|
||||
super("");
|
||||
this.pos = pos;
|
||||
this.resourceId = resourceId;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,20 +16,13 @@
|
|||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.petero.droidfish.buildtools;
|
||||
package chess;
|
||||
|
||||
|
||||
import java.io.FileOutputStream;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
|
||||
import org.petero.droidfish.FileUtil;
|
||||
import org.petero.droidfish.PGNOptions;
|
||||
import org.petero.droidfish.gamelogic.Game;
|
||||
import org.petero.droidfish.gamelogic.GameTree;
|
||||
import org.petero.droidfish.gamelogic.Move;
|
||||
import org.petero.droidfish.gamelogic.TimeControlData;
|
||||
|
||||
/** Build the ECO data file from eco.pgn. */
|
||||
public class EcoBuilder {
|
||||
public static void main(String[] args) throws Throwable {
|
File diff suppressed because it is too large
Load Diff
|
@ -16,7 +16,7 @@
|
|||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.petero.droidfish;
|
||||
package chess;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.FileInputStream;
|
||||
|
@ -42,55 +42,4 @@ public class FileUtil {
|
|||
inBuf.close();
|
||||
return ret.toArray(new String[0]);
|
||||
}
|
||||
|
||||
/** Read all data from an input stream. Return null if IO error. */
|
||||
public static String readFromStream(InputStream is) {
|
||||
InputStreamReader isr;
|
||||
try {
|
||||
isr = new InputStreamReader(is, "UTF-8");
|
||||
BufferedReader br = new BufferedReader(isr);
|
||||
StringBuilder sb = new StringBuilder();
|
||||
String line;
|
||||
while ((line = br.readLine()) != null) {
|
||||
sb.append(line);
|
||||
sb.append('\n');
|
||||
}
|
||||
br.close();
|
||||
return sb.toString();
|
||||
} catch (UnsupportedEncodingException e) {
|
||||
return null;
|
||||
} catch (IOException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/** Read data from input stream and write to file. */
|
||||
public static void writeFile(InputStream is, String outFile) throws IOException {
|
||||
OutputStream os = new FileOutputStream(outFile);
|
||||
try {
|
||||
byte[] buffer = new byte[16384];
|
||||
while (true) {
|
||||
int len = is.read(buffer);
|
||||
if (len <= 0)
|
||||
break;
|
||||
os.write(buffer, 0, len);
|
||||
}
|
||||
} finally {
|
||||
os.close();
|
||||
}
|
||||
}
|
||||
|
||||
/** Return the length of a file, or -1 if length can not be determined. */
|
||||
public static long getFileLength(String filename) {
|
||||
try {
|
||||
RandomAccessFile raf = new RandomAccessFile(filename, "r");
|
||||
try {
|
||||
return raf.length();
|
||||
} finally {
|
||||
raf.close();
|
||||
}
|
||||
} catch (IOException ex) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -16,7 +16,7 @@
|
|||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.petero.droidfish.gamelogic;
|
||||
package chess;
|
||||
|
||||
import java.io.DataInputStream;
|
||||
import java.io.DataOutputStream;
|
||||
|
@ -24,8 +24,7 @@ import java.io.IOException;
|
|||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.petero.droidfish.PGNOptions;
|
||||
import org.petero.droidfish.gamelogic.GameTree.Node;
|
||||
import chess.GameTree.Node;
|
||||
|
||||
public class Game {
|
||||
boolean pendingDrawOffer;
|
||||
|
@ -265,10 +264,6 @@ public class Game {
|
|||
}
|
||||
}
|
||||
|
||||
public final String getDrawInfo(boolean localized) {
|
||||
return tree.getGameStateInfo(localized);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the last played move, or null if no moves played yet.
|
||||
*/
|
||||
|
@ -520,7 +515,7 @@ public class Game {
|
|||
if (valid) {
|
||||
String playerAction = rep ? "draw rep" : "draw 50";
|
||||
if (m != null)
|
||||
playerAction += " " + TextIO.moveToString(pos, m, false, false);
|
||||
playerAction += " " + TextIO.moveToString(pos, m, false);
|
||||
addToGameTree(new Move(0, 0, 0), playerAction);
|
||||
} else {
|
||||
pendingDrawOffer = true;
|
|
@ -16,7 +16,7 @@
|
|||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.petero.droidfish.gamelogic;
|
||||
package chess;
|
||||
|
||||
import java.io.DataInputStream;
|
||||
import java.io.DataOutputStream;
|
||||
|
@ -30,9 +30,8 @@ import java.util.Locale;
|
|||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
|
||||
import org.petero.droidfish.PGNOptions;
|
||||
import org.petero.droidfish.gamelogic.Game.GameState;
|
||||
import org.petero.droidfish.gamelogic.TimeControlData.TimeControlField;
|
||||
import chess.Game.GameState;
|
||||
import chess.TimeControlData.TimeControlField;
|
||||
|
||||
public class GameTree {
|
||||
// Data from the seven tag roster (STR) part of the PGN standard
|
||||
|
@ -219,50 +218,6 @@ public class GameTree {
|
|||
}
|
||||
}
|
||||
|
||||
/** Update moveStrLocal in all game nodes. */
|
||||
public final void translateMoves() {
|
||||
List<Integer> currPath = new ArrayList<>();
|
||||
while (currentNode != rootNode) {
|
||||
Node child = currentNode;
|
||||
goBack();
|
||||
int childNum = currentNode.children.indexOf(child);
|
||||
currPath.add(childNum);
|
||||
}
|
||||
translateMovesHelper();
|
||||
for (int i = currPath.size() - 1; i >= 0; i--)
|
||||
goForward(currPath.get(i), false);
|
||||
}
|
||||
|
||||
private void translateMovesHelper() {
|
||||
ArrayList<Integer> currPath = new ArrayList<>();
|
||||
currPath.add(0);
|
||||
while (!currPath.isEmpty()) {
|
||||
int last = currPath.size() - 1;
|
||||
int currChild = currPath.get(last);
|
||||
if (currChild == 0) {
|
||||
ArrayList<Move> moves = MoveGen.instance.legalMoves(currentPos);
|
||||
currentNode.verifyChildren(currentPos, moves);
|
||||
int nc = currentNode.children.size();
|
||||
for (int i = 0; i < nc; i++) {
|
||||
Node child = currentNode.children.get(i);
|
||||
child.moveStrLocal = TextIO.moveToString(currentPos, child.move, false, true, moves);
|
||||
}
|
||||
}
|
||||
int nc = currentNode.children.size();
|
||||
if (currChild < nc) {
|
||||
goForward(currChild, false);
|
||||
currPath.add(0);
|
||||
} else {
|
||||
currPath.remove(last);
|
||||
last--;
|
||||
if (last >= 0) {
|
||||
currPath.set(last, currPath.get(last) + 1);
|
||||
goBack();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Export game tree in PGN format. */
|
||||
public final String toPGN(PGNOptions options) {
|
||||
PgnText pgnText = new PgnText();
|
||||
|
@ -733,8 +688,7 @@ public class GameTree {
|
|||
return -1;
|
||||
if (moves == null)
|
||||
moves = MoveGen.instance.legalMoves(currentPos);
|
||||
node.moveStr = TextIO.moveToString(currentPos, move, false, false, moves);
|
||||
node.moveStrLocal = TextIO.moveToString(currentPos, move, false, true, moves);
|
||||
node.moveStr = TextIO.moveToString(currentPos, move, false, moves);
|
||||
node.move = move;
|
||||
node.ui = new UndoInfo();
|
||||
currentNode.children.add(node);
|
||||
|
@ -867,38 +821,6 @@ public class GameTree {
|
|||
return GameState.ALIVE;
|
||||
}
|
||||
|
||||
/** Get additional info affecting gameState. A player "draw" or "resign" command. */
|
||||
final String getGameStateInfo(boolean localized) {
|
||||
String ret = "";
|
||||
String action = currentNode.playerAction;
|
||||
if (action.startsWith("draw rep ")) {
|
||||
ret = action.substring(9).trim();
|
||||
}
|
||||
if (action.startsWith("draw 50 ")) {
|
||||
ret = action.substring(8).trim();
|
||||
}
|
||||
if (localized) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for (int i = 0; i < ret.length(); i++) {
|
||||
int p = Piece.EMPTY;
|
||||
switch (ret.charAt(i)) {
|
||||
case 'Q': p = Piece.WQUEEN; break;
|
||||
case 'R': p = Piece.WROOK; break;
|
||||
case 'B': p = Piece.WBISHOP; break;
|
||||
case 'N': p = Piece.WKNIGHT; break;
|
||||
case 'K': p = Piece.WKING; break;
|
||||
case 'P': p = Piece.WPAWN; break;
|
||||
}
|
||||
if (p == Piece.EMPTY)
|
||||
sb.append(ret.charAt(i));
|
||||
else
|
||||
sb.append(TextIO.pieceToCharLocalized(p));
|
||||
}
|
||||
ret = sb.toString();
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/** Get PGN result string corresponding to the current position. */
|
||||
public final String getPGNResultString() {
|
||||
String gameResult = "*";
|
||||
|
@ -1007,7 +929,6 @@ public class GameTree {
|
|||
*/
|
||||
public static class Node {
|
||||
String moveStr; // String representation of move leading to this node. Empty string in root node.
|
||||
String moveStrLocal; // Localized version of moveStr
|
||||
public Move move; // Computed on demand for better PGN parsing performance.
|
||||
// Subtrees of invalid moves will be dropped when detected.
|
||||
// Always valid for current node.
|
||||
|
@ -1025,7 +946,6 @@ public class GameTree {
|
|||
|
||||
public Node() {
|
||||
this.moveStr = "";
|
||||
this.moveStrLocal = "";
|
||||
this.move = null;
|
||||
this.ui = null;
|
||||
this.playerAction = "";
|
||||
|
@ -1041,7 +961,6 @@ public class GameTree {
|
|||
public Node(Node parent, String moveStr, String playerAction, int remainingTime, int nag,
|
||||
String preComment, String postComment) {
|
||||
this.moveStr = moveStr;
|
||||
this.moveStrLocal = moveStr;
|
||||
this.move = null;
|
||||
this.ui = null;
|
||||
this.playerAction = playerAction;
|
||||
|
@ -1070,8 +989,7 @@ public class GameTree {
|
|||
moves = MoveGen.instance.legalMoves(nodePos);
|
||||
Move move = TextIO.stringToMove(nodePos, child.moveStr, moves);
|
||||
if (move != null) {
|
||||
child.moveStr = TextIO.moveToString(nodePos, move, false, false, moves);
|
||||
child.moveStrLocal = TextIO.moveToString(nodePos, move, false, true, moves);
|
||||
child.moveStr = TextIO.moveToString(nodePos, move, false, moves);
|
||||
child.move = move;
|
||||
child.ui = new UndoInfo();
|
||||
} else {
|
||||
|
@ -1139,7 +1057,6 @@ public class GameTree {
|
|||
static void readFromStream(DataInputStream dis, Node node) throws IOException {
|
||||
while (true) {
|
||||
node.moveStr = dis.readUTF();
|
||||
node.moveStrLocal = node.moveStr;
|
||||
int from = dis.readByte();
|
||||
if (from >= 0) {
|
||||
int to = dis.readByte();
|
||||
|
@ -1212,14 +1129,9 @@ public class GameTree {
|
|||
out.processToken(this, PgnToken.PERIOD, null);
|
||||
}
|
||||
}
|
||||
String str;
|
||||
if (options.exp.pieceType == PGNOptions.PT_ENGLISH) {
|
||||
str = moveStr;
|
||||
String str = moveStr;
|
||||
if (options.exp.pgnPromotions && (move != null) && (move.promoteTo != Piece.EMPTY))
|
||||
str = TextIO.pgnPromotion(str);
|
||||
} else {
|
||||
str = moveStrLocal;
|
||||
}
|
||||
out.processToken(this, PgnToken.SYMBOL, str);
|
||||
needMoveNr = false;
|
||||
}
|
||||
|
@ -1352,7 +1264,6 @@ public class GameTree {
|
|||
moveAdded = false;
|
||||
}
|
||||
nodeToAdd.moveStr = tok.token;
|
||||
nodeToAdd.moveStrLocal = tok.token;
|
||||
moveAdded = true;
|
||||
}
|
||||
break;
|
|
@ -100,8 +100,8 @@ public class Move {
|
|||
return (from * 64 + to) * 16 + promoteTo;
|
||||
}
|
||||
|
||||
/** Useful for debugging. */
|
||||
public final String toString() {
|
||||
return TextIO.moveToUCIString(this);
|
||||
/** Get move as a 16-bit value. */
|
||||
public int getCompressedMove() {
|
||||
return (from * 64 + to) * 16 + promoteTo;
|
||||
}
|
||||
}
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -16,7 +16,7 @@
|
|||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.petero.droidfish;
|
||||
package chess;
|
||||
|
||||
/** Settings controlling PGN import/export */
|
||||
public class PGNOptions {
|
|
@ -16,7 +16,7 @@
|
|||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.petero.droidfish.gamelogic;
|
||||
package chess;
|
||||
|
||||
/** A helper class that makes it possible to return two values from a function. */
|
||||
public final class Pair<T1, T2> {
|
|
@ -1,166 +0,0 @@
|
|||
package chess;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Map;
|
||||
import java.util.TreeMap;
|
||||
|
||||
public class Parameters {
|
||||
public static enum Type {
|
||||
CHECK,
|
||||
SPIN,
|
||||
COMBO,
|
||||
BUTTON,
|
||||
STRING
|
||||
}
|
||||
|
||||
public static class ParamBase {
|
||||
public String name;
|
||||
public Type type;
|
||||
public boolean visible;
|
||||
}
|
||||
|
||||
public static final class CheckParam extends ParamBase {
|
||||
public boolean value;
|
||||
public boolean defaultValue;
|
||||
CheckParam(String name, boolean visible, boolean def) {
|
||||
this.name = name;
|
||||
this.type = Type.CHECK;
|
||||
this.visible = visible;
|
||||
this.value = def;
|
||||
this.defaultValue = def;
|
||||
}
|
||||
}
|
||||
|
||||
public static final class SpinParam extends ParamBase {
|
||||
public int minValue;
|
||||
public int maxValue;
|
||||
public int value;
|
||||
public int defaultValue;
|
||||
SpinParam(String name, boolean visible, int minV, int maxV, int def) {
|
||||
this.name = name;
|
||||
this.type = Type.SPIN;
|
||||
this.visible = visible;
|
||||
this.minValue = minV;
|
||||
this.maxValue = maxV;
|
||||
this.value = def;
|
||||
this.defaultValue = def;
|
||||
}
|
||||
}
|
||||
|
||||
public static final class ComboParam extends ParamBase {
|
||||
public String[] allowedValues;
|
||||
public String value;
|
||||
public String defaultValue;
|
||||
ComboParam(String name, boolean visible, String[] allowed, String def) {
|
||||
this.name = name;
|
||||
this.type = Type.COMBO;
|
||||
this.visible = visible;
|
||||
this.allowedValues = allowed;
|
||||
this.value = def;
|
||||
this.defaultValue = def;
|
||||
}
|
||||
}
|
||||
|
||||
public static final class ButtonParam extends ParamBase {
|
||||
ButtonParam(String name, boolean visible) {
|
||||
this.name = name;
|
||||
this.type = Type.BUTTON;
|
||||
this.visible = visible;
|
||||
}
|
||||
}
|
||||
|
||||
public static final class StringParam extends ParamBase {
|
||||
public String value;
|
||||
public String defaultValue;
|
||||
StringParam(String name, boolean visible, String def) {
|
||||
this.name = name;
|
||||
this.type = Type.STRING;
|
||||
this.visible = visible;
|
||||
this.value = def;
|
||||
this.defaultValue = def;
|
||||
}
|
||||
}
|
||||
|
||||
public static Parameters instance() {
|
||||
return inst;
|
||||
}
|
||||
public final String[] getParamNames() {
|
||||
ArrayList<String> parNames = new ArrayList<>();
|
||||
for (Map.Entry<String, ParamBase> e : params.entrySet())
|
||||
if (e.getValue().visible)
|
||||
parNames.add(e.getKey());
|
||||
return parNames.toArray(new String[0]);
|
||||
}
|
||||
|
||||
public final ParamBase getParam(String name) {
|
||||
return params.get(name);
|
||||
}
|
||||
|
||||
private static final Parameters inst = new Parameters();
|
||||
private Map<String, ParamBase> params = new TreeMap<>();
|
||||
|
||||
private Parameters() {
|
||||
addPar(new SpinParam("qV", false, -200, 200, 0));
|
||||
addPar(new SpinParam("rV", false, -200, 200, 0));
|
||||
addPar(new SpinParam("bV", false, -200, 200, 0));
|
||||
addPar(new SpinParam("nV", false, -200, 200, 0));
|
||||
addPar(new SpinParam("pV", false, -200, 200, 0));
|
||||
}
|
||||
|
||||
private void addPar(ParamBase p) {
|
||||
params.put(p.name.toLowerCase(), p);
|
||||
}
|
||||
|
||||
final boolean getBooleanPar(String name) {
|
||||
return ((CheckParam)params.get(name.toLowerCase())).value;
|
||||
}
|
||||
final int getIntPar(String name) {
|
||||
int ret = ((SpinParam)params.get(name.toLowerCase())).value;
|
||||
return ret;
|
||||
}
|
||||
final String getStringPar(String name) {
|
||||
return ((StringParam)params.get(name.toLowerCase())).value;
|
||||
}
|
||||
|
||||
public final void set(String name, String value) {
|
||||
ParamBase p = params.get(name.toLowerCase());
|
||||
if (p == null)
|
||||
return;
|
||||
switch (p.type) {
|
||||
case CHECK: {
|
||||
CheckParam cp = (CheckParam)p;
|
||||
if (value.toLowerCase().equals("true"))
|
||||
cp.value = true;
|
||||
else if (value.toLowerCase().equals("false"))
|
||||
cp.value = false;
|
||||
break;
|
||||
}
|
||||
case SPIN: {
|
||||
SpinParam sp = (SpinParam)p;
|
||||
try {
|
||||
int val = Integer.parseInt(value);
|
||||
if ((val >= sp.minValue) && (val <= sp.maxValue))
|
||||
sp.value = val;
|
||||
} catch (NumberFormatException ex) {
|
||||
}
|
||||
break;
|
||||
}
|
||||
case COMBO: {
|
||||
ComboParam cp = (ComboParam)p;
|
||||
for (String allowed : cp.allowedValues)
|
||||
if (allowed.toLowerCase().equals(value.toLowerCase())) {
|
||||
cp.value = allowed;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case BUTTON:
|
||||
break;
|
||||
case STRING: {
|
||||
StringParam sp = (StringParam)p;
|
||||
sp.value = value;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -16,7 +16,7 @@
|
|||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.petero.droidfish.gamelogic;
|
||||
package chess;
|
||||
|
||||
/** A token in a PGN data stream. Used by the PGN parser. */
|
||||
public class PgnToken {
|
|
@ -35,9 +35,6 @@ public class Position {
|
|||
public long[] pieceTypeBB;
|
||||
public long whiteBB, blackBB;
|
||||
|
||||
// Piece square table scores
|
||||
public short[] psScore1, psScore2;
|
||||
|
||||
public boolean whiteMove;
|
||||
|
||||
/** Bit definitions for the castleMask bit mask. */
|
||||
|
@ -59,10 +56,6 @@ public class Position {
|
|||
private long hashKey; // Cached Zobrist hash key
|
||||
private long pHashKey;
|
||||
public int wKingSq, bKingSq; // Cached king positions
|
||||
public int wMtrl; // Total value of all white pieces and pawns
|
||||
public int bMtrl; // Total value of all black pieces and pawns
|
||||
public int wMtrlPawns; // Total value of all white pawns
|
||||
public int bMtrlPawns; // Total value of all black pawns
|
||||
|
||||
/** Initialize board to empty position. */
|
||||
public Position() {
|
||||
|
@ -70,12 +63,8 @@ public class Position {
|
|||
for (int i = 0; i < 64; i++)
|
||||
squares[i] = Piece.EMPTY;
|
||||
pieceTypeBB = new long[Piece.nPieceTypes];
|
||||
psScore1 = new short[Piece.nPieceTypes];
|
||||
psScore2 = new short[Piece.nPieceTypes];
|
||||
for (int i = 0; i < Piece.nPieceTypes; i++) {
|
||||
pieceTypeBB[i] = 0L;
|
||||
psScore1[i] = 0;
|
||||
psScore2[i] = 0;
|
||||
}
|
||||
whiteBB = blackBB = 0L;
|
||||
whiteMove = true;
|
||||
|
@ -85,8 +74,6 @@ public class Position {
|
|||
fullMoveCounter = 1;
|
||||
hashKey = computeZobristHash();
|
||||
wKingSq = bKingSq = -1;
|
||||
wMtrl = bMtrl = -Evaluate.kV;
|
||||
wMtrlPawns = bMtrlPawns = 0;
|
||||
}
|
||||
|
||||
public Position(Position other) {
|
||||
|
@ -94,12 +81,8 @@ public class Position {
|
|||
for (int i = 0; i < 64; i++)
|
||||
squares[i] = other.squares[i];
|
||||
pieceTypeBB = new long[Piece.nPieceTypes];
|
||||
psScore1 = new short[Piece.nPieceTypes];
|
||||
psScore2 = new short[Piece.nPieceTypes];
|
||||
for (int i = 0; i < Piece.nPieceTypes; i++) {
|
||||
pieceTypeBB[i] = other.pieceTypeBB[i];
|
||||
psScore1[i] = other.psScore1[i];
|
||||
psScore2[i] = other.psScore2[i];
|
||||
}
|
||||
whiteBB = other.whiteBB;
|
||||
blackBB = other.blackBB;
|
||||
|
@ -112,10 +95,6 @@ public class Position {
|
|||
pHashKey = other.pHashKey;
|
||||
wKingSq = other.wKingSq;
|
||||
bKingSq = other.bKingSq;
|
||||
wMtrl = other.wMtrl;
|
||||
bMtrl = other.bMtrl;
|
||||
wMtrlPawns = other.wMtrlPawns;
|
||||
bMtrlPawns = other.bMtrlPawns;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -235,9 +214,6 @@ public class Position {
|
|||
if (piece == Piece.BKING)
|
||||
bKingSq = to;
|
||||
}
|
||||
|
||||
psScore1[piece] += Evaluate.psTab1[piece][to] - Evaluate.psTab1[piece][from];
|
||||
psScore2[piece] += Evaluate.psTab2[piece][to] - Evaluate.psTab2[piece][from];
|
||||
}
|
||||
|
||||
/** Set a square to a piece value. */
|
||||
|
@ -255,52 +231,36 @@ public class Position {
|
|||
pieceTypeBB[piece] |= sqMask;
|
||||
|
||||
if (removedPiece != Piece.EMPTY) {
|
||||
int pVal = Evaluate.pieceValue[removedPiece];
|
||||
if (Piece.isWhite(removedPiece)) {
|
||||
wMtrl -= pVal;
|
||||
whiteBB &= ~sqMask;
|
||||
if (removedPiece == Piece.WPAWN) {
|
||||
wMtrlPawns -= pVal;
|
||||
pHashKey ^= psHashKeys[Piece.WPAWN][square];
|
||||
}
|
||||
} else {
|
||||
bMtrl -= pVal;
|
||||
blackBB &= ~sqMask;
|
||||
if (removedPiece == Piece.BPAWN) {
|
||||
bMtrlPawns -= pVal;
|
||||
pHashKey ^= psHashKeys[Piece.BPAWN][square];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (piece != Piece.EMPTY) {
|
||||
int pVal = Evaluate.pieceValue[piece];
|
||||
if (Piece.isWhite(piece)) {
|
||||
wMtrl += pVal;
|
||||
whiteBB |= sqMask;
|
||||
if (piece == Piece.WPAWN) {
|
||||
wMtrlPawns += pVal;
|
||||
pHashKey ^= psHashKeys[Piece.WPAWN][square];
|
||||
}
|
||||
if (piece == Piece.WKING)
|
||||
wKingSq = square;
|
||||
} else {
|
||||
bMtrl += pVal;
|
||||
blackBB |= sqMask;
|
||||
if (piece == Piece.BPAWN) {
|
||||
bMtrlPawns += pVal;
|
||||
pHashKey ^= psHashKeys[Piece.BPAWN][square];
|
||||
}
|
||||
if (piece == Piece.BKING)
|
||||
bKingSq = square;
|
||||
}
|
||||
}
|
||||
|
||||
// Update piece/square table scores
|
||||
psScore1[removedPiece] -= Evaluate.psTab1[removedPiece][square];
|
||||
psScore2[removedPiece] -= Evaluate.psTab2[removedPiece][square];
|
||||
psScore1[piece] += Evaluate.psTab1[piece][square];
|
||||
psScore2[piece] += Evaluate.psTab2[piece][square];
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -374,6 +334,15 @@ public class Position {
|
|||
return white ? wKingSq : bKingSq;
|
||||
}
|
||||
|
||||
/** Count number of pieces of a certain type. */
|
||||
public final int nPieces(int pType) {
|
||||
int ret = 0;
|
||||
for (int sq = 0; sq < 64; sq++)
|
||||
if (squares[sq] == pType)
|
||||
ret++;
|
||||
return ret;
|
||||
}
|
||||
|
||||
/** Apply a move to the current position. */
|
||||
public final void makeMove(Move move, UndoInfo ui) {
|
||||
ui.capturedPiece = squares[move.to];
|
||||
|
@ -636,10 +605,4 @@ public class Position {
|
|||
throw new UnsupportedOperationException("SHA-1 not available");
|
||||
}
|
||||
}
|
||||
|
||||
/** Useful for debugging. */
|
||||
public final String toString() {
|
||||
return TextIO.asciiBoard(this) + (whiteMove ? "white\n" : "black\n") +
|
||||
Long.toHexString(zobristHash()) + "\n";
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
CuckooChess - A java chess program.
|
||||
DroidFish - An Android chess program.
|
||||
Copyright (C) 2011 Peter Österlund, peterosterlund2@gmail.com
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
|
@ -18,17 +18,22 @@
|
|||
|
||||
package chess;
|
||||
|
||||
import java.util.Locale;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/** Handle conversion of positions and moves to/from text format. */
|
||||
public class TextIO {
|
||||
static public final String startPosFEN = "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1";
|
||||
|
||||
/** Parse a FEN string and return a chess Position object. */
|
||||
public static Position readFEN(String fen) throws ChessParseError {
|
||||
fen = fen.trim();
|
||||
Position pos = new Position();
|
||||
String[] words = fen.split(" ");
|
||||
if (words.length < 2) {
|
||||
throw new ChessParseError("Too few spaces");
|
||||
if (words.length < 2)
|
||||
throw new ChessParseError("too few pieces");
|
||||
for (int i = 0; i < words.length; i++) {
|
||||
words[i] = words[i].trim();
|
||||
}
|
||||
|
||||
// Piece placement
|
||||
|
@ -58,13 +63,21 @@ public class TextIO {
|
|||
case 'r': safeSetPiece(pos, col, row, Piece.BROOK); col++; break;
|
||||
case 'q': safeSetPiece(pos, col, row, Piece.BQUEEN); col++; break;
|
||||
case 'k': safeSetPiece(pos, col, row, Piece.BKING); col++; break;
|
||||
default: throw new ChessParseError("Invalid piece");
|
||||
default: throw new ChessParseError("invalid piece", pos);
|
||||
}
|
||||
}
|
||||
if (words[1].length() == 0) {
|
||||
throw new ChessParseError("Invalid side");
|
||||
|
||||
if (words[1].length() > 0) {
|
||||
boolean wtm;
|
||||
switch (words[1].charAt(0)) {
|
||||
case 'w': wtm = true; break;
|
||||
case 'b': wtm = false; break;
|
||||
default: throw new ChessParseError("invalid side", pos);
|
||||
}
|
||||
pos.setWhiteMove(wtm);
|
||||
} else {
|
||||
throw new ChessParseError("invalid side", pos);
|
||||
}
|
||||
pos.setWhiteMove(words[1].charAt(0) == 'w');
|
||||
|
||||
// Castling rights
|
||||
int castleMask = 0;
|
||||
|
@ -87,20 +100,32 @@ public class TextIO {
|
|||
case '-':
|
||||
break;
|
||||
default:
|
||||
throw new ChessParseError("Invalid castling flags");
|
||||
throw new ChessParseError("invalid castling flags", pos);
|
||||
}
|
||||
}
|
||||
}
|
||||
pos.setCastleMask(castleMask);
|
||||
removeBogusCastleFlags(pos);
|
||||
|
||||
if (words.length > 3) {
|
||||
// En passant target square
|
||||
String epString = words[3];
|
||||
if (!epString.equals("-")) {
|
||||
if (epString.length() < 2) {
|
||||
throw new ChessParseError("Invalid en passant square");
|
||||
if (epString.length() < 2)
|
||||
throw new ChessParseError("invalid en passant square", pos);
|
||||
int epSq = getSquare(epString);
|
||||
if (epSq != -1) {
|
||||
if (pos.whiteMove) {
|
||||
if ((Position.getY(epSq) != 5) || (pos.getPiece(epSq) != Piece.EMPTY) ||
|
||||
(pos.getPiece(epSq - 8) != Piece.BPAWN))
|
||||
epSq = -1;
|
||||
} else {
|
||||
if ((Position.getY(epSq) != 2) || (pos.getPiece(epSq) != Piece.EMPTY) ||
|
||||
(pos.getPiece(epSq + 8) != Piece.WPAWN))
|
||||
epSq = -1;
|
||||
}
|
||||
pos.setEpSquare(epSq);
|
||||
}
|
||||
pos.setEpSquare(getSquare(epString));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -116,30 +141,40 @@ public class TextIO {
|
|||
}
|
||||
|
||||
// Each side must have exactly one king
|
||||
int wKings = 0;
|
||||
int bKings = 0;
|
||||
for (int x = 0; x < 8; x++) {
|
||||
for (int y = 0; y < 8; y++) {
|
||||
int p = pos.getPiece(Position.getSquare(x, y));
|
||||
if (p == Piece.WKING) {
|
||||
wKings++;
|
||||
} else if (p == Piece.BKING) {
|
||||
bKings++;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (wKings != 1) {
|
||||
throw new ChessParseError("White must have exactly one king");
|
||||
}
|
||||
if (bKings != 1) {
|
||||
throw new ChessParseError("Black must have exactly one king");
|
||||
}
|
||||
int[] nPieces = new int[Piece.nPieceTypes];
|
||||
for (int i = 0; i < Piece.nPieceTypes; i++)
|
||||
nPieces[i] = 0;
|
||||
for (int x = 0; x < 8; x++)
|
||||
for (int y = 0; y < 8; y++)
|
||||
nPieces[pos.getPiece(Position.getSquare(x, y))]++;
|
||||
if (nPieces[Piece.WKING] != 1)
|
||||
throw new ChessParseError("white num kings", pos);
|
||||
if (nPieces[Piece.BKING] != 1)
|
||||
throw new ChessParseError("black num kings", pos);
|
||||
|
||||
// White must not have too many pieces
|
||||
int maxWPawns = 8;
|
||||
maxWPawns -= Math.max(0, nPieces[Piece.WKNIGHT] - 2);
|
||||
maxWPawns -= Math.max(0, nPieces[Piece.WBISHOP] - 2);
|
||||
maxWPawns -= Math.max(0, nPieces[Piece.WROOK ] - 2);
|
||||
maxWPawns -= Math.max(0, nPieces[Piece.WQUEEN ] - 1);
|
||||
if (nPieces[Piece.WPAWN] > maxWPawns)
|
||||
throw new ChessParseError("too many white pieces", pos);
|
||||
|
||||
// Black must not have too many pieces
|
||||
int maxBPawns = 8;
|
||||
maxBPawns -= Math.max(0, nPieces[Piece.BKNIGHT] - 2);
|
||||
maxBPawns -= Math.max(0, nPieces[Piece.BBISHOP] - 2);
|
||||
maxBPawns -= Math.max(0, nPieces[Piece.BROOK ] - 2);
|
||||
maxBPawns -= Math.max(0, nPieces[Piece.BQUEEN ] - 1);
|
||||
if (nPieces[Piece.BPAWN] > maxBPawns)
|
||||
throw new ChessParseError("too many black pieces", pos);
|
||||
|
||||
// Make sure king can not be captured
|
||||
Position pos2 = new Position(pos);
|
||||
pos2.setWhiteMove(!pos.whiteMove);
|
||||
if (MoveGen.inCheck(pos2)) {
|
||||
throw new ChessParseError("King capture possible");
|
||||
throw new ChessParseError("king capture possible", pos);
|
||||
}
|
||||
|
||||
fixupEPSquare(pos);
|
||||
|
@ -147,15 +182,28 @@ public class TextIO {
|
|||
return pos;
|
||||
}
|
||||
|
||||
public static void removeBogusCastleFlags(Position pos) {
|
||||
int castleMask = pos.getCastleMask();
|
||||
int validCastle = 0;
|
||||
if (pos.getPiece(4) == Piece.WKING) {
|
||||
if (pos.getPiece(0) == Piece.WROOK) validCastle |= (1 << Position.A1_CASTLE);
|
||||
if (pos.getPiece(7) == Piece.WROOK) validCastle |= (1 << Position.H1_CASTLE);
|
||||
}
|
||||
if (pos.getPiece(60) == Piece.BKING) {
|
||||
if (pos.getPiece(56) == Piece.BROOK) validCastle |= (1 << Position.A8_CASTLE);
|
||||
if (pos.getPiece(63) == Piece.BROOK) validCastle |= (1 << Position.H8_CASTLE);
|
||||
}
|
||||
castleMask &= validCastle;
|
||||
pos.setCastleMask(castleMask);
|
||||
}
|
||||
|
||||
/** Remove pseudo-legal EP square if it is not legal, ie would leave king in check. */
|
||||
public static void fixupEPSquare(Position pos) {
|
||||
int epSquare = pos.getEpSquare();
|
||||
if (epSquare >= 0) {
|
||||
MoveGen.MoveList moves = MoveGen.instance.pseudoLegalMoves(pos);
|
||||
MoveGen.removeIllegal(pos, moves);
|
||||
ArrayList<Move> moves = MoveGen.instance.legalMoves(pos);
|
||||
boolean epValid = false;
|
||||
for (int mi = 0; mi < moves.size; mi++) {
|
||||
Move m = moves.m[mi];
|
||||
for (Move m : moves) {
|
||||
if (m.to == epSquare) {
|
||||
if (pos.getPiece(m.from) == (pos.whiteMove ? Piece.WPAWN : Piece.BPAWN)) {
|
||||
epValid = true;
|
||||
|
@ -163,18 +211,17 @@ public class TextIO {
|
|||
}
|
||||
}
|
||||
}
|
||||
if (!epValid) {
|
||||
if (!epValid)
|
||||
pos.setEpSquare(-1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void safeSetPiece(Position pos, int col, int row, int p) throws ChessParseError {
|
||||
if (row < 0) throw new ChessParseError("Too many rows");
|
||||
if (col > 7) throw new ChessParseError("Too many columns");
|
||||
if (row < 0) throw new ChessParseError("too many rows");
|
||||
if (col > 7) throw new ChessParseError("too many columns");
|
||||
if ((p == Piece.WPAWN) || (p == Piece.BPAWN)) {
|
||||
if ((row == 0) || (row == 7))
|
||||
throw new ChessParseError("Pawn on first/last rank");
|
||||
throw new ChessParseError("pawn on first last rank");
|
||||
}
|
||||
pos.setPiece(Position.getSquare(col, row), p);
|
||||
}
|
||||
|
@ -269,14 +316,15 @@ public class TextIO {
|
|||
* @param pos The chess position.
|
||||
* @param move The executed move.
|
||||
* @param longForm If true, use long notation, eg Ng1-f3.
|
||||
* Otherwise, use short notation, eg Nf3
|
||||
* Otherwise, use short notation, eg Nf3.
|
||||
*/
|
||||
public static String moveToString(Position pos, Move move, boolean longForm) {
|
||||
MoveGen.MoveList moves = MoveGen.instance.pseudoLegalMoves(pos);
|
||||
MoveGen.removeIllegal(pos, moves);
|
||||
return moveToString(pos, move, longForm, moves);
|
||||
return moveToString(pos, move, longForm, null);
|
||||
}
|
||||
private static String moveToString(Position pos, Move move, boolean longForm, MoveGen.MoveList moves) {
|
||||
public static String moveToString(Position pos, Move move, boolean longForm,
|
||||
List<Move> moves) {
|
||||
if ((move == null) || move.equals(new Move(0, 0, 0)))
|
||||
return "--";
|
||||
StringBuilder ret = new StringBuilder();
|
||||
int wKingOrigPos = Position.getSquare(4, 0);
|
||||
int bKingOrigPos = Position.getSquare(4, 7);
|
||||
|
@ -315,10 +363,11 @@ public class TextIO {
|
|||
int numSameTarget = 0;
|
||||
int numSameFile = 0;
|
||||
int numSameRow = 0;
|
||||
for (int mi = 0; mi < moves.size; mi++) {
|
||||
Move m = moves.m[mi];
|
||||
if (m == null)
|
||||
break;
|
||||
if (moves == null)
|
||||
moves = MoveGen.instance.legalMoves(pos);
|
||||
int mSize = moves.size();
|
||||
for (int mi = 0; mi < mSize; mi++) {
|
||||
Move m = moves.get(mi);
|
||||
if ((pos.getPiece(m.from) == p) && (m.to == move.to)) {
|
||||
numSameTarget++;
|
||||
if (Position.getX(m.from) == x1)
|
||||
|
@ -344,26 +393,193 @@ public class TextIO {
|
|||
}
|
||||
ret.append((char) (x2 + 'a'));
|
||||
ret.append((char) (y2 + '1'));
|
||||
if (move.promoteTo != Piece.EMPTY) {
|
||||
if (move.promoteTo != Piece.EMPTY)
|
||||
ret.append(pieceToChar(move.promoteTo));
|
||||
}
|
||||
}
|
||||
UndoInfo ui = new UndoInfo();
|
||||
if (MoveGen.givesCheck(pos, move)) {
|
||||
pos.makeMove(move, ui);
|
||||
MoveGen.MoveList nextMoves = MoveGen.instance.pseudoLegalMoves(pos);
|
||||
MoveGen.removeIllegal(pos, nextMoves);
|
||||
if (nextMoves.size == 0) {
|
||||
boolean givesCheck = MoveGen.inCheck(pos);
|
||||
if (givesCheck) {
|
||||
ArrayList<Move> nextMoves = MoveGen.instance.legalMoves(pos);
|
||||
if (nextMoves.size() == 0) {
|
||||
ret.append('#');
|
||||
} else {
|
||||
ret.append('+');
|
||||
}
|
||||
pos.unMakeMove(move, ui);
|
||||
}
|
||||
pos.unMakeMove(move, ui);
|
||||
|
||||
return ret.toString();
|
||||
}
|
||||
|
||||
private static boolean isCapture(Position pos, Move move) {
|
||||
if (pos.getPiece(move.to) == Piece.EMPTY) {
|
||||
int p = pos.getPiece(move.from);
|
||||
if ((p == (pos.whiteMove ? Piece.WPAWN : Piece.BPAWN)) && (move.to == pos.getEpSquare())) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Decide if move is valid in position pos.
|
||||
* @param pos Position for which to test move.
|
||||
* @param move The move to check for validity.
|
||||
* @return True if move is valid in position pos, false otherwise.
|
||||
*/
|
||||
public static boolean isValid(Position pos, Move move) {
|
||||
if (move == null)
|
||||
return false;
|
||||
ArrayList<Move> moves = new MoveGen().legalMoves(pos);
|
||||
for (int i = 0; i < moves.size(); i++)
|
||||
if (move.equals(moves.get(i)))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
private final static class MoveInfo {
|
||||
int piece; // -1 for unspecified
|
||||
int fromX, fromY, toX, toY; // -1 for unspecified
|
||||
int promPiece; // -1 for unspecified
|
||||
MoveInfo() { piece = fromX = fromY = toX = toY = promPiece = -1; }
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a chess move string to a Move object.
|
||||
* The string may specify any combination of piece/source/target/promotion
|
||||
* information as long as it matches exactly one valid move.
|
||||
*/
|
||||
public static Move stringToMove(Position pos, String strMove) {
|
||||
return stringToMove(pos, strMove, null);
|
||||
}
|
||||
public static Move stringToMove(Position pos, String strMove,
|
||||
ArrayList<Move> moves) {
|
||||
if (strMove.equals("--"))
|
||||
return new Move(0, 0, 0);
|
||||
|
||||
strMove = strMove.replaceAll("=", "");
|
||||
strMove = strMove.replaceAll("\\+", "");
|
||||
strMove = strMove.replaceAll("#", "");
|
||||
boolean wtm = pos.whiteMove;
|
||||
|
||||
MoveInfo info = new MoveInfo();
|
||||
boolean capture = false;
|
||||
if (strMove.equals("O-O") || strMove.equals("0-0") || strMove.equals("o-o")) {
|
||||
info.piece = wtm ? Piece.WKING : Piece.BKING;
|
||||
info.fromX = 4;
|
||||
info.toX = 6;
|
||||
info.fromY = info.toY = wtm ? 0 : 7;
|
||||
info.promPiece = Piece.EMPTY;
|
||||
} else if (strMove.equals("O-O-O") || strMove.equals("0-0-0") || strMove.equals("o-o-o")) {
|
||||
info.piece = wtm ? Piece.WKING : Piece.BKING;
|
||||
info.fromX = 4;
|
||||
info.toX = 2;
|
||||
info.fromY = info.toY = wtm ? 0 : 7;
|
||||
info.promPiece = Piece.EMPTY;
|
||||
} else {
|
||||
boolean atToSq = false;
|
||||
for (int i = 0; i < strMove.length(); i++) {
|
||||
char c = strMove.charAt(i);
|
||||
if (i == 0) {
|
||||
int piece = charToPiece(wtm, c);
|
||||
if (piece >= 0) {
|
||||
info.piece = piece;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
int tmpX = c - 'a';
|
||||
if ((tmpX >= 0) && (tmpX < 8)) {
|
||||
if (atToSq || (info.fromX >= 0))
|
||||
info.toX = tmpX;
|
||||
else
|
||||
info.fromX = tmpX;
|
||||
}
|
||||
int tmpY = c - '1';
|
||||
if ((tmpY >= 0) && (tmpY < 8)) {
|
||||
if (atToSq || (info.fromY >= 0))
|
||||
info.toY = tmpY;
|
||||
else
|
||||
info.fromY = tmpY;
|
||||
}
|
||||
if ((c == 'x') || (c == '-')) {
|
||||
atToSq = true;
|
||||
if (c == 'x')
|
||||
capture = true;
|
||||
}
|
||||
if (i == strMove.length() - 1) {
|
||||
int promPiece = charToPiece(wtm, c);
|
||||
if (promPiece >= 0) {
|
||||
info.promPiece = promPiece;
|
||||
}
|
||||
}
|
||||
}
|
||||
if ((info.fromX >= 0) && (info.toX < 0)) {
|
||||
info.toX = info.fromX;
|
||||
info.fromX = -1;
|
||||
}
|
||||
if ((info.fromY >= 0) && (info.toY < 0)) {
|
||||
info.toY = info.fromY;
|
||||
info.fromY = -1;
|
||||
}
|
||||
if (info.piece < 0) {
|
||||
boolean haveAll = (info.fromX >= 0) && (info.fromY >= 0) &&
|
||||
(info.toX >= 0) && (info.toY >= 0);
|
||||
if (!haveAll)
|
||||
info.piece = wtm ? Piece.WPAWN : Piece.BPAWN;
|
||||
}
|
||||
if (info.promPiece < 0)
|
||||
info.promPiece = Piece.EMPTY;
|
||||
}
|
||||
|
||||
if (moves == null)
|
||||
moves = MoveGen.instance.legalMoves(pos);
|
||||
|
||||
ArrayList<Move> matches = new ArrayList<>(2);
|
||||
for (int i = 0; i < moves.size(); i++) {
|
||||
Move m = moves.get(i);
|
||||
int p = pos.getPiece(m.from);
|
||||
boolean match = true;
|
||||
if ((info.piece >= 0) && (info.piece != p))
|
||||
match = false;
|
||||
if ((info.fromX >= 0) && (info.fromX != Position.getX(m.from)))
|
||||
match = false;
|
||||
if ((info.fromY >= 0) && (info.fromY != Position.getY(m.from)))
|
||||
match = false;
|
||||
if ((info.toX >= 0) && (info.toX != Position.getX(m.to)))
|
||||
match = false;
|
||||
if ((info.toY >= 0) && (info.toY != Position.getY(m.to)))
|
||||
match = false;
|
||||
if ((info.promPiece >= 0) && (info.promPiece != m.promoteTo))
|
||||
match = false;
|
||||
if (match) {
|
||||
matches.add(m);
|
||||
}
|
||||
}
|
||||
int nMatches = matches.size();
|
||||
if (nMatches == 0)
|
||||
return null;
|
||||
else if (nMatches == 1)
|
||||
return matches.get(0);
|
||||
if (!capture)
|
||||
return null;
|
||||
Move move = null;
|
||||
for (int i = 0; i < matches.size(); i++) {
|
||||
Move m = matches.get(i);
|
||||
int capt = pos.getPiece(m.to);
|
||||
if (capt != Piece.EMPTY) {
|
||||
if (move == null)
|
||||
move = m;
|
||||
else
|
||||
return null;
|
||||
}
|
||||
}
|
||||
return move;
|
||||
}
|
||||
|
||||
/** Convert a move object to UCI string format. */
|
||||
public static String moveToUCIString(Move m) {
|
||||
String ret = squareToString(m.from);
|
||||
|
@ -392,10 +608,10 @@ public class TextIO {
|
|||
}
|
||||
|
||||
/**
|
||||
* Convert a string to a Move object.
|
||||
* Convert a string in UCI move format to a Move object.
|
||||
* @return A move object, or null if move has invalid syntax
|
||||
*/
|
||||
public static Move uciStringToMove(String move) {
|
||||
public static Move UCIstringToMove(String move) {
|
||||
Move m = null;
|
||||
if ((move.length() < 4) || (move.length() > 5))
|
||||
return m;
|
||||
|
@ -440,95 +656,6 @@ public class TextIO {
|
|||
return m;
|
||||
}
|
||||
|
||||
private static boolean isCapture(Position pos, Move move) {
|
||||
if (pos.getPiece(move.to) == Piece.EMPTY) {
|
||||
int p = pos.getPiece(move.from);
|
||||
if ((p == (pos.whiteMove ? Piece.WPAWN : Piece.BPAWN)) && (move.to == pos.getEpSquare())) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a chess move string to a Move object.
|
||||
* Any prefix of the string representation of a valid move counts as a legal move string,
|
||||
* as long as the string only matches one valid move.
|
||||
*/
|
||||
public static Move stringToMove(Position pos, String strMove) {
|
||||
strMove = strMove.replaceAll("=", "");
|
||||
Move move = null;
|
||||
if (strMove.length() == 0)
|
||||
return move;
|
||||
MoveGen.MoveList moves = MoveGen.instance.pseudoLegalMoves(pos);
|
||||
MoveGen.removeIllegal(pos, moves);
|
||||
{
|
||||
char lastChar = strMove.charAt(strMove.length() - 1);
|
||||
if ((lastChar == '#') || (lastChar == '+')) {
|
||||
MoveGen.MoveList subMoves = new MoveGen.MoveList();
|
||||
int len = 0;
|
||||
for (int mi = 0; mi < moves.size; mi++) {
|
||||
Move m = moves.m[mi];
|
||||
String str1 = TextIO.moveToString(pos, m, true, moves);
|
||||
if (str1.charAt(str1.length() - 1) == lastChar) {
|
||||
subMoves.m[len++] = m;
|
||||
}
|
||||
}
|
||||
subMoves.size = len;
|
||||
moves = subMoves;
|
||||
strMove = normalizeMoveString(strMove);
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < 2; i++) {
|
||||
// Search for full match
|
||||
for (int mi = 0; mi < moves.size; mi++) {
|
||||
Move m = moves.m[mi];
|
||||
String str1 = normalizeMoveString(TextIO.moveToString(pos, m, true, moves));
|
||||
String str2 = normalizeMoveString(TextIO.moveToString(pos, m, false, moves));
|
||||
if (i == 0) {
|
||||
if (strMove.equals(str1) || strMove.equals(str2)) {
|
||||
return m;
|
||||
}
|
||||
} else {
|
||||
if (strMove.toLowerCase().equals(str1.toLowerCase()) ||
|
||||
strMove.toLowerCase().equals(str2.toLowerCase())) {
|
||||
return m;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < 2; i++) {
|
||||
// Search for unique substring match
|
||||
for (int mi = 0; mi < moves.size; mi++) {
|
||||
Move m = moves.m[mi];
|
||||
String str1 = normalizeMoveString(TextIO.moveToString(pos, m, true));
|
||||
String str2 = normalizeMoveString(TextIO.moveToString(pos, m, false));
|
||||
boolean match;
|
||||
if (i == 0) {
|
||||
match = (str1.startsWith(strMove) || str2.startsWith(strMove));
|
||||
} else {
|
||||
match = (str1.toLowerCase().startsWith(strMove.toLowerCase()) ||
|
||||
str2.toLowerCase().startsWith(strMove.toLowerCase()));
|
||||
}
|
||||
if (match) {
|
||||
if (move != null) {
|
||||
return null; // More than one match, not ok
|
||||
} else {
|
||||
move = m;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (move != null)
|
||||
return move;
|
||||
}
|
||||
return move;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a string, such as "e4" to a square number.
|
||||
* @return The square number, or -1 if not a legal square.
|
||||
|
@ -553,50 +680,6 @@ public class TextIO {
|
|||
return ret.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an ascii representation of a position.
|
||||
*/
|
||||
public static String asciiBoard(Position pos) {
|
||||
StringBuilder ret = new StringBuilder(400);
|
||||
String nl = String.format(Locale.US, "%n");
|
||||
ret.append(" +----+----+----+----+----+----+----+----+"); ret.append(nl);
|
||||
for (int y = 7; y >= 0; y--) {
|
||||
ret.append(" |");
|
||||
for (int x = 0; x < 8; x++) {
|
||||
ret.append(' ');
|
||||
int p = pos.getPiece(Position.getSquare(x, y));
|
||||
if (p == Piece.EMPTY) {
|
||||
boolean dark = Position.darkSquare(x, y);
|
||||
ret.append(dark ? ".. |" : " |");
|
||||
} else {
|
||||
ret.append(Piece.isWhite(p) ? ' ' : '*');
|
||||
String pieceName = pieceToChar(p);
|
||||
if (pieceName.length() == 0)
|
||||
pieceName = "P";
|
||||
ret.append(pieceName);
|
||||
ret.append(" |");
|
||||
}
|
||||
}
|
||||
ret.append(nl);
|
||||
ret.append(" +----+----+----+----+----+----+----+----+");
|
||||
ret.append(nl);
|
||||
}
|
||||
return ret.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert move string to lower case and remove special check/mate symbols.
|
||||
*/
|
||||
private static String normalizeMoveString(String str) {
|
||||
if (str.length() > 0) {
|
||||
char lastChar = str.charAt(str.length() - 1);
|
||||
if ((lastChar == '#') || (lastChar == '+')) {
|
||||
str = str.substring(0, str.length() - 1);
|
||||
}
|
||||
}
|
||||
return str;
|
||||
}
|
||||
|
||||
private static String pieceToChar(int p) {
|
||||
switch (p) {
|
||||
case Piece.WQUEEN: case Piece.BQUEEN: return "Q";
|
||||
|
@ -607,4 +690,30 @@ public class TextIO {
|
|||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
private static int charToPiece(boolean white, char c) {
|
||||
switch (c) {
|
||||
case 'Q': case 'q': return white ? Piece.WQUEEN : Piece.BQUEEN;
|
||||
case 'R': case 'r': return white ? Piece.WROOK : Piece.BROOK;
|
||||
case 'B': return white ? Piece.WBISHOP : Piece.BBISHOP;
|
||||
case 'N': case 'n': return white ? Piece.WKNIGHT : Piece.BKNIGHT;
|
||||
case 'K': case 'k': return white ? Piece.WKING : Piece.BKING;
|
||||
case 'P': case 'p': return white ? Piece.WPAWN : Piece.BPAWN;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
/** Add an = sign to a promotion move, as required by the PGN standard. */
|
||||
public static String pgnPromotion(String str) {
|
||||
int idx = str.length() - 1;
|
||||
while (idx > 0) {
|
||||
char c = str.charAt(idx);
|
||||
if ((c != '#') && (c != '+'))
|
||||
break;
|
||||
idx--;
|
||||
}
|
||||
if ((idx > 0) && (charToPiece(true, str.charAt(idx)) != -1))
|
||||
idx--;
|
||||
return str.substring(0, idx + 1) + '=' + str.substring(idx + 1, str.length());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,14 +16,14 @@
|
|||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.petero.droidfish.gamelogic;
|
||||
package chess;
|
||||
|
||||
import java.io.DataInputStream;
|
||||
import java.io.DataOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
|
||||
import org.petero.droidfish.gamelogic.TimeControlData.TimeControlField;
|
||||
import chess.TimeControlData.TimeControlField;
|
||||
|
||||
/** Keep track of time control information for both players. */
|
||||
public class TimeControl {
|
|
@ -16,7 +16,7 @@
|
|||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.petero.droidfish.gamelogic;
|
||||
package chess;
|
||||
|
||||
import java.io.DataInputStream;
|
||||
import java.io.DataOutputStream;
|
|
@ -1,48 +0,0 @@
|
|||
/*
|
||||
DroidFish - An Android chess program.
|
||||
Copyright (C) 2011 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.gamelogic;
|
||||
|
||||
/** Exception class to represent parse errors in FEN or algebraic notation. */
|
||||
public class ChessParseError extends Exception {
|
||||
private static final long serialVersionUID = -6051856171275301175L;
|
||||
|
||||
public Position pos;
|
||||
public int resourceId = -1;
|
||||
|
||||
public ChessParseError(String msg) {
|
||||
super(msg);
|
||||
pos = null;
|
||||
}
|
||||
public ChessParseError(String msg, Position pos) {
|
||||
super(msg);
|
||||
this.pos = pos;
|
||||
}
|
||||
|
||||
public ChessParseError(int resourceId) {
|
||||
super("");
|
||||
pos = null;
|
||||
this.resourceId = resourceId;
|
||||
}
|
||||
|
||||
public ChessParseError(int resourceId, Position pos) {
|
||||
super("");
|
||||
this.pos = pos;
|
||||
this.resourceId = resourceId;
|
||||
}
|
||||
}
|
|
@ -1,76 +0,0 @@
|
|||
/*
|
||||
DroidFish - An Android chess program.
|
||||
Copyright (C) 2011 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.gamelogic;
|
||||
|
||||
public class Move {
|
||||
/** From square, 0-63. */
|
||||
public int from;
|
||||
|
||||
/** To square, 0-63. */
|
||||
public int to;
|
||||
|
||||
/** Promotion piece. */
|
||||
public int promoteTo;
|
||||
|
||||
/** Create a move object. */
|
||||
public Move(int from, int to, int promoteTo) {
|
||||
this.from = from;
|
||||
this.to = to;
|
||||
this.promoteTo = promoteTo;
|
||||
}
|
||||
|
||||
public Move(Move m) {
|
||||
this.from = m.from;
|
||||
this.to = m.to;
|
||||
this.promoteTo = m.promoteTo;
|
||||
}
|
||||
|
||||
/** Create object from compressed representation. */
|
||||
public static Move fromCompressed(int cm) {
|
||||
return new Move((cm >> 10) & 63, (cm >> 4) & 63, cm & 15);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if ((o == null) || (o.getClass() != this.getClass()))
|
||||
return false;
|
||||
Move other = (Move)o;
|
||||
if (from != other.from)
|
||||
return false;
|
||||
if (to != other.to)
|
||||
return false;
|
||||
if (promoteTo != other.promoteTo)
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return getCompressedMove();
|
||||
}
|
||||
|
||||
/** Get move as a 16-bit value. */
|
||||
public int getCompressedMove() {
|
||||
return (from * 64 + to) * 16 + promoteTo;
|
||||
}
|
||||
|
||||
/** Useful for debugging. */
|
||||
public final String toString() {
|
||||
return TextIO.moveToUCIString(this);
|
||||
}
|
||||
}
|
|
@ -1,313 +0,0 @@
|
|||
/*
|
||||
DroidFish - An Android chess program.
|
||||
Copyright (C) 2011 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.gamelogic;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
|
||||
public class MoveGen {
|
||||
public static MoveGen instance;
|
||||
static {
|
||||
instance = new MoveGen();
|
||||
}
|
||||
|
||||
/** Generate and return a list of legal moves. */
|
||||
public final ArrayList<Move> legalMoves(Position pos) {
|
||||
ArrayList<Move> moveList = pseudoLegalMoves(pos);
|
||||
moveList = MoveGen.removeIllegal(pos, moveList);
|
||||
return moveList;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate and return a list of pseudo-legal moves.
|
||||
* Pseudo-legal means that the moves don't necessarily defend from check threats.
|
||||
*/
|
||||
public final ArrayList<Move> pseudoLegalMoves(Position pos) {
|
||||
ArrayList<Move> moveList = new ArrayList<>(60);
|
||||
final boolean wtm = pos.whiteMove;
|
||||
for (int x = 0; x < 8; x++) {
|
||||
for (int y = 0; y < 8; y++) {
|
||||
int sq = Position.getSquare(x, y);
|
||||
int p = pos.getPiece(sq);
|
||||
if ((p == Piece.EMPTY) || (Piece.isWhite(p) != wtm)) {
|
||||
continue;
|
||||
}
|
||||
if ((p == Piece.WROOK) || (p == Piece.BROOK) || (p == Piece.WQUEEN) || (p == Piece.BQUEEN)) {
|
||||
if (addDirection(moveList, pos, sq, 7-x, 1)) return moveList;
|
||||
if (addDirection(moveList, pos, sq, 7-y, 8)) return moveList;
|
||||
if (addDirection(moveList, pos, sq, x, -1)) return moveList;
|
||||
if (addDirection(moveList, pos, sq, y, -8)) return moveList;
|
||||
}
|
||||
if ((p == Piece.WBISHOP) || (p == Piece.BBISHOP) || (p == Piece.WQUEEN) || (p == Piece.BQUEEN)) {
|
||||
if (addDirection(moveList, pos, sq, Math.min(7-x, 7-y), 9)) return moveList;
|
||||
if (addDirection(moveList, pos, sq, Math.min( x, 7-y), 7)) return moveList;
|
||||
if (addDirection(moveList, pos, sq, Math.min( x, y), -9)) return moveList;
|
||||
if (addDirection(moveList, pos, sq, Math.min(7-x, y), -7)) return moveList;
|
||||
}
|
||||
if ((p == Piece.WKNIGHT) || (p == Piece.BKNIGHT)) {
|
||||
if (x < 6 && y < 7 && addDirection(moveList, pos, sq, 1, 10)) return moveList;
|
||||
if (x < 7 && y < 6 && addDirection(moveList, pos, sq, 1, 17)) return moveList;
|
||||
if (x > 0 && y < 6 && addDirection(moveList, pos, sq, 1, 15)) return moveList;
|
||||
if (x > 1 && y < 7 && addDirection(moveList, pos, sq, 1, 6)) return moveList;
|
||||
if (x > 1 && y > 0 && addDirection(moveList, pos, sq, 1, -10)) return moveList;
|
||||
if (x > 0 && y > 1 && addDirection(moveList, pos, sq, 1, -17)) return moveList;
|
||||
if (x < 7 && y > 1 && addDirection(moveList, pos, sq, 1, -15)) return moveList;
|
||||
if (x < 6 && y > 0 && addDirection(moveList, pos, sq, 1, -6)) return moveList;
|
||||
}
|
||||
if ((p == Piece.WKING) || (p == Piece.BKING)) {
|
||||
if (x < 7 && addDirection(moveList, pos, sq, 1, 1)) return moveList;
|
||||
if (x < 7 && y < 7 && addDirection(moveList, pos, sq, 1, 9)) return moveList;
|
||||
if ( y < 7 && addDirection(moveList, pos, sq, 1, 8)) return moveList;
|
||||
if (x > 0 && y < 7 && addDirection(moveList, pos, sq, 1, 7)) return moveList;
|
||||
if (x > 0 && addDirection(moveList, pos, sq, 1, -1)) return moveList;
|
||||
if (x > 0 && y > 0 && addDirection(moveList, pos, sq, 1, -9)) return moveList;
|
||||
if ( y > 0 && addDirection(moveList, pos, sq, 1, -8)) return moveList;
|
||||
if (x < 7 && y > 0 && addDirection(moveList, pos, sq, 1, -7)) return moveList;
|
||||
|
||||
int k0 = wtm ? Position.getSquare(4,0) : Position.getSquare(4,7);
|
||||
if (Position.getSquare(x,y) == k0) {
|
||||
int aCastle = wtm ? Position.A1_CASTLE : Position.A8_CASTLE;
|
||||
int hCastle = wtm ? Position.H1_CASTLE : Position.H8_CASTLE;
|
||||
int rook = wtm ? Piece.WROOK : Piece.BROOK;
|
||||
if (((pos.getCastleMask() & (1 << hCastle)) != 0) &&
|
||||
(pos.getPiece(k0 + 1) == Piece.EMPTY) &&
|
||||
(pos.getPiece(k0 + 2) == Piece.EMPTY) &&
|
||||
(pos.getPiece(k0 + 3) == rook) &&
|
||||
!sqAttacked(pos, k0) &&
|
||||
!sqAttacked(pos, k0 + 1)) {
|
||||
moveList.add(getMoveObj(k0, k0 + 2, Piece.EMPTY));
|
||||
}
|
||||
if (((pos.getCastleMask() & (1 << aCastle)) != 0) &&
|
||||
(pos.getPiece(k0 - 1) == Piece.EMPTY) &&
|
||||
(pos.getPiece(k0 - 2) == Piece.EMPTY) &&
|
||||
(pos.getPiece(k0 - 3) == Piece.EMPTY) &&
|
||||
(pos.getPiece(k0 - 4) == rook) &&
|
||||
!sqAttacked(pos, k0) &&
|
||||
!sqAttacked(pos, k0 - 1)) {
|
||||
moveList.add(getMoveObj(k0, k0 - 2, Piece.EMPTY));
|
||||
}
|
||||
}
|
||||
}
|
||||
if ((p == Piece.WPAWN) || (p == Piece.BPAWN)) {
|
||||
int yDir = wtm ? 8 : -8;
|
||||
if (pos.getPiece(sq + yDir) == Piece.EMPTY) { // non-capture
|
||||
addPawnMoves(moveList, sq, sq + yDir);
|
||||
if ((y == (wtm ? 1 : 6)) &&
|
||||
(pos.getPiece(sq + 2 * yDir) == Piece.EMPTY)) { // double step
|
||||
addPawnMoves(moveList, sq, sq + yDir * 2);
|
||||
}
|
||||
}
|
||||
if (x > 0) { // Capture to the left
|
||||
int toSq = sq + yDir - 1;
|
||||
int cap = pos.getPiece(toSq);
|
||||
if (cap != Piece.EMPTY) {
|
||||
if (Piece.isWhite(cap) != wtm) {
|
||||
if (cap == (wtm ? Piece.BKING : Piece.WKING)) {
|
||||
moveList.clear();
|
||||
moveList.add(getMoveObj(sq, toSq, Piece.EMPTY));
|
||||
return moveList;
|
||||
} else {
|
||||
addPawnMoves(moveList, sq, toSq);
|
||||
}
|
||||
}
|
||||
} else if (toSq == pos.getEpSquare()) {
|
||||
addPawnMoves(moveList, sq, toSq);
|
||||
}
|
||||
}
|
||||
if (x < 7) { // Capture to the right
|
||||
int toSq = sq + yDir + 1;
|
||||
int cap = pos.getPiece(toSq);
|
||||
if (cap != Piece.EMPTY) {
|
||||
if (Piece.isWhite(cap) != wtm) {
|
||||
if (cap == (wtm ? Piece.BKING : Piece.WKING)) {
|
||||
moveList.clear();
|
||||
moveList.add(getMoveObj(sq, toSq, Piece.EMPTY));
|
||||
return moveList;
|
||||
} else {
|
||||
addPawnMoves(moveList, sq, toSq);
|
||||
}
|
||||
}
|
||||
} else if (toSq == pos.getEpSquare()) {
|
||||
addPawnMoves(moveList, sq, toSq);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return moveList;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if the side to move is in check.
|
||||
*/
|
||||
public static boolean inCheck(Position pos) {
|
||||
int kingSq = pos.getKingSq(pos.whiteMove);
|
||||
if (kingSq < 0)
|
||||
return false;
|
||||
return sqAttacked(pos, kingSq);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if a square is attacked by the opposite side.
|
||||
*/
|
||||
public static boolean sqAttacked(Position pos, int sq) {
|
||||
int x = Position.getX(sq);
|
||||
int y = Position.getY(sq);
|
||||
boolean isWhiteMove = pos.whiteMove;
|
||||
|
||||
final int oQueen= isWhiteMove ? Piece.BQUEEN: Piece.WQUEEN;
|
||||
final int oRook = isWhiteMove ? Piece.BROOK : Piece.WROOK;
|
||||
final int oBish = isWhiteMove ? Piece.BBISHOP : Piece.WBISHOP;
|
||||
final int oKnight = isWhiteMove ? Piece.BKNIGHT : Piece.WKNIGHT;
|
||||
|
||||
int p;
|
||||
if (y > 0) {
|
||||
p = checkDirection(pos, sq, y, -8); if ((p == oQueen) || (p == oRook)) return true;
|
||||
p = checkDirection(pos, sq, Math.min( x, y), -9); if ((p == oQueen) || (p == oBish)) return true;
|
||||
p = checkDirection(pos, sq, Math.min(7-x, y), -7); if ((p == oQueen) || (p == oBish)) return true;
|
||||
if (x > 1 ) { p = checkDirection(pos, sq, 1, -10); if (p == oKnight) return true; }
|
||||
if (x > 0 && y > 1) { p = checkDirection(pos, sq, 1, -17); if (p == oKnight) return true; }
|
||||
if (x < 7 && y > 1) { p = checkDirection(pos, sq, 1, -15); if (p == oKnight) return true; }
|
||||
if (x < 6 ) { p = checkDirection(pos, sq, 1, -6); if (p == oKnight) return true; }
|
||||
|
||||
if (!isWhiteMove) {
|
||||
if (x < 7 && y > 1) { p = checkDirection(pos, sq, 1, -7); if (p == Piece.WPAWN) return true; }
|
||||
if (x > 0 && y > 1) { p = checkDirection(pos, sq, 1, -9); if (p == Piece.WPAWN) return true; }
|
||||
}
|
||||
}
|
||||
if (y < 7) {
|
||||
p = checkDirection(pos, sq, 7-y, 8); if ((p == oQueen) || (p == oRook)) return true;
|
||||
p = checkDirection(pos, sq, Math.min(7-x, 7-y), 9); if ((p == oQueen) || (p == oBish)) return true;
|
||||
p = checkDirection(pos, sq, Math.min( x, 7-y), 7); if ((p == oQueen) || (p == oBish)) return true;
|
||||
if (x < 6 ) { p = checkDirection(pos, sq, 1, 10); if (p == oKnight) return true; }
|
||||
if (x < 7 && y < 6) { p = checkDirection(pos, sq, 1, 17); if (p == oKnight) return true; }
|
||||
if (x > 0 && y < 6) { p = checkDirection(pos, sq, 1, 15); if (p == oKnight) return true; }
|
||||
if (x > 1 ) { p = checkDirection(pos, sq, 1, 6); if (p == oKnight) return true; }
|
||||
if (isWhiteMove) {
|
||||
if (x < 7 && y < 6) { p = checkDirection(pos, sq, 1, 9); if (p == Piece.BPAWN) return true; }
|
||||
if (x > 0 && y < 6) { p = checkDirection(pos, sq, 1, 7); if (p == Piece.BPAWN) return true; }
|
||||
}
|
||||
}
|
||||
p = checkDirection(pos, sq, 7-x, 1); if ((p == oQueen) || (p == oRook)) return true;
|
||||
p = checkDirection(pos, sq, x, -1); if ((p == oQueen) || (p == oRook)) return true;
|
||||
|
||||
int oKingSq = pos.getKingSq(!isWhiteMove);
|
||||
if (oKingSq >= 0) {
|
||||
int ox = Position.getX(oKingSq);
|
||||
int oy = Position.getY(oKingSq);
|
||||
if ((Math.abs(x - ox) <= 1) && (Math.abs(y - oy) <= 1))
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove all illegal moves from moveList.
|
||||
* "moveList" is assumed to be a list of pseudo-legal moves.
|
||||
* This function removes the moves that don't defend from check threats.
|
||||
*/
|
||||
public static ArrayList<Move> removeIllegal(Position pos, ArrayList<Move> moveList) {
|
||||
ArrayList<Move> ret = new ArrayList<>();
|
||||
UndoInfo ui = new UndoInfo();
|
||||
int mlSize = moveList.size();
|
||||
for (int mi = 0; mi < mlSize; mi++) {
|
||||
Move m = moveList.get(mi);
|
||||
pos.makeMove(m, ui);
|
||||
pos.setWhiteMove(!pos.whiteMove);
|
||||
if (!inCheck(pos))
|
||||
ret.add(m);
|
||||
pos.setWhiteMove(!pos.whiteMove);
|
||||
pos.unMakeMove(m, ui);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add all moves from square sq0 in direction delta.
|
||||
* @param maxSteps Max steps until reaching a border. Set to 1 for non-sliding pieces.
|
||||
* @ return True if the enemy king could be captured, false otherwise.
|
||||
*/
|
||||
private boolean addDirection(ArrayList<Move> moveList, Position pos, int sq0, int maxSteps, int delta) {
|
||||
int sq = sq0;
|
||||
boolean wtm = pos.whiteMove;
|
||||
final int oKing = (wtm ? Piece.BKING : Piece.WKING);
|
||||
while (maxSteps > 0) {
|
||||
sq += delta;
|
||||
int p = pos.getPiece(sq);
|
||||
if (p == Piece.EMPTY) {
|
||||
moveList.add(getMoveObj(sq0, sq, Piece.EMPTY));
|
||||
} else {
|
||||
if (Piece.isWhite(p) != wtm) {
|
||||
if (p == oKing) {
|
||||
moveList.clear();
|
||||
moveList.add(getMoveObj(sq0, sq, Piece.EMPTY));
|
||||
return true;
|
||||
} else {
|
||||
moveList.add(getMoveObj(sq0, sq, Piece.EMPTY));
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
maxSteps--;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate all possible pawn moves from (x0,y0) to (x1,y1), taking pawn promotions into account.
|
||||
*/
|
||||
private void addPawnMoves(ArrayList<Move> moveList, int sq0, int sq1) {
|
||||
if (sq1 >= 56) { // White promotion
|
||||
moveList.add(getMoveObj(sq0, sq1, Piece.WQUEEN));
|
||||
moveList.add(getMoveObj(sq0, sq1, Piece.WKNIGHT));
|
||||
moveList.add(getMoveObj(sq0, sq1, Piece.WROOK));
|
||||
moveList.add(getMoveObj(sq0, sq1, Piece.WBISHOP));
|
||||
} else if (sq1 < 8) { // Black promotion
|
||||
moveList.add(getMoveObj(sq0, sq1, Piece.BQUEEN));
|
||||
moveList.add(getMoveObj(sq0, sq1, Piece.BKNIGHT));
|
||||
moveList.add(getMoveObj(sq0, sq1, Piece.BROOK));
|
||||
moveList.add(getMoveObj(sq0, sq1, Piece.BBISHOP));
|
||||
} else { // No promotion
|
||||
moveList.add(getMoveObj(sq0, sq1, Piece.EMPTY));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if there is an attacking piece in a given direction starting from sq.
|
||||
* The direction is given by delta.
|
||||
* @param maxSteps Max steps until reaching a border. Set to 1 for non-sliding pieces.
|
||||
* @return The first piece in the given direction, or EMPTY if there is no piece
|
||||
* in that direction.
|
||||
*/
|
||||
private static int checkDirection(Position pos, int sq, int maxSteps, int delta) {
|
||||
while (maxSteps > 0) {
|
||||
sq += delta;
|
||||
int p = pos.getPiece(sq);
|
||||
if (p != Piece.EMPTY)
|
||||
return p;
|
||||
maxSteps--;
|
||||
}
|
||||
return Piece.EMPTY;
|
||||
}
|
||||
|
||||
private static Move getMoveObj(int from, int to, int promoteTo) {
|
||||
return new Move(from, to, promoteTo);
|
||||
}
|
||||
}
|
|
@ -1,90 +0,0 @@
|
|||
/*
|
||||
DroidFish - An Android chess program.
|
||||
Copyright (C) 2011 Peter Österlund, peterosterlund2@gmail.com
|
||||
Copyright (C) 2012 Leo Mayer
|
||||
|
||||
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.gamelogic;
|
||||
|
||||
/** Constants for different piece types. */
|
||||
public class Piece {
|
||||
public static final int EMPTY = 0;
|
||||
|
||||
public static final int WKING = 1;
|
||||
public static final int WQUEEN = 2;
|
||||
public static final int WROOK = 3;
|
||||
public static final int WBISHOP = 4;
|
||||
public static final int WKNIGHT = 5;
|
||||
public static final int WPAWN = 6;
|
||||
|
||||
public static final int BKING = 7;
|
||||
public static final int BQUEEN = 8;
|
||||
public static final int BROOK = 9;
|
||||
public static final int BBISHOP = 10;
|
||||
public static final int BKNIGHT = 11;
|
||||
public static final int BPAWN = 12;
|
||||
|
||||
public static final int nPieceTypes = 13;
|
||||
|
||||
// Unicode for color neutral chess pieces
|
||||
public static final char NOTATION_KING = 0xe050;
|
||||
public static final char NOTATION_QUEEN = 0xe051;
|
||||
public static final char NOTATION_ROOK = 0xe052;
|
||||
public static final char NOTATION_BISHOP = 0xe053;
|
||||
public static final char NOTATION_KNIGHT = 0xe054;
|
||||
public static final char NOTATION_PAWN = 0xe055;
|
||||
|
||||
// Unicode for white chess pieces
|
||||
public static final char WHITE_KING = 0x2654;
|
||||
public static final char WHITE_QUEEN = 0x2655;
|
||||
public static final char WHITE_ROOK = 0x2656;
|
||||
public static final char WHITE_BISHOP = 0x2657;
|
||||
public static final char WHITE_KNIGHT = 0x2658;
|
||||
public static final char WHITE_PAWN = 0x2659;
|
||||
|
||||
// Unicode for black chess pieces
|
||||
public static final char BLACK_KING = 0x265A;
|
||||
public static final char BLACK_QUEEN = 0x265B;
|
||||
public static final char BLACK_ROOK = 0x265C;
|
||||
public static final char BLACK_BISHOP = 0x265D;
|
||||
public static final char BLACK_KNIGHT = 0x265E;
|
||||
public static final char BLACK_PAWN = 0x265F;
|
||||
|
||||
/**
|
||||
* Return true if p is a white piece, false otherwise.
|
||||
* Note that if p is EMPTY, an unspecified value is returned.
|
||||
*/
|
||||
public static boolean isWhite(int pType) {
|
||||
return pType < BKING;
|
||||
}
|
||||
public static int makeWhite(int pType) {
|
||||
return pType < BKING ? pType : pType - (BKING - WKING);
|
||||
}
|
||||
public static int makeBlack(int pType) {
|
||||
return ((pType >= WKING) && (pType <= WPAWN)) ? pType + (BKING - WKING) : pType;
|
||||
}
|
||||
public static int swapColor(int pType) {
|
||||
if (pType == EMPTY)
|
||||
return EMPTY;
|
||||
return isWhite(pType) ? pType + (BKING - WKING) : pType - (BKING - WKING);
|
||||
}
|
||||
|
||||
/** Converts the piece into a character for the material diff. */
|
||||
public static char toUniCode(int p) {
|
||||
// As we assume, the coding of the pieces is sequential, lets do some math.
|
||||
return (char)(WHITE_KING + p - 1);
|
||||
}
|
||||
}
|
|
@ -1,435 +0,0 @@
|
|||
/*
|
||||
DroidFish - An Android chess program.
|
||||
Copyright (C) 2011 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.gamelogic;
|
||||
|
||||
import java.security.MessageDigest;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
|
||||
/**
|
||||
* Stores the state of a chess position.
|
||||
* All required state is stored, except for all previous positions
|
||||
* since the last capture or pawn move. That state is only needed
|
||||
* for three-fold repetition draw detection, and is better stored
|
||||
* in a separate hash table.
|
||||
*/
|
||||
public class Position {
|
||||
private int[] squares;
|
||||
|
||||
public boolean whiteMove;
|
||||
|
||||
/** Bit definitions for the castleMask bit mask. */
|
||||
public static final int A1_CASTLE = 0; /** White long castle. */
|
||||
public static final int H1_CASTLE = 1; /** White short castle. */
|
||||
public static final int A8_CASTLE = 2; /** Black long castle. */
|
||||
public static final int H8_CASTLE = 3; /** Black short castle. */
|
||||
|
||||
private int castleMask;
|
||||
|
||||
private int epSquare;
|
||||
|
||||
/** Number of half-moves since last 50-move reset. */
|
||||
public int halfMoveClock;
|
||||
|
||||
/** Game move number, starting from 1. */
|
||||
public int fullMoveCounter;
|
||||
|
||||
private long hashKey; // Cached Zobrist hash key
|
||||
private int wKingSq, bKingSq; // Cached king positions
|
||||
|
||||
/** Initialize board to empty position. */
|
||||
public Position() {
|
||||
squares = new int[64];
|
||||
for (int i = 0; i < 64; i++)
|
||||
squares[i] = Piece.EMPTY;
|
||||
whiteMove = true;
|
||||
castleMask = 0;
|
||||
epSquare = -1;
|
||||
halfMoveClock = 0;
|
||||
fullMoveCounter = 1;
|
||||
hashKey = computeZobristHash();
|
||||
wKingSq = bKingSq = -1;
|
||||
}
|
||||
|
||||
public Position(Position other) {
|
||||
squares = new int[64];
|
||||
System.arraycopy(other.squares, 0, squares, 0, 64);
|
||||
whiteMove = other.whiteMove;
|
||||
castleMask = other.castleMask;
|
||||
epSquare = other.epSquare;
|
||||
halfMoveClock = other.halfMoveClock;
|
||||
fullMoveCounter = other.fullMoveCounter;
|
||||
hashKey = other.hashKey;
|
||||
wKingSq = other.wKingSq;
|
||||
bKingSq = other.bKingSq;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if ((o == null) || (o.getClass() != this.getClass()))
|
||||
return false;
|
||||
Position other = (Position)o;
|
||||
if (!drawRuleEquals(other))
|
||||
return false;
|
||||
if (halfMoveClock != other.halfMoveClock)
|
||||
return false;
|
||||
if (fullMoveCounter != other.fullMoveCounter)
|
||||
return false;
|
||||
if (hashKey != other.hashKey)
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return (int)hashKey;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return Zobrish hash value for the current position.
|
||||
* Everything except the move counters are included in the hash value.
|
||||
*/
|
||||
public final long zobristHash() {
|
||||
return hashKey;
|
||||
}
|
||||
|
||||
/**
|
||||
* Decide if two positions are equal in the sense of the draw by repetition rule.
|
||||
* @return True if positions are equal, false otherwise.
|
||||
*/
|
||||
final public boolean drawRuleEquals(Position other) {
|
||||
for (int i = 0; i < 64; i++) {
|
||||
if (squares[i] != other.squares[i])
|
||||
return false;
|
||||
}
|
||||
if (whiteMove != other.whiteMove)
|
||||
return false;
|
||||
if (castleMask != other.castleMask)
|
||||
return false;
|
||||
if (epSquare != other.epSquare)
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
public final void setWhiteMove(boolean whiteMove) {
|
||||
if (whiteMove != this.whiteMove) {
|
||||
hashKey ^= whiteHashKey;
|
||||
this.whiteMove = whiteMove;
|
||||
}
|
||||
}
|
||||
/** Return index in squares[] vector corresponding to (x,y). */
|
||||
public static int getSquare(int x, int y) {
|
||||
return y * 8 + x;
|
||||
}
|
||||
/** Return x position (file) corresponding to a square. */
|
||||
public static int getX(int square) {
|
||||
return square & 7;
|
||||
}
|
||||
/** Return y position (rank) corresponding to a square. */
|
||||
public static int getY(int square) {
|
||||
return square >> 3;
|
||||
}
|
||||
/** Return true if (x,y) is a dark square. */
|
||||
public static boolean darkSquare(int x, int y) {
|
||||
return (x & 1) == (y & 1);
|
||||
}
|
||||
|
||||
/** Return piece occupying a square. */
|
||||
public final int getPiece(int square) {
|
||||
return squares[square];
|
||||
}
|
||||
/** Set a square to a piece value. */
|
||||
public final void setPiece(int square, int piece) {
|
||||
// Update hash key
|
||||
int oldPiece = squares[square];
|
||||
hashKey ^= psHashKeys[oldPiece][square];
|
||||
hashKey ^= psHashKeys[piece][square];
|
||||
|
||||
// Update board
|
||||
squares[square] = piece;
|
||||
|
||||
// Update king position
|
||||
if (piece == Piece.WKING) {
|
||||
wKingSq = square;
|
||||
} else if (piece == Piece.BKING) {
|
||||
bKingSq = square;
|
||||
}
|
||||
}
|
||||
|
||||
/** Return true if white long castling right has not been lost. */
|
||||
public final boolean a1Castle() {
|
||||
return (castleMask & (1 << A1_CASTLE)) != 0;
|
||||
}
|
||||
/** Return true if white short castling right has not been lost. */
|
||||
public final boolean h1Castle() {
|
||||
return (castleMask & (1 << H1_CASTLE)) != 0;
|
||||
}
|
||||
/** Return true if black long castling right has not been lost. */
|
||||
public final boolean a8Castle() {
|
||||
return (castleMask & (1 << A8_CASTLE)) != 0;
|
||||
}
|
||||
/** Return true if black short castling right has not been lost. */
|
||||
public final boolean h8Castle() {
|
||||
return (castleMask & (1 << H8_CASTLE)) != 0;
|
||||
}
|
||||
/** Bitmask describing castling rights. */
|
||||
public final int getCastleMask() {
|
||||
return castleMask;
|
||||
}
|
||||
public final void setCastleMask(int castleMask) {
|
||||
hashKey ^= castleHashKeys[this.castleMask];
|
||||
hashKey ^= castleHashKeys[castleMask];
|
||||
this.castleMask = castleMask;
|
||||
}
|
||||
|
||||
/** En passant square, or -1 if no ep possible. */
|
||||
public final int getEpSquare() {
|
||||
return epSquare;
|
||||
}
|
||||
public final void setEpSquare(int epSquare) {
|
||||
if (this.epSquare != epSquare) {
|
||||
hashKey ^= epHashKeys[(this.epSquare >= 0) ? getX(this.epSquare) + 1 : 0];
|
||||
hashKey ^= epHashKeys[(epSquare >= 0) ? getX(epSquare) + 1 : 0];
|
||||
this.epSquare = epSquare;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public final int getKingSq(boolean whiteMove) {
|
||||
return whiteMove ? wKingSq : bKingSq;
|
||||
}
|
||||
|
||||
/** Count number of pieces of a certain type. */
|
||||
public final int nPieces(int pType) {
|
||||
int ret = 0;
|
||||
for (int sq = 0; sq < 64; sq++)
|
||||
if (squares[sq] == pType)
|
||||
ret++;
|
||||
return ret;
|
||||
}
|
||||
|
||||
/** Count total number of pieces. */
|
||||
public final int nPieces() {
|
||||
int ret = 0;
|
||||
for (int sq = 0; sq < 64; sq++)
|
||||
if (squares[sq] != Piece.EMPTY)
|
||||
ret++;
|
||||
return ret;
|
||||
}
|
||||
|
||||
/** Apply a move to the current position. */
|
||||
public final void makeMove(Move move, UndoInfo ui) {
|
||||
ui.capturedPiece = squares[move.to];
|
||||
ui.castleMask = castleMask;
|
||||
ui.epSquare = epSquare;
|
||||
ui.halfMoveClock = halfMoveClock;
|
||||
boolean wtm = whiteMove;
|
||||
|
||||
int p = squares[move.from];
|
||||
int capP = squares[move.to];
|
||||
|
||||
boolean nullMove = (move.from == 0) && (move.to == 0);
|
||||
|
||||
if (nullMove || (capP != Piece.EMPTY) || (p == (wtm ? Piece.WPAWN : Piece.BPAWN))) {
|
||||
halfMoveClock = 0;
|
||||
} else {
|
||||
halfMoveClock++;
|
||||
}
|
||||
if (!wtm) {
|
||||
fullMoveCounter++;
|
||||
}
|
||||
|
||||
// Handle castling
|
||||
int king = wtm ? Piece.WKING : Piece.BKING;
|
||||
int k0 = move.from;
|
||||
if (p == king) {
|
||||
if (move.to == k0 + 2) { // O-O
|
||||
setPiece(k0 + 1, squares[k0 + 3]);
|
||||
setPiece(k0 + 3, Piece.EMPTY);
|
||||
} else if (move.to == k0 - 2) { // O-O-O
|
||||
setPiece(k0 - 1, squares[k0 - 4]);
|
||||
setPiece(k0 - 4, Piece.EMPTY);
|
||||
}
|
||||
if (wtm) {
|
||||
setCastleMask(castleMask & ~(1 << Position.A1_CASTLE));
|
||||
setCastleMask(castleMask & ~(1 << Position.H1_CASTLE));
|
||||
} else {
|
||||
setCastleMask(castleMask & ~(1 << Position.A8_CASTLE));
|
||||
setCastleMask(castleMask & ~(1 << Position.H8_CASTLE));
|
||||
}
|
||||
}
|
||||
if (!nullMove) {
|
||||
int rook = wtm ? Piece.WROOK : Piece.BROOK;
|
||||
if (p == rook) {
|
||||
removeCastleRights(move.from);
|
||||
}
|
||||
int oRook = wtm ? Piece.BROOK : Piece.WROOK;
|
||||
if (capP == oRook) {
|
||||
removeCastleRights(move.to);
|
||||
}
|
||||
}
|
||||
|
||||
// Handle en passant and epSquare
|
||||
int prevEpSquare = epSquare;
|
||||
setEpSquare(-1);
|
||||
if (p == Piece.WPAWN) {
|
||||
if (move.to - move.from == 2 * 8) {
|
||||
int x = Position.getX(move.to);
|
||||
if ( ((x > 0) && (squares[move.to - 1] == Piece.BPAWN)) ||
|
||||
((x < 7) && (squares[move.to + 1] == Piece.BPAWN))) {
|
||||
setEpSquare(move.from + 8);
|
||||
}
|
||||
} else if (move.to == prevEpSquare) {
|
||||
setPiece(move.to - 8, Piece.EMPTY);
|
||||
}
|
||||
} else if (p == Piece.BPAWN) {
|
||||
if (move.to - move.from == -2 * 8) {
|
||||
int x = Position.getX(move.to);
|
||||
if ( ((x > 0) && (squares[move.to - 1] == Piece.WPAWN)) ||
|
||||
((x < 7) && (squares[move.to + 1] == Piece.WPAWN))) {
|
||||
setEpSquare(move.from - 8);
|
||||
}
|
||||
} else if (move.to == prevEpSquare) {
|
||||
setPiece(move.to + 8, Piece.EMPTY);
|
||||
}
|
||||
}
|
||||
|
||||
// Perform move
|
||||
setPiece(move.from, Piece.EMPTY);
|
||||
// Handle promotion
|
||||
if (move.promoteTo != Piece.EMPTY) {
|
||||
setPiece(move.to, move.promoteTo);
|
||||
} else {
|
||||
setPiece(move.to, p);
|
||||
}
|
||||
setWhiteMove(!wtm);
|
||||
}
|
||||
|
||||
public final void unMakeMove(Move move, UndoInfo ui) {
|
||||
setWhiteMove(!whiteMove);
|
||||
int p = squares[move.to];
|
||||
setPiece(move.from, p);
|
||||
setPiece(move.to, ui.capturedPiece);
|
||||
setCastleMask(ui.castleMask);
|
||||
setEpSquare(ui.epSquare);
|
||||
halfMoveClock = ui.halfMoveClock;
|
||||
boolean wtm = whiteMove;
|
||||
if (move.promoteTo != Piece.EMPTY) {
|
||||
p = wtm ? Piece.WPAWN : Piece.BPAWN;
|
||||
setPiece(move.from, p);
|
||||
}
|
||||
if (!wtm) {
|
||||
fullMoveCounter--;
|
||||
}
|
||||
|
||||
// Handle castling
|
||||
int king = wtm ? Piece.WKING : Piece.BKING;
|
||||
int k0 = move.from;
|
||||
if (p == king) {
|
||||
if (move.to == k0 + 2) { // O-O
|
||||
setPiece(k0 + 3, squares[k0 + 1]);
|
||||
setPiece(k0 + 1, Piece.EMPTY);
|
||||
} else if (move.to == k0 - 2) { // O-O-O
|
||||
setPiece(k0 - 4, squares[k0 - 1]);
|
||||
setPiece(k0 - 1, Piece.EMPTY);
|
||||
}
|
||||
}
|
||||
|
||||
// Handle en passant
|
||||
if (move.to == epSquare) {
|
||||
if (p == Piece.WPAWN) {
|
||||
setPiece(move.to - 8, Piece.BPAWN);
|
||||
} else if (p == Piece.BPAWN) {
|
||||
setPiece(move.to + 8, Piece.WPAWN);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void removeCastleRights(int square) {
|
||||
if (square == Position.getSquare(0, 0)) {
|
||||
setCastleMask(castleMask & ~(1 << Position.A1_CASTLE));
|
||||
} else if (square == Position.getSquare(7, 0)) {
|
||||
setCastleMask(castleMask & ~(1 << Position.H1_CASTLE));
|
||||
} else if (square == Position.getSquare(0, 7)) {
|
||||
setCastleMask(castleMask & ~(1 << Position.A8_CASTLE));
|
||||
} else if (square == Position.getSquare(7, 7)) {
|
||||
setCastleMask(castleMask & ~(1 << Position.H8_CASTLE));
|
||||
}
|
||||
}
|
||||
|
||||
/* ------------- Hashing code ------------------ */
|
||||
|
||||
private static long[][] psHashKeys; // [piece][square]
|
||||
private static long whiteHashKey;
|
||||
private static long[] castleHashKeys; // [castleMask]
|
||||
private static long[] epHashKeys; // [epFile + 1] (epFile==-1 for no ep)
|
||||
|
||||
static {
|
||||
psHashKeys = new long[Piece.nPieceTypes][64];
|
||||
castleHashKeys = new long[16];
|
||||
epHashKeys = new long[9];
|
||||
int rndNo = 0;
|
||||
for (int p = 0; p < Piece.nPieceTypes; p++) {
|
||||
for (int sq = 0; sq < 64; sq++) {
|
||||
psHashKeys[p][sq] = getRandomHashVal(rndNo++);
|
||||
}
|
||||
}
|
||||
whiteHashKey = getRandomHashVal(rndNo++);
|
||||
for (int cm = 0; cm < castleHashKeys.length; cm++)
|
||||
castleHashKeys[cm] = getRandomHashVal(rndNo++);
|
||||
for (int f = 0; f < epHashKeys.length; f++)
|
||||
epHashKeys[f] = getRandomHashVal(rndNo++);
|
||||
}
|
||||
|
||||
/**
|
||||
* Compute the Zobrist hash value non-incrementally. Only useful for test programs.
|
||||
*/
|
||||
final long computeZobristHash() {
|
||||
long hash = 0;
|
||||
for (int sq = 0; sq < 64; sq++) {
|
||||
int p = squares[sq];
|
||||
hash ^= psHashKeys[p][sq];
|
||||
}
|
||||
if (whiteMove)
|
||||
hash ^= whiteHashKey;
|
||||
hash ^= castleHashKeys[castleMask];
|
||||
hash ^= epHashKeys[(epSquare >= 0) ? getX(epSquare) + 1 : 0];
|
||||
return hash;
|
||||
}
|
||||
|
||||
private static long getRandomHashVal(int rndNo) {
|
||||
try {
|
||||
MessageDigest md = MessageDigest.getInstance("SHA-1");
|
||||
byte[] input = new byte[4];
|
||||
for (int i = 0; i < 4; i++)
|
||||
input[i] = (byte)((rndNo >> (i * 8)) & 0xff);
|
||||
byte[] digest = md.digest(input);
|
||||
long ret = 0;
|
||||
for (int i = 0; i < 8; i++) {
|
||||
ret ^= ((long)digest[i]) << (i * 8);
|
||||
}
|
||||
return ret;
|
||||
} catch (NoSuchAlgorithmException ex) {
|
||||
throw new UnsupportedOperationException("SHA-1 not available");
|
||||
}
|
||||
}
|
||||
|
||||
/** Useful for debugging. */
|
||||
public final String toString() {
|
||||
return TextIO.asciiBoard(this);
|
||||
}
|
||||
}
|
|
@ -1,803 +0,0 @@
|
|||
/*
|
||||
DroidFish - An Android chess program.
|
||||
Copyright (C) 2011 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.gamelogic;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
|
||||
import org.petero.droidfish.PGNOptions;
|
||||
|
||||
|
||||
/** Handle conversion of positions and moves to/from text format. */
|
||||
public class TextIO {
|
||||
static public final String startPosFEN = "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1";
|
||||
|
||||
/** Localized version of "P N B R Q K". */
|
||||
private static String[] pieceNames = null;
|
||||
|
||||
/** Set localized piece names. */
|
||||
public static void setPieceNames(String pieceNames) {
|
||||
String[] pn = pieceNames.split(" ");
|
||||
if (pn.length == 6)
|
||||
TextIO.pieceNames = pn;
|
||||
}
|
||||
|
||||
/** Parse a FEN string and return a chess Position object. */
|
||||
public static Position readFEN(String fen) throws ChessParseError {
|
||||
fen = fen.trim();
|
||||
Position pos = new Position();
|
||||
String[] words = fen.split(" ");
|
||||
if (words.length < 2) {
|
||||
throw new ChessParseError("R.string.err_too_few_spaces");
|
||||
}
|
||||
for (int i = 0; i < words.length; i++) {
|
||||
words[i] = words[i].trim();
|
||||
}
|
||||
|
||||
// Piece placement
|
||||
int row = 7;
|
||||
int col = 0;
|
||||
for (int i = 0; i < words[0].length(); i++) {
|
||||
char c = words[0].charAt(i);
|
||||
switch (c) {
|
||||
case '1': col += 1; break;
|
||||
case '2': col += 2; break;
|
||||
case '3': col += 3; break;
|
||||
case '4': col += 4; break;
|
||||
case '5': col += 5; break;
|
||||
case '6': col += 6; break;
|
||||
case '7': col += 7; break;
|
||||
case '8': col += 8; break;
|
||||
case '/': row--; col = 0; break;
|
||||
case 'P': safeSetPiece(pos, col, row, Piece.WPAWN); col++; break;
|
||||
case 'N': safeSetPiece(pos, col, row, Piece.WKNIGHT); col++; break;
|
||||
case 'B': safeSetPiece(pos, col, row, Piece.WBISHOP); col++; break;
|
||||
case 'R': safeSetPiece(pos, col, row, Piece.WROOK); col++; break;
|
||||
case 'Q': safeSetPiece(pos, col, row, Piece.WQUEEN); col++; break;
|
||||
case 'K': safeSetPiece(pos, col, row, Piece.WKING); col++; break;
|
||||
case 'p': safeSetPiece(pos, col, row, Piece.BPAWN); col++; break;
|
||||
case 'n': safeSetPiece(pos, col, row, Piece.BKNIGHT); col++; break;
|
||||
case 'b': safeSetPiece(pos, col, row, Piece.BBISHOP); col++; break;
|
||||
case 'r': safeSetPiece(pos, col, row, Piece.BROOK); col++; break;
|
||||
case 'q': safeSetPiece(pos, col, row, Piece.BQUEEN); col++; break;
|
||||
case 'k': safeSetPiece(pos, col, row, Piece.BKING); col++; break;
|
||||
default: throw new ChessParseError("R.string.err_invalid_piece, pos");
|
||||
}
|
||||
}
|
||||
|
||||
if (words[1].length() > 0) {
|
||||
boolean wtm;
|
||||
switch (words[1].charAt(0)) {
|
||||
case 'w': wtm = true; break;
|
||||
case 'b': wtm = false; break;
|
||||
default: throw new ChessParseError("R.string.err_invalid_side", pos);
|
||||
}
|
||||
pos.setWhiteMove(wtm);
|
||||
} else {
|
||||
throw new ChessParseError("R.string.err_invalid_side", pos);
|
||||
}
|
||||
|
||||
// Castling rights
|
||||
int castleMask = 0;
|
||||
if (words.length > 2) {
|
||||
for (int i = 0; i < words[2].length(); i++) {
|
||||
char c = words[2].charAt(i);
|
||||
switch (c) {
|
||||
case 'K':
|
||||
castleMask |= (1 << Position.H1_CASTLE);
|
||||
break;
|
||||
case 'Q':
|
||||
castleMask |= (1 << Position.A1_CASTLE);
|
||||
break;
|
||||
case 'k':
|
||||
castleMask |= (1 << Position.H8_CASTLE);
|
||||
break;
|
||||
case 'q':
|
||||
castleMask |= (1 << Position.A8_CASTLE);
|
||||
break;
|
||||
case '-':
|
||||
break;
|
||||
default:
|
||||
throw new ChessParseError("R.string.err_invalid_castling_flags", pos);
|
||||
}
|
||||
}
|
||||
}
|
||||
pos.setCastleMask(castleMask);
|
||||
removeBogusCastleFlags(pos);
|
||||
|
||||
if (words.length > 3) {
|
||||
// En passant target square
|
||||
String epString = words[3];
|
||||
if (!epString.equals("-")) {
|
||||
if (epString.length() < 2)
|
||||
throw new ChessParseError("R.string.err_invalid_en_passant_square", pos);
|
||||
int epSq = getSquare(epString);
|
||||
if (epSq != -1) {
|
||||
if (pos.whiteMove) {
|
||||
if ((Position.getY(epSq) != 5) || (pos.getPiece(epSq) != Piece.EMPTY) ||
|
||||
(pos.getPiece(epSq - 8) != Piece.BPAWN))
|
||||
epSq = -1;
|
||||
} else {
|
||||
if ((Position.getY(epSq) != 2) || (pos.getPiece(epSq) != Piece.EMPTY) ||
|
||||
(pos.getPiece(epSq + 8) != Piece.WPAWN))
|
||||
epSq = -1;
|
||||
}
|
||||
pos.setEpSquare(epSq);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
if (words.length > 4) {
|
||||
pos.halfMoveClock = Integer.parseInt(words[4]);
|
||||
}
|
||||
if (words.length > 5) {
|
||||
pos.fullMoveCounter = Integer.parseInt(words[5]);
|
||||
}
|
||||
} catch (NumberFormatException nfe) {
|
||||
// Ignore errors here, since the fields are optional
|
||||
}
|
||||
|
||||
// Each side must have exactly one king
|
||||
int[] nPieces = new int[Piece.nPieceTypes];
|
||||
for (int i = 0; i < Piece.nPieceTypes; i++)
|
||||
nPieces[i] = 0;
|
||||
for (int x = 0; x < 8; x++)
|
||||
for (int y = 0; y < 8; y++)
|
||||
nPieces[pos.getPiece(Position.getSquare(x, y))]++;
|
||||
if (nPieces[Piece.WKING] != 1)
|
||||
throw new ChessParseError("R.string.err_white_num_kings", pos);
|
||||
if (nPieces[Piece.BKING] != 1)
|
||||
throw new ChessParseError("R.string.err_black_num_kings", pos);
|
||||
|
||||
// White must not have too many pieces
|
||||
int maxWPawns = 8;
|
||||
maxWPawns -= Math.max(0, nPieces[Piece.WKNIGHT] - 2);
|
||||
maxWPawns -= Math.max(0, nPieces[Piece.WBISHOP] - 2);
|
||||
maxWPawns -= Math.max(0, nPieces[Piece.WROOK ] - 2);
|
||||
maxWPawns -= Math.max(0, nPieces[Piece.WQUEEN ] - 1);
|
||||
if (nPieces[Piece.WPAWN] > maxWPawns)
|
||||
throw new ChessParseError("R.string.err_too_many_white_pieces", pos);
|
||||
|
||||
// Black must not have too many pieces
|
||||
int maxBPawns = 8;
|
||||
maxBPawns -= Math.max(0, nPieces[Piece.BKNIGHT] - 2);
|
||||
maxBPawns -= Math.max(0, nPieces[Piece.BBISHOP] - 2);
|
||||
maxBPawns -= Math.max(0, nPieces[Piece.BROOK ] - 2);
|
||||
maxBPawns -= Math.max(0, nPieces[Piece.BQUEEN ] - 1);
|
||||
if (nPieces[Piece.BPAWN] > maxBPawns)
|
||||
throw new ChessParseError("R.string.err_too_many_black_pieces", pos);
|
||||
|
||||
// Make sure king can not be captured
|
||||
Position pos2 = new Position(pos);
|
||||
pos2.setWhiteMove(!pos.whiteMove);
|
||||
if (MoveGen.inCheck(pos2)) {
|
||||
throw new ChessParseError("R.string.err_king_capture_possible", pos);
|
||||
}
|
||||
|
||||
fixupEPSquare(pos);
|
||||
|
||||
return pos;
|
||||
}
|
||||
|
||||
public static void removeBogusCastleFlags(Position pos) {
|
||||
int castleMask = pos.getCastleMask();
|
||||
int validCastle = 0;
|
||||
if (pos.getPiece(4) == Piece.WKING) {
|
||||
if (pos.getPiece(0) == Piece.WROOK) validCastle |= (1 << Position.A1_CASTLE);
|
||||
if (pos.getPiece(7) == Piece.WROOK) validCastle |= (1 << Position.H1_CASTLE);
|
||||
}
|
||||
if (pos.getPiece(60) == Piece.BKING) {
|
||||
if (pos.getPiece(56) == Piece.BROOK) validCastle |= (1 << Position.A8_CASTLE);
|
||||
if (pos.getPiece(63) == Piece.BROOK) validCastle |= (1 << Position.H8_CASTLE);
|
||||
}
|
||||
castleMask &= validCastle;
|
||||
pos.setCastleMask(castleMask);
|
||||
}
|
||||
|
||||
/** Remove pseudo-legal EP square if it is not legal, ie would leave king in check. */
|
||||
public static void fixupEPSquare(Position pos) {
|
||||
int epSquare = pos.getEpSquare();
|
||||
if (epSquare >= 0) {
|
||||
ArrayList<Move> moves = MoveGen.instance.legalMoves(pos);
|
||||
boolean epValid = false;
|
||||
for (Move m : moves) {
|
||||
if (m.to == epSquare) {
|
||||
if (pos.getPiece(m.from) == (pos.whiteMove ? Piece.WPAWN : Piece.BPAWN)) {
|
||||
epValid = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!epValid)
|
||||
pos.setEpSquare(-1);
|
||||
}
|
||||
}
|
||||
|
||||
private static void safeSetPiece(Position pos, int col, int row, int p) throws ChessParseError {
|
||||
if (row < 0) throw new ChessParseError("R.string.err_too_many_rows");
|
||||
if (col > 7) throw new ChessParseError("R.string.err_too_many_columns");
|
||||
if ((p == Piece.WPAWN) || (p == Piece.BPAWN)) {
|
||||
if ((row == 0) || (row == 7))
|
||||
throw new ChessParseError("Pawn on first/last rank");
|
||||
}
|
||||
pos.setPiece(Position.getSquare(col, row), p);
|
||||
}
|
||||
|
||||
/** Return a FEN string corresponding to a chess Position object. */
|
||||
public static String toFEN(Position pos) {
|
||||
StringBuilder ret = new StringBuilder();
|
||||
// Piece placement
|
||||
for (int r = 7; r >=0; r--) {
|
||||
int numEmpty = 0;
|
||||
for (int c = 0; c < 8; c++) {
|
||||
int p = pos.getPiece(Position.getSquare(c, r));
|
||||
if (p == Piece.EMPTY) {
|
||||
numEmpty++;
|
||||
} else {
|
||||
if (numEmpty > 0) {
|
||||
ret.append(numEmpty);
|
||||
numEmpty = 0;
|
||||
}
|
||||
switch (p) {
|
||||
case Piece.WKING: ret.append('K'); break;
|
||||
case Piece.WQUEEN: ret.append('Q'); break;
|
||||
case Piece.WROOK: ret.append('R'); break;
|
||||
case Piece.WBISHOP: ret.append('B'); break;
|
||||
case Piece.WKNIGHT: ret.append('N'); break;
|
||||
case Piece.WPAWN: ret.append('P'); break;
|
||||
case Piece.BKING: ret.append('k'); break;
|
||||
case Piece.BQUEEN: ret.append('q'); break;
|
||||
case Piece.BROOK: ret.append('r'); break;
|
||||
case Piece.BBISHOP: ret.append('b'); break;
|
||||
case Piece.BKNIGHT: ret.append('n'); break;
|
||||
case Piece.BPAWN: ret.append('p'); break;
|
||||
default: throw new RuntimeException();
|
||||
}
|
||||
}
|
||||
}
|
||||
if (numEmpty > 0) {
|
||||
ret.append(numEmpty);
|
||||
}
|
||||
if (r > 0) {
|
||||
ret.append('/');
|
||||
}
|
||||
}
|
||||
ret.append(pos.whiteMove ? " w " : " b ");
|
||||
|
||||
// Castling rights
|
||||
boolean anyCastle = false;
|
||||
if (pos.h1Castle()) {
|
||||
ret.append('K');
|
||||
anyCastle = true;
|
||||
}
|
||||
if (pos.a1Castle()) {
|
||||
ret.append('Q');
|
||||
anyCastle = true;
|
||||
}
|
||||
if (pos.h8Castle()) {
|
||||
ret.append('k');
|
||||
anyCastle = true;
|
||||
}
|
||||
if (pos.a8Castle()) {
|
||||
ret.append('q');
|
||||
anyCastle = true;
|
||||
}
|
||||
if (!anyCastle) {
|
||||
ret.append('-');
|
||||
}
|
||||
|
||||
// En passant target square
|
||||
{
|
||||
ret.append(' ');
|
||||
if (pos.getEpSquare() >= 0) {
|
||||
int x = Position.getX(pos.getEpSquare());
|
||||
int y = Position.getY(pos.getEpSquare());
|
||||
ret.append((char)(x + 'a'));
|
||||
ret.append((char)(y + '1'));
|
||||
} else {
|
||||
ret.append('-');
|
||||
}
|
||||
}
|
||||
|
||||
// Move counters
|
||||
ret.append(' ');
|
||||
ret.append(pos.halfMoveClock);
|
||||
ret.append(' ');
|
||||
ret.append(pos.fullMoveCounter);
|
||||
|
||||
return ret.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a chess move to human readable form.
|
||||
* @param pos The chess position.
|
||||
* @param move The executed move.
|
||||
* @param longForm If true, use long notation, eg Ng1-f3.
|
||||
* Otherwise, use short notation, eg Nf3.
|
||||
* @param localized If true, use localized piece names.
|
||||
*/
|
||||
public static String moveToString(Position pos, Move move, boolean longForm,
|
||||
boolean localized) {
|
||||
return moveToString(pos, move, longForm, localized, null);
|
||||
}
|
||||
public static String moveToString(Position pos, Move move, boolean longForm,
|
||||
boolean localized, List<Move> moves) {
|
||||
if ((move == null) || move.equals(new Move(0, 0, 0)))
|
||||
return "--";
|
||||
StringBuilder ret = new StringBuilder();
|
||||
int wKingOrigPos = Position.getSquare(4, 0);
|
||||
int bKingOrigPos = Position.getSquare(4, 7);
|
||||
if (move.from == wKingOrigPos && pos.getPiece(wKingOrigPos) == Piece.WKING) {
|
||||
// Check white castle
|
||||
if (move.to == Position.getSquare(6, 0)) {
|
||||
ret.append("O-O");
|
||||
} else if (move.to == Position.getSquare(2, 0)) {
|
||||
ret.append("O-O-O");
|
||||
}
|
||||
} else if (move.from == bKingOrigPos && pos.getPiece(bKingOrigPos) == Piece.BKING) {
|
||||
// Check black castle
|
||||
if (move.to == Position.getSquare(6, 7)) {
|
||||
ret.append("O-O");
|
||||
} else if (move.to == Position.getSquare(2, 7)) {
|
||||
ret.append("O-O-O");
|
||||
}
|
||||
}
|
||||
if (ret.length() == 0) {
|
||||
if (pieceNames == null)
|
||||
localized = false;
|
||||
int p = pos.getPiece(move.from);
|
||||
if (localized)
|
||||
ret.append(pieceToCharLocalized(p));
|
||||
else
|
||||
ret.append(pieceToChar(p));
|
||||
int x1 = Position.getX(move.from);
|
||||
int y1 = Position.getY(move.from);
|
||||
int x2 = Position.getX(move.to);
|
||||
int y2 = Position.getY(move.to);
|
||||
if (longForm) {
|
||||
ret.append((char)(x1 + 'a'));
|
||||
ret.append((char) (y1 + '1'));
|
||||
ret.append(isCapture(pos, move) ? 'x' : '-');
|
||||
} else {
|
||||
if (p == (pos.whiteMove ? Piece.WPAWN : Piece.BPAWN)) {
|
||||
if (isCapture(pos, move)) {
|
||||
ret.append((char) (x1 + 'a'));
|
||||
}
|
||||
} else {
|
||||
int numSameTarget = 0;
|
||||
int numSameFile = 0;
|
||||
int numSameRow = 0;
|
||||
if (moves == null)
|
||||
moves = MoveGen.instance.legalMoves(pos);
|
||||
int mSize = moves.size();
|
||||
for (int mi = 0; mi < mSize; mi++) {
|
||||
Move m = moves.get(mi);
|
||||
if ((pos.getPiece(m.from) == p) && (m.to == move.to)) {
|
||||
numSameTarget++;
|
||||
if (Position.getX(m.from) == x1)
|
||||
numSameFile++;
|
||||
if (Position.getY(m.from) == y1)
|
||||
numSameRow++;
|
||||
}
|
||||
}
|
||||
if (numSameTarget < 2) {
|
||||
// No file/row info needed
|
||||
} else if (numSameFile < 2) {
|
||||
ret.append((char) (x1 + 'a')); // Only file info needed
|
||||
} else if (numSameRow < 2) {
|
||||
ret.append((char) (y1 + '1')); // Only row info needed
|
||||
} else {
|
||||
ret.append((char) (x1 + 'a')); // File and row info needed
|
||||
ret.append((char) (y1 + '1'));
|
||||
}
|
||||
}
|
||||
if (isCapture(pos, move)) {
|
||||
ret.append('x');
|
||||
}
|
||||
}
|
||||
ret.append((char) (x2 + 'a'));
|
||||
ret.append((char) (y2 + '1'));
|
||||
if (move.promoteTo != Piece.EMPTY) {
|
||||
if (localized)
|
||||
ret.append(pieceToCharLocalized(move.promoteTo));
|
||||
else
|
||||
ret.append(pieceToChar(move.promoteTo));
|
||||
}
|
||||
}
|
||||
UndoInfo ui = new UndoInfo();
|
||||
pos.makeMove(move, ui);
|
||||
boolean givesCheck = MoveGen.inCheck(pos);
|
||||
if (givesCheck) {
|
||||
ArrayList<Move> nextMoves = MoveGen.instance.legalMoves(pos);
|
||||
if (nextMoves.size() == 0) {
|
||||
ret.append('#');
|
||||
} else {
|
||||
ret.append('+');
|
||||
}
|
||||
}
|
||||
pos.unMakeMove(move, ui);
|
||||
|
||||
return ret.toString();
|
||||
}
|
||||
|
||||
private static boolean isCapture(Position pos, Move move) {
|
||||
if (pos.getPiece(move.to) == Piece.EMPTY) {
|
||||
int p = pos.getPiece(move.from);
|
||||
if ((p == (pos.whiteMove ? Piece.WPAWN : Piece.BPAWN)) && (move.to == pos.getEpSquare())) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Decide if move is valid in position pos.
|
||||
* @param pos Position for which to test move.
|
||||
* @param move The move to check for validity.
|
||||
* @return True if move is valid in position pos, false otherwise.
|
||||
*/
|
||||
public static boolean isValid(Position pos, Move move) {
|
||||
if (move == null)
|
||||
return false;
|
||||
ArrayList<Move> moves = new MoveGen().legalMoves(pos);
|
||||
for (int i = 0; i < moves.size(); i++)
|
||||
if (move.equals(moves.get(i)))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
private final static class MoveInfo {
|
||||
int piece; // -1 for unspecified
|
||||
int fromX, fromY, toX, toY; // -1 for unspecified
|
||||
int promPiece; // -1 for unspecified
|
||||
MoveInfo() { piece = fromX = fromY = toX = toY = promPiece = -1; }
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a chess move string to a Move object.
|
||||
* The string may specify any combination of piece/source/target/promotion
|
||||
* information as long as it matches exactly one valid move.
|
||||
*/
|
||||
public static Move stringToMove(Position pos, String strMove) {
|
||||
return stringToMove(pos, strMove, null);
|
||||
}
|
||||
public static Move stringToMove(Position pos, String strMove,
|
||||
ArrayList<Move> moves) {
|
||||
if (strMove.equals("--"))
|
||||
return new Move(0, 0, 0);
|
||||
|
||||
strMove = strMove.replaceAll("=", "");
|
||||
strMove = strMove.replaceAll("\\+", "");
|
||||
strMove = strMove.replaceAll("#", "");
|
||||
boolean wtm = pos.whiteMove;
|
||||
|
||||
MoveInfo info = new MoveInfo();
|
||||
boolean capture = false;
|
||||
if (strMove.equals("O-O") || strMove.equals("0-0") || strMove.equals("o-o")) {
|
||||
info.piece = wtm ? Piece.WKING : Piece.BKING;
|
||||
info.fromX = 4;
|
||||
info.toX = 6;
|
||||
info.fromY = info.toY = wtm ? 0 : 7;
|
||||
info.promPiece = Piece.EMPTY;
|
||||
} else if (strMove.equals("O-O-O") || strMove.equals("0-0-0") || strMove.equals("o-o-o")) {
|
||||
info.piece = wtm ? Piece.WKING : Piece.BKING;
|
||||
info.fromX = 4;
|
||||
info.toX = 2;
|
||||
info.fromY = info.toY = wtm ? 0 : 7;
|
||||
info.promPiece = Piece.EMPTY;
|
||||
} else {
|
||||
boolean atToSq = false;
|
||||
for (int i = 0; i < strMove.length(); i++) {
|
||||
char c = strMove.charAt(i);
|
||||
if (i == 0) {
|
||||
int piece = charToPiece(wtm, c);
|
||||
if (piece >= 0) {
|
||||
info.piece = piece;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
int tmpX = c - 'a';
|
||||
if ((tmpX >= 0) && (tmpX < 8)) {
|
||||
if (atToSq || (info.fromX >= 0))
|
||||
info.toX = tmpX;
|
||||
else
|
||||
info.fromX = tmpX;
|
||||
}
|
||||
int tmpY = c - '1';
|
||||
if ((tmpY >= 0) && (tmpY < 8)) {
|
||||
if (atToSq || (info.fromY >= 0))
|
||||
info.toY = tmpY;
|
||||
else
|
||||
info.fromY = tmpY;
|
||||
}
|
||||
if ((c == 'x') || (c == '-')) {
|
||||
atToSq = true;
|
||||
if (c == 'x')
|
||||
capture = true;
|
||||
}
|
||||
if (i == strMove.length() - 1) {
|
||||
int promPiece = charToPiece(wtm, c);
|
||||
if (promPiece >= 0) {
|
||||
info.promPiece = promPiece;
|
||||
}
|
||||
}
|
||||
}
|
||||
if ((info.fromX >= 0) && (info.toX < 0)) {
|
||||
info.toX = info.fromX;
|
||||
info.fromX = -1;
|
||||
}
|
||||
if ((info.fromY >= 0) && (info.toY < 0)) {
|
||||
info.toY = info.fromY;
|
||||
info.fromY = -1;
|
||||
}
|
||||
if (info.piece < 0) {
|
||||
boolean haveAll = (info.fromX >= 0) && (info.fromY >= 0) &&
|
||||
(info.toX >= 0) && (info.toY >= 0);
|
||||
if (!haveAll)
|
||||
info.piece = wtm ? Piece.WPAWN : Piece.BPAWN;
|
||||
}
|
||||
if (info.promPiece < 0)
|
||||
info.promPiece = Piece.EMPTY;
|
||||
}
|
||||
|
||||
if (moves == null)
|
||||
moves = MoveGen.instance.legalMoves(pos);
|
||||
|
||||
ArrayList<Move> matches = new ArrayList<>(2);
|
||||
for (int i = 0; i < moves.size(); i++) {
|
||||
Move m = moves.get(i);
|
||||
int p = pos.getPiece(m.from);
|
||||
boolean match = true;
|
||||
if ((info.piece >= 0) && (info.piece != p))
|
||||
match = false;
|
||||
if ((info.fromX >= 0) && (info.fromX != Position.getX(m.from)))
|
||||
match = false;
|
||||
if ((info.fromY >= 0) && (info.fromY != Position.getY(m.from)))
|
||||
match = false;
|
||||
if ((info.toX >= 0) && (info.toX != Position.getX(m.to)))
|
||||
match = false;
|
||||
if ((info.toY >= 0) && (info.toY != Position.getY(m.to)))
|
||||
match = false;
|
||||
if ((info.promPiece >= 0) && (info.promPiece != m.promoteTo))
|
||||
match = false;
|
||||
if (match) {
|
||||
matches.add(m);
|
||||
}
|
||||
}
|
||||
int nMatches = matches.size();
|
||||
if (nMatches == 0)
|
||||
return null;
|
||||
else if (nMatches == 1)
|
||||
return matches.get(0);
|
||||
if (!capture)
|
||||
return null;
|
||||
Move move = null;
|
||||
for (int i = 0; i < matches.size(); i++) {
|
||||
Move m = matches.get(i);
|
||||
int capt = pos.getPiece(m.to);
|
||||
if (capt != Piece.EMPTY) {
|
||||
if (move == null)
|
||||
move = m;
|
||||
else
|
||||
return null;
|
||||
}
|
||||
}
|
||||
return move;
|
||||
}
|
||||
|
||||
/** Convert a move object to UCI string format. */
|
||||
public static String moveToUCIString(Move m) {
|
||||
String ret = squareToString(m.from);
|
||||
ret += squareToString(m.to);
|
||||
switch (m.promoteTo) {
|
||||
case Piece.WQUEEN:
|
||||
case Piece.BQUEEN:
|
||||
ret += "q";
|
||||
break;
|
||||
case Piece.WROOK:
|
||||
case Piece.BROOK:
|
||||
ret += "r";
|
||||
break;
|
||||
case Piece.WBISHOP:
|
||||
case Piece.BBISHOP:
|
||||
ret += "b";
|
||||
break;
|
||||
case Piece.WKNIGHT:
|
||||
case Piece.BKNIGHT:
|
||||
ret += "n";
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a string in UCI move format to a Move object.
|
||||
* @return A move object, or null if move has invalid syntax
|
||||
*/
|
||||
public static Move UCIstringToMove(String move) {
|
||||
Move m = null;
|
||||
if ((move.length() < 4) || (move.length() > 5))
|
||||
return m;
|
||||
int fromSq = TextIO.getSquare(move.substring(0, 2));
|
||||
int toSq = TextIO.getSquare(move.substring(2, 4));
|
||||
if ((fromSq < 0) || (toSq < 0)) {
|
||||
return m;
|
||||
}
|
||||
char prom = ' ';
|
||||
boolean white = true;
|
||||
if (move.length() == 5) {
|
||||
prom = move.charAt(4);
|
||||
if (Position.getY(toSq) == 7) {
|
||||
white = true;
|
||||
} else if (Position.getY(toSq) == 0) {
|
||||
white = false;
|
||||
} else {
|
||||
return m;
|
||||
}
|
||||
}
|
||||
int promoteTo;
|
||||
switch (prom) {
|
||||
case ' ':
|
||||
promoteTo = Piece.EMPTY;
|
||||
break;
|
||||
case 'q':
|
||||
promoteTo = white ? Piece.WQUEEN : Piece.BQUEEN;
|
||||
break;
|
||||
case 'r':
|
||||
promoteTo = white ? Piece.WROOK : Piece.BROOK;
|
||||
break;
|
||||
case 'b':
|
||||
promoteTo = white ? Piece.WBISHOP : Piece.BBISHOP;
|
||||
break;
|
||||
case 'n':
|
||||
promoteTo = white ? Piece.WKNIGHT : Piece.BKNIGHT;
|
||||
break;
|
||||
default:
|
||||
return m;
|
||||
}
|
||||
m = new Move(fromSq, toSq, promoteTo);
|
||||
return m;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a string, such as "e4" to a square number.
|
||||
* @return The square number, or -1 if not a legal square.
|
||||
*/
|
||||
public static int getSquare(String s) {
|
||||
int x = s.charAt(0) - 'a';
|
||||
int y = s.charAt(1) - '1';
|
||||
if ((x < 0) || (x > 7) || (y < 0) || (y > 7))
|
||||
return -1;
|
||||
return Position.getSquare(x, y);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a square number to a string, such as "e4".
|
||||
*/
|
||||
public static String squareToString(int square) {
|
||||
StringBuilder ret = new StringBuilder();
|
||||
int x = Position.getX(square);
|
||||
int y = Position.getY(square);
|
||||
ret.append((char) (x + 'a'));
|
||||
ret.append((char) (y + '1'));
|
||||
return ret.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an ascii representation of a position.
|
||||
*/
|
||||
public static String asciiBoard(Position pos) {
|
||||
StringBuilder ret = new StringBuilder(400);
|
||||
String nl = String.format(Locale.US, "%n");
|
||||
ret.append(" +----+----+----+----+----+----+----+----+"); ret.append(nl);
|
||||
for (int y = 7; y >= 0; y--) {
|
||||
ret.append(" |");
|
||||
for (int x = 0; x < 8; x++) {
|
||||
ret.append(' ');
|
||||
int p = pos.getPiece(Position.getSquare(x, y));
|
||||
if (p == Piece.EMPTY) {
|
||||
boolean dark = Position.darkSquare(x, y);
|
||||
ret.append(dark ? ".. |" : " |");
|
||||
} else {
|
||||
ret.append(Piece.isWhite(p) ? ' ' : '*');
|
||||
String pieceName = pieceToChar(p);
|
||||
if (pieceName.length() == 0)
|
||||
pieceName = "P";
|
||||
ret.append(pieceName);
|
||||
ret.append(" |");
|
||||
}
|
||||
}
|
||||
ret.append(nl);
|
||||
ret.append(" +----+----+----+----+----+----+----+----+");
|
||||
ret.append(nl);
|
||||
}
|
||||
return ret.toString();
|
||||
}
|
||||
|
||||
/** Convert a piece and a square to a string, such as Nf3. */
|
||||
public static String pieceAndSquareToString(int currentPieceType, int p, int sq) {
|
||||
StringBuilder ret = new StringBuilder();
|
||||
if (currentPieceType == PGNOptions.PT_FIGURINE) {
|
||||
ret.append(Piece.toUniCode(p));
|
||||
} else {
|
||||
boolean localized = (currentPieceType != PGNOptions.PT_ENGLISH);
|
||||
if ((p == Piece.WPAWN) || (p == Piece.BPAWN))
|
||||
ret.append(localized ? pieceNames[0] : "P");
|
||||
else
|
||||
ret.append(localized ? pieceToCharLocalized(p) : pieceToChar(p));
|
||||
}
|
||||
ret.append(squareToString(sq));
|
||||
return ret.toString();
|
||||
}
|
||||
|
||||
private static String pieceToChar(int p) {
|
||||
switch (p) {
|
||||
case Piece.WQUEEN: case Piece.BQUEEN: return "Q";
|
||||
case Piece.WROOK: case Piece.BROOK: return "R";
|
||||
case Piece.WBISHOP: case Piece.BBISHOP: return "B";
|
||||
case Piece.WKNIGHT: case Piece.BKNIGHT: return "N";
|
||||
case Piece.WKING: case Piece.BKING: return "K";
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
public static String pieceToCharLocalized(int p) {
|
||||
switch (p) {
|
||||
case Piece.WQUEEN: case Piece.BQUEEN: return pieceNames[4];
|
||||
case Piece.WROOK: case Piece.BROOK: return pieceNames[3];
|
||||
case Piece.WBISHOP: case Piece.BBISHOP: return pieceNames[2];
|
||||
case Piece.WKNIGHT: case Piece.BKNIGHT: return pieceNames[1];
|
||||
case Piece.WKING: case Piece.BKING: return pieceNames[5];
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
private static int charToPiece(boolean white, char c) {
|
||||
switch (c) {
|
||||
case 'Q': case 'q': return white ? Piece.WQUEEN : Piece.BQUEEN;
|
||||
case 'R': case 'r': return white ? Piece.WROOK : Piece.BROOK;
|
||||
case 'B': return white ? Piece.WBISHOP : Piece.BBISHOP;
|
||||
case 'N': case 'n': return white ? Piece.WKNIGHT : Piece.BKNIGHT;
|
||||
case 'K': case 'k': return white ? Piece.WKING : Piece.BKING;
|
||||
case 'P': case 'p': return white ? Piece.WPAWN : Piece.BPAWN;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
/** Add an = sign to a promotion move, as required by the PGN standard. */
|
||||
public static String pgnPromotion(String str) {
|
||||
int idx = str.length() - 1;
|
||||
while (idx > 0) {
|
||||
char c = str.charAt(idx);
|
||||
if ((c != '#') && (c != '+'))
|
||||
break;
|
||||
idx--;
|
||||
}
|
||||
if ((idx > 0) && (charToPiece(true, str.charAt(idx)) != -1))
|
||||
idx--;
|
||||
return str.substring(0, idx + 1) + '=' + str.substring(idx + 1, str.length());
|
||||
}
|
||||
}
|
|
@ -1,30 +0,0 @@
|
|||
/*
|
||||
DroidFish - An Android chess program.
|
||||
Copyright (C) 2011 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.gamelogic;
|
||||
|
||||
/**
|
||||
* Contains enough information to undo a previous move.
|
||||
* Set by makeMove(). Used by unMakeMove().
|
||||
*/
|
||||
public class UndoInfo {
|
||||
int capturedPiece;
|
||||
int castleMask;
|
||||
int epSquare;
|
||||
int halfMoveClock;
|
||||
}
|
Loading…
Reference in New Issue
Block a user