From de36444c9fec1a9e3a93133edeaa0fdef080094d Mon Sep 17 00:00:00 2001 From: Peter Osterlund Date: Sun, 17 Mar 2019 22:48:06 +0100 Subject: [PATCH] Remove unneeded code from buildSrc. --- buildSrc/src/main/java/chess/Book.java | 7 +- .../src/main/java/chess/ChessParseError.java | 25 +- .../buildtools => chess}/EcoBuilder.java | 9 +- buildSrc/src/main/java/chess/Evaluate.java | 1228 ----------------- .../petero/droidfish => chess}/FileUtil.java | 53 +- .../droidfish/gamelogic => chess}/Game.java | 11 +- .../gamelogic => chess}/GameTree.java | 105 +- buildSrc/src/main/java/chess/Move.java | 6 +- buildSrc/src/main/java/chess/MoveGen.java | 1226 ++++------------ .../droidfish => chess}/PGNOptions.java | 2 +- .../droidfish/gamelogic => chess}/Pair.java | 2 +- buildSrc/src/main/java/chess/Parameters.java | 166 --- .../gamelogic => chess}/PgnToken.java | 2 +- buildSrc/src/main/java/chess/Position.java | 55 +- buildSrc/src/main/java/chess/TextIO.java | 529 ++++--- .../gamelogic => chess}/TimeControl.java | 4 +- .../gamelogic => chess}/TimeControlData.java | 2 +- .../droidfish/buildtools => chess}/eco.pgn | 0 .../droidfish/gamelogic/ChessParseError.java | 48 - .../org/petero/droidfish/gamelogic/Move.java | 76 - .../petero/droidfish/gamelogic/MoveGen.java | 313 ----- .../org/petero/droidfish/gamelogic/Piece.java | 90 -- .../petero/droidfish/gamelogic/Position.java | 435 ------ .../petero/droidfish/gamelogic/TextIO.java | 803 ----------- .../petero/droidfish/gamelogic/UndoInfo.java | 30 - 25 files changed, 610 insertions(+), 4617 deletions(-) rename buildSrc/src/main/java/{org/petero/droidfish/buildtools => chess}/EcoBuilder.java (95%) delete mode 100644 buildSrc/src/main/java/chess/Evaluate.java rename buildSrc/src/main/java/{org/petero/droidfish => chess}/FileUtil.java (50%) rename buildSrc/src/main/java/{org/petero/droidfish/gamelogic => chess}/Game.java (98%) rename buildSrc/src/main/java/{org/petero/droidfish/gamelogic => chess}/GameTree.java (93%) rename buildSrc/src/main/java/{org/petero/droidfish => chess}/PGNOptions.java (98%) rename buildSrc/src/main/java/{org/petero/droidfish/gamelogic => chess}/Pair.java (96%) delete mode 100644 buildSrc/src/main/java/chess/Parameters.java rename buildSrc/src/main/java/{org/petero/droidfish/gamelogic => chess}/PgnToken.java (98%) rename buildSrc/src/main/java/{org/petero/droidfish/gamelogic => chess}/TimeControl.java (98%) rename buildSrc/src/main/java/{org/petero/droidfish/gamelogic => chess}/TimeControlData.java (99%) rename buildSrc/src/main/java/{org/petero/droidfish/buildtools => chess}/eco.pgn (100%) delete mode 100644 buildSrc/src/main/java/org/petero/droidfish/gamelogic/ChessParseError.java delete mode 100644 buildSrc/src/main/java/org/petero/droidfish/gamelogic/Move.java delete mode 100644 buildSrc/src/main/java/org/petero/droidfish/gamelogic/MoveGen.java delete mode 100644 buildSrc/src/main/java/org/petero/droidfish/gamelogic/Piece.java delete mode 100644 buildSrc/src/main/java/org/petero/droidfish/gamelogic/Position.java delete mode 100644 buildSrc/src/main/java/org/petero/droidfish/gamelogic/TextIO.java delete mode 100644 buildSrc/src/main/java/org/petero/droidfish/gamelogic/UndoInfo.java diff --git a/buildSrc/src/main/java/chess/Book.java b/buildSrc/src/main/java/chess/Book.java index dda25e3..54be7a7 100644 --- a/buildSrc/src/main/java/chess/Book.java +++ b/buildSrc/src/main/java/chess/Book.java @@ -130,14 +130,13 @@ public class Book { return null; } - MoveGen.MoveList legalMoves = new MoveGen().pseudoLegalMoves(pos); - MoveGen.removeIllegal(pos, legalMoves); + ArrayList 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; } diff --git a/buildSrc/src/main/java/chess/ChessParseError.java b/buildSrc/src/main/java/chess/ChessParseError.java index 3172681..6e0ce56 100644 --- a/buildSrc/src/main/java/chess/ChessParseError.java +++ b/buildSrc/src/main/java/chess/ChessParseError.java @@ -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; } } diff --git a/buildSrc/src/main/java/org/petero/droidfish/buildtools/EcoBuilder.java b/buildSrc/src/main/java/chess/EcoBuilder.java similarity index 95% rename from buildSrc/src/main/java/org/petero/droidfish/buildtools/EcoBuilder.java rename to buildSrc/src/main/java/chess/EcoBuilder.java index b83bb7a..e52136e 100644 --- a/buildSrc/src/main/java/org/petero/droidfish/buildtools/EcoBuilder.java +++ b/buildSrc/src/main/java/chess/EcoBuilder.java @@ -16,20 +16,13 @@ along with this program. If not, see . */ -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 { diff --git a/buildSrc/src/main/java/chess/Evaluate.java b/buildSrc/src/main/java/chess/Evaluate.java deleted file mode 100644 index 6bf3668..0000000 --- a/buildSrc/src/main/java/chess/Evaluate.java +++ /dev/null @@ -1,1228 +0,0 @@ -/* - CuckooChess - A java 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 . -*/ - -package chess; - -import java.io.IOException; -import java.io.InputStream; - -/** Position evaluation routines. */ -public class Evaluate { - static final int pV = 92 + Parameters.instance().getIntPar("pV"); - static final int nV = 385 + Parameters.instance().getIntPar("nV"); - static final int bV = 385 + Parameters.instance().getIntPar("bV"); - static final int rV = 593 + Parameters.instance().getIntPar("rV"); - static final int qV = 1244 + Parameters.instance().getIntPar("qV"); - static final int kV = 9900; // Used by SEE algorithm, but not included in board material sums - - static final int[] pieceValue; - static { - // Initialize material table - pieceValue = new int[Piece.nPieceTypes]; - pieceValue[Piece.WKING ] = kV; - pieceValue[Piece.WQUEEN ] = qV; - pieceValue[Piece.WROOK ] = rV; - pieceValue[Piece.WBISHOP] = bV; - pieceValue[Piece.WKNIGHT] = nV; - pieceValue[Piece.WPAWN ] = pV; - pieceValue[Piece.BKING ] = kV; - pieceValue[Piece.BQUEEN ] = qV; - pieceValue[Piece.BROOK ] = rV; - pieceValue[Piece.BBISHOP] = bV; - pieceValue[Piece.BKNIGHT] = nV; - pieceValue[Piece.BPAWN ] = pV; - pieceValue[Piece.EMPTY ] = 0; - } - - /** Piece/square table for king during middle game. */ - static final int[] kt1b = { -22,-35,-40,-40,-40,-40,-35,-22, - -22,-35,-40,-40,-40,-40,-35,-22, - -25,-35,-40,-45,-45,-40,-35,-25, - -15,-30,-35,-40,-40,-35,-30,-15, - -10,-15,-20,-25,-25,-20,-15,-10, - 4, -2, -5,-15,-15, -5, -2, 4, - 16, 14, 7, -3, -3, 7, 14, 16, - 24, 24, 9, 0, 0, 9, 24, 24 }; - - /** Piece/square table for king during end game. */ - static final int[] kt2b = { 0, 8, 16, 24, 24, 16, 8, 0, - 8, 16, 24, 32, 32, 24, 16, 8, - 16, 24, 32, 40, 40, 32, 24, 16, - 24, 32, 40, 48, 48, 40, 32, 24, - 24, 32, 40, 48, 48, 40, 32, 24, - 16, 24, 32, 40, 40, 32, 24, 16, - 8, 16, 24, 32, 32, 24, 16, 8, - 0, 8, 16, 24, 24, 16, 8, 0 }; - - /** Piece/square table for pawns during middle game. */ - static final int[] pt1b = { 0, 0, 0, 0, 0, 0, 0, 0, - 8, 16, 24, 32, 32, 24, 16, 8, - 3, 12, 20, 28, 28, 20, 12, 3, - -5, 4, 10, 20, 20, 10, 4, -5, - -6, 4, 5, 16, 16, 5, 4, -6, - -6, 4, 2, 5, 5, 2, 4, -6, - -6, 4, 4,-15,-15, 4, 4, -6, - 0, 0, 0, 0, 0, 0, 0, 0 }; - - /** Piece/square table for pawns during end game. */ - static final int[] pt2b = { 0, 0, 0, 0, 0, 0, 0, 0, - 25, 40, 45, 45, 45, 45, 40, 25, - 17, 32, 35, 35, 35, 35, 32, 17, - 5, 24, 24, 24, 24, 24, 24, 5, - -9, 11, 11, 11, 11, 11, 11, -9, - -17, 3, 3, 3, 3, 3, 3,-17, - -20, 0, 0, 0, 0, 0, 0,-20, - 0, 0, 0, 0, 0, 0, 0, 0 }; - - /** Piece/square table for knights during middle game. */ - static final int[] nt1b = { -53,-42,-32,-21,-21,-32,-42,-53, - -42,-32,-10, 0, 0,-10,-32,-42, - -21, 5, 10, 16, 16, 10, 5,-21, - -18, 0, 10, 21, 21, 10, 0,-18, - -18, 0, 3, 21, 21, 3, 0,-18, - -21,-10, 0, 0, 0, 0,-10,-21, - -42,-32,-10, 0, 0,-10,-32,-42, - -53,-42,-32,-21,-21,-32,-42,-53 }; - - /** Piece/square table for knights during end game. */ - static final int[] nt2b = { -56,-44,-34,-22,-22,-34,-44,-56, - -44,-34,-10, 0, 0,-10,-34,-44, - -22, 5, 10, 17, 17, 10, 5,-22, - -19, 0, 10, 22, 22, 10, 0,-19, - -19, 0, 3, 22, 22, 3, 0,-19, - -22,-10, 0, 0, 0, 0,-10,-22, - -44,-34,-10, 0, 0,-10,-34,-44, - -56,-44,-34,-22,-22,-34,-44,-56 }; - - /** Piece/square table for bishops during middle game. */ - static final int[] bt1b = { 0, 0, 0, 0, 0, 0, 0, 0, - 0, 4, 2, 2, 2, 2, 4, 0, - 0, 2, 4, 4, 4, 4, 2, 0, - 0, 2, 4, 4, 4, 4, 2, 0, - 0, 2, 4, 4, 4, 4, 2, 0, - 0, 3, 4, 4, 4, 4, 3, 0, - 0, 4, 2, 2, 2, 2, 4, 0, - -5, -5, -7, -5, -5, -7, -5, -5 }; - - /** Piece/square table for bishops during middle game. */ - static final int[] bt2b = { 0, 0, 0, 0, 0, 0, 0, 0, - 0, 2, 2, 2, 2, 2, 2, 0, - 0, 2, 4, 4, 4, 4, 2, 0, - 0, 2, 4, 4, 4, 4, 2, 0, - 0, 2, 4, 4, 4, 4, 2, 0, - 0, 2, 4, 4, 4, 4, 2, 0, - 0, 2, 2, 2, 2, 2, 2, 0, - 0, 0, 0, 0, 0, 0, 0, 0 }; - - /** Piece/square table for queens during middle game. */ - static final int[] qt1b = { -10, -5, 0, 0, 0, 0, -5,-10, - -5, 0, 5, 5, 5, 5, 0, -5, - 0, 5, 5, 6, 6, 5, 5, 0, - 0, 5, 6, 6, 6, 6, 5, 0, - 0, 5, 6, 6, 6, 6, 5, 0, - 0, 5, 5, 6, 6, 5, 5, 0, - -5, 0, 5, 5, 5, 5, 0, -5, - -10, -5, 0, 0, 0, 0, -5,-10 }; - - /** Piece/square table for rooks during middle game. */ - static final int[] rt1b = { 8, 11, 13, 13, 13, 13, 11, 8, - 22, 27, 27, 27, 27, 27, 27, 22, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - -2, 0, 0, 0, 0, 0, 0, -2, - -2, 0, 0, 2, 2, 0, 0, -2, - -3, 2, 5, 5, 5, 5, 2, -3, - 0, 3, 5, 5, 5, 5, 3, 0 }; - - static final int[] kt1w, qt1w, rt1w, bt1w, nt1w, pt1w, kt2w, bt2w, nt2w, pt2w; - static { - kt1w = new int[64]; - qt1w = new int[64]; - rt1w = new int[64]; - bt1w = new int[64]; - nt1w = new int[64]; - pt1w = new int[64]; - kt2w = new int[64]; - bt2w = new int[64]; - nt2w = new int[64]; - pt2w = new int[64]; - for (int i = 0; i < 64; i++) { - kt1w[i] = kt1b[63-i]; - qt1w[i] = qt1b[63-i]; - rt1w[i] = rt1b[63-i]; - bt1w[i] = bt1b[63-i]; - nt1w[i] = nt1b[63-i]; - pt1w[i] = pt1b[63-i]; - kt2w[i] = kt2b[63-i]; - bt2w[i] = bt2b[63-i]; - nt2w[i] = nt2b[63-i]; - pt2w[i] = pt2b[63-i]; - } - } - - private static final int[] empty = { 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0}; - static final int[][] psTab1 = { empty, kt1w, qt1w, rt1w, bt1w, nt1w, pt1w, - kt1b, qt1b, rt1b, bt1b, nt1b, pt1b }; - static final int[][] psTab2 = { empty, kt2w, qt1w, rt1w, bt2w, nt2w, pt2w, - kt2b, qt1b, rt1b, bt2b, nt2b, pt2b }; - - static final int[][] distToH1A8 = { { 0, 1, 2, 3, 4, 5, 6, 7 }, - { 1, 2, 3, 4, 5, 6, 7, 6 }, - { 2, 3, 4, 5, 6, 7, 6, 5 }, - { 3, 4, 5, 6, 7, 6, 5, 4 }, - { 4, 5, 6, 7, 6, 5, 4, 3 }, - { 5, 6, 7, 6, 5, 4, 3, 2 }, - { 6, 7, 6, 5, 4, 3, 2, 1 }, - { 7, 6, 5, 4, 3, 2, 1, 0 } }; - - static final int[] rookMobScore = {-10,-7,-4,-1,2,5,7,9,11,12,13,14,14,14,14}; - static final int[] bishMobScore = {-15,-10,-6,-2,2,6,10,13,16,18,20,22,23,24}; - static final int[] queenMobScore = {-5,-4,-3,-2,-1,0,1,2,3,4,5,6,7,8,9,9,10,10,10,10,10,10,10,10,10,10,10,10}; - - private static final class PawnHashData { - long key; - int score; // Positive score means good for white - short passedBonusW; - short passedBonusB; - long passedPawnsW; // The most advanced passed pawns for each file - long passedPawnsB; - } - static final PawnHashData[] pawnHash; - static { - final int numEntries = 1<<16; - pawnHash = new PawnHashData[numEntries]; - for (int i = 0; i < numEntries; i++) { - PawnHashData phd = new PawnHashData(); - phd.key = -1; // Non-zero to avoid collision for positions with no pawns - phd.score = 0; - pawnHash[i] = phd; - } - } - - static byte[] kpkTable = null; - static byte[] krkpTable = null; - - // King safety variables - private long wKingZone, bKingZone; // Squares close to king that are worth attacking - private int wKingAttacks, bKingAttacks; // Number of attacks close to white/black king - private long wAttacksBB, bAttacksBB; - private long wPawnAttacks, bPawnAttacks; // Squares attacked by white/black pawns - - /** Constructor. */ - public Evaluate() { - if (kpkTable == null) - kpkTable = readTable("/kpk.bitbase", 2*32*64*48/8); - if (krkpTable == null) - krkpTable = readTable("/krkp.winmasks", 2*32*48*8); - } - - private byte[] readTable(String resource, int length) { - byte[] table = new byte[2*32*64*48/8]; - InputStream inStream = getClass().getResourceAsStream(resource); - try { - int off = 0; - while (off < table.length) { - int len = inStream.read(table, off, table.length - off); - if (len < 0) - throw new RuntimeException(); - off += len; - } - inStream.close(); - return table; - } catch (IOException e) { - throw new RuntimeException(); - } - } - - /** - * Static evaluation of a position. - * @param pos The position to evaluate. - * @return The evaluation score, measured in centipawns. - * Positive values are good for the side to make the next move. - */ - final public int evalPos(Position pos) { - int score = pos.wMtrl - pos.bMtrl; - - wKingAttacks = bKingAttacks = 0; - wKingZone = BitBoard.kingAttacks[pos.getKingSq(true)]; wKingZone |= wKingZone << 8; - bKingZone = BitBoard.kingAttacks[pos.getKingSq(false)]; bKingZone |= bKingZone >>> 8; - wAttacksBB = bAttacksBB = 0L; - - long pawns = pos.pieceTypeBB[Piece.WPAWN]; - wPawnAttacks = ((pawns & BitBoard.maskBToHFiles) << 7) | - ((pawns & BitBoard.maskAToGFiles) << 9); - pawns = pos.pieceTypeBB[Piece.BPAWN]; - bPawnAttacks = ((pawns & BitBoard.maskBToHFiles) >>> 9) | - ((pawns & BitBoard.maskAToGFiles) >>> 7); - - score += pieceSquareEval(pos); - score += pawnBonus(pos); - score += tradeBonus(pos); - score += castleBonus(pos); - - score += rookBonus(pos); - score += bishopEval(pos, score); - score += threatBonus(pos); - score += kingSafety(pos); - score = endGameEval(pos, score); - - if (!pos.whiteMove) - score = -score; - return score; - } - - /** Compute white_material - black_material. */ - static int material(Position pos) { - return pos.wMtrl - pos.bMtrl; - } - - /** Compute score based on piece square tables. Positive values are good for white. */ - private int pieceSquareEval(Position pos) { - int score = 0; - final int wMtrl = pos.wMtrl; - final int bMtrl = pos.bMtrl; - final int wMtrlPawns = pos.wMtrlPawns; - final int bMtrlPawns = pos.bMtrlPawns; - - // Kings - { - final int t1 = qV + 2 * rV + 2 * bV; - final int t2 = rV; - { - final int k1 = pos.psScore1[Piece.WKING]; - final int k2 = pos.psScore2[Piece.WKING]; - final int t = bMtrl - bMtrlPawns; - score += interpolate(t, t2, k2, t1, k1); - } - { - final int k1 = pos.psScore1[Piece.BKING]; - final int k2 = pos.psScore2[Piece.BKING]; - final int t = wMtrl - wMtrlPawns; - score -= interpolate(t, t2, k2, t1, k1); - } - } - - // Pawns - { - final int t1 = qV + 2 * rV + 2 * bV; - final int t2 = rV; - int wp1 = pos.psScore1[Piece.WPAWN]; - int wp2 = pos.psScore2[Piece.WPAWN]; - if ((wp1 != 0) || (wp2 != 0)) { - final int tw = bMtrl - bMtrlPawns; - score += interpolate(tw, t2, wp2, t1, wp1); - } - int bp1 = pos.psScore1[Piece.BPAWN]; - int bp2 = pos.psScore2[Piece.BPAWN]; - if ((bp1 != 0) || (bp2 != 0)) { - final int tb = wMtrl - wMtrlPawns; - score -= interpolate(tb, t2, bp2, t1, bp1); - } - } - - // Knights - { - final int t1 = qV + 2 * rV + 1 * bV + 1 * nV + 6 * pV; - final int t2 = nV + 8 * pV; - int n1 = pos.psScore1[Piece.WKNIGHT]; - int n2 = pos.psScore2[Piece.WKNIGHT]; - if ((n1 != 0) || (n2 != 0)) { - score += interpolate(bMtrl, t2, n2, t1, n1); - } - n1 = pos.psScore1[Piece.BKNIGHT]; - n2 = pos.psScore2[Piece.BKNIGHT]; - if ((n1 != 0) || (n2 != 0)) { - score -= interpolate(wMtrl, t2, n2, t1, n1); - } - } - - // Bishops - { - score += pos.psScore1[Piece.WBISHOP]; - score -= pos.psScore1[Piece.BBISHOP]; - } - - // Queens - { - final long occupied = pos.whiteBB | pos.blackBB; - score += pos.psScore1[Piece.WQUEEN]; - long m = pos.pieceTypeBB[Piece.WQUEEN]; - while (m != 0) { - int sq = BitBoard.numberOfTrailingZeros(m); - long atk = BitBoard.rookAttacks(sq, occupied) | BitBoard.bishopAttacks(sq, occupied); - wAttacksBB |= atk; - score += queenMobScore[Long.bitCount(atk & ~(pos.whiteBB | bPawnAttacks))]; - bKingAttacks += Long.bitCount(atk & bKingZone) * 2; - m &= m-1; - } - score -= pos.psScore1[Piece.BQUEEN]; - m = pos.pieceTypeBB[Piece.BQUEEN]; - while (m != 0) { - int sq = BitBoard.numberOfTrailingZeros(m); - long atk = BitBoard.rookAttacks(sq, occupied) | BitBoard.bishopAttacks(sq, occupied); - bAttacksBB |= atk; - score -= queenMobScore[Long.bitCount(atk & ~(pos.blackBB | wPawnAttacks))]; - wKingAttacks += Long.bitCount(atk & wKingZone) * 2; - m &= m-1; - } - } - - // Rooks - { - int r1 = pos.psScore1[Piece.WROOK]; - if (r1 != 0) { - final int nP = bMtrlPawns / pV; - final int s = r1 * Math.min(nP, 6) / 6; - score += s; - } - r1 = pos.psScore1[Piece.BROOK]; - if (r1 != 0) { - final int nP = wMtrlPawns / pV; - final int s = r1 * Math.min(nP, 6) / 6; - score -= s; - } - } - - return score; - } - - /** Implement the "when ahead trade pieces, when behind trade pawns" rule. */ - private int tradeBonus(Position pos) { - final int wM = pos.wMtrl; - final int bM = pos.bMtrl; - final int wPawn = pos.wMtrlPawns; - final int bPawn = pos.bMtrlPawns; - final int deltaScore = wM - bM; - - int pBonus = 0; - pBonus += interpolate((deltaScore > 0) ? wPawn : bPawn, 0, -30 * deltaScore / 100, 6 * pV, 0); - pBonus += interpolate((deltaScore > 0) ? bM : wM, 0, 30 * deltaScore / 100, qV + 2 * rV + 2 * bV + 2 * nV, 0); - - return pBonus; - } - - private static final int[] castleFactor; - static { - castleFactor = new int[256]; - for (int i = 0; i < 256; i++) { - int h1Dist = 100; - boolean h1Castle = (i & (1<<7)) != 0; - if (h1Castle) - h1Dist = 2 + Long.bitCount(i & 0x0000000000000060L); // f1,g1 - int a1Dist = 100; - boolean a1Castle = (i & 1) != 0; - if (a1Castle) - a1Dist = 2 + Long.bitCount(i & 0x000000000000000EL); // b1,c1,d1 - castleFactor[i] = 1024 / Math.min(a1Dist, h1Dist); - } - } - - /** Score castling ability. */ - private int castleBonus(Position pos) { - if (pos.getCastleMask() == 0) return 0; - - final int k1 = kt1b[7*8+6] - kt1b[7*8+4]; - final int k2 = kt2b[7*8+6] - kt2b[7*8+4]; - final int t1 = qV + 2 * rV + 2 * bV; - final int t2 = rV; - final int t = pos.bMtrl - pos.bMtrlPawns; - final int ks = interpolate(t, t2, k2, t1, k1); - - final int castleValue = ks + rt1b[7*8+5] - rt1b[7*8+7]; - if (castleValue <= 0) - return 0; - - long occupied = pos.whiteBB | pos.blackBB; - int tmp = (int) (occupied & 0x6E); - if (pos.a1Castle()) tmp |= 1; - if (pos.h1Castle()) tmp |= (1 << 7); - final int wBonus = (castleValue * castleFactor[tmp]) >> 10; - - tmp = (int) ((occupied >>> 56) & 0x6E); - if (pos.a8Castle()) tmp |= 1; - if (pos.h8Castle()) tmp |= (1 << 7); - final int bBonus = (castleValue * castleFactor[tmp]) >> 10; - - return wBonus - bBonus; - } - - private int pawnBonus(Position pos) { - long key = pos.pawnZobristHash(); - PawnHashData phd = pawnHash[(int)key & (pawnHash.length - 1)]; - if (phd.key != key) - computePawnHashData(pos, phd); - int score = phd.score; - - final int hiMtrl = qV + rV; - score += interpolate(pos.bMtrl - pos.bMtrlPawns, 0, 2 * phd.passedBonusW, hiMtrl, phd.passedBonusW); - score -= interpolate(pos.wMtrl - pos.wMtrlPawns, 0, 2 * phd.passedBonusB, hiMtrl, phd.passedBonusB); - - // Passed pawns are more dangerous if enemy king is far away - int bestWPawnDist = 8; - int bestWPromSq = -1; - long m = phd.passedPawnsW; - if (m != 0) { - int mtrlNoPawns = pos.bMtrl - pos.bMtrlPawns; - if (mtrlNoPawns < hiMtrl) { - int kingPos = pos.getKingSq(false); - while (m != 0) { - int sq = BitBoard.numberOfTrailingZeros(m); - int x = Position.getX(sq); - int y = Position.getY(sq); - int pawnDist = Math.min(5, 7 - y); - int kingDist = BitBoard.getDistance(kingPos, Position.getSquare(x, 7)); - int kScore = kingDist * 4; - if (kingDist > pawnDist) kScore += (kingDist - pawnDist) * (kingDist - pawnDist); - score += interpolate(mtrlNoPawns, 0, kScore, hiMtrl, 0); - if (!pos.whiteMove) - kingDist--; - if ((pawnDist < kingDist) && (mtrlNoPawns == 0)) { - if ((BitBoard.northFill(1L< pawnDist) kScore += (kingDist - pawnDist) * (kingDist - pawnDist); - score -= interpolate(mtrlNoPawns, 0, kScore, hiMtrl, 0); - if (pos.whiteMove) - kingDist--; - if ((pawnDist < kingDist) && (mtrlNoPawns == 0)) { - if ((BitBoard.southFill(1L<= 0) { - if (bestBPromSq >= 0) { - int wPly = bestWPawnDist * 2; if (pos.whiteMove) wPly--; - int bPly = bestBPawnDist * 2; if (!pos.whiteMove) bPly--; - if (wPly < bPly - 1) { - score += 500; - } else if (wPly == bPly - 1) { - if (BitBoard.getDirection(bestWPromSq, pos.getKingSq(false)) != 0) - score += 500; - } else if (wPly == bPly + 1) { - if (BitBoard.getDirection(bestBPromSq, pos.getKingSq(true)) != 0) - score -= 500; - } else { - score -= 500; - } - } else - score += 500; - } else if (bestBPromSq >= 0) - score -= 500; - - return score; - } - - /** Compute pawn hash data for pos. */ - private void computePawnHashData(Position pos, PawnHashData ph) { - int score = 0; - - // Evaluate double pawns and pawn islands - long wPawns = pos.pieceTypeBB[Piece.WPAWN]; - long wPawnFiles = BitBoard.southFill(wPawns) & 0xff; - int wDouble = Long.bitCount(wPawns) - Long.bitCount(wPawnFiles); - int wIslands = Long.bitCount(((~wPawnFiles) >>> 1) & wPawnFiles); - int wIsolated = Long.bitCount(~(wPawnFiles<<1) & wPawnFiles & ~(wPawnFiles>>>1)); - - - long bPawns = pos.pieceTypeBB[Piece.BPAWN]; - long bPawnFiles = BitBoard.southFill(bPawns) & 0xff; - int bDouble = Long.bitCount(bPawns) - Long.bitCount(bPawnFiles); - int bIslands = Long.bitCount(((~bPawnFiles) >>> 1) & bPawnFiles); - int bIsolated = Long.bitCount(~(bPawnFiles<<1) & bPawnFiles & ~(bPawnFiles>>>1)); - - score -= (wDouble - bDouble) * 25; - score -= (wIslands - bIslands) * 15; - score -= (wIsolated - bIsolated) * 15; - - // Evaluate backward pawns, defined as a pawn that guards a friendly pawn, - // can't be guarded by friendly pawns, can advance, but can't advance without - // being captured by an enemy pawn. - long wPawnAttacks = (((wPawns & BitBoard.maskBToHFiles) << 7) | - ((wPawns & BitBoard.maskAToGFiles) << 9)); - long bPawnAttacks = (((bPawns & BitBoard.maskBToHFiles) >>> 9) | - ((bPawns & BitBoard.maskAToGFiles) >>> 7)); - long wBackward = wPawns & ~((wPawns | bPawns) >>> 8) & (bPawnAttacks >>> 8) & - ~BitBoard.northFill(wPawnAttacks); - wBackward &= (((wPawns & BitBoard.maskBToHFiles) >>> 9) | - ((wPawns & BitBoard.maskAToGFiles) >>> 7)); - wBackward &= ~BitBoard.northFill(bPawnFiles); - long bBackward = bPawns & ~((wPawns | bPawns) << 8) & (wPawnAttacks << 8) & - ~BitBoard.southFill(bPawnAttacks); - bBackward &= (((bPawns & BitBoard.maskBToHFiles) << 7) | - ((bPawns & BitBoard.maskAToGFiles) << 9)); - bBackward &= ~BitBoard.northFill(wPawnFiles); - score -= (Long.bitCount(wBackward) - Long.bitCount(bBackward)) * 15; - - // Evaluate passed pawn bonus, white - long passedPawnsW = wPawns & ~BitBoard.southFill(bPawns | bPawnAttacks | (wPawns >>> 8)); - final int[] ppBonus = {-1,24,26,30,36,55,100,-1}; - int passedBonusW = 0; - if (passedPawnsW != 0) { - long guardedPassedW = passedPawnsW & (((wPawns & BitBoard.maskBToHFiles) << 7) | - ((wPawns & BitBoard.maskAToGFiles) << 9)); - passedBonusW += 15 * Long.bitCount(guardedPassedW); - long m = passedPawnsW; - while (m != 0) { - int sq = Long .numberOfTrailingZeros(m); - int y = Position.getY(sq); - passedBonusW += ppBonus[y]; - m &= m-1; - } - } - - // Evaluate passed pawn bonus, black - long passedPawnsB = bPawns & ~BitBoard.northFill(wPawns | wPawnAttacks | (bPawns << 8)); - int passedBonusB = 0; - if (passedPawnsB != 0) { - long guardedPassedB = passedPawnsB & (((bPawns & BitBoard.maskBToHFiles) >>> 9) | - ((bPawns & BitBoard.maskAToGFiles) >>> 7)); - passedBonusB += 15 * Long.bitCount(guardedPassedB); - long m = passedPawnsB; - while (m != 0) { - int sq = Long .numberOfTrailingZeros(m); - int y = Position.getY(sq); - passedBonusB += ppBonus[7-y]; - m &= m-1; - } - } - - // Connected passed pawn bonus. Seems logical but scored -8 elo in tests -// if (passedPawnsW != 0) { -// long mask = passedPawnsW; -// mask = (((mask >> 7) | (mask << 1) | (mask << 9)) & BitBoard.maskBToHFiles) | -// (((mask >> 9) | (mask >> 1) | (mask << 7)) & BitBoard.maskAToGFiles); -// passedBonusW += 13 * Long.bitCount(passedPawnsW & mask); -// } -// if (passedPawnsB != 0) { -// long mask = passedPawnsB; -// mask = (((mask >> 7) | (mask << 1) | (mask << 9)) & BitBoard.maskBToHFiles) | -// (((mask >> 9) | (mask >> 1) | (mask << 7)) & BitBoard.maskAToGFiles); -// passedBonusB += 13 * Long.bitCount(passedPawnsB & mask); -// } - - ph.key = pos.pawnZobristHash(); - ph.score = score; - ph.passedBonusW = (short)passedBonusW; - ph.passedBonusB = (short)passedBonusB; - ph.passedPawnsW = passedPawnsW; - ph.passedPawnsB = passedPawnsB; - } - - /** Compute rook bonus. Rook on open/half-open file. */ - private int rookBonus(Position pos) { - int score = 0; - final long wPawns = pos.pieceTypeBB[Piece.WPAWN]; - final long bPawns = pos.pieceTypeBB[Piece.BPAWN]; - final long occupied = pos.whiteBB | pos.blackBB; - long m = pos.pieceTypeBB[Piece.WROOK]; - while (m != 0) { - int sq = BitBoard.numberOfTrailingZeros(m); - final int x = Position.getX(sq); - if ((wPawns & BitBoard.maskFile[x]) == 0) { // At least half-open file - score += (bPawns & BitBoard.maskFile[x]) == 0 ? 25 : 12; - } - long atk = BitBoard.rookAttacks(sq, occupied); - wAttacksBB |= atk; - score += rookMobScore[Long.bitCount(atk & ~(pos.whiteBB | bPawnAttacks))]; - if ((atk & bKingZone) != 0) - bKingAttacks += Long.bitCount(atk & bKingZone); - m &= m-1; - } - long r7 = (pos.pieceTypeBB[Piece.WROOK] >>> 48) & 0x00ffL; - if (((r7 & (r7 - 1)) != 0) && - ((pos.pieceTypeBB[Piece.BKING] & 0xff00000000000000L) != 0)) - score += 30; // Two rooks on 7:th row - m = pos.pieceTypeBB[Piece.BROOK]; - while (m != 0) { - int sq = BitBoard.numberOfTrailingZeros(m); - final int x = Position.getX(sq); - if ((bPawns & BitBoard.maskFile[x]) == 0) { - score -= (wPawns & BitBoard.maskFile[x]) == 0 ? 25 : 12; - } - long atk = BitBoard.rookAttacks(sq, occupied); - bAttacksBB |= atk; - score -= rookMobScore[Long.bitCount(atk & ~(pos.blackBB | wPawnAttacks))]; - if ((atk & wKingZone) != 0) - wKingAttacks += Long.bitCount(atk & wKingZone); - m &= m-1; - } - r7 = pos.pieceTypeBB[Piece.BROOK] & 0xff00L; - if (((r7 & (r7 - 1)) != 0) && - ((pos.pieceTypeBB[Piece.WKING] & 0xffL) != 0)) - score -= 30; // Two rooks on 2:nd row - return score; - } - - /** Compute bishop evaluation. */ - private int bishopEval(Position pos, int oldScore) { - int score = 0; - final long occupied = pos.whiteBB | pos.blackBB; - long wBishops = pos.pieceTypeBB[Piece.WBISHOP]; - long bBishops = pos.pieceTypeBB[Piece.BBISHOP]; - if ((wBishops | bBishops) == 0) - return 0; - long m = wBishops; - while (m != 0) { - int sq = BitBoard.numberOfTrailingZeros(m); - long atk = BitBoard.bishopAttacks(sq, occupied); - wAttacksBB |= atk; - score += bishMobScore[Long.bitCount(atk & ~(pos.whiteBB | bPawnAttacks))]; - if ((atk & bKingZone) != 0) - bKingAttacks += Long.bitCount(atk & bKingZone); - m &= m-1; - } - m = bBishops; - while (m != 0) { - int sq = BitBoard.numberOfTrailingZeros(m); - long atk = BitBoard.bishopAttacks(sq, occupied); - bAttacksBB |= atk; - score -= bishMobScore[Long.bitCount(atk & ~(pos.blackBB | wPawnAttacks))]; - if ((atk & wKingZone) != 0) - wKingAttacks += Long.bitCount(atk & wKingZone); - m &= m-1; - } - - boolean whiteDark = (pos.pieceTypeBB[Piece.WBISHOP] & BitBoard.maskDarkSq ) != 0; - boolean whiteLight = (pos.pieceTypeBB[Piece.WBISHOP] & BitBoard.maskLightSq) != 0; - boolean blackDark = (pos.pieceTypeBB[Piece.BBISHOP] & BitBoard.maskDarkSq ) != 0; - boolean blackLight = (pos.pieceTypeBB[Piece.BBISHOP] & BitBoard.maskLightSq) != 0; - int numWhite = (whiteDark ? 1 : 0) + (whiteLight ? 1 : 0); - int numBlack = (blackDark ? 1 : 0) + (blackLight ? 1 : 0); - - // Bishop pair bonus - if (numWhite == 2) { - final int numPawns = pos.wMtrlPawns / pV; - score += 28 + (8 - numPawns) * 3; - } - if (numBlack == 2) { - final int numPawns = pos.bMtrlPawns / pV; - score -= 28 + (8 - numPawns) * 3; - } - - if ((numWhite == 1) && (numBlack == 1) && (whiteDark != blackDark) && - (pos.wMtrl - pos.wMtrlPawns == pos.bMtrl - pos.bMtrlPawns)) { - final int penalty = (oldScore + score) / 2; - final int loMtrl = 2 * bV; - final int hiMtrl = 2 * (qV + rV + bV); - int mtrl = pos.wMtrl + pos.bMtrl - pos.wMtrlPawns - pos.bMtrlPawns; - score -= interpolate(mtrl, loMtrl, penalty, hiMtrl, 0); - } - - // Penalty for bishop trapped behind pawn at a2/h2/a7/h7 - if (((wBishops | bBishops) & 0x0081000000008100L) != 0) { - if ((pos.squares[48] == Piece.WBISHOP) && // a7 - (pos.squares[41] == Piece.BPAWN) && - (pos.squares[50] == Piece.BPAWN)) - score -= pV * 3 / 2; - if ((pos.squares[55] == Piece.WBISHOP) && // h7 - (pos.squares[46] == Piece.BPAWN) && - (pos.squares[53] == Piece.BPAWN)) - score -= (pos.pieceTypeBB[Piece.WQUEEN] != 0) ? pV : pV * 3 / 2; - if ((pos.squares[8] == Piece.BBISHOP) && // a2 - (pos.squares[17] == Piece.WPAWN) && - (pos.squares[10] == Piece.WPAWN)) - score += pV * 3 / 2; - if ((pos.squares[15] == Piece.BBISHOP) && // h2 - (pos.squares[22] == Piece.WPAWN) && - (pos.squares[13] == Piece.WPAWN)) - score += (pos.pieceTypeBB[Piece.BQUEEN] != 0) ? pV : pV * 3 / 2; - } - - return score; - } - - private int threatBonus(Position pos) { - int score = 0; - - // Sum values for all black pieces under attack - long m = pos.pieceTypeBB[Piece.WKNIGHT]; - while (m != 0) { - int sq = BitBoard.numberOfTrailingZeros(m); - wAttacksBB |= BitBoard.knightAttacks[sq]; - m &= m-1; - } - wAttacksBB &= (pos.pieceTypeBB[Piece.BKNIGHT] | - pos.pieceTypeBB[Piece.BBISHOP] | - pos.pieceTypeBB[Piece.BROOK] | - pos.pieceTypeBB[Piece.BQUEEN]); - wAttacksBB |= wPawnAttacks; - m = wAttacksBB & pos.blackBB & ~pos.pieceTypeBB[Piece.BKING]; - int tmp = 0; - while (m != 0) { - int sq = BitBoard.numberOfTrailingZeros(m); - tmp += pieceValue[pos.squares[sq]]; - m &= m-1; - } - score += tmp + tmp * tmp / qV; - - // Sum values for all white pieces under attack - m = pos.pieceTypeBB[Piece.BKNIGHT]; - while (m != 0) { - int sq = BitBoard.numberOfTrailingZeros(m); - bAttacksBB |= BitBoard.knightAttacks[sq]; - m &= m-1; - } - bAttacksBB &= (pos.pieceTypeBB[Piece.WKNIGHT] | - pos.pieceTypeBB[Piece.WBISHOP] | - pos.pieceTypeBB[Piece.WROOK] | - pos.pieceTypeBB[Piece.WQUEEN]); - bAttacksBB |= bPawnAttacks; - m = bAttacksBB & pos.whiteBB & ~pos.pieceTypeBB[Piece.WKING]; - tmp = 0; - while (m != 0) { - int sq = BitBoard.numberOfTrailingZeros(m); - tmp += pieceValue[pos.squares[sq]]; - m &= m-1; - } - score -= tmp + tmp * tmp / qV; - return score / 64; - } - - /** Compute king safety for both kings. */ - private int kingSafety(Position pos) { - final int minM = rV + bV; - final int m = (pos.wMtrl - pos.wMtrlPawns + pos.bMtrl - pos.bMtrlPawns) / 2; - if (m <= minM) - return 0; - final int maxM = qV + 2 * rV + 2 * bV + 2 * nV; - int score = kingSafetyKPPart(pos); - if (Position.getY(pos.wKingSq) == 0) { - if (((pos.pieceTypeBB[Piece.WKING] & 0x60L) != 0) && // King on f1 or g1 - ((pos.pieceTypeBB[Piece.WROOK] & 0xC0L) != 0) && // Rook on g1 or h1 - ((pos.pieceTypeBB[Piece.WPAWN] & BitBoard.maskFile[6]) != 0) && - ((pos.pieceTypeBB[Piece.WPAWN] & BitBoard.maskFile[7]) != 0)) { - score -= 6 * 15; - } else - if (((pos.pieceTypeBB[Piece.WKING] & 0x6L) != 0) && // King on b1 or c1 - ((pos.pieceTypeBB[Piece.WROOK] & 0x3L) != 0) && // Rook on a1 or b1 - ((pos.pieceTypeBB[Piece.WPAWN] & BitBoard.maskFile[0]) != 0) && - ((pos.pieceTypeBB[Piece.WPAWN] & BitBoard.maskFile[1]) != 0)) { - score -= 6 * 15; - } - } - if (Position.getY(pos.bKingSq) == 7) { - if (((pos.pieceTypeBB[Piece.BKING] & 0x6000000000000000L) != 0) && // King on f8 or g8 - ((pos.pieceTypeBB[Piece.BROOK] & 0xC000000000000000L) != 0) && // Rook on g8 or h8 - ((pos.pieceTypeBB[Piece.BPAWN] & BitBoard.maskFile[6]) != 0) && - ((pos.pieceTypeBB[Piece.BPAWN] & BitBoard.maskFile[7]) != 0)) { - score += 6 * 15; - } else - if (((pos.pieceTypeBB[Piece.BKING] & 0x600000000000000L) != 0) && // King on b8 or c8 - ((pos.pieceTypeBB[Piece.BROOK] & 0x300000000000000L) != 0) && // Rook on a8 or b8 - ((pos.pieceTypeBB[Piece.BPAWN] & BitBoard.maskFile[0]) != 0) && - ((pos.pieceTypeBB[Piece.BPAWN] & BitBoard.maskFile[1]) != 0)) { - score += 6 * 15; - } - } - score += (bKingAttacks - wKingAttacks) * 4; - final int kSafety = interpolate(m, minM, 0, maxM, score); - return kSafety; - } - - private static final class KingSafetyHashData { - long key; - int score; - } - private static final KingSafetyHashData[] kingSafetyHash; - static { - final int numEntries = 1 << 15; - kingSafetyHash = new KingSafetyHashData[numEntries]; - for (int i = 0; i < numEntries; i++) { - KingSafetyHashData ksh = new KingSafetyHashData(); - ksh.key = -1; - ksh.score = 0; - kingSafetyHash[i] = ksh; - } - } - - private int kingSafetyKPPart(Position pos) { - final long key = pos.pawnZobristHash() ^ pos.kingZobristHash(); - KingSafetyHashData ksh = kingSafetyHash[(int)key & (kingSafetyHash.length - 1)]; - if (ksh.key != key) { - int score = 0; - long wPawns = pos.pieceTypeBB[Piece.WPAWN]; - long bPawns = pos.pieceTypeBB[Piece.BPAWN]; - { - int safety = 0; - int halfOpenFiles = 0; - if (Position.getY(pos.wKingSq) < 2) { - long shelter = 1L << Position.getX(pos.wKingSq); - shelter |= ((shelter & BitBoard.maskBToHFiles) >>> 1) | - ((shelter & BitBoard.maskAToGFiles) << 1); - shelter <<= 8; - safety += 3 * Long.bitCount(wPawns & shelter); - safety -= 2 * Long.bitCount(bPawns & (shelter | (shelter << 8))); - shelter <<= 8; - safety += 2 * Long.bitCount(wPawns & shelter); - shelter <<= 8; - safety -= Long.bitCount(bPawns & shelter); - - long wOpen = BitBoard.southFill(shelter) & (~BitBoard.southFill(wPawns)) & 0xff; - if (wOpen != 0) { - halfOpenFiles += 25 * Long.bitCount(wOpen & 0xe7); - halfOpenFiles += 10 * Long.bitCount(wOpen & 0x18); - } - long bOpen = BitBoard.southFill(shelter) & (~BitBoard.southFill(bPawns)) & 0xff; - if (bOpen != 0) { - halfOpenFiles += 25 * Long.bitCount(bOpen & 0xe7); - halfOpenFiles += 10 * Long.bitCount(bOpen & 0x18); - } - safety = Math.min(safety, 8); - } - final int kSafety = (safety - 9) * 15 - halfOpenFiles; - score += kSafety; - } - { - int safety = 0; - int halfOpenFiles = 0; - if (Position.getY(pos.bKingSq) >= 6) { - long shelter = 1L << (56 + Position.getX(pos.bKingSq)); - shelter |= ((shelter & BitBoard.maskBToHFiles) >>> 1) | - ((shelter & BitBoard.maskAToGFiles) << 1); - shelter >>>= 8; - safety += 3 * Long.bitCount(bPawns & shelter); - safety -= 2 * Long.bitCount(wPawns & (shelter | (shelter >>> 8))); - shelter >>>= 8; - safety += 2 * Long.bitCount(bPawns & shelter); - shelter >>>= 8; - safety -= Long.bitCount(wPawns & shelter); - - long wOpen = BitBoard.southFill(shelter) & (~BitBoard.southFill(wPawns)) & 0xff; - if (wOpen != 0) { - halfOpenFiles += 25 * Long.bitCount(wOpen & 0xe7); - halfOpenFiles += 10 * Long.bitCount(wOpen & 0x18); - } - long bOpen = BitBoard.southFill(shelter) & (~BitBoard.southFill(bPawns)) & 0xff; - if (bOpen != 0) { - halfOpenFiles += 25 * Long.bitCount(bOpen & 0xe7); - halfOpenFiles += 10 * Long.bitCount(bOpen & 0x18); - } - safety = Math.min(safety, 8); - } - final int kSafety = (safety - 9) * 15 - halfOpenFiles; - score -= kSafety; - } - ksh.key = key; - ksh.score = score; - } - return ksh.score; - } - - /** Implements special knowledge for some endgame situations. */ - private int endGameEval(Position pos, int oldScore) { - int score = oldScore; - if (pos.wMtrl + pos.bMtrl > 6 * rV) - return score; - final int wMtrlPawns = pos.wMtrlPawns; - final int bMtrlPawns = pos.bMtrlPawns; - final int wMtrlNoPawns = pos.wMtrl - wMtrlPawns; - final int bMtrlNoPawns = pos.bMtrl - bMtrlPawns; - - boolean handled = false; - if ((wMtrlPawns + bMtrlPawns == 0) && (wMtrlNoPawns < rV) && (bMtrlNoPawns < rV)) { - // King + minor piece vs king + minor piece is a draw - return 0; - } - if (!handled && (pos.wMtrl == qV) && (pos.bMtrl == pV) && (pos.pieceTypeBB[Piece.WQUEEN] != 0)) { - int wk = BitBoard.numberOfTrailingZeros(pos.pieceTypeBB[Piece.WKING]); - int wq = BitBoard.numberOfTrailingZeros(pos.pieceTypeBB[Piece.WQUEEN]); - int bk = BitBoard.numberOfTrailingZeros(pos.pieceTypeBB[Piece.BKING]); - int bp = BitBoard.numberOfTrailingZeros(pos.pieceTypeBB[Piece.BPAWN]); - score = evalKQKP(wk, wq, bk, bp, pos.whiteMove); - handled = true; - } - if (!handled && (pos.wMtrl == rV) && (pos.pieceTypeBB[Piece.WROOK] != 0)) { - if (pos.bMtrl == pV) { - int bp = BitBoard.numberOfTrailingZeros(pos.pieceTypeBB[Piece.BPAWN]); - score = krkpEval(pos.getKingSq(true), pos.getKingSq(false), - bp, pos.whiteMove); - handled = true; - } else if ((pos.bMtrl == bV) && (pos.pieceTypeBB[Piece.BBISHOP] != 0)) { - score /= 8; - final int kSq = pos.getKingSq(false); - final int x = Position.getX(kSq); - final int y = Position.getY(kSq); - if ((pos.pieceTypeBB[Piece.BBISHOP] & BitBoard.maskDarkSq) != 0) { - score += (7 - distToH1A8[7-y][7-x]) * 7; - } else { - score += (7 - distToH1A8[7-y][x]) * 7; - } - handled = true; - } - } - if (!handled && (pos.bMtrl == qV) && (pos.wMtrl == pV) && (pos.pieceTypeBB[Piece.BQUEEN] != 0)) { - int bk = BitBoard.numberOfTrailingZeros(pos.pieceTypeBB[Piece.BKING]); - int bq = BitBoard.numberOfTrailingZeros(pos.pieceTypeBB[Piece.BQUEEN]); - int wk = BitBoard.numberOfTrailingZeros(pos.pieceTypeBB[Piece.WKING]); - int wp = BitBoard.numberOfTrailingZeros(pos.pieceTypeBB[Piece.WPAWN]); - score = -evalKQKP(63-bk, 63-bq, 63-wk, 63-wp, !pos.whiteMove); - handled = true; - } - if (!handled && (pos.bMtrl == rV) && (pos.pieceTypeBB[Piece.BROOK] != 0)) { - if (pos.wMtrl == pV) { - int wp = BitBoard.numberOfTrailingZeros(pos.pieceTypeBB[Piece.WPAWN]); - score = -krkpEval(63-pos.getKingSq(false), 63-pos.getKingSq(true), - 63-wp, !pos.whiteMove); - handled = true; - } else if ((pos.wMtrl == bV) && (pos.pieceTypeBB[Piece.WBISHOP] != 0)) { - score /= 8; - final int kSq = pos.getKingSq(true); - final int x = Position.getX(kSq); - final int y = Position.getY(kSq); - if ((pos.pieceTypeBB[Piece.WBISHOP] & BitBoard.maskDarkSq) != 0) { - score -= (7 - distToH1A8[7-y][7-x]) * 7; - } else { - score -= (7 - distToH1A8[7-y][x]) * 7; - } - handled = true; - } - } - if (!handled && (score > 0)) { - if ((wMtrlPawns == 0) && (wMtrlNoPawns <= bMtrlNoPawns + bV)) { - if (wMtrlNoPawns < rV) { - return -pos.bMtrl / 50; - } else { - score /= 8; // Too little excess material, probably draw - handled = true; - } - } else if ((pos.pieceTypeBB[Piece.WROOK] | pos.pieceTypeBB[Piece.WKNIGHT] | - pos.pieceTypeBB[Piece.WQUEEN]) == 0) { - // Check for rook pawn + wrong color bishop - if (((pos.pieceTypeBB[Piece.WPAWN] & BitBoard.maskBToHFiles) == 0) && - ((pos.pieceTypeBB[Piece.WBISHOP] & BitBoard.maskLightSq) == 0) && - ((pos.pieceTypeBB[Piece.BKING] & 0x0303000000000000L) != 0)) { - return 0; - } else - if (((pos.pieceTypeBB[Piece.WPAWN] & BitBoard.maskAToGFiles) == 0) && - ((pos.pieceTypeBB[Piece.WBISHOP] & BitBoard.maskDarkSq) == 0) && - ((pos.pieceTypeBB[Piece.BKING] & 0xC0C0000000000000L) != 0)) { - return 0; - } - } - } - if (!handled) { - if (bMtrlPawns == 0) { - if (wMtrlNoPawns - bMtrlNoPawns > bV) { - int wKnights = Long.bitCount(pos.pieceTypeBB[Piece.WKNIGHT]); - int wBishops = Long.bitCount(pos.pieceTypeBB[Piece.WBISHOP]); - if ((wKnights == 2) && (pos.wMtrl == 2 * nV) && (bMtrlNoPawns == 0)) { - score /= 50; // KNNK is a draw - } else if ((wKnights == 1) && (wBishops == 1) && (wMtrlNoPawns == nV + bV) && (bMtrlNoPawns == 0)) { - score /= 10; - score += nV + bV + 300; - final int kSq = pos.getKingSq(false); - final int x = Position.getX(kSq); - final int y = Position.getY(kSq); - if ((pos.pieceTypeBB[Piece.WBISHOP] & BitBoard.maskDarkSq) != 0) { - score += (7 - distToH1A8[7-y][7-x]) * 10; - } else { - score += (7 - distToH1A8[7-y][x]) * 10; - } - } else { - score += 300; // Enough excess material, should win - } - handled = true; - } else if ((wMtrlNoPawns + bMtrlNoPawns == 0) && (wMtrlPawns == pV)) { // KPK - int wp = BitBoard.numberOfTrailingZeros(pos.pieceTypeBB[Piece.WPAWN]); - score = kpkEval(pos.getKingSq(true), pos.getKingSq(false), - wp, pos.whiteMove); - handled = true; - } - } - } - if (!handled && (score < 0)) { - if ((bMtrlPawns == 0) && (bMtrlNoPawns <= wMtrlNoPawns + bV)) { - if (bMtrlNoPawns < rV) { - return pos.wMtrl / 50; - } else { - score /= 8; // Too little excess material, probably draw - handled = true; - } - } else if ((pos.pieceTypeBB[Piece.BROOK] | pos.pieceTypeBB[Piece.BKNIGHT] | - pos.pieceTypeBB[Piece.BQUEEN]) == 0) { - // Check for rook pawn + wrong color bishop - if (((pos.pieceTypeBB[Piece.BPAWN] & BitBoard.maskBToHFiles) == 0) && - ((pos.pieceTypeBB[Piece.BBISHOP] & BitBoard.maskDarkSq) == 0) && - ((pos.pieceTypeBB[Piece.WKING] & 0x0303L) != 0)) { - return 0; - } else - if (((pos.pieceTypeBB[Piece.BPAWN] & BitBoard.maskAToGFiles) == 0) && - ((pos.pieceTypeBB[Piece.BBISHOP] & BitBoard.maskLightSq) == 0) && - ((pos.pieceTypeBB[Piece.WKING] & 0xC0C0L) != 0)) { - return 0; - } - } - } - if (!handled) { - if (wMtrlPawns == 0) { - if (bMtrlNoPawns - wMtrlNoPawns > bV) { - int bKnights = Long.bitCount(pos.pieceTypeBB[Piece.BKNIGHT]); - int bBishops = Long.bitCount(pos.pieceTypeBB[Piece.BBISHOP]); - if ((bKnights == 2) && (pos.bMtrl == 2 * nV) && (wMtrlNoPawns == 0)) { - score /= 50; // KNNK is a draw - } else if ((bKnights == 1) && (bBishops == 1) && (bMtrlNoPawns == nV + bV) && (wMtrlNoPawns == 0)) { - score /= 10; - score -= nV + bV + 300; - final int kSq = pos.getKingSq(true); - final int x = Position.getX(kSq); - final int y = Position.getY(kSq); - if ((pos.pieceTypeBB[Piece.BBISHOP] & BitBoard.maskDarkSq) != 0) { - score -= (7 - distToH1A8[7-y][7-x]) * 10; - } else { - score -= (7 - distToH1A8[7-y][x]) * 10; - } - } else { - score -= 300; // Enough excess material, should win - } - handled = true; - } else if ((wMtrlNoPawns + bMtrlNoPawns == 0) && (bMtrlPawns == pV)) { // KPK - int bp = BitBoard.numberOfTrailingZeros(pos.pieceTypeBB[Piece.BPAWN]); - score = -kpkEval(63-pos.getKingSq(false), 63-pos.getKingSq(true), - 63-bp, !pos.whiteMove); - handled = true; - } - } - } - return score; - } - - private static int evalKQKP(int wKing, int wQueen, int bKing, int bPawn, boolean whiteMove) { - boolean canWin = false; - if (((1L << bKing) & 0xFFFF) == 0) { - canWin = true; // King doesn't support pawn - } else if (Math.abs(Position.getX(bPawn) - Position.getX(bKing)) > 2) { - canWin = true; // King doesn't support pawn - } else { - switch (bPawn) { - case 8: // a2 - canWin = ((1L << wKing) & 0x0F1F1F1F1FL) != 0; - if (canWin && (bKing == 0) && (Position.getX(wQueen) == 1) && !whiteMove) - canWin = false; // Stale-mate - break; - case 10: // c2 - canWin = ((1L << wKing) & 0x071F1F1FL) != 0; - break; - case 13: // f2 - canWin = ((1L << wKing) & 0xE0F8F8F8L) != 0; - break; - case 15: // h2 - canWin = ((1L << wKing) & 0xF0F8F8F8F8L) != 0; - if (canWin && (bKing == 7) && (Position.getX(wQueen) == 6) && !whiteMove) - canWin = false; // Stale-mate - break; - default: - canWin = true; - break; - } - } - - final int dist = BitBoard.getDistance(wKing, bPawn); - int score = qV - pV - 20 * dist; - if (!canWin) - score /= 50; - return score; - } - - private static int kpkEval(int wKing, int bKing, int wPawn, boolean whiteMove) { - if (Position.getX(wKing) >= 4) { // Mirror X - wKing ^= 7; - bKing ^= 7; - wPawn ^= 7; - } - int index = whiteMove ? 0 : 1; - index = index * 32 + Position.getY(wKing)*4+Position.getX(wKing); - index = index * 64 + bKing; - index = index * 48 + wPawn - 8; - - int bytePos = index / 8; - int bitPos = index % 8; - boolean draw = (((int)kpkTable[bytePos]) & (1 << bitPos)) == 0; - if (draw) - return 0; - return qV - pV / 4 * (7-Position.getY(wPawn)); - } - - private static int krkpEval(int wKing, int bKing, int bPawn, boolean whiteMove) { - if (Position.getX(bKing) >= 4) { // Mirror X - wKing ^= 7; - bKing ^= 7; - bPawn ^= 7; - } - int index = whiteMove ? 0 : 1; - index = index * 32 + Position.getY(bKing)*4+Position.getX(bKing); - index = index * 48 + bPawn - 8; - index = index * 8 + Position.getY(wKing); - byte mask = krkpTable[index]; - boolean canWin = (mask & (1 << Position.getX(wKing))) != 0; - - int score = rV - pV + Position.getY(bPawn) * pV / 4; - if (canWin) - score += 150; - else - score /= 50; - return score; - } - - /** - * Interpolate between (x1,y1) and (x2,y2). - * If x < x1, return y1, if x > x2 return y2. Otherwise, use linear interpolation. - */ - static int interpolate(int x, int x1, int y1, int x2, int y2) { - if (x > x2) { - return y2; - } else if (x < x1) { - return y1; - } else { - return (x - x1) * (y2 - y1) / (x2 - x1) + y1; - } - } -} diff --git a/buildSrc/src/main/java/org/petero/droidfish/FileUtil.java b/buildSrc/src/main/java/chess/FileUtil.java similarity index 50% rename from buildSrc/src/main/java/org/petero/droidfish/FileUtil.java rename to buildSrc/src/main/java/chess/FileUtil.java index bd80ba5..c3da5a8 100644 --- a/buildSrc/src/main/java/org/petero/droidfish/FileUtil.java +++ b/buildSrc/src/main/java/chess/FileUtil.java @@ -16,7 +16,7 @@ along with this program. If not, see . */ -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; - } - } } diff --git a/buildSrc/src/main/java/org/petero/droidfish/gamelogic/Game.java b/buildSrc/src/main/java/chess/Game.java similarity index 98% rename from buildSrc/src/main/java/org/petero/droidfish/gamelogic/Game.java rename to buildSrc/src/main/java/chess/Game.java index 1f870a3..73a0685 100644 --- a/buildSrc/src/main/java/org/petero/droidfish/gamelogic/Game.java +++ b/buildSrc/src/main/java/chess/Game.java @@ -16,7 +16,7 @@ along with this program. If not, see . */ -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; diff --git a/buildSrc/src/main/java/org/petero/droidfish/gamelogic/GameTree.java b/buildSrc/src/main/java/chess/GameTree.java similarity index 93% rename from buildSrc/src/main/java/org/petero/droidfish/gamelogic/GameTree.java rename to buildSrc/src/main/java/chess/GameTree.java index a3b0c5e..80f2eb3 100644 --- a/buildSrc/src/main/java/org/petero/droidfish/gamelogic/GameTree.java +++ b/buildSrc/src/main/java/chess/GameTree.java @@ -16,7 +16,7 @@ along with this program. If not, see . */ -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 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 currPath = new ArrayList<>(); - currPath.add(0); - while (!currPath.isEmpty()) { - int last = currPath.size() - 1; - int currChild = currPath.get(last); - if (currChild == 0) { - ArrayList 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; - if (options.exp.pgnPromotions && (move != null) && (move.promoteTo != Piece.EMPTY)) - str = TextIO.pgnPromotion(str); - } else { - str = moveStrLocal; - } + String str = moveStr; + if (options.exp.pgnPromotions && (move != null) && (move.promoteTo != Piece.EMPTY)) + str = TextIO.pgnPromotion(str); 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; diff --git a/buildSrc/src/main/java/chess/Move.java b/buildSrc/src/main/java/chess/Move.java index 542b50b..1def1eb 100644 --- a/buildSrc/src/main/java/chess/Move.java +++ b/buildSrc/src/main/java/chess/Move.java @@ -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; } } diff --git a/buildSrc/src/main/java/chess/MoveGen.java b/buildSrc/src/main/java/chess/MoveGen.java index 7ab830b..d0fd171 100644 --- a/buildSrc/src/main/java/chess/MoveGen.java +++ b/buildSrc/src/main/java/chess/MoveGen.java @@ -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,698 +18,137 @@ package chess; -import java.util.List; +import java.util.ArrayList; -public final class MoveGen { - static final MoveGen instance; + +public class MoveGen { + public static MoveGen instance; static { instance = new MoveGen(); } - public final static class MoveList { - public final Move[] m; - public int size; - MoveList() { - m = new Move[MAX_MOVES]; - this.size = 0; - } - public final void filter(List searchMoves) { - int used = 0; - for (int i = 0; i < size; i++) - if (searchMoves.contains(m[i])) - m[used++] = m[i]; - size = used; - } + /** Generate and return a list of legal moves. */ + public final ArrayList legalMoves(Position pos) { + ArrayList 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 MoveList pseudoLegalMoves(Position pos) { - MoveList moveList = getMoveListObj(); - final long occupied = pos.whiteBB | pos.blackBB; - if (pos.whiteMove) { - // Queen moves - long squares = pos.pieceTypeBB[Piece.WQUEEN]; - while (squares != 0) { - int sq = BitBoard.numberOfTrailingZeros(squares); - long m = (BitBoard.rookAttacks(sq, occupied) | BitBoard.bishopAttacks(sq, occupied)) & ~pos.whiteBB; - if (addMovesByMask(moveList, pos, sq, m)) return moveList; - squares &= squares-1; - } + public final ArrayList pseudoLegalMoves(Position pos) { + ArrayList 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; - // Rook moves - squares = pos.pieceTypeBB[Piece.WROOK]; - while (squares != 0) { - int sq = BitBoard.numberOfTrailingZeros(squares); - long m = BitBoard.rookAttacks(sq, occupied) & ~pos.whiteBB; - if (addMovesByMask(moveList, pos, sq, m)) return moveList; - squares &= squares-1; - } - - // Bishop moves - squares = pos.pieceTypeBB[Piece.WBISHOP]; - while (squares != 0) { - int sq = BitBoard.numberOfTrailingZeros(squares); - long m = BitBoard.bishopAttacks(sq, occupied) & ~pos.whiteBB; - if (addMovesByMask(moveList, pos, sq, m)) return moveList; - squares &= squares-1; - } - - // King moves - { - int sq = pos.getKingSq(true); - long m = BitBoard.kingAttacks[sq] & ~pos.whiteBB; - if (addMovesByMask(moveList, pos, sq, m)) return moveList; - final int k0 = 4; - if (sq == k0) { - final long OO_SQ = 0x60L; - final long OOO_SQ = 0xEL; - if (((pos.getCastleMask() & (1 << Position.H1_CASTLE)) != 0) && - ((OO_SQ & (pos.whiteBB | pos.blackBB)) == 0) && - (pos.getPiece(k0 + 3) == Piece.WROOK) && - !sqAttacked(pos, k0) && - !sqAttacked(pos, k0 + 1)) { - setMove(moveList, k0, k0 + 2, Piece.EMPTY); + 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 (((pos.getCastleMask() & (1 << Position.A1_CASTLE)) != 0) && - ((OOO_SQ & (pos.whiteBB | pos.blackBB)) == 0) && - (pos.getPiece(k0 - 4) == Piece.WROOK) && - !sqAttacked(pos, k0) && - !sqAttacked(pos, k0 - 1)) { - setMove(moveList, 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); + } } } } - - // Knight moves - long knights = pos.pieceTypeBB[Piece.WKNIGHT]; - while (knights != 0) { - int sq = BitBoard.numberOfTrailingZeros(knights); - long m = BitBoard.knightAttacks[sq] & ~pos.whiteBB; - if (addMovesByMask(moveList, pos, sq, m)) return moveList; - knights &= knights-1; - } - - // Pawn moves - long pawns = pos.pieceTypeBB[Piece.WPAWN]; - long m = (pawns << 8) & ~occupied; - if (addPawnMovesByMask(moveList, pos, m, -8, true)) return moveList; - m = ((m & BitBoard.maskRow3) << 8) & ~occupied; - addPawnDoubleMovesByMask(moveList, pos, m, -16); - - int epSquare = pos.getEpSquare(); - long epMask = (epSquare >= 0) ? (1L << epSquare) : 0L; - m = (pawns << 7) & BitBoard.maskAToGFiles & (pos.blackBB | epMask); - if (addPawnMovesByMask(moveList, pos, m, -7, true)) return moveList; - - m = (pawns << 9) & BitBoard.maskBToHFiles & (pos.blackBB | epMask); - if (addPawnMovesByMask(moveList, pos, m, -9, true)) return moveList; - } else { - // Queen moves - long squares = pos.pieceTypeBB[Piece.BQUEEN]; - while (squares != 0) { - int sq = BitBoard.numberOfTrailingZeros(squares); - long m = (BitBoard.rookAttacks(sq, occupied) | BitBoard.bishopAttacks(sq, occupied)) & ~pos.blackBB; - if (addMovesByMask(moveList, pos, sq, m)) return moveList; - squares &= squares-1; - } - - // Rook moves - squares = pos.pieceTypeBB[Piece.BROOK]; - while (squares != 0) { - int sq = BitBoard.numberOfTrailingZeros(squares); - long m = BitBoard.rookAttacks(sq, occupied) & ~pos.blackBB; - if (addMovesByMask(moveList, pos, sq, m)) return moveList; - squares &= squares-1; - } - - // Bishop moves - squares = pos.pieceTypeBB[Piece.BBISHOP]; - while (squares != 0) { - int sq = BitBoard.numberOfTrailingZeros(squares); - long m = BitBoard.bishopAttacks(sq, occupied) & ~pos.blackBB; - if (addMovesByMask(moveList, pos, sq, m)) return moveList; - squares &= squares-1; - } - - // King moves - { - int sq = pos.getKingSq(false); - long m = BitBoard.kingAttacks[sq] & ~pos.blackBB; - if (addMovesByMask(moveList, pos, sq, m)) return moveList; - final int k0 = 60; - if (sq == k0) { - final long OO_SQ = 0x6000000000000000L; - final long OOO_SQ = 0xE00000000000000L; - if (((pos.getCastleMask() & (1 << Position.H8_CASTLE)) != 0) && - ((OO_SQ & (pos.whiteBB | pos.blackBB)) == 0) && - (pos.getPiece(k0 + 3) == Piece.BROOK) && - !sqAttacked(pos, k0) && - !sqAttacked(pos, k0 + 1)) { - setMove(moveList, k0, k0 + 2, Piece.EMPTY); - } - if (((pos.getCastleMask() & (1 << Position.A8_CASTLE)) != 0) && - ((OOO_SQ & (pos.whiteBB | pos.blackBB)) == 0) && - (pos.getPiece(k0 - 4) == Piece.BROOK) && - !sqAttacked(pos, k0) && - !sqAttacked(pos, k0 - 1)) { - setMove(moveList, k0, k0 - 2, Piece.EMPTY); - } - } - } - - // Knight moves - long knights = pos.pieceTypeBB[Piece.BKNIGHT]; - while (knights != 0) { - int sq = BitBoard.numberOfTrailingZeros(knights); - long m = BitBoard.knightAttacks[sq] & ~pos.blackBB; - if (addMovesByMask(moveList, pos, sq, m)) return moveList; - knights &= knights-1; - } - - // Pawn moves - long pawns = pos.pieceTypeBB[Piece.BPAWN]; - long m = (pawns >>> 8) & ~occupied; - if (addPawnMovesByMask(moveList, pos, m, 8, true)) return moveList; - m = ((m & BitBoard.maskRow6) >>> 8) & ~occupied; - addPawnDoubleMovesByMask(moveList, pos, m, 16); - - int epSquare = pos.getEpSquare(); - long epMask = (epSquare >= 0) ? (1L << epSquare) : 0L; - m = (pawns >>> 9) & BitBoard.maskAToGFiles & (pos.whiteBB | epMask); - if (addPawnMovesByMask(moveList, pos, m, 9, true)) return moveList; - - m = (pawns >>> 7) & BitBoard.maskBToHFiles & (pos.whiteBB | epMask); - if (addPawnMovesByMask(moveList, pos, m, 7, true)) return moveList; - } - return moveList; - } - - /** - * Generate and return a list of pseudo-legal check evasion moves. - * Pseudo-legal means that the moves doesn't necessarily defend from check threats. - */ - public final MoveList checkEvasions(Position pos) { - MoveList moveList = getMoveListObj(); - final long occupied = pos.whiteBB | pos.blackBB; - if (pos.whiteMove) { - long kingThreats = pos.pieceTypeBB[Piece.BKNIGHT] & BitBoard.knightAttacks[pos.wKingSq]; - long rookPieces = pos.pieceTypeBB[Piece.BROOK] | pos.pieceTypeBB[Piece.BQUEEN]; - if (rookPieces != 0) - kingThreats |= rookPieces & BitBoard.rookAttacks(pos.wKingSq, occupied); - long bishPieces = pos.pieceTypeBB[Piece.BBISHOP] | pos.pieceTypeBB[Piece.BQUEEN]; - if (bishPieces != 0) - kingThreats |= bishPieces & BitBoard.bishopAttacks(pos.wKingSq, occupied); - kingThreats |= pos.pieceTypeBB[Piece.BPAWN] & BitBoard.wPawnAttacks[pos.wKingSq]; - long validTargets = 0; - if ((kingThreats != 0) && ((kingThreats & (kingThreats-1)) == 0)) { // Exactly one attacking piece - int threatSq = BitBoard.numberOfTrailingZeros(kingThreats); - validTargets = kingThreats | BitBoard.squaresBetween[pos.wKingSq][threatSq]; - } - validTargets |= pos.pieceTypeBB[Piece.BKING]; - // Queen moves - long squares = pos.pieceTypeBB[Piece.WQUEEN]; - while (squares != 0) { - int sq = BitBoard.numberOfTrailingZeros(squares); - long m = (BitBoard.rookAttacks(sq, occupied) | BitBoard.bishopAttacks(sq, occupied)) & - ~pos.whiteBB & validTargets; - if (addMovesByMask(moveList, pos, sq, m)) return moveList; - squares &= squares-1; - } - - // Rook moves - squares = pos.pieceTypeBB[Piece.WROOK]; - while (squares != 0) { - int sq = BitBoard.numberOfTrailingZeros(squares); - long m = BitBoard.rookAttacks(sq, occupied) & ~pos.whiteBB & validTargets; - if (addMovesByMask(moveList, pos, sq, m)) return moveList; - squares &= squares-1; - } - - // Bishop moves - squares = pos.pieceTypeBB[Piece.WBISHOP]; - while (squares != 0) { - int sq = BitBoard.numberOfTrailingZeros(squares); - long m = BitBoard.bishopAttacks(sq, occupied) & ~pos.whiteBB & validTargets; - if (addMovesByMask(moveList, pos, sq, m)) return moveList; - squares &= squares-1; - } - - // King moves - { - int sq = pos.getKingSq(true); - long m = BitBoard.kingAttacks[sq] & ~pos.whiteBB; - if (addMovesByMask(moveList, pos, sq, m)) return moveList; - } - - // Knight moves - long knights = pos.pieceTypeBB[Piece.WKNIGHT]; - while (knights != 0) { - int sq = BitBoard.numberOfTrailingZeros(knights); - long m = BitBoard.knightAttacks[sq] & ~pos.whiteBB & validTargets; - if (addMovesByMask(moveList, pos, sq, m)) return moveList; - knights &= knights-1; - } - - // Pawn moves - long pawns = pos.pieceTypeBB[Piece.WPAWN]; - long m = (pawns << 8) & ~occupied; - if (addPawnMovesByMask(moveList, pos, m & validTargets, -8, true)) return moveList; - m = ((m & BitBoard.maskRow3) << 8) & ~occupied; - addPawnDoubleMovesByMask(moveList, pos, m & validTargets, -16); - - int epSquare = pos.getEpSquare(); - long epMask = (epSquare >= 0) ? (1L << epSquare) : 0L; - m = (pawns << 7) & BitBoard.maskAToGFiles & ((pos.blackBB & validTargets) | epMask); - if (addPawnMovesByMask(moveList, pos, m, -7, true)) return moveList; - - m = (pawns << 9) & BitBoard.maskBToHFiles & ((pos.blackBB & validTargets) | epMask); - if (addPawnMovesByMask(moveList, pos, m, -9, true)) return moveList; - } else { - long kingThreats = pos.pieceTypeBB[Piece.WKNIGHT] & BitBoard.knightAttacks[pos.bKingSq]; - long rookPieces = pos.pieceTypeBB[Piece.WROOK] | pos.pieceTypeBB[Piece.WQUEEN]; - if (rookPieces != 0) - kingThreats |= rookPieces & BitBoard.rookAttacks(pos.bKingSq, occupied); - long bishPieces = pos.pieceTypeBB[Piece.WBISHOP] | pos.pieceTypeBB[Piece.WQUEEN]; - if (bishPieces != 0) - kingThreats |= bishPieces & BitBoard.bishopAttacks(pos.bKingSq, occupied); - kingThreats |= pos.pieceTypeBB[Piece.WPAWN] & BitBoard.bPawnAttacks[pos.bKingSq]; - long validTargets = 0; - if ((kingThreats != 0) && ((kingThreats & (kingThreats-1)) == 0)) { // Exactly one attacking piece - int threatSq = BitBoard.numberOfTrailingZeros(kingThreats); - validTargets = kingThreats | BitBoard.squaresBetween[pos.bKingSq][threatSq]; - } - validTargets |= pos.pieceTypeBB[Piece.WKING]; - // Queen moves - long squares = pos.pieceTypeBB[Piece.BQUEEN]; - while (squares != 0) { - int sq = BitBoard.numberOfTrailingZeros(squares); - long m = (BitBoard.rookAttacks(sq, occupied) | BitBoard.bishopAttacks(sq, occupied)) & - ~pos.blackBB & validTargets; - if (addMovesByMask(moveList, pos, sq, m)) return moveList; - squares &= squares-1; - } - - // Rook moves - squares = pos.pieceTypeBB[Piece.BROOK]; - while (squares != 0) { - int sq = BitBoard.numberOfTrailingZeros(squares); - long m = BitBoard.rookAttacks(sq, occupied) & ~pos.blackBB & validTargets; - if (addMovesByMask(moveList, pos, sq, m)) return moveList; - squares &= squares-1; - } - - // Bishop moves - squares = pos.pieceTypeBB[Piece.BBISHOP]; - while (squares != 0) { - int sq = BitBoard.numberOfTrailingZeros(squares); - long m = BitBoard.bishopAttacks(sq, occupied) & ~pos.blackBB & validTargets; - if (addMovesByMask(moveList, pos, sq, m)) return moveList; - squares &= squares-1; - } - - // King moves - { - int sq = pos.getKingSq(false); - long m = BitBoard.kingAttacks[sq] & ~pos.blackBB; - if (addMovesByMask(moveList, pos, sq, m)) return moveList; - } - - // Knight moves - long knights = pos.pieceTypeBB[Piece.BKNIGHT]; - while (knights != 0) { - int sq = BitBoard.numberOfTrailingZeros(knights); - long m = BitBoard.knightAttacks[sq] & ~pos.blackBB & validTargets; - if (addMovesByMask(moveList, pos, sq, m)) return moveList; - knights &= knights-1; - } - - // Pawn moves - long pawns = pos.pieceTypeBB[Piece.BPAWN]; - long m = (pawns >>> 8) & ~occupied; - if (addPawnMovesByMask(moveList, pos, m & validTargets, 8, true)) return moveList; - m = ((m & BitBoard.maskRow6) >>> 8) & ~occupied; - addPawnDoubleMovesByMask(moveList, pos, m & validTargets, 16); - - int epSquare = pos.getEpSquare(); - long epMask = (epSquare >= 0) ? (1L << epSquare) : 0L; - m = (pawns >>> 9) & BitBoard.maskAToGFiles & ((pos.whiteBB & validTargets) | epMask); - if (addPawnMovesByMask(moveList, pos, m, 9, true)) return moveList; - - m = (pawns >>> 7) & BitBoard.maskBToHFiles & ((pos.whiteBB & validTargets) | epMask); - if (addPawnMovesByMask(moveList, pos, m, 7, true)) return moveList; - } - - /* Extra debug checks - { - ArrayList allMoves = pseudoLegalMoves(pos); - allMoves = MoveGen.removeIllegal(pos, allMoves); - HashSet evMoves = new HashSet<>(); - for (Move m : moveList) - evMoves.add(TextIO.moveToUCIString(m)); - for (Move m : allMoves) - if (!evMoves.contains(TextIO.moveToUCIString(m))) - throw new RuntimeException(); - } - */ - - return moveList; - } - - /** Generate captures, checks, and possibly some other moves that are too hard to filter out. */ - public final MoveList pseudoLegalCapturesAndChecks(Position pos) { - MoveList moveList = getMoveListObj(); - long occupied = pos.whiteBB | pos.blackBB; - if (pos.whiteMove) { - int bKingSq = pos.getKingSq(false); - long discovered = 0; // Squares that could generate discovered checks - long kRookAtk = BitBoard.rookAttacks(bKingSq, occupied); - if ((BitBoard.rookAttacks(bKingSq, occupied & ~kRookAtk) & - (pos.pieceTypeBB[Piece.WQUEEN] | pos.pieceTypeBB[Piece.WROOK])) != 0) - discovered |= kRookAtk; - long kBishAtk = BitBoard.bishopAttacks(bKingSq, occupied); - if ((BitBoard.bishopAttacks(bKingSq, occupied & ~kBishAtk) & - (pos.pieceTypeBB[Piece.WQUEEN] | pos.pieceTypeBB[Piece.WBISHOP])) != 0) - discovered |= kBishAtk; - - // Queen moves - long squares = pos.pieceTypeBB[Piece.WQUEEN]; - while (squares != 0) { - int sq = BitBoard.numberOfTrailingZeros(squares); - long m = (BitBoard.rookAttacks(sq, occupied) | BitBoard.bishopAttacks(sq, occupied)); - if ((discovered & (1L<= 0) ? (1L << epSquare) : 0L; - long m = (pawns << 7) & BitBoard.maskAToGFiles & (pos.blackBB | epMask); - if (addPawnMovesByMask(moveList, pos, m, -7, false)) return moveList; - m = (pawns << 9) & BitBoard.maskBToHFiles & (pos.blackBB | epMask); - if (addPawnMovesByMask(moveList, pos, m, -9, false)) return moveList; - - // Discovered checks and promotions - long pawnAll = discovered | BitBoard.maskRow7; - m = ((pawns & pawnAll) << 8) & ~(pos.whiteBB | pos.blackBB); - if (addPawnMovesByMask(moveList, pos, m, -8, false)) return moveList; - m = ((m & BitBoard.maskRow3) << 8) & ~(pos.whiteBB | pos.blackBB); - addPawnDoubleMovesByMask(moveList, pos, m, -16); - - // Normal checks - m = ((pawns & ~pawnAll) << 8) & ~(pos.whiteBB | pos.blackBB); - if (addPawnMovesByMask(moveList, pos, m & BitBoard.bPawnAttacks[bKingSq], -8, false)) return moveList; - m = ((m & BitBoard.maskRow3) << 8) & ~(pos.whiteBB | pos.blackBB); - addPawnDoubleMovesByMask(moveList, pos, m & BitBoard.bPawnAttacks[bKingSq], -16); - } else { - int wKingSq = pos.getKingSq(true); - long discovered = 0; // Squares that could generate discovered checks - long kRookAtk = BitBoard.rookAttacks(wKingSq, occupied); - if ((BitBoard.rookAttacks(wKingSq, occupied & ~kRookAtk) & - (pos.pieceTypeBB[Piece.BQUEEN] | pos.pieceTypeBB[Piece.BROOK])) != 0) - discovered |= kRookAtk; - long kBishAtk = BitBoard.bishopAttacks(wKingSq, occupied); - if ((BitBoard.bishopAttacks(wKingSq, occupied & ~kBishAtk) & - (pos.pieceTypeBB[Piece.BQUEEN] | pos.pieceTypeBB[Piece.BBISHOP])) != 0) - discovered |= kBishAtk; - - // Queen moves - long squares = pos.pieceTypeBB[Piece.BQUEEN]; - while (squares != 0) { - int sq = BitBoard.numberOfTrailingZeros(squares); - long m = (BitBoard.rookAttacks(sq, occupied) | BitBoard.bishopAttacks(sq, occupied)); - if ((discovered & (1L<= 0) ? (1L << epSquare) : 0L; - long m = (pawns >>> 9) & BitBoard.maskAToGFiles & (pos.whiteBB | epMask); - if (addPawnMovesByMask(moveList, pos, m, 9, false)) return moveList; - m = (pawns >>> 7) & BitBoard.maskBToHFiles & (pos.whiteBB | epMask); - if (addPawnMovesByMask(moveList, pos, m, 7, false)) return moveList; - - // Discovered checks and promotions - long pawnAll = discovered | BitBoard.maskRow2; - m = ((pawns & pawnAll) >>> 8) & ~(pos.whiteBB | pos.blackBB); - if (addPawnMovesByMask(moveList, pos, m, 8, false)) return moveList; - m = ((m & BitBoard.maskRow6) >>> 8) & ~(pos.whiteBB | pos.blackBB); - addPawnDoubleMovesByMask(moveList, pos, m, 16); - - // Normal checks - m = ((pawns & ~pawnAll) >>> 8) & ~(pos.whiteBB | pos.blackBB); - if (addPawnMovesByMask(moveList, pos, m & BitBoard.wPawnAttacks[wKingSq], 8, false)) return moveList; - m = ((m & BitBoard.maskRow6) >>> 8) & ~(pos.whiteBB | pos.blackBB); - addPawnDoubleMovesByMask(moveList, pos, m & BitBoard.wPawnAttacks[wKingSq], 16); - } - - return moveList; - } - - public final MoveList pseudoLegalCaptures(Position pos) { - MoveList moveList = getMoveListObj(); - long occupied = pos.whiteBB | pos.blackBB; - if (pos.whiteMove) { - // Queen moves - long squares = pos.pieceTypeBB[Piece.WQUEEN]; - while (squares != 0) { - int sq = BitBoard.numberOfTrailingZeros(squares); - long m = (BitBoard.rookAttacks(sq, occupied) | BitBoard.bishopAttacks(sq, occupied)) & pos.blackBB; - if (addMovesByMask(moveList, pos, sq, m)) return moveList; - squares &= squares-1; - } - - // Rook moves - squares = pos.pieceTypeBB[Piece.WROOK]; - while (squares != 0) { - int sq = BitBoard.numberOfTrailingZeros(squares); - long m = BitBoard.rookAttacks(sq, occupied) & pos.blackBB; - if (addMovesByMask(moveList, pos, sq, m)) return moveList; - squares &= squares-1; - } - - // Bishop moves - squares = pos.pieceTypeBB[Piece.WBISHOP]; - while (squares != 0) { - int sq = BitBoard.numberOfTrailingZeros(squares); - long m = BitBoard.bishopAttacks(sq, occupied) & pos.blackBB; - if (addMovesByMask(moveList, pos, sq, m)) return moveList; - squares &= squares-1; - } - - // Knight moves - long knights = pos.pieceTypeBB[Piece.WKNIGHT]; - while (knights != 0) { - int sq = BitBoard.numberOfTrailingZeros(knights); - long m = BitBoard.knightAttacks[sq] & pos.blackBB; - if (addMovesByMask(moveList, pos, sq, m)) return moveList; - knights &= knights-1; - } - - // King moves - int sq = pos.getKingSq(true); - long m = BitBoard.kingAttacks[sq] & pos.blackBB; - if (addMovesByMask(moveList, pos, sq, m)) return moveList; - - // Pawn moves - long pawns = pos.pieceTypeBB[Piece.WPAWN]; - m = (pawns << 8) & ~(pos.whiteBB | pos.blackBB); - m &= BitBoard.maskRow8; - if (addPawnMovesByMask(moveList, pos, m, -8, false)) return moveList; - - int epSquare = pos.getEpSquare(); - long epMask = (epSquare >= 0) ? (1L << epSquare) : 0L; - m = (pawns << 7) & BitBoard.maskAToGFiles & (pos.blackBB | epMask); - if (addPawnMovesByMask(moveList, pos, m, -7, false)) return moveList; - m = (pawns << 9) & BitBoard.maskBToHFiles & (pos.blackBB | epMask); - if (addPawnMovesByMask(moveList, pos, m, -9, false)) return moveList; - } else { - // Queen moves - long squares = pos.pieceTypeBB[Piece.BQUEEN]; - while (squares != 0) { - int sq = BitBoard.numberOfTrailingZeros(squares); - long m = (BitBoard.rookAttacks(sq, occupied) | BitBoard.bishopAttacks(sq, occupied)) & pos.whiteBB; - if (addMovesByMask(moveList, pos, sq, m)) return moveList; - squares &= squares-1; - } - - // Rook moves - squares = pos.pieceTypeBB[Piece.BROOK]; - while (squares != 0) { - int sq = BitBoard.numberOfTrailingZeros(squares); - long m = BitBoard.rookAttacks(sq, occupied) & pos.whiteBB; - if (addMovesByMask(moveList, pos, sq, m)) return moveList; - squares &= squares-1; - } - - // Bishop moves - squares = pos.pieceTypeBB[Piece.BBISHOP]; - while (squares != 0) { - int sq = BitBoard.numberOfTrailingZeros(squares); - long m = BitBoard.bishopAttacks(sq, occupied) & pos.whiteBB; - if (addMovesByMask(moveList, pos, sq, m)) return moveList; - squares &= squares-1; - } - - // Knight moves - long knights = pos.pieceTypeBB[Piece.BKNIGHT]; - while (knights != 0) { - int sq = BitBoard.numberOfTrailingZeros(knights); - long m = BitBoard.knightAttacks[sq] & pos.whiteBB; - if (addMovesByMask(moveList, pos, sq, m)) return moveList; - knights &= knights-1; - } - - // King moves - int sq = pos.getKingSq(false); - long m = BitBoard.kingAttacks[sq] & pos.whiteBB; - if (addMovesByMask(moveList, pos, sq, m)) return moveList; - - // Pawn moves - long pawns = pos.pieceTypeBB[Piece.BPAWN]; - m = (pawns >>> 8) & ~(pos.whiteBB | pos.blackBB); - m &= BitBoard.maskRow1; - if (addPawnMovesByMask(moveList, pos, m, 8, false)) return moveList; - - int epSquare = pos.getEpSquare(); - long epMask = (epSquare >= 0) ? (1L << epSquare) : 0L; - m = (pawns >>> 9) & BitBoard.maskAToGFiles & (pos.whiteBB | epMask); - if (addPawnMovesByMask(moveList, pos, m, 9, false)) return moveList; - m = (pawns >>> 7) & BitBoard.maskBToHFiles & (pos.whiteBB | epMask); - if (addPawnMovesByMask(moveList, pos, m, 7, false)) return moveList; } return moveList; } @@ -719,200 +158,63 @@ public final class MoveGen { */ public static boolean inCheck(Position pos) { int kingSq = pos.getKingSq(pos.whiteMove); + if (kingSq < 0) + return false; return sqAttacked(pos, kingSq); } - /** - * Return the next piece in a given direction, starting from sq. - */ - private static int nextPiece(Position pos, int sq, int delta) { - while (true) { - sq += delta; - int p = pos.getPiece(sq); - if (p != Piece.EMPTY) - return p; - } - } - - /** Like nextPiece(), but handles board edges. */ - private static int nextPieceSafe(Position pos, int sq, int delta) { - int dx = 0, dy = 0; - switch (delta) { - case 1: dx=1; dy=0; break; - case 9: dx=1; dy=1; break; - case 8: dx=0; dy=1; break; - case 7: dx=-1; dy=1; break; - case -1: dx=-1; dy=0; break; - case -9: dx=-1; dy=-1; break; - case -8: dx=0; dy=-1; break; - case -7: dx=1; dy=-1; break; - } - int x = Position.getX(sq); - int y = Position.getY(sq); - while (true) { - x += dx; - y += dy; - if ((x < 0) || (x > 7) || (y < 0) || (y > 7)) { - return Piece.EMPTY; - } - int p = pos.getPiece(Position.getSquare(x, y)); - if (p != Piece.EMPTY) - return p; - } - } - - /** - * Return true if making a move delivers check to the opponent - */ - public static boolean givesCheck(Position pos, Move m) { - boolean wtm = pos.whiteMove; - int oKingSq = pos.getKingSq(!wtm); - int oKing = wtm ? Piece.BKING : Piece.WKING; - int p = Piece.makeWhite(m.promoteTo == Piece.EMPTY ? pos.getPiece(m.from) : m.promoteTo); - int d1 = BitBoard.getDirection(m.to, oKingSq); - switch (d1) { - case 8: case -8: case 1: case -1: // Rook direction - if ((p == Piece.WQUEEN) || (p == Piece.WROOK)) - if ((d1 != 0) && (MoveGen.nextPiece(pos, m.to, d1) == oKing)) - return true; - break; - case 9: case 7: case -9: case -7: // Bishop direction - if ((p == Piece.WQUEEN) || (p == Piece.WBISHOP)) { - if ((d1 != 0) && (MoveGen.nextPiece(pos, m.to, d1) == oKing)) - return true; - } else if (p == Piece.WPAWN) { - if (((d1 > 0) == wtm) && (pos.getPiece(m.to + d1) == oKing)) - return true; - } - break; - default: - if (d1 != 0) { // Knight direction - if (p == Piece.WKNIGHT) - return true; - } - } - int d2 = BitBoard.getDirection(m.from, oKingSq); - if ((d2 != 0) && (d2 != d1) && (MoveGen.nextPiece(pos, m.from, d2) == oKing)) { - int p2 = MoveGen.nextPieceSafe(pos, m.from, -d2); - switch (d2) { - case 8: case -8: case 1: case -1: // Rook direction - if ((p2 == (wtm ? Piece.WQUEEN : Piece.BQUEEN)) || - (p2 == (wtm ? Piece.WROOK : Piece.BROOK))) - return true; - break; - case 9: case 7: case -9: case -7: // Bishop direction - if ((p2 == (wtm ? Piece.WQUEEN : Piece.BQUEEN)) || - (p2 == (wtm ? Piece.WBISHOP : Piece.BBISHOP))) - return true; - break; - } - } - if ((m.promoteTo != Piece.EMPTY) && (d1 != 0) && (d1 == d2)) { - switch (d1) { - case 8: case -8: case 1: case -1: // Rook direction - if ((p == Piece.WQUEEN) || (p == Piece.WROOK)) - if ((d1 != 0) && (MoveGen.nextPiece(pos, m.from, d1) == oKing)) - return true; - break; - case 9: case 7: case -9: case -7: // Bishop direction - if ((p == Piece.WQUEEN) || (p == Piece.WBISHOP)) { - if ((d1 != 0) && (MoveGen.nextPiece(pos, m.from, d1) == oKing)) - return true; - } - break; - } - } - if (p == Piece.WKING) { - if (m.to - m.from == 2) { // O-O - if (MoveGen.nextPieceSafe(pos, m.from, -1) == oKing) - return true; - if (MoveGen.nextPieceSafe(pos, m.from + 1, wtm ? 8 : -8) == oKing) - return true; - } else if (m.to - m.from == -2) { // O-O-O - if (MoveGen.nextPieceSafe(pos, m.from, 1) == oKing) - return true; - if (MoveGen.nextPieceSafe(pos, m.from - 1, wtm ? 8 : -8) == oKing) - return true; - } - } else if (p == Piece.WPAWN) { - if (pos.getPiece(m.to) == Piece.EMPTY) { - int dx = Position.getX(m.to) - Position.getX(m.from); - if (dx != 0) { // en passant - int epSq = m.from + dx; - int d3 = BitBoard.getDirection(epSq, oKingSq); - switch (d3) { - case 9: case 7: case -9: case -7: - if (MoveGen.nextPiece(pos, epSq, d3) == oKing) { - int p2 = MoveGen.nextPieceSafe(pos, epSq, -d3); - if ((p2 == (wtm ? Piece.WQUEEN : Piece.BQUEEN)) || - (p2 == (wtm ? Piece.WBISHOP : Piece.BBISHOP))) - return true; - } - break; - case 1: - if (MoveGen.nextPiece(pos, Math.max(epSq, m.from), d3) == oKing) { - int p2 = MoveGen.nextPieceSafe(pos, Math.min(epSq, m.from), -d3); - if ((p2 == (wtm ? Piece.WQUEEN : Piece.BQUEEN)) || - (p2 == (wtm ? Piece.WROOK : Piece.BROOK))) - return true; - } - break; - case -1: - if (MoveGen.nextPiece(pos, Math.min(epSq, m.from), d3) == oKing) { - int p2 = MoveGen.nextPieceSafe(pos, Math.max(epSq, m.from), -d3); - if ((p2 == (wtm ? Piece.WQUEEN : Piece.BQUEEN)) || - (p2 == (wtm ? Piece.WROOK : Piece.BROOK))) - return true; - } - break; - } - } - } - } - return false; - } - - /** - * Return true if the side to move can take the opponents king. - */ - public static boolean canTakeKing(Position pos) { - pos.setWhiteMove(!pos.whiteMove); - boolean ret = inCheck(pos); - pos.setWhiteMove(!pos.whiteMove); - return ret; - } - /** * Return true if a square is attacked by the opposite side. */ public static boolean sqAttacked(Position pos, int sq) { - if (pos.whiteMove) { - if ((BitBoard.knightAttacks[sq] & pos.pieceTypeBB[Piece.BKNIGHT]) != 0) - return true; - if ((BitBoard.kingAttacks[sq] & pos.pieceTypeBB[Piece.BKING]) != 0) - return true; - if ((BitBoard.wPawnAttacks[sq] & pos.pieceTypeBB[Piece.BPAWN]) != 0) - return true; - long occupied = pos.whiteBB | pos.blackBB; - long bbQueen = pos.pieceTypeBB[Piece.BQUEEN]; - if ((BitBoard.bishopAttacks(sq, occupied) & (pos.pieceTypeBB[Piece.BBISHOP] | bbQueen)) != 0) - return true; - if ((BitBoard.rookAttacks(sq, occupied) & (pos.pieceTypeBB[Piece.BROOK] | bbQueen)) != 0) - return true; - } else { - if ((BitBoard.knightAttacks[sq] & pos.pieceTypeBB[Piece.WKNIGHT]) != 0) - return true; - if ((BitBoard.kingAttacks[sq] & pos.pieceTypeBB[Piece.WKING]) != 0) - return true; - if ((BitBoard.bPawnAttacks[sq] & pos.pieceTypeBB[Piece.WPAWN]) != 0) - return true; - long occupied = pos.whiteBB | pos.blackBB; - long bbQueen = pos.pieceTypeBB[Piece.WQUEEN]; - if ((BitBoard.bishopAttacks(sq, occupied) & (pos.pieceTypeBB[Piece.WBISHOP] | bbQueen)) != 0) - return true; - if ((BitBoard.rookAttacks(sq, occupied) & (pos.pieceTypeBB[Piece.WROOK] | bbQueen)) != 0) + 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; } @@ -921,149 +223,91 @@ public final class MoveGen { * "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 void removeIllegal(Position pos, MoveList moveList) { - int length = 0; + public static ArrayList removeIllegal(Position pos, ArrayList moveList) { + ArrayList ret = new ArrayList<>(); UndoInfo ui = new UndoInfo(); - - boolean isInCheck = inCheck(pos); - final long occupied = pos.whiteBB | pos.blackBB; - int kSq = pos.getKingSq(pos.whiteMove); - long kingAtks = BitBoard.rookAttacks(kSq, occupied) | BitBoard.bishopAttacks(kSq, occupied); - int epSquare = pos.getEpSquare(); - if (isInCheck) { - kingAtks |= pos.pieceTypeBB[pos.whiteMove ? Piece.BKNIGHT : Piece.WKNIGHT]; - for (int mi = 0; mi < moveList.size; mi++) { - Move m = moveList.m[mi]; - boolean legal; - if ((m.from != kSq) && ((kingAtks & (1L<= 56) { // White promotion - setMove(moveList, sq0, sq, Piece.WQUEEN); - setMove(moveList, sq0, sq, Piece.WKNIGHT); - if (allPromotions) { - setMove(moveList, sq0, sq, Piece.WROOK); - setMove(moveList, sq0, sq, Piece.WBISHOP); - } - } else { // Black promotion - setMove(moveList, sq0, sq, Piece.BQUEEN); - setMove(moveList, sq0, sq, Piece.BKNIGHT); - if (allPromotions) { - setMove(moveList, sq0, sq, Piece.BROOK); - setMove(moveList, sq0, sq, Piece.BBISHOP); + /** + * 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 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; } - promMask &= (promMask - 1); - } - while (mask != 0) { - int sq = BitBoard.numberOfTrailingZeros(mask); - setMove(moveList, sq + delta, sq, Piece.EMPTY); - mask &= (mask - 1); + maxSteps--; } return false; } - private static void addPawnDoubleMovesByMask(MoveList moveList, Position pos, - long mask, int delta) { - while (mask != 0) { - int sq = BitBoard.numberOfTrailingZeros(mask); - setMove(moveList, sq + delta, sq, Piece.EMPTY); - mask &= (mask - 1); + /** + * Generate all possible pawn moves from (x0,y0) to (x1,y1), taking pawn promotions into account. + */ + private void addPawnMoves(ArrayList 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)); } } - - private static boolean addMovesByMask(MoveList moveList, Position pos, int sq0, long mask) { - long oKingMask = pos.pieceTypeBB[pos.whiteMove ? Piece.BKING : Piece.WKING]; - if ((mask & oKingMask) != 0) { - int sq = BitBoard.numberOfTrailingZeros(mask & oKingMask); - moveList.size = 0; - setMove(moveList, sq0, sq, Piece.EMPTY); - return true; + + /** + * 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--; } - while (mask != 0) { - int sq = BitBoard.numberOfTrailingZeros(mask); - setMove(moveList, sq0, sq, Piece.EMPTY); - mask &= (mask - 1); - } - return false; + return Piece.EMPTY; } - private static void setMove(MoveList moveList, int from, int to, int promoteTo) { - Move m = moveList.m[moveList.size++]; - m.from = from; - m.to = to; - m.promoteTo = promoteTo; - m.score = 0; - } - - // Code to handle the Move cache. - private Object[] moveListCache = new Object[200]; - private int moveListsInCache = 0; - - private static final int MAX_MOVES = 256; - - private MoveList getMoveListObj() { - MoveList ml; - if (moveListsInCache > 0) { - ml = (MoveList)moveListCache[--moveListsInCache]; - ml.size = 0; - } else { - ml = new MoveList(); - for (int i = 0; i < MAX_MOVES; i++) - ml.m[i] = new Move(0, 0, Piece.EMPTY); - } - return ml; - } - - /** Return all move objects in moveList to the move cache. */ - public final void returnMoveList(MoveList moveList) { - if (moveListsInCache < moveListCache.length) { - moveListCache[moveListsInCache++] = moveList; - } + private static Move getMoveObj(int from, int to, int promoteTo) { + return new Move(from, to, promoteTo); } } diff --git a/buildSrc/src/main/java/org/petero/droidfish/PGNOptions.java b/buildSrc/src/main/java/chess/PGNOptions.java similarity index 98% rename from buildSrc/src/main/java/org/petero/droidfish/PGNOptions.java rename to buildSrc/src/main/java/chess/PGNOptions.java index 3f7ef04..99b7650 100644 --- a/buildSrc/src/main/java/org/petero/droidfish/PGNOptions.java +++ b/buildSrc/src/main/java/chess/PGNOptions.java @@ -16,7 +16,7 @@ along with this program. If not, see . */ -package org.petero.droidfish; +package chess; /** Settings controlling PGN import/export */ public class PGNOptions { diff --git a/buildSrc/src/main/java/org/petero/droidfish/gamelogic/Pair.java b/buildSrc/src/main/java/chess/Pair.java similarity index 96% rename from buildSrc/src/main/java/org/petero/droidfish/gamelogic/Pair.java rename to buildSrc/src/main/java/chess/Pair.java index 47fed86..045f6bf 100644 --- a/buildSrc/src/main/java/org/petero/droidfish/gamelogic/Pair.java +++ b/buildSrc/src/main/java/chess/Pair.java @@ -16,7 +16,7 @@ along with this program. If not, see . */ -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 { diff --git a/buildSrc/src/main/java/chess/Parameters.java b/buildSrc/src/main/java/chess/Parameters.java deleted file mode 100644 index 9df818b..0000000 --- a/buildSrc/src/main/java/chess/Parameters.java +++ /dev/null @@ -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 parNames = new ArrayList<>(); - for (Map.Entry 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 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; - } - } - } -} diff --git a/buildSrc/src/main/java/org/petero/droidfish/gamelogic/PgnToken.java b/buildSrc/src/main/java/chess/PgnToken.java similarity index 98% rename from buildSrc/src/main/java/org/petero/droidfish/gamelogic/PgnToken.java rename to buildSrc/src/main/java/chess/PgnToken.java index a34333a..dc4054f 100644 --- a/buildSrc/src/main/java/org/petero/droidfish/gamelogic/PgnToken.java +++ b/buildSrc/src/main/java/chess/PgnToken.java @@ -16,7 +16,7 @@ along with this program. If not, see . */ -package org.petero.droidfish.gamelogic; +package chess; /** A token in a PGN data stream. Used by the PGN parser. */ public class PgnToken { diff --git a/buildSrc/src/main/java/chess/Position.java b/buildSrc/src/main/java/chess/Position.java index fc8468d..ef3aeda 100644 --- a/buildSrc/src/main/java/chess/Position.java +++ b/buildSrc/src/main/java/chess/Position.java @@ -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"; - } } diff --git a/buildSrc/src/main/java/chess/TextIO.java b/buildSrc/src/main/java/chess/TextIO.java index ccffc8c..f4677ac 100644 --- a/buildSrc/src/main/java/chess/TextIO.java +++ b/buildSrc/src/main/java/chess/TextIO.java @@ -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,19 +18,24 @@ 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 int row = 7; int col = 0; @@ -48,23 +53,31 @@ public class TextIO { 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("Invalid piece"); + 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("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 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,22 +211,21 @@ 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); } - + /** Return a FEN string corresponding to a chess Position object. */ public static String toFEN(Position pos) { StringBuilder ret = new StringBuilder(); @@ -241,7 +288,7 @@ public class TextIO { if (!anyCastle) { ret.append('-'); } - + // En passant target square { ret.append(' '); @@ -263,27 +310,28 @@ public class TextIO { 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 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. */ 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 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"); + ret.append("O-O"); } else if (move.to == Position.getSquare(2, 0)) { ret.append("O-O-O"); } @@ -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) { + pos.makeMove(move, ui); + boolean givesCheck = MoveGen.inCheck(pos); + if (givesCheck) { + ArrayList 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 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 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 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()); + } } diff --git a/buildSrc/src/main/java/org/petero/droidfish/gamelogic/TimeControl.java b/buildSrc/src/main/java/chess/TimeControl.java similarity index 98% rename from buildSrc/src/main/java/org/petero/droidfish/gamelogic/TimeControl.java rename to buildSrc/src/main/java/chess/TimeControl.java index 2bfc1a5..4a89c16 100644 --- a/buildSrc/src/main/java/org/petero/droidfish/gamelogic/TimeControl.java +++ b/buildSrc/src/main/java/chess/TimeControl.java @@ -16,14 +16,14 @@ along with this program. If not, see . */ -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 { diff --git a/buildSrc/src/main/java/org/petero/droidfish/gamelogic/TimeControlData.java b/buildSrc/src/main/java/chess/TimeControlData.java similarity index 99% rename from buildSrc/src/main/java/org/petero/droidfish/gamelogic/TimeControlData.java rename to buildSrc/src/main/java/chess/TimeControlData.java index 42ca42b..9092cbe 100644 --- a/buildSrc/src/main/java/org/petero/droidfish/gamelogic/TimeControlData.java +++ b/buildSrc/src/main/java/chess/TimeControlData.java @@ -16,7 +16,7 @@ along with this program. If not, see . */ -package org.petero.droidfish.gamelogic; +package chess; import java.io.DataInputStream; import java.io.DataOutputStream; diff --git a/buildSrc/src/main/java/org/petero/droidfish/buildtools/eco.pgn b/buildSrc/src/main/java/chess/eco.pgn similarity index 100% rename from buildSrc/src/main/java/org/petero/droidfish/buildtools/eco.pgn rename to buildSrc/src/main/java/chess/eco.pgn diff --git a/buildSrc/src/main/java/org/petero/droidfish/gamelogic/ChessParseError.java b/buildSrc/src/main/java/org/petero/droidfish/gamelogic/ChessParseError.java deleted file mode 100644 index e65a019..0000000 --- a/buildSrc/src/main/java/org/petero/droidfish/gamelogic/ChessParseError.java +++ /dev/null @@ -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 . -*/ - -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; - } -} diff --git a/buildSrc/src/main/java/org/petero/droidfish/gamelogic/Move.java b/buildSrc/src/main/java/org/petero/droidfish/gamelogic/Move.java deleted file mode 100644 index 3cb93e2..0000000 --- a/buildSrc/src/main/java/org/petero/droidfish/gamelogic/Move.java +++ /dev/null @@ -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 . -*/ - -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); - } -} diff --git a/buildSrc/src/main/java/org/petero/droidfish/gamelogic/MoveGen.java b/buildSrc/src/main/java/org/petero/droidfish/gamelogic/MoveGen.java deleted file mode 100644 index ef509f2..0000000 --- a/buildSrc/src/main/java/org/petero/droidfish/gamelogic/MoveGen.java +++ /dev/null @@ -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 . -*/ - -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 legalMoves(Position pos) { - ArrayList 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 pseudoLegalMoves(Position pos) { - ArrayList 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 removeIllegal(Position pos, ArrayList moveList) { - ArrayList 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 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 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); - } -} diff --git a/buildSrc/src/main/java/org/petero/droidfish/gamelogic/Piece.java b/buildSrc/src/main/java/org/petero/droidfish/gamelogic/Piece.java deleted file mode 100644 index 20b77f7..0000000 --- a/buildSrc/src/main/java/org/petero/droidfish/gamelogic/Piece.java +++ /dev/null @@ -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 . -*/ - -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); - } -} diff --git a/buildSrc/src/main/java/org/petero/droidfish/gamelogic/Position.java b/buildSrc/src/main/java/org/petero/droidfish/gamelogic/Position.java deleted file mode 100644 index d4d5c77..0000000 --- a/buildSrc/src/main/java/org/petero/droidfish/gamelogic/Position.java +++ /dev/null @@ -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 . -*/ - -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); - } -} diff --git a/buildSrc/src/main/java/org/petero/droidfish/gamelogic/TextIO.java b/buildSrc/src/main/java/org/petero/droidfish/gamelogic/TextIO.java deleted file mode 100644 index fbb52c0..0000000 --- a/buildSrc/src/main/java/org/petero/droidfish/gamelogic/TextIO.java +++ /dev/null @@ -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 . -*/ - -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 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 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 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 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 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 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()); - } -} diff --git a/buildSrc/src/main/java/org/petero/droidfish/gamelogic/UndoInfo.java b/buildSrc/src/main/java/org/petero/droidfish/gamelogic/UndoInfo.java deleted file mode 100644 index fe3aefe..0000000 --- a/buildSrc/src/main/java/org/petero/droidfish/gamelogic/UndoInfo.java +++ /dev/null @@ -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 . -*/ - -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; -}