Remove unneeded code from buildSrc.

This commit is contained in:
Peter Osterlund 2019-03-17 22:48:06 +01:00
parent 1a533ca1fb
commit de36444c9f
25 changed files with 610 additions and 4617 deletions

View File

@ -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;
}

View File

@ -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;
}
}

View File

@ -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

View File

@ -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;
}
}
}

View File

@ -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;

View File

@ -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;

View File

@ -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

View File

@ -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 {

View File

@ -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> {

View File

@ -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;
}
}
}
}

View File

@ -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 {

View File

@ -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";
}
}

View File

@ -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());
}
}

View File

@ -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 {

View File

@ -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;

View File

@ -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;
}
}

View File

@ -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);
}
}

View File

@ -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);
}
}

View File

@ -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);
}
}

View File

@ -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);
}
}

View File

@ -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());
}
}

View File

@ -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;
}