DroidFish: Display chess moves using localized piece names.

This commit is contained in:
Peter Osterlund 2012-08-19 10:05:42 +00:00
parent 986d30991d
commit 5c161d2101
22 changed files with 271 additions and 111 deletions

View File

@ -48,7 +48,7 @@ public final class MoveGen {
/**
* Generate and return a list of pseudo-legal moves.
* Pseudo-legal means that the moves doesn't necessarily defend from check threats.
* Pseudo-legal means that the moves don't necessarily defend from check threats.
*/
public final MoveList pseudoLegalMoves(Position pos) {
MoveList moveList = getMoveListObj();

View File

@ -108,7 +108,7 @@ wenn Sie es nicht aktiv nutzen.\
<string name="filename">Dateiname:</string>
<string name="halfmove">Halbzug:</string>
<string name="fullmove">Zugnummer:</string>
<string name="filter_text">Filtertext</string>
<string name="filter_text">Suche…</string>
<string name="value_percent">Wert (%):</string>
<string name="header_event">Ereignis:</string>
<string name="header_site">Ort:</string>
@ -171,6 +171,7 @@ wenn Sie es nicht aktiv nutzen.\
<string name="uci_protocol_error">UCI-Protokollfehler</string>
<string name="start_new_game">Neue Partie starten?</string>
<string name="strength_cuckoo_hint">Nutzen Sie die CuckooChess-Engine für eine noch geringere Spielstärke.</string>
<string name="piece_names">B S L T D K</string>
<string name="err_too_few_spaces">Zuwenig Felder</string>
<string name="err_invalid_piece">Unzulässige Figur</string>
<string name="err_invalid_side">Unzulässige Seite</string>

View File

@ -169,6 +169,7 @@ Si está usted utilizando la batería, se recomienda que cambie los ajustes para
<string name="uci_protocol_error">Fallo en el protocolo UCI</string>
<string name="start_new_game">¿Empezar nueva partida?</string>
<string name="strength_cuckoo_hint">Utilice el motor CuckooChess para rebajar aún más el nivel de juego.</string>
<string name="piece_names">P C A T D R</string>
<string name="err_too_few_spaces">Pocos espacios</string>
<string name="err_invalid_piece">Pieza incorrecta</string>
<string name="err_invalid_side">Lado incorrecto</string>

View File

@ -107,7 +107,7 @@ não estiver usando o programa diretamente.\</string>
<string name="filename">Arquivo:</string>
<string name="halfmove">Relógio para meio-lance:</string>
<string name="fullmove">Contador de lances completos:</string>
<string name="filter_text">Pesquisar...</string>
<string name="filter_text">Pesquisar</string>
<string name="value_percent">Valor(%):</string>
<string name="header_event">Evento:</string>
<string name="header_site">Site:</string>
@ -170,6 +170,7 @@ não estiver usando o programa diretamente.\</string>
<string name="uci_protocol_error">Erro de protocolo UCI</string>
<string name="start_new_game">Iniciar nova partida?</string>
<string name="strength_cuckoo_hint">Use o CuckooChess para um nível ainda menor.</string>
<string name="piece_names">P C B T D R</string>
<string name="err_too_few_spaces">Poucos espaços</string>
<string name="err_invalid_piece">Peça inválida</string>
<string name="err_invalid_side">Lado inválido</string>
@ -238,7 +239,7 @@ não estiver usando o programa diretamente.\</string>
<string name="prefs_fullScreenMode_summary">Modo tela cheia oculta a barra de notificações</string>
<string name="prefs_wakeLock_title">Desabilita apagar a tela automaticamente</string>
<string name="prefs_drawSquareLabels_title">Rótulos nas casas</string>
<string name="prefs_drawSquareLabels_summary">Mostrar rótulos nas casas: a-h e 1-8</string>
<string name="prefs_drawSquareLabels_summary">Mostrar rótulos nas casas: ah e 18</string>
<string name="prefs_scrollSensitivity_title">Velocidade do movimento</string>
<string name="prefs_scrollSensitivity_summary">Velocidade do movimento para navegação em partida</string>
<string name="prefs_invertScrollDirection_title">Inverter direção do movimento</string>

View File

@ -159,6 +159,7 @@
<string name="uci_protocol_error">Ошибка UCI протокола</string>
<string name="start_new_game">Начать новую партию?</string>
<string name="strength_cuckoo_hint">Использовать движок CuckooChess для наименьшего уровня сложности.</string>
<string name="piece_names">П К С Л Ф Кр</string>
<string name="err_too_few_spaces">Слишком малое пространство</string>
<string name="err_invalid_piece">Неверная фигура</string>
<string name="err_invalid_side">Неверное местоположение</string>

View File

@ -10,6 +10,7 @@
<string name="thinking_arrows_default">2</string>
<string name="scroll_sensitivity_default">2</string>
<string name="book_line_length_default">1000000</string>
<string name="viewPieceType_default">1</string>
<string name="cpu_warning">\
<b>CPU Usage</b>\n\
If you leave DroidFish running in the background and <i>GameMode</i> is set to \
@ -185,6 +186,7 @@ you are not actively using the program.\
<string name="invalid_network_port">Invalid network port</string>
<string name="start_new_game">Start New Game?</string>
<string name="strength_cuckoo_hint">Use the CuckooChess engine for even lower strength.</string>
<string name="piece_names">P N B R Q K</string>
<string name="err_too_few_spaces">Too few spaces</string>
<string name="err_invalid_piece">Invalid piece</string>
<string name="err_invalid_side">Invalid side</string>
@ -323,6 +325,8 @@ you are not actively using the program.\
<string name="prefs_viewNAG_summary">Include numeric annotation glyphs (NAGs), such as ! and ?</string>
<string name="prefs_viewHeaders_title">Headers</string>
<string name="prefs_viewHeaders_summary">Show PGN header lines</string>
<string name="prefs_viewPieceType_title">Pieces</string>
<string name="prefs_viewPieceType_summary">Control how chess pieces are displayed</string>
<string name="prefs_pgn_import">PGN import</string>
<string name="prefs_importVariations_title">Variations</string>
<string name="prefs_importVariations_summary">Include non-mainline moves</string>
@ -376,6 +380,14 @@ you are not actively using the program.\
<item>6</item>
<item>8</item>
</string-array>
<string-array name="viewPieceType_texts">
<item>English letters</item>
<item>Local language letters</item>
</string-array>
<string-array name="viewPieceType_values">
<item>0</item>
<item>1</item>
</string-array>
<string-array name="engine_hash_texts">
<item>16 MB</item>
<item>32 MB</item>

View File

@ -513,6 +513,14 @@
android:summary="@string/prefs_viewHeaders_summary"
android:defaultValue="false">
</CheckBoxPreference>
<ListPreference
android:key="viewPieceType"
android:title="@string/prefs_viewPieceType_title"
android:summary="@string/prefs_viewPieceType_summary"
android:entryValues="@array/viewPieceType_values"
android:entries="@array/viewPieceType_texts"
android:defaultValue="@string/viewPieceType_default">
</ListPreference>
</PreferenceCategory>
<PreferenceCategory
android:title="@string/prefs_pgn_import">

View File

@ -126,7 +126,6 @@ public class DroidFish extends Activity implements GUIInterface {
// FIXME!!! PGN view option: game continuation (for training)
// FIXME!!! Remove invalid playerActions in PGN import (should be done in verifyChildren)
// FIXME!!! Implement bookmark mechanism for positions in pgn files
// FIXME!!! Display chess notation in local language
// FIXME!!! Add support for "Chess Leipzig" font
// FIXME!!! Implement figurine notation
@ -253,7 +252,7 @@ public class DroidFish extends Activity implements GUIInterface {
public void run() {
pgnOptions.view.variations = toggleBooleanPref("viewVariations");
gameTextListener.clear();
ctrl.prefsChanged();
ctrl.prefsChanged(false);
}
});
addAction(new UIAction() {
@ -264,7 +263,7 @@ public class DroidFish extends Activity implements GUIInterface {
public void run() {
pgnOptions.view.comments = toggleBooleanPref("viewComments");
gameTextListener.clear();
ctrl.prefsChanged();
ctrl.prefsChanged(false);
}
});
addAction(new UIAction() {
@ -275,7 +274,7 @@ public class DroidFish extends Activity implements GUIInterface {
public void run() {
pgnOptions.view.headers = toggleBooleanPref("viewHeaders");
gameTextListener.clear();
ctrl.prefsChanged();
ctrl.prefsChanged(false);
}
});
addAction(new UIAction() {
@ -352,6 +351,7 @@ public class DroidFish extends Activity implements GUIInterface {
custom3ButtonActions = new ButtonActions("custom3", CUSTOM3_BUTTON_DIALOG,
R.string.select_action);
TextIO.setPieceNames(getString(R.string.piece_names));
initUI(true);
gameTextListener = new PgnScreenText(pgnOptions);
@ -808,6 +808,8 @@ public class DroidFish extends Activity implements GUIInterface {
pgnOptions.view.comments = settings.getBoolean("viewComments", true);
pgnOptions.view.nag = settings.getBoolean("viewNAG", true);
pgnOptions.view.headers = settings.getBoolean("viewHeaders", false);
final int oldViewPieceType = pgnOptions.view.pieceType;
pgnOptions.view.pieceType = getIntSetting("viewPieceType", PGNOptions.PT_LOCAL);
pgnOptions.imp.variations = settings.getBoolean("importVariations", true);
pgnOptions.imp.comments = settings.getBoolean("importComments", true);
pgnOptions.imp.nag = settings.getBoolean("importNAG", true);
@ -821,7 +823,7 @@ public class DroidFish extends Activity implements GUIInterface {
cb.setColors();
gameTextListener.clear();
ctrl.prefsChanged();
ctrl.prefsChanged(oldViewPieceType != pgnOptions.view.pieceType);
}
private void updateButtons() {
@ -1829,7 +1831,7 @@ public class DroidFish extends Activity implements GUIInterface {
ColorTheme.instance().setTheme(settings, item);
cb.setColors();
gameTextListener.clear();
ctrl.prefsChanged();
ctrl.prefsChanged(false);
dialog.dismiss();
}
});

View File

@ -20,11 +20,15 @@ package org.petero.droidfish;
/** Settings controlling PGN import/export */
public class PGNOptions {
public static final int PT_ENGLISH = 0; // Piece type english letters
public static final int PT_LOCAL = 1; // Piece type local language letters
public static class Viewer {
public boolean variations;
public boolean comments;
public boolean nag;
public boolean headers;
public int pieceType;
}
public static class Import {
public boolean variations;
@ -39,6 +43,7 @@ public class PGNOptions {
public boolean clockInfo;
public boolean pgnPromotions;
public boolean moveNrAfterNag;
public int pieceType;
}
public Viewer view;
@ -50,5 +55,6 @@ public class PGNOptions {
imp = new Import();
exp = new Export();
exp.moveNrAfterNag = true;
exp.pieceType = PT_ENGLISH;
}
}

View File

@ -114,7 +114,8 @@ public final class DroidBook {
}
/** Return all book moves, both as a formatted string and as a list of moves. */
public final synchronized Pair<String,ArrayList<Move>> getAllBookMoves(Position pos) {
public final synchronized Pair<String,ArrayList<Move>> getAllBookMoves(Position pos,
boolean localized) {
StringBuilder ret = new StringBuilder();
ArrayList<Move> bookMoveList = new ArrayList<Move>();
List<BookEntry> bookMoves = getBook().getBookEntries(pos);
@ -150,7 +151,7 @@ public final class DroidBook {
for (BookEntry be : bookMoves) {
Move m = be.move;
bookMoveList.add(m);
String moveStr = TextIO.moveToString(pos, m, false);
String moveStr = TextIO.moveToString(pos, m, false, localized);
if (first)
first = false;
else

View File

@ -275,8 +275,8 @@ public class DroidComputerPlayer {
}
/** Return all book moves, both as a formatted string and as a list of moves. */
public final Pair<String, ArrayList<Move>> getBookHints(Position pos) {
return book.getAllBookMoves(pos);
public final Pair<String, ArrayList<Move>> getBookHints(Position pos, boolean localized) {
return book.getAllBookMoves(pos, localized);
}
/** Get engine reported name. */
@ -371,7 +371,9 @@ public class DroidComputerPlayer {
Move bookMove = book.getBookMove(sr.currPos);
if (bookMove != null) {
if (canClaimDraw(sr.currPos, posHashList, posHashListSize, bookMove) == "") {
listener.notifySearchResult(sr.searchId, TextIO.moveToString(sr.currPos, bookMove, false), null);
listener.notifySearchResult(sr.searchId,
TextIO.moveToString(sr.currPos, bookMove, false, false),
null);
return;
}
}
@ -803,7 +805,7 @@ public class DroidComputerPlayer {
} else if (canClaimDrawRep(pos, posHashList, posHashListSize, posHashListSize)) {
drawStr = "draw rep";
} else if (move != null) {
String strMove = TextIO.moveToString(pos, move, false);
String strMove = TextIO.moveToString(pos, move, false, false);
posHashList[posHashListSize++] = pos.zobristHash();
UndoInfo ui = new UndoInfo();
pos.makeMove(move, ui);

View File

@ -184,15 +184,22 @@ public class DroidChessController {
}
/** Notify controller that preferences has changed. */
public final synchronized void prefsChanged() {
public final synchronized void prefsChanged(boolean translateMoves) {
if (game == null)
translateMoves = false;
if (translateMoves)
game.tree.translateMoves();
updateBookHints();
updateMoveList();
listener.prefsChanged(searchId);
listener.prefsChanged(searchId, translateMoves);
if (translateMoves)
updateGUI();
}
/** De-serialize from byte array. */
public final synchronized void fromByteArray(byte[] data) {
game.fromByteArray(data);
game.tree.translateMoves();
}
/** Serialize to byte array. */
@ -221,6 +228,7 @@ public class DroidChessController {
// Try read as PGN instead
if (!newGame.readPGN(fenPgn, pgnOptions))
throw e;
newGame.tree.translateMoves();
}
searchId++;
game = newGame;
@ -554,7 +562,7 @@ public class DroidChessController {
public final synchronized CommentInfo getComments() {
Node cur = game.tree.currentNode;
CommentInfo ret = new CommentInfo();
ret.move = cur.moveStr;
ret.move = cur.moveStrLocal;
ret.preComment = cur.preComment;
ret.postComment = cur.postComment;
ret.nag = cur.nag;
@ -571,11 +579,17 @@ public class DroidChessController {
updateGUI();
}
/** Return true if localized piece names should be used. */
private final boolean localPt() {
return pgnOptions.view.pieceType == PGNOptions.PT_LOCAL;
}
/** Engine search information receiver. */
private final class SearchListener implements org.petero.droidfish.gamelogic.SearchListener {
private int currDepth = 0;
private int currMoveNr = 0;
private String currMove = "";
private Move currMove = null;
private String currMoveStr = "";
private int currNodes = 0;
private int currNps = 0;
private int currTime = 0;
@ -586,8 +600,10 @@ public class DroidChessController {
private Move ponderMove = null;
private ArrayList<PvInfo> pvInfoV = new ArrayList<PvInfo>();
private int pvInfoSearchId = -1; // Search ID corresponding to pvInfoV
public final void clearSearchInfo(int id) {
pvInfoSearchId = -1;
ponderMove = null;
pvInfoV.clear();
currDepth = 0;
@ -620,7 +636,7 @@ public class DroidChessController {
buf.append(pvi.pvStr);
}
final String statStr = (currDepth > 0) ?
String.format("d:%d %d:%s t:%.2f n:%d nps:%d", currDepth, currMoveNr, currMove,
String.format("d:%d %d:%s t:%.2f n:%d nps:%d", currDepth, currMoveNr, currMoveStr,
currTime / 1000.0, currNodes, currNps)
: "";
final String newPV = buf.toString();
@ -652,7 +668,8 @@ public class DroidChessController {
@Override
public void notifyCurrMove(int id, Position pos, Move m, int moveNr) {
currMove = TextIO.moveToString(pos, m, false);
currMove = m;
currMoveStr = TextIO.moveToString(pos, m, false, localPt());
currMoveNr = moveNr;
setSearchInfo(id);
}
@ -661,6 +678,7 @@ public class DroidChessController {
@Override
public void notifyPV(int id, Position pos, ArrayList<PvInfo> pvInfo, Move ponderMove) {
this.ponderMove = ponderMove;
this.pvInfoSearchId = id;
pvInfoV = (ArrayList<PvInfo>) pvInfo.clone();
for (PvInfo pv : pvInfo) {
currTime = pv.time;
@ -671,7 +689,7 @@ public class DroidChessController {
Position tmpPos = new Position(pos);
UndoInfo ui = new UndoInfo();
if (ponderMove != null) {
String moveStr = TextIO.moveToString(tmpPos, ponderMove, false);
String moveStr = TextIO.moveToString(tmpPos, ponderMove, false, localPt());
buf.append(String.format(" [%s]", moveStr));
tmpPos.makeMove(ponderMove, ui);
}
@ -680,7 +698,7 @@ public class DroidChessController {
break;
if (!TextIO.isValid(tmpPos, m))
break;
String moveStr = TextIO.moveToString(tmpPos, m, false);
String moveStr = TextIO.moveToString(tmpPos, m, false, localPt());
buf.append(String.format(" %s", moveStr));
tmpPos.makeMove(m, ui);
}
@ -706,9 +724,16 @@ public class DroidChessController {
setSearchInfo(id);
}
public void prefsChanged(int id) {
public void prefsChanged(int id, boolean translateMoves) {
if (translateMoves && (id == pvInfoSearchId)) {
Position pos = game.currPos();
if (currMove != null)
notifyCurrMove(id, pos, currMove, currMoveNr);
notifyPV(id, pos, pvInfoV, ponderMove);
} else {
setSearchInfo(id);
}
}
@Override
public void notifySearchResult(final int id, final String cmd, final Move ponder) {
@ -758,7 +783,7 @@ public class DroidChessController {
private final void updateBookHints() {
if (humansTurn()) {
Pair<String, ArrayList<Move>> bi = computerPlayer.getBookHints(game.currPos());
Pair<String, ArrayList<Move>> bi = computerPlayer.getBookHints(game.currPos(), localPt());
listener.notifyBookInfo(searchId, bi.first, bi.second);
}
}
@ -906,8 +931,7 @@ public class DroidChessController {
*/
private final boolean doMove(Move move) {
Position pos = game.currPos();
ArrayList<Move> moves = new MoveGen().pseudoLegalMoves(pos);
moves = MoveGen.removeIllegal(pos, moves);
ArrayList<Move> moves = new MoveGen().legalMoves(pos);
int promoteTo = move.promoteTo;
for (Move m : moves) {
if ((m.from == move.from) && (m.to == move.to)) {
@ -917,7 +941,7 @@ public class DroidChessController {
return false;
}
if (m.promoteTo == promoteTo) {
String strMove = TextIO.moveToString(pos, m, false);
String strMove = TextIO.moveToString(pos, m, false, false, moves);
game.processString(strMove);
return true;
}
@ -943,7 +967,7 @@ public class DroidChessController {
}
} else {
if ((s.state == GameState.DRAW_REP) || (s.state == GameState.DRAW_50))
s.drawInfo = game.getDrawInfo();
s.drawInfo = game.getDrawInfo(localPt());
}
gui.setStatus(s);
updateMoveList();
@ -957,7 +981,7 @@ public class DroidChessController {
if (i > 0) sb.append(' ');
if (i == game.tree.currentNode.defaultChild)
sb.append(Util.boldStart);
sb.append(TextIO.moveToString(pos, prevVarList.get(i), false));
sb.append(TextIO.moveToString(pos, prevVarList.get(i), false, localPt()));
if (i == game.tree.currentNode.defaultChild)
sb.append(Util.boldStop);
}
@ -985,6 +1009,7 @@ public class DroidChessController {
tmpOptions.exp.playerAction = false;
tmpOptions.exp.clockInfo = false;
tmpOptions.exp.moveNrAfterNag = false;
tmpOptions.exp.pieceType = pgnOptions.view.pieceType;
gameTextListener.clear();
game.tree.pgnTreeWalker(tmpOptions, gameTextListener);
}

View File

@ -185,8 +185,8 @@ public class Game {
}
}
public final String getDrawInfo() {
return tree.getGameStateInfo();
public final String getDrawInfo(boolean localized) {
return tree.getGameStateInfo(localized);
}
/**
@ -404,7 +404,7 @@ public class Game {
if (valid) {
String playerAction = rep ? "draw rep" : "draw 50";
if (m != null)
playerAction += " " + TextIO.moveToString(pos, m, false);
playerAction += " " + TextIO.moveToString(pos, m, false, false);
addToGameTree(new Move(0, 0, 0), playerAction);
} else {
pendingDrawOffer = true;

View File

@ -218,10 +218,38 @@ public class GameTree {
}
}
/** Update moveStrLocal in all game nodes. */
public final void translateMoves() {
List<Integer> currPath = new ArrayList<Integer>();
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 final void translateMovesHelper() {
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);
goForward(i, false);
translateMovesHelper();
goBack();
}
}
/** Export game tree in PGN format. */
public final String toPGN(PGNOptions options) {
PgnText pgnText = new PgnText();
options.exp.pgnPromotions = true;
options.exp.pieceType = PGNOptions.PT_ENGLISH;
pgnTreeWalker(options, pgnText);
return pgnText.getPgnString();
}
@ -235,7 +263,7 @@ public class GameTree {
while (currentNode != rootNode) {
Node child = currentNode;
goBack();
int childNum = variations().indexOf(child);
int childNum = currentNode.children.indexOf(child);
currPath.add(childNum);
}
while (variations().size() > 0)
@ -692,11 +720,17 @@ public class GameTree {
int idx = currentNode.children.size();
Node node = new Node(currentNode, moveStr, playerAction, Integer.MIN_VALUE, nag, preComment, postComment);
Move move = TextIO.UCIstringToMove(moveStr);
if (move == null)
move = TextIO.stringToMove(currentPos, moveStr);
ArrayList<Move> moves = null;
if (move == null) {
moves = MoveGen.instance.legalMoves(currentPos);
move = TextIO.stringToMove(currentPos, moveStr, moves);
}
if (move == null)
return -1;
node.moveStr = TextIO.moveToString(currentPos, move, false);
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.move = move;
node.ui = new UndoInfo();
currentNode.children.add(node);
@ -831,7 +865,7 @@ public class GameTree {
}
/** Get additional info affecting gameState. A player "draw" or "resign" command. */
final String getGameStateInfo() {
final String getGameStateInfo(boolean localized) {
String ret = "";
String action = currentNode.playerAction;
if (action.startsWith("draw rep ")) {
@ -840,6 +874,25 @@ public class GameTree {
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;
}
@ -930,7 +983,8 @@ public class GameTree {
* The root node is special in that it doesn't have a move.
*/
public static class Node {
String moveStr; // String representation of move leading to this node. Empty string root node.
String moveStr; // String representation of move leading to this node. Empty string in root node.
String moveStrLocal; // Localized version of moveStr
Move move; // Computed on demand for better PGN parsing performance.
// Subtrees of invalid moves will be dropped when detected.
// Always valid for current node.
@ -948,6 +1002,7 @@ public class GameTree {
public Node() {
this.moveStr = "";
this.moveStrLocal = "";
this.move = null;
this.ui = null;
this.playerAction = "";
@ -963,6 +1018,7 @@ 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;
@ -981,12 +1037,18 @@ public class GameTree {
/** nodePos must represent the same position as this Node object. */
private final boolean verifyChildren(Position nodePos) {
return verifyChildren(nodePos, null);
}
private final boolean verifyChildren(Position nodePos, ArrayList<Move> moves) {
boolean anyToRemove = false;
for (Node child : children) {
if (child.move == null) {
Move move = TextIO.stringToMove(nodePos, child.moveStr);
if (moves == null)
moves = MoveGen.instance.legalMoves(nodePos);
Move move = TextIO.stringToMove(nodePos, child.moveStr, moves);
if (move != null) {
child.moveStr = TextIO.moveToString(nodePos, move, false);
child.moveStr = TextIO.moveToString(nodePos, move, false, false, moves);
child.moveStrLocal = TextIO.moveToString(nodePos, move, false, true, moves);
child.move = move;
child.ui = new UndoInfo();
} else {
@ -1053,6 +1115,7 @@ public class GameTree {
static final 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();
@ -1105,7 +1168,7 @@ public class GameTree {
}
}
/** Export this node in PGN format. */
/** Export this node in PGN (or display text) format. */
private final boolean addPgnDataOneNode(PgnToken.PgnTokenReceiver out, MoveNumber mn,
boolean needMoveNr, PGNOptions options) {
if ((preComment.length() > 0) && options.exp.comments) {
@ -1125,8 +1188,12 @@ public class GameTree {
out.processToken(this, PgnToken.PERIOD, null);
}
}
String str = moveStr;
if (options.exp.pgnPromotions && (move != null) && (move.promoteTo != Piece.EMPTY)) {
String str;
if (options.exp.pieceType == PGNOptions.PT_LOCAL) {
str = moveStrLocal;
} else {
str = moveStr;
if (options.exp.pgnPromotions && (move != null) && (move.promoteTo != Piece.EMPTY))
str = TextIO.pgnPromotion(str);
}
out.processToken(this, PgnToken.SYMBOL, str);
@ -1261,6 +1328,7 @@ public class GameTree {
moveAdded = false;
}
nodeToAdd.moveStr = tok.token;
nodeToAdd.moveStrLocal = tok.token;
moveAdded = true;
}
break;

View File

@ -31,9 +31,16 @@ public class MoveGen {
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 doesn't necessarily defend from check threats.
* Pseudo-legal means that the moves don't necessarily defend from check threats.
*/
public final ArrayList<Move> pseudoLegalMoves(Position pos) {
ArrayList<Move> moveList = getMoveListObj();

View File

@ -37,21 +37,6 @@ public interface SearchListener {
ArrayList<Move> pv;
String pvStr = "";
public PvInfo(PvInfo pvi) {
depth = pvi.depth;
score = pvi.score;
time = pvi.time;
nodes = pvi.nodes;
nps = pvi.nps;
isMate = pvi.isMate;
upperBound = pvi.upperBound;
lowerBound = pvi.lowerBound;
pv = new ArrayList<Move>(pvi.pv.size());
for (int i = 0; i < pvi.pv.size(); i++)
pv.add(pvi.pv.get(i));
pvStr = pvi.pvStr;
}
public PvInfo(int depth, int score, int time, int nodes, int nps,
boolean isMate, boolean upperBound, boolean lowerBound, ArrayList<Move> pv) {
this.depth = depth;

View File

@ -25,12 +25,22 @@ import org.petero.droidfish.R;
/**
*
* Handle conversion of positions and moves to/from text format.
* @author petero
*/
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 final 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 final Position readFEN(String fen) throws ChessParseError {
Position pos = new Position();
@ -312,15 +322,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.
* @param localized If true, use localized piece names.
*/
public static final String moveToString(Position pos, Move move, boolean longForm) {
ArrayList<Move> moves = MoveGen.instance.pseudoLegalMoves(pos);
moves = MoveGen.removeIllegal(pos, moves);
return moveToString(pos, move, longForm, moves);
public static final String moveToString(Position pos, Move move, boolean longForm,
boolean localized) {
return moveToString(pos, move, longForm, localized, null);
}
private static final String moveToString(Position pos, Move move, boolean longForm,
List<Move> moves) {
public static final 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();
@ -342,7 +352,12 @@ public class TextIO {
}
}
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);
@ -361,6 +376,8 @@ public class TextIO {
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);
@ -389,9 +406,13 @@ public class TextIO {
}
ret.append((char) (x2 + 'a'));
ret.append((char) (y2 + '1'));
if (move.promoteTo != Piece.EMPTY)
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);
@ -453,6 +474,10 @@ public class TextIO {
* information as long as it matches exactly one valid move.
*/
public static final Move stringToMove(Position pos, String strMove) {
return stringToMove(pos, strMove, null);
}
public static final Move stringToMove(Position pos, String strMove,
ArrayList<Move> moves) {
if (strMove.equals("--"))
return new Move(0, 0, 0);
@ -530,8 +555,8 @@ public class TextIO {
info.promPiece = Piece.EMPTY;
}
ArrayList<Move> moves = MoveGen.instance.pseudoLegalMoves(pos);
moves = MoveGen.removeIllegal(pos, moves);
if (moves == null)
moves = MoveGen.instance.legalMoves(pos);
ArrayList<Move> matches = new ArrayList<Move>(2);
for (int i = 0; i < moves.size(); i++) {
@ -717,6 +742,17 @@ public class TextIO {
return "";
}
public final 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 final static int charToPiece(boolean white, char c) {
switch (c) {
case 'Q': case 'q': return white ? Piece.WQUEEN : Piece.BQUEEN;

View File

@ -60,7 +60,7 @@ public class BookTest extends TestCase {
public void testGetAllBookMoves() throws ChessParseError {
Position pos = TextIO.readFEN(TextIO.startPosFEN);
DroidBook book = DroidBook.getInstance();
String moveListString = book.getAllBookMoves(pos).first;
String moveListString = book.getAllBookMoves(pos, false).first;
String[] strMoves = moveListString.split(":[0-9]* ");
assertTrue(strMoves.length > 1);
for (String strMove : strMoves) {

View File

@ -140,7 +140,7 @@ public class GameTest extends TestCase {
game.setPos(TextIO.readFEN(fen));
game.processString("draw 50 Nc3");
assertEquals(Game.GameState.DRAW_50, game.getGameState()); // Draw claim valid
assertEquals("Nc3", game.getDrawInfo());
assertEquals("Nc3", game.getDrawInfo(false));
game.setPos(TextIO.readFEN(fen));
game.processString("draw 50 a6");

View File

@ -225,7 +225,7 @@ public class GameTreeTest extends TestCase {
for (int i = 0; i < vars.size(); i++) {
if (i > 0)
ret.append(' ');
String moveStr = TextIO.moveToString(gt.currentPos, vars.get(i), false);
String moveStr = TextIO.moveToString(gt.currentPos, vars.get(i), false, false);
ret.append(moveStr);
}
return ret.toString();
@ -477,7 +477,7 @@ public class GameTreeTest extends TestCase {
assertEquals("A \"good\" player", gt.white);
assertEquals("e4", getVariationsAsString(gt));
// Test for broken PGN headers: [White "A "good" player"]
// Test for broken PGN headers: [White "A "good old" player"]
res = gt.readPGN("[White \"A \"good old\" player\"]\ne4", options);
assertEquals(true, res);
assertEquals("A \"good old\" player", gt.white);

View File

@ -195,7 +195,7 @@ public class MoveGenTest extends TestCase {
}
ArrayList<String> strMoves = new ArrayList<String>();
for (Move m : moves) {
String mStr = TextIO.moveToString(pos, m, true);
String mStr = TextIO.moveToString(pos, m, true, false);
strMoves.add(mStr);
// System.out.println(mStr);
}

View File

@ -123,6 +123,10 @@ public class TextIOTest extends TestCase {
return wasError;
}
private final static String moveToString(Position pos, Move move, boolean longForm) {
return TextIO.moveToString(pos, move, longForm, false);
}
/**
* Test of moveToString method, of class TextIO.
*/
@ -132,39 +136,39 @@ public class TextIOTest extends TestCase {
Move move = new Move(Position.getSquare(4, 1), Position.getSquare(4, 3),
Piece.EMPTY);
boolean longForm = true;
String result = TextIO.moveToString(pos, move, longForm);
String result = moveToString(pos, move, longForm);
assertEquals("e2-e4", result);
move = new Move(Position.getSquare(6, 0), Position.getSquare(5, 2), Piece.EMPTY);
result = TextIO.moveToString(pos, move, longForm);
result = moveToString(pos, move, longForm);
assertEquals("Ng1-f3", result);
move = new Move(Position.getSquare(4, 7), Position.getSquare(2, 7),
Piece.EMPTY);
result = TextIO.moveToString(pos, move, longForm);
result = moveToString(pos, move, longForm);
assertEquals("O-O-O", result);
String fen = "1r3k2/2P5/8/8/8/4K3/8/8 w - - 0 1";
pos = TextIO.readFEN(fen);
assertEquals(fen, TextIO.toFEN(pos));
move = new Move(Position.getSquare(2,6), Position.getSquare(1,7), Piece.WROOK);
result = TextIO.moveToString(pos, move, longForm);
result = moveToString(pos, move, longForm);
assertEquals("c7xb8R+", result);
move = new Move(Position.getSquare(2,6), Position.getSquare(2,7), Piece.WKNIGHT);
result = TextIO.moveToString(pos, move, longForm);
result = moveToString(pos, move, longForm);
assertEquals("c7-c8N", result);
move = new Move(Position.getSquare(2,6), Position.getSquare(2,7), Piece.WQUEEN);
result = TextIO.moveToString(pos, move, longForm);
result = moveToString(pos, move, longForm);
assertEquals("c7-c8Q+", result);
// Test null move
pos = TextIO.readFEN(TextIO.startPosFEN);
Move nullMove = new Move(0, 0, 0);
result = TextIO.moveToString(pos, nullMove, false);
result = moveToString(pos, nullMove, false);
assertEquals("--", result);
result = TextIO.moveToString(pos, nullMove, true);
result = moveToString(pos, nullMove, true);
assertEquals("--", result);
}
@ -176,19 +180,19 @@ public class TextIOTest extends TestCase {
boolean longForm = true;
Move move = new Move(Position.getSquare(1, 6), Position.getSquare(1, 7), Piece.WROOK);
String result = TextIO.moveToString(pos, move, longForm);
String result = moveToString(pos, move, longForm);
assertEquals("b7-b8R+", result); // check
move = new Move(Position.getSquare(1, 6), Position.getSquare(1, 7), Piece.WQUEEN);
result = TextIO.moveToString(pos, move, longForm);
result = moveToString(pos, move, longForm);
assertEquals("b7-b8Q#", result); // check mate
move = new Move(Position.getSquare(1, 6), Position.getSquare(1, 7), Piece.WKNIGHT);
result = TextIO.moveToString(pos, move, longForm);
result = moveToString(pos, move, longForm);
assertEquals("b7-b8N", result);
move = new Move(Position.getSquare(1, 6), Position.getSquare(1, 7), Piece.WBISHOP);
result = TextIO.moveToString(pos, move, longForm);
result = moveToString(pos, move, longForm);
assertEquals("b7-b8B", result); // stalemate
}
@ -202,43 +206,43 @@ public class TextIOTest extends TestCase {
boolean longForm = false;
Move move = new Move(Position.getSquare(4,5), Position.getSquare(4,3), Piece.EMPTY);
String result = TextIO.moveToString(pos, move, longForm);
String result = moveToString(pos, move, longForm);
assertEquals("Qee4", result); // File disambiguation needed
move = new Move(Position.getSquare(2,5), Position.getSquare(4,3), Piece.EMPTY);
result = TextIO.moveToString(pos, move, longForm);
result = moveToString(pos, move, longForm);
assertEquals("Qc6e4", result); // Full disambiguation needed
move = new Move(Position.getSquare(2,3), Position.getSquare(4,3), Piece.EMPTY);
result = TextIO.moveToString(pos, move, longForm);
result = moveToString(pos, move, longForm);
assertEquals("Q4e4", result); // Row disambiguation needed
move = new Move(Position.getSquare(2,3), Position.getSquare(2,0), Piece.EMPTY);
result = TextIO.moveToString(pos, move, longForm);
result = moveToString(pos, move, longForm);
assertEquals("Qc1+", result); // No disambiguation needed
move = new Move(Position.getSquare(0,1), Position.getSquare(0,0), Piece.BQUEEN);
result = TextIO.moveToString(pos, move, longForm);
result = moveToString(pos, move, longForm);
assertEquals("a1Q", result); // Normal promotion
move = new Move(Position.getSquare(0,1), Position.getSquare(1,0), Piece.BQUEEN);
result = TextIO.moveToString(pos, move, longForm);
result = moveToString(pos, move, longForm);
assertEquals("axb1Q#", result); // Capture promotion and check mate
move = new Move(Position.getSquare(0,1), Position.getSquare(1,0), Piece.BKNIGHT);
result = TextIO.moveToString(pos, move, longForm);
result = moveToString(pos, move, longForm);
assertEquals("axb1N", result); // Capture promotion
move = new Move(Position.getSquare(3,6), Position.getSquare(4,4), Piece.EMPTY);
result = TextIO.moveToString(pos, move, longForm);
result = moveToString(pos, move, longForm);
assertEquals("Ne5", result); // Other knight pinned, no disambiguation needed
move = new Move(Position.getSquare(7,6), Position.getSquare(7,4), Piece.EMPTY);
result = TextIO.moveToString(pos, move, longForm);
result = moveToString(pos, move, longForm);
assertEquals("h5", result); // Regular pawn move
move = new Move(Position.getSquare(5,7), Position.getSquare(3,7), Piece.EMPTY);
result = TextIO.moveToString(pos, move, longForm);
result = moveToString(pos, move, longForm);
assertEquals("Rfd8", result); // File disambiguation needed
}