diff --git a/README.md b/README.md index 03a8431..046fcc3 100644 --- a/README.md +++ b/README.md @@ -2,20 +2,20 @@ A chessengine build in Java that uses the UCI protocol to communicate with graphical interfaces. Should be used with a 64 bit JRE for optimal performance. -The binaries are build using Java 10 and are not compatible with older Java versions. -Score is about 3025 elo (CCRL 40/4). +The binaries are build using Java 11 and are not compatible with older Java versions. +Score is about 3100 elo (CCRL 40/4). ## Features - (magic) bitboards - transposition tables - (internal) iterative-deepening -- killer-moves and history-heuristics for move ordering +- killer/counter-moves and history-heuristics for move ordering - principal variation search - (static) null move pruning - razoring - late move reductions and pruning - futility pruning -- static exchange evaluation for move ordering and pruning +- static exchange evaluation pruning - aspiration window - evaluation parameters tuned using the Texel's tuning method - tapered eval @@ -25,7 +25,9 @@ Score is about 3025 elo (CCRL 40/4). ## Future +- improved SMP - singular extensions +- syzygy - improved king safety - ... diff --git a/logo.png b/logo.png new file mode 100644 index 0000000..bb6e2fe Binary files /dev/null and b/logo.png differ diff --git a/pom.xml b/pom.xml index 69824c4..95b10ae 100644 --- a/pom.xml +++ b/pom.xml @@ -8,7 +8,7 @@ chess22k - 1.11 + 1.12 UTF-8 @@ -42,14 +42,13 @@ maven-compiler-plugin 3.8.0 - 1.10 - 1.10 + 11 org.openclover clover-maven-plugin - 4.2.1 + 4.3.1 @@ -63,7 +62,7 @@ org.openclover clover-maven-plugin - 4.2.1 + 4.3.1 diff --git a/release-notes.txt b/release-notes.txt index 8b316f0..9891b76 100644 --- a/release-notes.txt +++ b/release-notes.txt @@ -1,17 +1,28 @@ -1.11 - 05-09-2018 - 3025 elo +1.12 - 03-12-2018 - 3100 elo +- participated in CSVN 54 +- rewritten SMP implementation (inspired by Laser, performs worse than 1.11 at the moment...) +- added some endgame knowledge +- implemented counter move heuristic +- improved LMR reductions calculation (inspired by Ethereal) + + +1.11 - 05-09-2018 - 3030 elo +- participated in TCEC S14 - implemented pondering - improved SMP - SEE pruning -- split-up several evaluation terms in midgame and endgame +- split-up more evaluation terms in midgame and endgame - better space evaluation (idea by Laser) 1.10 - 09-07-2018 - 3000 elo +- participated in TCEC S13 - lazy SMP - several simplifications and small updates -1.9 - 23-04-2018 - 3000 elo +1.9 - 23-04-2018 - 2970 elo +- participated in CSVN 53 - fixed some crashes - improved sorting efficiency - calculation instead of lookups for pawn move generation @@ -56,7 +67,8 @@ - split-up methods into smaller ones for better inlining (suggested by JITWatch) -1.6 - 03-11-2017 - 2800 elo +1.6 - 03-11-2017 - 2850 elo +- participated CSVN 52 - more accurate SEE (pinned pieces) - improved king safety - added some eval terms @@ -69,7 +81,7 @@ - fixed some crashes -1.5 - 12-08-2017 - 2700 elo +1.5 - 12-08-2017 - 2750 elo - implemented tapered-eval - recognize drawish positions - added more evaluation terms to the texel-tuner @@ -82,10 +94,9 @@ - safe mobility for knights and rooks - small king-safety updates - small passed-pawn updates - - ... -1.4 - 18-06-2017 - 2600 elo +1.4 - 18-06-2017 - 2650 elo - tuned evaluation values using the Texel's tuning method - implemented futility pruning - small tweaks @@ -102,7 +113,7 @@ - fixed some engine crashes -1.2 - 19-03-2017 - 2400 elo, this time for real? :P +1.2 - 19-03-2017 - 2400 elo - improved passed-pawn scoring in endgames - added time to UCI output - improved time control: x moves in y minutes @@ -112,7 +123,7 @@ - bugs, again ;) -1.1 - 09-02-2017 - 2400 elo +1.1 - 09-02-2017 - 2300 elo - re-enabled mobility evaluation - knight outposts - implemented king safety (using Ed Schroders idea), but disabled for the moment @@ -120,7 +131,7 @@ - bug fixes, of course -1.0 - 13-01-2017 - 2300 elo +1.0 - 13-01-2017 - 2200 elo - (magic) bitboards - transposition table - (internal) iterative-deepening diff --git a/src/main/java/nl/s22k/chess/Bitboard.java b/src/main/java/nl/s22k/chess/Bitboard.java index a3c6208..0b3122b 100644 --- a/src/main/java/nl/s22k/chess/Bitboard.java +++ b/src/main/java/nl/s22k/chess/Bitboard.java @@ -107,18 +107,16 @@ public class Bitboard { public static final long F8_G8 = F8 | G8; public static final long F8_H8 = F8 | H8; public static final long G8_H8 = G8 | H8; - public static final long A1_B1_C1 = A1 | B1 | C1; - public static final long B1_C1_D1 = B1 | C1 | D1; - public static final long F1_G1_H1 = F1 | G1 | H1; - public static final long A8_B8_C8 = A8 | B8 | C8; - public static final long B8_C8_D8 = B8 | C8 | D8; - public static final long F8_G8_H8 = F8 | G8 | H8; - public static final long A1_B1_A2_B2 = A1 | B1 | A2 | B2; - public static final long D1_E1_D2_E2 = D1 | E1 | D2 | E2; - public static final long G1_H1_G2_H2 = G1 | H1 | G2 | H2; - public static final long D7_E7_D8_E8 = D7 | E7 | D8 | E8; - public static final long A7_B7_A8_B8 = A7 | B7 | A8 | B8; - public static final long G7_H7_G8_H8 = G7 | H7 | G8 | H8; + public static final long A1B1C1 = A1 | B1 | C1; + public static final long B1C1D1 = B1 | C1 | D1; + public static final long A8B8C8 = A8 | B8 | C8; + public static final long B8C8D8 = B8 | C8 | D8; + public static final long A1B1A2B2 = A1 | B1 | A2 | B2; + public static final long D1E1D2E2 = D1 | E1 | D2 | E2; + public static final long G1H1G2H2 = G1 | H1 | G2 | H2; + public static final long D7E7D8E8 = D7 | E7 | D8 | E8; + public static final long A7B7A8B8 = A7 | B7 | A8 | B8; + public static final long G7H7G8H8 = G7 | H7 | G8 | H8; public static final long WHITE_SQUARES = 0xaa55aa55aa55aa55L; public static final long BLACK_SQUARES = ~WHITE_SQUARES; public static final long CORNER_SQUARES = A1 | H1 | A8 | H8; @@ -153,14 +151,26 @@ public class Bitboard { public static final long FILE_F = F1 | F2 | F3 | F4 | F5 | F6 | F7 | F8; public static final long FILE_G = G1 | G2 | G3 | G4 | G5 | G6 | G7 | G8; public static final long FILE_H = H1 | H2 | H3 | H4 | H5 | H6 | H7 | H8; - public static final long FILE_D_E = FILE_D | FILE_E; - public static final long FILE_CDEF = FILE_C | FILE_D| FILE_E | FILE_F; + public static final long FILE_ABC = FILE_A | FILE_B | FILE_C; + public static final long FILE_FGH = FILE_F | FILE_G | FILE_H; + public static final long FILE_CDEF = FILE_C | FILE_D | FILE_E | FILE_F; public static final long NOT_FILE_A = ~FILE_A; public static final long NOT_FILE_H = ~FILE_H; + // special + public static final long WHITE_CORNERS = 0xf8f0e0c183070f1fL; + public static final long BLACK_CORNERS = 0x1f0f0783c1e0f0f8L; + public static final long RANKS[] = { RANK_1, RANK_2, RANK_3, RANK_4, RANK_5, RANK_6, RANK_7, RANK_8 }; public static final long FILES[] = { FILE_H, FILE_G, FILE_F, FILE_E, FILE_D, FILE_C, FILE_B, FILE_A }; - public static final long FILES_ADJACENT[] = { FILE_G, FILE_H | FILE_F, FILE_G | FILE_E, FILE_F | FILE_D, FILE_E | FILE_C, FILE_D | FILE_B, FILE_C | FILE_A, + public static final long FILES_ADJACENT[] = { // + FILE_G, // + FILE_H | FILE_F, // + FILE_G | FILE_E, // + FILE_F | FILE_D, // + FILE_E | FILE_C, // + FILE_D | FILE_B, // + FILE_C | FILE_A, // FILE_B }; public static final long KING_SIDE = FILE_F | FILE_G | FILE_H; @@ -195,4 +205,23 @@ public static int manhattanCenterDistance(int sq) { return (file + rank) & 7; } + public static long getWhitePassedPawnMask(final int index) { + return (FILES[index & 7] | FILES_ADJACENT[index & 7]) << ((index >>> 3 << 3) + 8); + } + + public static long getBlackPassedPawnMask(final int index) { + if (index < 8) { + return 0; + } + return (FILES[index & 7] | FILES_ADJACENT[index & 7]) >>> ((71 - index) >>> 3 << 3); + } + + public static long getWhiteAdjacentMask(final int index) { + return getWhitePassedPawnMask(index) & ~FILES[index & 7]; + } + + public static long getBlackAdjacentMask(final int index) { + return getBlackPassedPawnMask(index) & ~FILES[index & 7]; + } + } diff --git a/src/main/java/nl/s22k/chess/CastlingUtil.java b/src/main/java/nl/s22k/chess/CastlingUtil.java index 04b6685..7435547 100644 --- a/src/main/java/nl/s22k/chess/CastlingUtil.java +++ b/src/main/java/nl/s22k/chess/CastlingUtil.java @@ -12,6 +12,9 @@ public final class CastlingUtil { // 4 bits: white-king,white-queen,black-king,black-queen public static long getCastlingIndexes(final ChessBoard cb) { + if (cb.castlingRights == 0) { + return 0; + } if (cb.colorToMove == WHITE) { switch (cb.castlingRights) { case 0: @@ -91,16 +94,16 @@ public static long getRookInBetweenIndex(final int castlingIndex) { case 1: return Bitboard.F1_G1; case 5: - return Bitboard.B1_C1_D1; + return Bitboard.B1C1D1; case 57: return Bitboard.F8_G8; case 61: - return Bitboard.B8_C8_D8; + return Bitboard.B8C8D8; } throw new RuntimeException("Incorrect castling-index: " + castlingIndex); } - public static void uncastleRook(final ChessBoard cb, final int kingToIndex) { + public static void uncastleRookUpdatePsqt(final ChessBoard cb, final int kingToIndex) { switch (kingToIndex) { case 1: // white rook from 2 to 0 @@ -108,6 +111,7 @@ public static void uncastleRook(final ChessBoard cb, final int kingToIndex) { cb.friendlyPieces[cb.colorToMoveInverse] ^= Bitboard.F1_H1; cb.pieceIndexes[2] = EMPTY; cb.pieceIndexes[0] = ROOK; + cb.psqtScore += EvalConstants.PSQT[ROOK][cb.colorToMoveInverse][0] - EvalConstants.PSQT[ROOK][cb.colorToMoveInverse][2]; return; case 57: // black rook from 58 to 56 @@ -115,6 +119,7 @@ public static void uncastleRook(final ChessBoard cb, final int kingToIndex) { cb.friendlyPieces[cb.colorToMoveInverse] ^= Bitboard.F8_H8; cb.pieceIndexes[58] = EMPTY; cb.pieceIndexes[56] = ROOK; + cb.psqtScore += EvalConstants.PSQT[ROOK][cb.colorToMoveInverse][56] - EvalConstants.PSQT[ROOK][cb.colorToMoveInverse][58]; return; case 5: // white rook from 4 to 7 @@ -122,6 +127,7 @@ public static void uncastleRook(final ChessBoard cb, final int kingToIndex) { cb.friendlyPieces[cb.colorToMoveInverse] ^= Bitboard.A1_D1; cb.pieceIndexes[4] = EMPTY; cb.pieceIndexes[7] = ROOK; + cb.psqtScore += EvalConstants.PSQT[ROOK][cb.colorToMoveInverse][7] - EvalConstants.PSQT[ROOK][cb.colorToMoveInverse][4]; return; case 61: // black rook from 60 to 63 @@ -129,6 +135,7 @@ public static void uncastleRook(final ChessBoard cb, final int kingToIndex) { cb.friendlyPieces[cb.colorToMoveInverse] ^= Bitboard.A8_D8; cb.pieceIndexes[60] = EMPTY; cb.pieceIndexes[63] = ROOK; + cb.psqtScore += EvalConstants.PSQT[ROOK][cb.colorToMoveInverse][63] - EvalConstants.PSQT[ROOK][cb.colorToMoveInverse][60]; return; } throw new RuntimeException("Incorrect king castling to-index: " + kingToIndex); @@ -142,7 +149,7 @@ public static void castleRookUpdateKeyAndPsqt(final ChessBoard cb, final int kin cb.friendlyPieces[cb.colorToMove] ^= Bitboard.F1_H1; cb.pieceIndexes[0] = EMPTY; cb.pieceIndexes[2] = ROOK; - cb.zobristKey ^= ChessBoard.zkPieceValues[0][WHITE][ROOK] ^ ChessBoard.zkPieceValues[2][WHITE][ROOK]; + cb.zobristKey ^= Zobrist.piece[0][WHITE][ROOK] ^ Zobrist.piece[2][WHITE][ROOK]; cb.psqtScore += EvalConstants.PSQT[ROOK][cb.colorToMove][2] - EvalConstants.PSQT[ROOK][cb.colorToMove][0]; return; case 57: @@ -151,7 +158,7 @@ public static void castleRookUpdateKeyAndPsqt(final ChessBoard cb, final int kin cb.friendlyPieces[cb.colorToMove] ^= Bitboard.F8_H8; cb.pieceIndexes[56] = EMPTY; cb.pieceIndexes[58] = ROOK; - cb.zobristKey ^= ChessBoard.zkPieceValues[56][BLACK][ROOK] ^ ChessBoard.zkPieceValues[58][BLACK][ROOK]; + cb.zobristKey ^= Zobrist.piece[56][BLACK][ROOK] ^ Zobrist.piece[58][BLACK][ROOK]; cb.psqtScore += EvalConstants.PSQT[ROOK][cb.colorToMove][58] - EvalConstants.PSQT[ROOK][cb.colorToMove][56]; return; case 5: @@ -160,7 +167,7 @@ public static void castleRookUpdateKeyAndPsqt(final ChessBoard cb, final int kin cb.friendlyPieces[cb.colorToMove] ^= Bitboard.A1_D1; cb.pieceIndexes[7] = EMPTY; cb.pieceIndexes[4] = ROOK; - cb.zobristKey ^= ChessBoard.zkPieceValues[7][WHITE][ROOK] ^ ChessBoard.zkPieceValues[4][WHITE][ROOK]; + cb.zobristKey ^= Zobrist.piece[7][WHITE][ROOK] ^ Zobrist.piece[4][WHITE][ROOK]; cb.psqtScore += EvalConstants.PSQT[ROOK][cb.colorToMove][4] - EvalConstants.PSQT[ROOK][cb.colorToMove][7]; return; case 61: @@ -169,7 +176,7 @@ public static void castleRookUpdateKeyAndPsqt(final ChessBoard cb, final int kin cb.friendlyPieces[cb.colorToMove] ^= Bitboard.A8_D8; cb.pieceIndexes[63] = EMPTY; cb.pieceIndexes[60] = ROOK; - cb.zobristKey ^= ChessBoard.zkPieceValues[63][BLACK][ROOK] ^ ChessBoard.zkPieceValues[60][BLACK][ROOK]; + cb.zobristKey ^= Zobrist.piece[63][BLACK][ROOK] ^ Zobrist.piece[60][BLACK][ROOK]; cb.psqtScore += EvalConstants.PSQT[ROOK][cb.colorToMove][60] - EvalConstants.PSQT[ROOK][cb.colorToMove][63]; return; } diff --git a/src/main/java/nl/s22k/chess/ChessBoard.java b/src/main/java/nl/s22k/chess/ChessBoard.java index bfabd69..ba033c7 100644 --- a/src/main/java/nl/s22k/chess/ChessBoard.java +++ b/src/main/java/nl/s22k/chess/ChessBoard.java @@ -10,27 +10,52 @@ import static nl.s22k.chess.ChessConstants.ROOK; import static nl.s22k.chess.ChessConstants.WHITE; -import java.security.SecureRandom; -import java.util.Arrays; - import nl.s22k.chess.engine.EngineConstants; +import nl.s22k.chess.engine.MainEngine; import nl.s22k.chess.eval.EvalConstants; import nl.s22k.chess.eval.EvalUtil; -import nl.s22k.chess.eval.KPKBitbase; import nl.s22k.chess.eval.MaterialUtil; import nl.s22k.chess.move.MagicUtil; import nl.s22k.chess.move.MoveUtil; +import nl.s22k.chess.move.StaticMoves; public final class ChessBoard { - private static ChessBoard instance = new ChessBoard(); + private ChessBoard() { + + } + private static ChessBoard[] instances; + static { + initInstances(MainEngine.nrOfThreads); + } + + public static ChessBoard getInstance() { + return instances[0]; + } + + public static ChessBoard getInstance(int instanceNumber) { + return instances[instanceNumber]; + } - // zobrist-keys - public static long zkWhiteToMove; - public static final long[] zkCastling = new long[16]; - public static final long[] zkEPIndex = new long[48]; - public static final long[][][] zkPieceValues = new long[64][2][7]; + public static ChessBoard getTestInstance() { + return instances[1]; + } + + public static void initInstances(int numberOfInstances) { + instances = new ChessBoard[numberOfInstances]; + for (int i = 0; i < numberOfInstances; i++) { + instances[i] = new ChessBoard(); + } + } + + public static long getTotalMoveCount() { + long totalMoveCount = 0; + for (int i = 0; i < MainEngine.nrOfThreads; i++) { + totalMoveCount += getInstance(i).moveCount; + } + return totalMoveCount; + } /** color, piece */ public final long[][] pieces = new long[2][7]; @@ -56,7 +81,6 @@ public final class ChessBoard { public final long[] kingArea = new long[2]; public int moveCounter = 0; - public final int[] psqtScoreHistory = new int[EngineConstants.MAX_MOVES]; public final int[] castlingHistory = new int[EngineConstants.MAX_MOVES]; public final int[] epIndexHistory = new int[EngineConstants.MAX_MOVES]; public final long[] zobristKeyHistory = new long[EngineConstants.MAX_MOVES]; @@ -72,48 +96,6 @@ public final class ChessBoard { public long passedPawnsAndOutposts; - public static ChessBoard getInstance() { - return instance; - } - - public static ChessBoard getTestInstance() { - return new ChessBoard(); - } - - public static void initInstances(int numberOfInstances) { - instances = new ChessBoard[numberOfInstances]; - for (int i = 0; i < numberOfInstances; i++) { - instances[i] = new ChessBoard(); - } - } - - public static ChessBoard getInstance(int instanceNumber) { - return instances[instanceNumber]; - } - - /** - * Initialize the zobrist-keys - */ - static { - SecureRandom r = new SecureRandom(); - for (int bitIndex = 0; bitIndex < 64; bitIndex++) { - for (int colorIndex = 0; colorIndex < zkPieceValues[0].length; colorIndex++) { - for (int pieceIndex = 0; pieceIndex < zkPieceValues[0][0].length; pieceIndex++) { - zkPieceValues[bitIndex][colorIndex][pieceIndex] = r.nextLong(); - } - } - } - for (int i = 0; i < zkCastling.length; i++) { - zkCastling[i] = r.nextLong(); - } - - // skip first item: contains only zeros, default value and has no effect when xorring - for (int i = 1; i < zkEPIndex.length; i++) { - zkEPIndex[i] = r.nextLong(); - } - zkWhiteToMove = r.nextLong(); - } - @Override public String toString() { return ChessBoardUtil.toString(this); @@ -125,93 +107,10 @@ public boolean isDrawishByMaterial(final int color) { return false; } - // only nights? - if (MaterialUtil.hasOnlyNights(materialKey, color)) { - return true; - } - // material difference bigger than bishop + 50 // TODO do not include pawn score (why...?) - return EvalUtil.getImbalances(this) * ChessConstants.COLOR_FACTOR[color] < EvalConstants.MATERIAL[BISHOP] + 50; - } - - public boolean isDrawByMaterial(final int color) { - if (Long.bitCount(friendlyPieces[color]) > 2) { - return false; - } - if (Long.bitCount(friendlyPieces[color]) == 1) { - // K - return true; - } else { - // KN or KB - if (pieces[color][NIGHT] != 0 || pieces[color][BISHOP] != 0) { - return true; - } - // KP, KR or KQ - return false; - } - } - - public boolean isDrawByMaterial() { - - boolean isDraw = false; - switch (Long.bitCount(allPieces)) { - case 2: - // KK - isDraw = true; - break; - case 3: - if (pieces[WHITE][PAWN] != 0 || pieces[BLACK][PAWN] != 0) { - // KPK - isDraw = KPKBitbase.isDraw(this); - } else { - // KKB or KKN? - isDraw = pieces[WHITE][BISHOP] != 0 || pieces[BLACK][BISHOP] != 0 || pieces[WHITE][NIGHT] != 0 || pieces[BLACK][NIGHT] != 0; - } - break; - case 4: - isDraw = isDrawByMtrl4Pieces(); - } - - if (Statistics.ENABLED && isDraw) { - Statistics.drawByMaterialCount++; - } - return isDraw; - - } - - private boolean isDrawByMtrl4Pieces() { - if (Long.bitCount(pieces[WHITE][NIGHT]) + Long.bitCount(pieces[BLACK][NIGHT]) == 2) { - // KNNK or KNKN - return true; - } - switch (Long.bitCount(pieces[WHITE][BISHOP] | pieces[BLACK][BISHOP])) { - case 1: - if (Long.bitCount(pieces[WHITE][PAWN] | pieces[WHITE][BISHOP]) == 2) { - if ((pieces[WHITE][PAWN] & Bitboard.FILE_A) != 0 && (Bitboard.WHITE_SQUARES & pieces[WHITE][BISHOP]) == 0) { - return (pieces[BLACK][KING] & Bitboard.A8) != 0; - } - if ((pieces[WHITE][PAWN] & Bitboard.FILE_H) != 0 && (Bitboard.BLACK_SQUARES & pieces[WHITE][BISHOP]) == 0) { - return (pieces[BLACK][KING] & Bitboard.H8) != 0; - } - } else if (Long.bitCount(pieces[BLACK][PAWN] | pieces[BLACK][BISHOP]) == 2) { - if ((pieces[BLACK][PAWN] & Bitboard.FILE_A) != 0 && (Bitboard.BLACK_SQUARES & pieces[BLACK][BISHOP]) == 0) { - return (pieces[WHITE][KING] & Bitboard.A1) != 0; - } - if ((pieces[BLACK][PAWN] & Bitboard.FILE_H) != 0 && (Bitboard.WHITE_SQUARES & pieces[BLACK][BISHOP]) == 0) { - return (pieces[WHITE][KING] & Bitboard.H1) != 0; - } - } else { - // KBKN or KNKB? - return (Long.bitCount(pieces[WHITE][NIGHT] | pieces[BLACK][BISHOP]) == 2 || Long.bitCount(pieces[BLACK][NIGHT] | pieces[WHITE][BISHOP]) == 2); - } - break; - case 2: - // KBKB? - return Long.bitCount(pieces[WHITE][BISHOP]) == 1; - } - - return false; + return EvalUtil.getImbalances(this) * ChessConstants.COLOR_FACTOR[color] < EvalConstants.MATERIAL[BISHOP] + + EvalConstants.OTHER_SCORES[EvalConstants.IX_DRAWISH]; } public void changeSideToMove() { @@ -220,11 +119,10 @@ public void changeSideToMove() { } public boolean isDiscoveredMove(final int fromIndex) { - return (discoveredPieces & Util.POWER_LOOKUP[fromIndex]) != 0; + return (discoveredPieces & (1L << fromIndex)) != 0; } private void pushHistoryValues() { - psqtScoreHistory[moveCounter] = psqtScore; castlingHistory[moveCounter] = castlingRights; epIndexHistory[moveCounter] = epIndex; zobristKeyHistory[moveCounter] = zobristKey; @@ -238,7 +136,6 @@ private void popHistoryValues() { moveCounter--; epIndex = epIndexHistory[moveCounter]; zobristKey = zobristKeyHistory[moveCounter]; - psqtScore = psqtScoreHistory[moveCounter]; castlingRights = castlingHistory[moveCounter]; pinnedPieces = pinnedPiecesHistory[moveCounter]; discoveredPieces = discoveredPiecesHistory[moveCounter]; @@ -248,8 +145,11 @@ private void popHistoryValues() { public void doNullMove() { pushHistoryValues(); - zobristKey ^= zkEPIndex[epIndex] ^ zkWhiteToMove; - epIndex = 0; + zobristKey ^= Zobrist.sideToMove; + if (epIndex != 0) { + zobristKey ^= Zobrist.epIndex[epIndex]; + epIndex = 0; + } changeSideToMove(); if (EngineConstants.ASSERT) { @@ -271,10 +171,9 @@ public void doMove(int move) { moveCount++; final int fromIndex = MoveUtil.getFromIndex(move); - final long fromMask = Util.POWER_LOOKUP[fromIndex]; int toIndex = MoveUtil.getToIndex(move); - long toMask = Util.POWER_LOOKUP[toIndex]; - final long fromToMask = fromMask ^ toMask; + long toMask = 1L << toIndex; + final long fromToMask = (1L << fromIndex) ^ toMask; final int sourcePieceIndex = MoveUtil.getSourcePieceIndex(move); final int attackedPieceIndex = MoveUtil.getAttackedPieceIndex(move); @@ -285,9 +184,11 @@ public void doMove(int move) { pushHistoryValues(); - zobristKey ^= zkEPIndex[epIndex] ^ zkPieceValues[fromIndex][colorToMove][sourcePieceIndex] ^ zkPieceValues[toIndex][colorToMove][sourcePieceIndex] - ^ zkWhiteToMove; - epIndex = 0; + zobristKey ^= Zobrist.piece[fromIndex][colorToMove][sourcePieceIndex] ^ Zobrist.piece[toIndex][colorToMove][sourcePieceIndex] ^ Zobrist.sideToMove; + if (epIndex != 0) { + zobristKey ^= Zobrist.epIndex[epIndex]; + epIndex = 0; + } friendlyPieces[colorToMove] ^= fromToMask; pieceIndexes[fromIndex] = EMPTY; @@ -297,30 +198,33 @@ public void doMove(int move) { switch (sourcePieceIndex) { case PAWN: - pawnZobristKey ^= zkPieceValues[fromIndex][colorToMove][PAWN]; + pawnZobristKey ^= Zobrist.piece[fromIndex][colorToMove][PAWN]; if (MoveUtil.isPromotion(move)) { phase -= EvalConstants.PHASE[MoveUtil.getMoveType(move)]; materialKey += MaterialUtil.VALUES[colorToMove][MoveUtil.getMoveType(move)] - MaterialUtil.VALUES[colorToMove][PAWN]; pieces[colorToMove][PAWN] ^= toMask; pieces[colorToMove][MoveUtil.getMoveType(move)] |= toMask; pieceIndexes[toIndex] = MoveUtil.getMoveType(move); - zobristKey ^= zkPieceValues[toIndex][colorToMove][PAWN] ^ zkPieceValues[toIndex][colorToMove][MoveUtil.getMoveType(move)]; + zobristKey ^= Zobrist.piece[toIndex][colorToMove][PAWN] ^ Zobrist.piece[toIndex][colorToMove][MoveUtil.getMoveType(move)]; psqtScore += EvalConstants.PSQT[MoveUtil.getMoveType(move)][colorToMove][toIndex] - EvalConstants.PSQT[PAWN][colorToMove][toIndex]; } else { - pawnZobristKey ^= zkPieceValues[toIndex][colorToMove][PAWN]; - // check 2-move + pawnZobristKey ^= Zobrist.piece[toIndex][colorToMove][PAWN]; + // 2-move if (ChessConstants.IN_BETWEEN[fromIndex][toIndex] != 0) { - epIndex = Long.numberOfTrailingZeros(ChessConstants.IN_BETWEEN[fromIndex][toIndex]); - zobristKey ^= zkEPIndex[epIndex]; + if ((StaticMoves.PAWN_ATTACKS[colorToMove][Long.numberOfTrailingZeros(ChessConstants.IN_BETWEEN[fromIndex][toIndex])] + & pieces[colorToMoveInverse][PAWN]) != 0) { + epIndex = Long.numberOfTrailingZeros(ChessConstants.IN_BETWEEN[fromIndex][toIndex]); + zobristKey ^= Zobrist.epIndex[epIndex]; + } } } break; case ROOK: if (castlingRights != 0) { - zobristKey ^= zkCastling[castlingRights]; + zobristKey ^= Zobrist.castling[castlingRights]; castlingRights = CastlingUtil.getRookMovedOrAttackedCastlingRights(castlingRights, fromIndex); - zobristKey ^= zkCastling[castlingRights]; + zobristKey ^= Zobrist.castling[castlingRights]; } break; @@ -330,9 +234,9 @@ public void doMove(int move) { if (MoveUtil.isCastlingMove(move)) { CastlingUtil.castleRookUpdateKeyAndPsqt(this, toIndex); } - zobristKey ^= zkCastling[castlingRights]; + zobristKey ^= Zobrist.castling[castlingRights]; castlingRights = CastlingUtil.getKingMovedCastlingRights(castlingRights, fromIndex); - zobristKey ^= zkCastling[castlingRights]; + zobristKey ^= Zobrist.castling[castlingRights]; } } @@ -346,25 +250,26 @@ public void doMove(int move) { toMask = Util.POWER_LOOKUP[toIndex]; pieceIndexes[toIndex] = EMPTY; } - pawnZobristKey ^= zkPieceValues[toIndex][colorToMoveInverse][PAWN]; - psqtScore -= EvalConstants.PSQT[attackedPieceIndex][colorToMoveInverse][toIndex]; + pawnZobristKey ^= Zobrist.piece[toIndex][colorToMoveInverse][PAWN]; + psqtScore -= EvalConstants.PSQT[PAWN][colorToMoveInverse][toIndex]; friendlyPieces[colorToMoveInverse] ^= toMask; - pieces[colorToMoveInverse][attackedPieceIndex] ^= toMask; - zobristKey ^= zkPieceValues[toIndex][colorToMoveInverse][attackedPieceIndex]; + pieces[colorToMoveInverse][PAWN] ^= toMask; + zobristKey ^= Zobrist.piece[toIndex][colorToMoveInverse][PAWN]; materialKey -= MaterialUtil.VALUES[colorToMoveInverse][PAWN]; break; case ROOK: if (castlingRights != 0) { - zobristKey ^= zkCastling[castlingRights]; + zobristKey ^= Zobrist.castling[castlingRights]; castlingRights = CastlingUtil.getRookMovedOrAttackedCastlingRights(castlingRights, toIndex); - zobristKey ^= zkCastling[castlingRights]; + zobristKey ^= Zobrist.castling[castlingRights]; } + // fall-through default: phase += EvalConstants.PHASE[attackedPieceIndex]; psqtScore -= EvalConstants.PSQT[attackedPieceIndex][colorToMoveInverse][toIndex]; friendlyPieces[colorToMoveInverse] ^= toMask; pieces[colorToMoveInverse][attackedPieceIndex] ^= toMask; - zobristKey ^= zkPieceValues[toIndex][colorToMoveInverse][attackedPieceIndex]; + zobristKey ^= Zobrist.piece[toIndex][colorToMoveInverse][attackedPieceIndex]; materialKey -= MaterialUtil.VALUES[colorToMoveInverse][attackedPieceIndex]; } @@ -405,8 +310,8 @@ public void setPinnedAndDiscoPieces() { continue; } - long enemyPiece = (pieces[enemyColor][BISHOP] | pieces[enemyColor][QUEEN]) & MagicUtil.bishopMovesEmptyBoard[kingIndex[kingColor]] - | (pieces[enemyColor][ROOK] | pieces[enemyColor][QUEEN]) & MagicUtil.rookMovesEmptyBoard[kingIndex[kingColor]]; + long enemyPiece = (pieces[enemyColor][BISHOP] | pieces[enemyColor][QUEEN]) & MagicUtil.getBishopMovesEmptyBoard(kingIndex[kingColor]) + | (pieces[enemyColor][ROOK] | pieces[enemyColor][QUEEN]) & MagicUtil.getRookMovesEmptyBoard(kingIndex[kingColor]); while (enemyPiece != 0) { final long checkedPiece = ChessConstants.IN_BETWEEN[kingIndex[kingColor]][Long.numberOfTrailingZeros(enemyPiece)] & allPieces; if (Long.bitCount(checkedPiece) == 1) { @@ -423,8 +328,8 @@ public void undoMove(int move) { final int fromIndex = MoveUtil.getFromIndex(move); int toIndex = MoveUtil.getToIndex(move); - long toMask = Util.POWER_LOOKUP[toIndex]; - final long fromToMask = Util.POWER_LOOKUP[fromIndex] ^ toMask; + long toMask = 1L << toIndex; + final long fromToMask = (1L << fromIndex) ^ toMask; final int sourcePieceIndex = MoveUtil.getSourcePieceIndex(move); final int attackedPieceIndex = MoveUtil.getAttackedPieceIndex(move); @@ -434,25 +339,28 @@ public void undoMove(int move) { friendlyPieces[colorToMoveInverse] ^= fromToMask; pieceIndexes[fromIndex] = sourcePieceIndex; pieces[colorToMoveInverse][sourcePieceIndex] ^= fromToMask; + psqtScore += EvalConstants.PSQT[sourcePieceIndex][colorToMoveInverse][fromIndex] - EvalConstants.PSQT[sourcePieceIndex][colorToMoveInverse][toIndex]; switch (sourcePieceIndex) { case EMPTY: // not necessary but provides a table-index break; case PAWN: - pawnZobristKey ^= zkPieceValues[fromIndex][colorToMoveInverse][PAWN]; + pawnZobristKey ^= Zobrist.piece[fromIndex][colorToMoveInverse][PAWN]; if (MoveUtil.isPromotion(move)) { phase += EvalConstants.PHASE[MoveUtil.getMoveType(move)]; materialKey -= MaterialUtil.VALUES[colorToMoveInverse][MoveUtil.getMoveType(move)] - MaterialUtil.VALUES[colorToMoveInverse][PAWN]; pieces[colorToMoveInverse][PAWN] ^= toMask; pieces[colorToMoveInverse][MoveUtil.getMoveType(move)] ^= toMask; + psqtScore += EvalConstants.PSQT[PAWN][colorToMoveInverse][toIndex] + - EvalConstants.PSQT[MoveUtil.getMoveType(move)][colorToMoveInverse][toIndex]; } else { - pawnZobristKey ^= zkPieceValues[toIndex][colorToMoveInverse][PAWN]; + pawnZobristKey ^= Zobrist.piece[toIndex][colorToMoveInverse][PAWN]; } break; case KING: if (MoveUtil.isCastlingMove(move)) { - CastlingUtil.uncastleRook(this, toIndex); + CastlingUtil.uncastleRookUpdatePsqt(this, toIndex); } updateKingValues(colorToMoveInverse, fromIndex); } @@ -467,12 +375,14 @@ public void undoMove(int move) { toIndex += ChessConstants.COLOR_FACTOR_8[colorToMove]; toMask = Util.POWER_LOOKUP[toIndex]; } - pawnZobristKey ^= zkPieceValues[toIndex][colorToMove][PAWN]; + psqtScore += EvalConstants.PSQT[PAWN][colorToMove][toIndex]; + pawnZobristKey ^= Zobrist.piece[toIndex][colorToMove][PAWN]; pieces[colorToMove][attackedPieceIndex] |= toMask; friendlyPieces[colorToMove] |= toMask; materialKey += MaterialUtil.VALUES[colorToMove][PAWN]; break; default: + psqtScore += EvalConstants.PSQT[attackedPieceIndex][colorToMove][toIndex]; phase -= EvalConstants.PHASE[attackedPieceIndex]; materialKey += MaterialUtil.VALUES[colorToMove][attackedPieceIndex]; pieces[colorToMove][attackedPieceIndex] |= toMask; @@ -538,15 +448,15 @@ private boolean isLegalEPMove(final int fromIndex) { } - public boolean isValidQuietMove(int move) { + public boolean isValidQuietMove(final int move) { - // check piece at from-position + // check piece at from square final int fromIndex = MoveUtil.getFromIndex(move); if ((pieces[colorToMove][MoveUtil.getSourcePieceIndex(move)] & Util.POWER_LOOKUP[fromIndex]) == 0) { return false; } - // no piece should be at to-position + // no piece should be at to square final int toIndex = MoveUtil.getToIndex(move); if (pieceIndexes[toIndex] != EMPTY) { return false; @@ -627,15 +537,19 @@ public boolean isValidMove(int move) { return true; } - public boolean isRepetition() { + public boolean isRepetition(final int move) { if (!EngineConstants.ENABLE_REPETITION_TABLE) { return false; } + // if move was an attacking-move or pawn move, no repetition + if (!MoveUtil.isQuiet(move) || MoveUtil.getSourcePieceIndex(move) == PAWN) { + return false; + } + final int moveCounterMin = Math.max(0, moveCounter - 50); - for (int i = moveCounter - 1; i >= moveCounterMin; i--) { - // TODO if move was an attacking-move or pawn move, no repetition! + for (int i = moveCounter - 2; i >= moveCounterMin; i -= 2) { if (zobristKey == zobristKeyHistory[i]) { if (Statistics.ENABLED) { Statistics.repetitions++; @@ -646,16 +560,6 @@ public boolean isRepetition() { return false; } - public void clearHistoryValues() { - // history - Arrays.fill(psqtScoreHistory, 0); - Arrays.fill(castlingHistory, 0); - Arrays.fill(epIndexHistory, 0); - Arrays.fill(zobristKeyHistory, 0); - Arrays.fill(checkingPiecesHistory, 0); - Arrays.fill(pinnedPiecesHistory, 0); - } - public void clearEvalAttacks() { kingAttackersFlag[WHITE] = 0; kingAttackersFlag[BLACK] = 0; diff --git a/src/main/java/nl/s22k/chess/ChessBoardUtil.java b/src/main/java/nl/s22k/chess/ChessBoardUtil.java index 75504e7..b0abdea 100644 --- a/src/main/java/nl/s22k/chess/ChessBoardUtil.java +++ b/src/main/java/nl/s22k/chess/ChessBoardUtil.java @@ -23,7 +23,6 @@ public static ChessBoard getNewCB() { public static ChessBoard getNewCB(String fen) { ChessBoard cb = ChessBoard.getInstance(); - cb.clearHistoryValues(); setFenValues(fen, cb); init(cb); return cb; @@ -100,17 +99,17 @@ public static void calculateZobristKeys(ChessBoard cb) { for (int piece = PAWN; piece <= KING; piece++) { long pieces = cb.pieces[color][piece]; while (pieces != 0) { - cb.zobristKey ^= ChessBoard.zkPieceValues[Long.numberOfTrailingZeros(pieces)][color][piece]; + cb.zobristKey ^= Zobrist.piece[Long.numberOfTrailingZeros(pieces)][color][piece]; pieces &= pieces - 1; } } } - cb.zobristKey ^= ChessBoard.zkCastling[cb.castlingRights]; + cb.zobristKey ^= Zobrist.castling[cb.castlingRights]; if (cb.colorToMove == WHITE) { - cb.zobristKey ^= ChessBoard.zkWhiteToMove; + cb.zobristKey ^= Zobrist.sideToMove; } - cb.zobristKey ^= ChessBoard.zkEPIndex[cb.epIndex]; + cb.zobristKey ^= Zobrist.epIndex[cb.epIndex]; } public static void calculatePawnZobristKeys(ChessBoard cb) { @@ -118,12 +117,12 @@ public static void calculatePawnZobristKeys(ChessBoard cb) { long pieces = cb.pieces[WHITE][PAWN]; while (pieces != 0) { - cb.pawnZobristKey ^= ChessBoard.zkPieceValues[Long.numberOfTrailingZeros(pieces)][WHITE][PAWN]; + cb.pawnZobristKey ^= Zobrist.piece[Long.numberOfTrailingZeros(pieces)][WHITE][PAWN]; pieces &= pieces - 1; } pieces = cb.pieces[BLACK][PAWN]; while (pieces != 0) { - cb.pawnZobristKey ^= ChessBoard.zkPieceValues[Long.numberOfTrailingZeros(pieces)][BLACK][PAWN]; + cb.pawnZobristKey ^= Zobrist.piece[Long.numberOfTrailingZeros(pieces)][BLACK][PAWN]; pieces &= pieces - 1; } } @@ -225,13 +224,7 @@ public static void copy(final ChessBoard source, final ChessBoard target) { // large arrays System.arraycopy(source.pieceIndexes, 0, target.pieceIndexes, 0, source.pieceIndexes.length); - System.arraycopy(source.psqtScoreHistory, 0, target.psqtScoreHistory, 0, source.psqtScoreHistory.length); - System.arraycopy(source.castlingHistory, 0, target.castlingHistory, 0, source.castlingHistory.length); - System.arraycopy(source.epIndexHistory, 0, target.epIndexHistory, 0, source.epIndexHistory.length); System.arraycopy(source.zobristKeyHistory, 0, target.zobristKeyHistory, 0, source.zobristKeyHistory.length); - System.arraycopy(source.checkingPiecesHistory, 0, target.checkingPiecesHistory, 0, source.checkingPiecesHistory.length); - System.arraycopy(source.pinnedPiecesHistory, 0, target.pinnedPiecesHistory, 0, source.pinnedPiecesHistory.length); - System.arraycopy(source.discoveredPiecesHistory, 0, target.discoveredPiecesHistory, 0, source.discoveredPiecesHistory.length); // multi-dimensional arrays System.arraycopy(source.pieces[WHITE], 0, target.pieces[WHITE], 0, source.pieces[WHITE].length); diff --git a/src/main/java/nl/s22k/chess/ChessConstants.java b/src/main/java/nl/s22k/chess/ChessConstants.java index eca0cea..297524e 100644 --- a/src/main/java/nl/s22k/chess/ChessConstants.java +++ b/src/main/java/nl/s22k/chess/ChessConstants.java @@ -22,33 +22,17 @@ public class ChessConstants { public static final int WHITE = 0; public static final int BLACK = 1; + public static final int SCORE_NOT_RUNNING = 7777; + public static final int[] COLOR_FACTOR = { 1, -1 }; public static final int[] COLOR_FACTOR_8 = { 8, -8 }; public static final long[][] KING_AREA = new long[2][64]; - public static final long[][] PASSED_PAWN_MASKS = new long[2][64]; - public static final long[][] IN_BETWEEN = new long[64][64]; /** pinned-piece index, king index */ public static final long[][] PINNED_MOVEMENT = new long[64][64]; - public static final long[] MASK_ADJACENT_FILE_UP = new long[64]; - public static final long[] MASK_ADJACENT_FILE_DOWN = new long[64]; - static { - for (int i = 0; i < 64; i++) { - long adjacentFile = Bitboard.FILES_ADJACENT[i % 8]; - while (adjacentFile != 0) { - if (Long.numberOfTrailingZeros(adjacentFile) > i + 1) { - MASK_ADJACENT_FILE_UP[i] |= Util.POWER_LOOKUP[Long.numberOfTrailingZeros(adjacentFile)]; - } else if (Long.numberOfTrailingZeros(adjacentFile) < i - 1) { - MASK_ADJACENT_FILE_DOWN[i] |= Util.POWER_LOOKUP[Long.numberOfTrailingZeros(adjacentFile)]; - } - adjacentFile &= adjacentFile - 1; - } - } - } - static { int i; @@ -170,14 +154,6 @@ public class ChessConstants { } } - static { - // fill passed-pawn-masks - for (int i = 0; i < 64; i++) { - PASSED_PAWN_MASKS[WHITE][i] = ((Bitboard.FILES[i & 7] | Bitboard.FILES_ADJACENT[i & 7]) & ~Bitboard.RANKS[i / 8]) >>> i << i; - PASSED_PAWN_MASKS[BLACK][i] = ((Bitboard.FILES[i & 7] | Bitboard.FILES_ADJACENT[i & 7]) & ~Bitboard.RANKS[i / 8]) << (63 - i) >>> (63 - i); - } - } - static { // fill king-safety masks: // @@ -237,7 +213,7 @@ public class ChessConstants { } public enum ScoreType { - EXACT(" "), ALPHA(" upperbound "), BETA(" lowerbound "); + EXACT(" "), UPPER(" upperbound "), LOWER(" lowerbound "); private ScoreType(String uci) { this.uci = uci; diff --git a/src/main/java/nl/s22k/chess/Statistics.java b/src/main/java/nl/s22k/chess/Statistics.java index d214a88..57b0850 100644 --- a/src/main/java/nl/s22k/chess/Statistics.java +++ b/src/main/java/nl/s22k/chess/Statistics.java @@ -6,34 +6,29 @@ import nl.s22k.chess.eval.EvalCache; import nl.s22k.chess.eval.MaterialCache; import nl.s22k.chess.eval.PawnEvalCache; -import nl.s22k.chess.move.TreeMove; -import nl.s22k.chess.search.NegamaxUtil; +import nl.s22k.chess.eval.SEEUtil; +import nl.s22k.chess.move.MoveUtil; import nl.s22k.chess.search.TTUtil; public class Statistics { public static final boolean ENABLED = false; - public static boolean panic = false; public static long startTime = System.nanoTime(); public static long evalNodes, abNodes, seeNodes, pvNodes, cutNodes, allNodes, qNodes, evaluatedInCheck; public static long ttHits, ttMisses; public static int staleMateCount, mateCount; public static int depth, maxDepth; - public static TreeMove bestMove; public static int epCount, castleCount, promotionCount; public static long pawnEvalCacheHits, pawnEvalCacheMisses; - public static int materialCacheMisses, materialCacheHits; - public static int bestMoveTT, bestMoveTTLower, bestMoveTTUpper, bestMoveKiller1, bestMoveKiller2, bestMoveKillerEvasive1, bestMoveKillerEvasive2, - bestMoveOther, bestMovePromotion, bestMoveWinningCapture, bestMoveLosingCapture; + public static long materialCacheMisses, materialCacheHits; + public static int bestMoveTT, bestMoveTTLower, bestMoveTTUpper, bestMoveCounter, bestMoveKiller1, bestMoveKiller2, bestMoveKillerEvasive1, + bestMoveKillerEvasive2, bestMoveOther, bestMovePromotion, bestMoveWinningCapture, bestMoveLosingCapture; public static int repetitions, repetitionTests; public static int checkExtensions, endGameExtensions; public static int nullMoveHit, nullMoveMiss; - public static long lmrMoveHit, lmrMoveMiss; - public static long pvsMoveHit, pvsMoveMiss; public static long evalCacheHits, evalCacheMisses; public static int iidCount; - public static int drawByMaterialCount; public static final int[] razored = new int[10]; public static final int[] futile = new int[10]; public static final int[] staticNullMoved = new int[10]; @@ -42,7 +37,7 @@ public class Statistics { public static int drawishByMaterialCount; public static long calculateNps() { - return NegamaxUtil.getTotalMoveCount() * 1000 / Math.max(getPassedTimeMs(), 1); + return ChessBoard.getTotalMoveCount() * 1000 / Math.max(getPassedTimeMs(), 1); } public static void reset() { @@ -52,16 +47,15 @@ public static void reset() { Arrays.fill(lmped, 0); Arrays.fill(failHigh, 0); + bestMoveCounter = 0; evaluatedInCheck = 0; qNodes = 0; pvNodes = 1; // so we never divide by zero cutNodes = 0; allNodes = 0; drawishByMaterialCount = 0; - drawByMaterialCount = 0; pawnEvalCacheMisses = 0; pawnEvalCacheHits = 0; - bestMove = null; startTime = System.nanoTime(); castleCount = 0; epCount = 0; @@ -78,10 +72,6 @@ public static void reset() { repetitions = 0; nullMoveHit = 0; nullMoveMiss = 0; - lmrMoveHit = 0; - lmrMoveMiss = 0; - pvsMoveHit = 0; - pvsMoveMiss = 0; bestMoveTT = 0; bestMoveTTLower = 0; bestMoveTTUpper = 0; @@ -99,7 +89,6 @@ public static void reset() { evalCacheHits = 0; evalCacheMisses = 0; iidCount = 0; - panic = false; } public static void print() { @@ -107,10 +96,6 @@ public static void print() { return; } System.out.println("Time " + getPassedTimeMs() + "ms"); - if (bestMove != null) { - System.out.println("Bestmove " + bestMove.toString()); - System.out.println("Score " + bestMove.score); - } System.out.println("NPS " + calculateNps() / 1000 + "k"); System.out.println("Depth " + depth + "/" + maxDepth); System.out.println("AB-nodes " + abNodes); @@ -124,14 +109,13 @@ public static void print() { System.out.println("See-nodes " + seeNodes); System.out.println("Evaluated " + evalNodes); System.out.println("Eval in check " + evaluatedInCheck); - System.out.println("Moves " + NegamaxUtil.getTotalMoveCount()); + System.out.println("Moves " + ChessBoard.getTotalMoveCount()); System.out.println("IID " + iidCount); - System.out.println("Panic " + panic); System.out.println("### Caches #######"); printPercentage("TT ", ttHits, ttMisses); if (TTUtil.maxEntries != 0) { - System.out.println("usage " + TTUtil.usageCounter * 100 / (TTUtil.maxEntries * 2) + "%"); + System.out.println("usage " + TTUtil.getUsagePercentage() + "%"); } printPercentage("Eval ", evalCacheHits, evalCacheMisses); System.out.println("usage " + EvalCache.usageCounter * 100 / EvalCache.MAX_TABLE_ENTRIES + "%"); @@ -151,13 +135,13 @@ public static void print() { System.out.println("Killer2 " + bestMoveKiller2); System.out.println("Killer1 evasi " + bestMoveKillerEvasive1); System.out.println("Killer2 evasi " + bestMoveKillerEvasive2); + System.out.println("Counter " + bestMoveCounter); System.out.println("Other " + bestMoveOther); System.out.println("### Outcome #####"); System.out.println("Checkmate " + mateCount); System.out.println("Stalemate " + staleMateCount); System.out.println("Repetitions " + repetitions + "(" + repetitionTests + ")"); - System.out.println("Draw-by-mtrl " + drawByMaterialCount); System.out.println("Drawish-mtrl " + drawishByMaterialCount); System.out.println("### Extensions #####"); @@ -166,8 +150,6 @@ public static void print() { System.out.println("### Pruning #####"); printPercentage("Null-move ", nullMoveHit, nullMoveMiss); - printPercentage("LMR ", lmrMoveHit, lmrMoveMiss); - printPercentage("PVS ", pvsMoveHit, pvsMoveMiss); printDepthTotals("Static nmp ", staticNullMoved, false); printDepthTotals("Razored ", razored, false); printDepthTotals("Futile ", futile, false); @@ -195,4 +177,45 @@ public static long getPassedTimeMs() { return (System.nanoTime() - startTime) / 1000000; } + public static void setBestMove(ChessBoard cb, int bestMove, int ttMove, long ttValue, int flag, int counterMove, int killer1Move, int killer2Move) { + if (flag == TTUtil.FLAG_LOWER) { + Statistics.cutNodes++; + } else if (flag == TTUtil.FLAG_UPPER) { + Statistics.allNodes++; + } else { + Statistics.pvNodes++; + } + if (bestMove == ttMove) { + if (TTUtil.getFlag(ttValue) == TTUtil.FLAG_LOWER) { + Statistics.bestMoveTTLower++; + } else if (TTUtil.getFlag(ttValue) == TTUtil.FLAG_UPPER) { + Statistics.bestMoveTTUpper++; + } else { + Statistics.bestMoveTT++; + } + } else if (MoveUtil.isPromotion(bestMove)) { + Statistics.bestMovePromotion++; + } else if (MoveUtil.getAttackedPieceIndex(bestMove) != 0) { + // slow but disabled when statistics are disabled + if (SEEUtil.getSeeCaptureScore(cb, bestMove) < 0) { + Statistics.bestMoveLosingCapture++; + } else { + Statistics.bestMoveWinningCapture++; + } + } else if (bestMove == counterMove) { + Statistics.bestMoveCounter++; + } else if (bestMove == killer1Move && cb.checkingPieces == 0) { + Statistics.bestMoveKiller1++; + } else if (bestMove == killer2Move && cb.checkingPieces == 0) { + Statistics.bestMoveKiller2++; + } else if (bestMove == killer1Move && cb.checkingPieces != 0) { + Statistics.bestMoveKillerEvasive1++; + } else if (bestMove == killer2Move && cb.checkingPieces != 0) { + Statistics.bestMoveKillerEvasive2++; + } else { + Statistics.bestMoveOther++; + } + + } + } diff --git a/src/main/java/nl/s22k/chess/Util.java b/src/main/java/nl/s22k/chess/Util.java index 55f821f..90a1939 100644 --- a/src/main/java/nl/s22k/chess/Util.java +++ b/src/main/java/nl/s22k/chess/Util.java @@ -50,4 +50,9 @@ public static int getDistance(final int index1, final int index2) { return Math.max(Math.abs((index1 >>> 3) - (index2 >>> 3)), Math.abs((index1 & 7) - (index2 & 7))); } + public static int getDistance(final long sq1, final long sq2) { + return Math.max(Math.abs((Long.numberOfTrailingZeros(sq1) >>> 3) - (Long.numberOfTrailingZeros(sq2) >>> 3)), + Math.abs((Long.numberOfTrailingZeros(sq1) & 7) - (Long.numberOfTrailingZeros(sq2) & 7))); + } + } diff --git a/src/main/java/nl/s22k/chess/Zobrist.java b/src/main/java/nl/s22k/chess/Zobrist.java new file mode 100644 index 0000000..f95aabd --- /dev/null +++ b/src/main/java/nl/s22k/chess/Zobrist.java @@ -0,0 +1,32 @@ +package nl.s22k.chess; + +import java.security.SecureRandom; + +public class Zobrist { + + public static long sideToMove; + public static final long[] castling = new long[16]; + public static final long[] epIndex = new long[48]; + public static final long[][][] piece = new long[64][2][7]; + + static { + SecureRandom r = new SecureRandom(); + for (int bitIndex = 0; bitIndex < 64; bitIndex++) { + for (int colorIndex = 0; colorIndex < piece[0].length; colorIndex++) { + for (int pieceIndex = 0; pieceIndex < piece[0][0].length; pieceIndex++) { + piece[bitIndex][colorIndex][pieceIndex] = r.nextLong(); + } + } + } + for (int i = 0; i < castling.length; i++) { + castling[i] = r.nextLong(); + } + + // skip first item: contains only zeros, default value and has no effect when xorring + for (int i = 1; i < epIndex.length; i++) { + epIndex[i] = r.nextLong(); + } + sideToMove = r.nextLong(); + } + +} diff --git a/src/main/java/nl/s22k/chess/engine/EngineConstants.java b/src/main/java/nl/s22k/chess/engine/EngineConstants.java index 29db2eb..c50d252 100644 --- a/src/main/java/nl/s22k/chess/engine/EngineConstants.java +++ b/src/main/java/nl/s22k/chess/engine/EngineConstants.java @@ -6,8 +6,7 @@ public class EngineConstants { public static final int MAX_PLIES = 64; public static final int MAX_MOVES = 768; - public static final int MAX_THREADS = 16; - public static final int THREADS_DEFAULT = 1; + public static final int MAX_THREADS = 64; public static final boolean ENABLE_PONDERING = true; public static final boolean GENERATE_BR_PROMOTIONS = false; @@ -21,32 +20,36 @@ public class EngineConstants { public static final boolean ENABLE_REPETITION_TABLE = true; // TT values - public static int POWER_2_TT_ENTRIES = 23; // 23 - public static final boolean VERIFY_TT_MOVE = false; + public static int POWER_2_TT_ENTRIES = 23; + public static final boolean VERIFY_TT_MOVE = false; // Search improvements - public static final boolean ENABLE_KILLER_MOVES = true; - public static final boolean ENABLE_HISTORY_HEURISTIC = true; - public static final boolean ENABLE_ASPIRATION = true; - public static final int ASPIRATION_WINDOW_DELTA = 20; // 20 - public static final boolean ENABLE_IID = true; - public static final int IID_REDUCTION = 1; + public static final boolean ENABLE_COUNTER_MOVES = true; + public static final boolean ENABLE_KILLER_MOVES = true; + public static final boolean ENABLE_HISTORY_HEURISTIC = true; + public static final boolean ENABLE_ASPIRATION = true; + public static final int ASPIRATION_WINDOW_DELTA = 20; + public static final boolean ENABLE_IID = true; + public static final int IID_REDUCTION = 1; // Search extensions - public static final boolean ENABLE_CHECK_EXTENSION = true; - public static final boolean ENABLE_ENDGAME_EXTENSION= true; - public static final int ENDGAME_EXTENSION_DEPTH = 3; + public static final boolean ENABLE_CHECK_EXTENSION = true; + public static final boolean ENABLE_ENDGAME_EXTENSION = true; + public static final int ENDGAME_EXTENSION_DEPTH = 3; // Search reductions - public static final boolean ENABLE_NULL_MOVE = true; - public static final boolean ENABLE_LMR = true; - public static final boolean ENABLE_LMP = true; - public static final boolean ENABLE_Q_PRUNE_BAD_CAPTURES = true; - public static final boolean ENABLE_PVS = true; + public static final boolean ENABLE_NULL_MOVE = true; + public static final boolean ENABLE_LMR = true; + public static final boolean ENABLE_LMP = true; + public static final boolean ENABLE_PVS = true; public static final boolean ENABLE_MATE_DISTANCE_PRUNING = true; - public static final boolean ENABLE_STATIC_NULL_MOVE = true; - public static final boolean ENABLE_RAZORING = true; - public static final boolean ENABLE_FUTILITY_PRUNING = true; + public static final boolean ENABLE_STATIC_NULL_MOVE = true; + public static final boolean ENABLE_RAZORING = true; + public static final boolean ENABLE_FUTILITY_PRUNING = true; + public static final boolean ENABLE_SEE_PRUNING = true; + public static final boolean ENABLE_Q_PRUNE_BAD_CAPTURES = true; + public static final boolean ENABLE_Q_FUTILITY_PRUNING = true; + public static final boolean USE_TT_SCORE_AS_EVAL = true; // Evaluation-function public static final boolean ENABLE_EVAL_CACHE = true; diff --git a/src/main/java/nl/s22k/chess/engine/MainEngine.java b/src/main/java/nl/s22k/chess/engine/MainEngine.java index 79e9b83..addf78a 100644 --- a/src/main/java/nl/s22k/chess/engine/MainEngine.java +++ b/src/main/java/nl/s22k/chess/engine/MainEngine.java @@ -13,10 +13,9 @@ import nl.s22k.chess.eval.MaterialCache; import nl.s22k.chess.eval.PassedPawnEval; import nl.s22k.chess.eval.PawnEvalCache; -import nl.s22k.chess.move.MagicUtil; +import nl.s22k.chess.move.MoveGenerator; import nl.s22k.chess.move.MoveWrapper; -import nl.s22k.chess.move.TreeMove; -import nl.s22k.chess.search.Mode; +import nl.s22k.chess.move.PV; import nl.s22k.chess.search.NegamaxUtil; import nl.s22k.chess.search.TTUtil; import nl.s22k.chess.search.TimeUtil; @@ -31,7 +30,7 @@ public class MainEngine { private static boolean maxTimeExceeded = false; public static int maxDepth = EngineConstants.MAX_PLIES; - public static int nrOfThreads = EngineConstants.THREADS_DEFAULT; + public static int nrOfThreads = 1; public static boolean pondering = false; private static Object synchronizedObject = new Object(); @@ -52,7 +51,7 @@ public void run() { } maxTimeExceeded = false; - NegamaxUtil.start(cb, nrOfThreads); + NegamaxUtil.start(cb); // calculation ready calculating = false; @@ -86,8 +85,8 @@ public void run() { Thread.sleep(TimeUtil.getMaxTimeMs()); if (pondering) { maxTimeExceeded = true; - } else if (Statistics.bestMove != null) { - NegamaxUtil.mode.set(Mode.STOP); + } else if (PV.getBestMove() != 0) { + NegamaxUtil.isRunning = false; } } catch (InterruptedException e) { @@ -123,7 +122,6 @@ public void run() { } public static void main(String[] args) { - MagicUtil.init(); cb = ChessBoardUtil.getNewCB(); searchThread.start(); maxTimeThread.start(); @@ -140,8 +138,8 @@ private static void start() { if (tokens[0].equals("uci")) { System.out.println("id name chess22k " + getVersion()); System.out.println("id author Sander MvdB"); - System.out.println("option name Hash type spin default 128 min 1 max 4096"); - System.out.println("option name Threads type spin default 1 min 1 max 16"); + System.out.println("option name Hash type spin default 128 min 1 max 16384"); + System.out.println("option name Threads type spin default 1 min 1 max " + EngineConstants.MAX_THREADS); if (EngineConstants.ENABLE_PONDERING) { System.out.println("option name Ponder type check default false"); } @@ -161,7 +159,7 @@ private static void start() { } else if (tokens[0].equals("ponderhit")) { pondering = false; if (maxTimeExceeded) { - NegamaxUtil.mode.set(Mode.STOP); + NegamaxUtil.isRunning = false; } } else if (tokens[0].equals("eval")) { eval(); @@ -171,7 +169,7 @@ private static void start() { sc.close(); System.exit(0); } else if (tokens[0].equals("stop")) { - NegamaxUtil.mode.set(Mode.STOP); + NegamaxUtil.isRunning = false; } else { System.out.println("Unknown command: " + tokens[0]); } @@ -217,39 +215,11 @@ private static void setOption(String optionName, String optionValue) { // setoption name Hash value 128 if (optionName.toLowerCase().equals("hash")) { int value = Integer.parseInt(optionValue); - switch (value) { - case 1: - case 2: - case 4: - case 8: - case 16: - case 32: - case 64: - case 128: - case 256: - case 512: - case 1024: - case 2048: - case 4096: - case 8192: - case 16384: - int power2Entries = (int) (Math.log(value) / Math.log(2) + 16); - if (EngineConstants.POWER_2_TT_ENTRIES != power2Entries) { - EngineConstants.POWER_2_TT_ENTRIES = power2Entries; - TTUtil.init(true); - } - break; - default: - System.out.println("Hash-size must be between 1-4096 and a multiple of 2. Setting default size of 128mb"); - power2Entries = (int) (Math.log(128) / Math.log(2) + 16); - if (EngineConstants.POWER_2_TT_ENTRIES != power2Entries) { - EngineConstants.POWER_2_TT_ENTRIES = power2Entries; - TTUtil.init(true); - } - } - + TTUtil.setSizeMB(value); } else if (optionName.toLowerCase().equals("threads")) { nrOfThreads = Integer.parseInt(optionValue); + ChessBoard.initInstances(nrOfThreads); + MoveGenerator.initInstances(nrOfThreads); } else if (optionName.toLowerCase().equals("ponder")) { ponder = Boolean.parseBoolean(optionValue); } else { @@ -261,8 +231,7 @@ private static void go(String[] goCommandTokens, int moveCount, int colorToMove) // go movestogo 30 wtime 3600000 btime 3600000 // go wtime 40847 btime 48019 winc 0 binc 0 movestogo 20 - TreeMove bestMove = Statistics.bestMove; - if (bestMove != null && bestMove.score < -50) { + if (PV.getBestMove() != 0 && PV.getScore() < -50) { TimeUtil.setLosing(true); } @@ -353,11 +322,10 @@ private static void sendBestMove() { } Statistics.print(); - TreeMove bestMove = Statistics.bestMove; - if (ponder && bestMove.nextMove != null) { - System.out.println("bestmove " + new MoveWrapper(bestMove.move) + " ponder " + new MoveWrapper(bestMove.nextMove.move)); + if (ponder && PV.getPonderMove() != 0) { + System.out.println("bestmove " + new MoveWrapper(PV.getBestMove()) + " ponder " + new MoveWrapper(PV.getPonderMove())); } else { - System.out.println("bestmove " + new MoveWrapper(bestMove.move)); + System.out.println("bestmove " + new MoveWrapper(PV.getBestMove())); } } @@ -365,8 +333,7 @@ public static void sendInfo() { if (noOutput) { return; } - System.out.println("info nodes " + NegamaxUtil.getTotalMoveCount() + " nps " + Statistics.calculateNps() + " hashfull " - + TTUtil.usageCounter * 1000 / (TTUtil.maxEntries * 2)); + System.out.println("info nodes " + ChessBoard.getTotalMoveCount() + " nps " + Statistics.calculateNps() + " hashfull " + TTUtil.getUsagePercentage()); } public static void sendPlyInfo() { @@ -377,14 +344,13 @@ public static void sendPlyInfo() { // restart info thread infoThread.interrupt(); - final String hashInfo = TTUtil.usageCounter == 0 ? "" : " hashfull " + TTUtil.usageCounter * 1000 / (TTUtil.maxEntries * 2); + final String hashInfo = TTUtil.getUsagePercentage() == 0 ? "" : " hashfull " + TTUtil.getUsagePercentage(); // info depth 1 seldepth 2 score cp 50 pv d2d4 d7d5 e2e3 hashfull 0 nps 1000 nodes 22 // info depth 4 seldepth 10 score cp 40 upperbound pv d2d4 d7d5 e2e3 hashfull 0 nps 30000 nodes 1422 - TreeMove bestMove = Statistics.bestMove; - System.out.println( - "info depth " + Statistics.depth + " seldepth " + Statistics.maxDepth + " time " + Statistics.getPassedTimeMs() + " score cp " + bestMove.score - + bestMove.scoreType + "nps " + Statistics.calculateNps() + " nodes " + NegamaxUtil.getTotalMoveCount() + hashInfo + " pv " + bestMove); + System.out.println("info depth " + Statistics.depth + " seldepth " + Statistics.maxDepth + " time " + Statistics.getPassedTimeMs() + " score cp " + + PV.getScore() + PV.getScoreType() + "nps " + Statistics.calculateNps() + " nodes " + ChessBoard.getTotalMoveCount() + hashInfo + " pv " + + PV.asString()); } public static String getVersion() { diff --git a/src/main/java/nl/s22k/chess/eval/EndGameEvaluator.java b/src/main/java/nl/s22k/chess/eval/EndGameEvaluator.java new file mode 100644 index 0000000..cef794d --- /dev/null +++ b/src/main/java/nl/s22k/chess/eval/EndGameEvaluator.java @@ -0,0 +1,86 @@ +package nl.s22k.chess.eval; + +import static nl.s22k.chess.ChessConstants.BISHOP; +import static nl.s22k.chess.ChessConstants.BLACK; +import static nl.s22k.chess.ChessConstants.KING; +import static nl.s22k.chess.ChessConstants.PAWN; +import static nl.s22k.chess.ChessConstants.QUEEN; +import static nl.s22k.chess.ChessConstants.WHITE; + +import nl.s22k.chess.Bitboard; +import nl.s22k.chess.ChessBoard; +import nl.s22k.chess.Util; + +public class EndGameEvaluator { + + public static int getDrawishScore(final ChessBoard cb, final int color) { + // TODO KRKN: night close to king in the center? + // TODO KRKB? + return Bitboard.manhattanCenterDistance(cb.kingIndex[1 - color]) * 10; + } + + public static int calculateKBKNScore(final ChessBoard cb) { + if (Long.bitCount(cb.friendlyPieces[WHITE]) > 1) { + return 1000 + calculateKBKNScore(cb, WHITE); + } + return -1000 - calculateKBKNScore(cb, BLACK); + } + + private static int calculateKBKNScore(final ChessBoard cb, final int color) { + if ((cb.pieces[color][BISHOP] & Bitboard.WHITE_SQUARES) != 0) { + return Bitboard.manhattanCenterDistance(cb.kingIndex[1 - color]) * 10 * ((Bitboard.WHITE_CORNERS & cb.pieces[1 - color][KING]) != 0 ? 4 : -1); + } + return Bitboard.manhattanCenterDistance(cb.kingIndex[1 - color]) * 10 * ((Bitboard.BLACK_CORNERS & cb.pieces[1 - color][KING]) != 0 ? 4 : -1); + } + + public static boolean isKBPKDraw(final long[][] pieces) { + + if (pieces[WHITE][PAWN] != 0) { + if ((pieces[WHITE][PAWN] & Bitboard.FILE_A) != 0 && (Bitboard.WHITE_SQUARES & pieces[WHITE][BISHOP]) == 0) { + return (pieces[BLACK][KING] & Bitboard.A7B7A8B8) != 0; + } else if ((pieces[WHITE][PAWN] & Bitboard.FILE_H) != 0 && (Bitboard.BLACK_SQUARES & pieces[WHITE][BISHOP]) == 0) { + return (pieces[BLACK][KING] & Bitboard.G7H7G8H8) != 0; + } + } else { + if ((pieces[BLACK][PAWN] & Bitboard.FILE_A) != 0 && (Bitboard.BLACK_SQUARES & pieces[BLACK][BISHOP]) == 0) { + return (pieces[WHITE][KING] & Bitboard.A1B1A2B2) != 0; + } else if ((pieces[BLACK][PAWN] & Bitboard.FILE_H) != 0 && (Bitboard.WHITE_SQUARES & pieces[BLACK][BISHOP]) == 0) { + return (pieces[WHITE][KING] & Bitboard.G1H1G2H2) != 0; + } + } + + return false; + } + + public static boolean isKQKPDraw(final ChessBoard cb) { + final int leadingColor = cb.pieces[WHITE][QUEEN] != 0 ? WHITE : BLACK; + final long ranks12 = leadingColor == WHITE ? Bitboard.RANK_12 : Bitboard.RANK_78; + final long pawn = cb.pieces[1 - leadingColor][PAWN]; + long pawnZone; + + if ((Bitboard.FILE_A & pawn) != 0) { + pawnZone = Bitboard.FILE_ABC & ranks12; + } else if ((Bitboard.FILE_C & pawn) != 0) { + pawnZone = Bitboard.FILE_ABC & ranks12; + } else if ((Bitboard.FILE_F & pawn) != 0) { + pawnZone = Bitboard.FILE_FGH & ranks12; + } else if ((Bitboard.FILE_H & pawn) != 0) { + pawnZone = Bitboard.FILE_FGH & ranks12; + } else { + return false; + } + + if ((pawn & pawnZone) == 0) { + return false; + } + + if ((pawnZone & cb.pieces[1 - leadingColor][KING]) != 0) { + if (Util.getDistance(cb.kingIndex[leadingColor], Long.numberOfTrailingZeros(pawn)) >= 4) { + return true; + } + } + + return false; + } + +} diff --git a/src/main/java/nl/s22k/chess/eval/EvalConstants.java b/src/main/java/nl/s22k/chess/eval/EvalConstants.java index e5142cb..a02e9a2 100644 --- a/src/main/java/nl/s22k/chess/eval/EvalConstants.java +++ b/src/main/java/nl/s22k/chess/eval/EvalConstants.java @@ -23,7 +23,7 @@ public class EvalConstants { public static final int SCORE_MATE_BOUND = 30000; // other - public static final int[] OTHER_SCORES = {-8, 12, 18, 8, 18, 12, 150, 12}; + public static final int[] OTHER_SCORES = {-8, 12, 18, 8, 18, 12, 150, 12, 56}; public static final int IX_ROOK_FILE_SEMI_OPEN = 0; public static final int IX_ROOK_FILE_SEMI_OPEN_ISOLATED = 1; public static final int IX_ROOK_FILE_OPEN = 2; @@ -32,6 +32,7 @@ public class EvalConstants { public static final int IX_BISHOP_LONG = 5; public static final int IX_BISHOP_PRISON = 6; public static final int IX_SPACE = 7; + public static final int IX_DRAWISH = 8; // threats public static final int[] THREATS_MG = {38, 68, 100, 16, 56, 144, 66, 52, 8, 16, -6}; @@ -50,10 +51,11 @@ public class EvalConstants { public static final int IX_PAWN_ATTACKED = 10; // pawn - public static final int[] PAWN_SCORES = {6, 10, 12}; + public static final int[] PAWN_SCORES = {6, 10, 12, 6}; public static final int IX_PAWN_DOUBLE = 0; public static final int IX_PAWN_ISOLATED = 1; public static final int IX_PAWN_BACKWARD = 2; + public static final int IX_PAWN_INVERSE = 3; // imbalance public static final int[] IMBALANCE_SCORES = {32, 54, 16}; @@ -75,6 +77,7 @@ public class EvalConstants { public static final int[] ROOK_TRAPPED = {64, 62, 28}; public static final int[] ONLY_MAJOR_DEFENDERS = {0, 6, 14, 24, 4, 10, 0}; public static final int[] NIGHT_PAWN = {68, -14, -2, 2, 8, 12, 20, 30, 36}; + public static final int[] ROOK_PAWN = {48, -4, -4, -4, -4, 0, 0, 0, 0}; public static final int[] BISHOP_PAWN = {-20, -8, -6, 0, 6, 12, 22, 32, 46}; public static final int[] SPACE = {0, 0, 0, 0, 0, -6, -6, -8, -7, -4, -4, -2, 0, -1, 0, 3, 7}; @@ -95,9 +98,9 @@ public class EvalConstants { public static final int[] PASSED_SCORE_MG = {0, -4, -2, 0, 18, 22, -6}; public static final int[] PASSED_SCORE_EG = {0, 18, 18, 38, 62, 136, 262}; - public static final int[] PASSED_CANDIDATE = {0, -2, 2, 12, 14, 32}; + public static final int[] PASSED_CANDIDATE = {0, 2, 2, 8, 14, 40}; - public static final float[] PASSED_KING_MULTI = {0, 1.4f, 1.3f, 1.1f, 1.1f, 1.0f, 0.8f, 0.8f}; + public static final float[] PASSED_KING_MULTI = {0, 1.4f, 1.3f, 1.1f, 1.1f, 1.0f, 0.8f, 0.8f}; public static final float[] PASSED_MULTIPLIERS = { 0.5f, // blocked 1.2f, // next square attacked @@ -295,14 +298,14 @@ public class EvalConstants { } public static final long[] ROOK_PRISON = { - 0, Bitboard.A8, Bitboard.A8_B8, Bitboard.A8_B8_C8, 0, Bitboard.G8_H8, Bitboard.H8, 0, + 0, Bitboard.A8, Bitboard.A8_B8, Bitboard.A8B8C8, 0, Bitboard.G8_H8, Bitboard.H8, 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, Bitboard.A1, Bitboard.A1_B1, Bitboard.A1_B1_C1, 0, Bitboard.G1_H1, Bitboard.H1, 0 + 0, Bitboard.A1, Bitboard.A1_B1, Bitboard.A1B1C1, 0, Bitboard.G1_H1, Bitboard.H1, 0 }; public static final long[] BISHOP_PRISON = { @@ -326,6 +329,7 @@ public class EvalConstants { MATERIAL[ChessConstants.QUEEN] - MATERIAL[ChessConstants.PAWN], }; + public static void initMgEg() { initMgEg(MOBILITY_KNIGHT, MOBILITY_KNIGHT_MG, MOBILITY_KNIGHT_EG); initMgEg(MOBILITY_BISHOP, MOBILITY_BISHOP_MG, MOBILITY_BISHOP_EG); @@ -351,19 +355,33 @@ private static void initMgEg(int[] array, int[] arrayMg, int[] arrayEg) { } } + public static final int[] MIRRORED_LEFT_RIGHT = new int[64]; + static { + for (int i = 0; i < 64; i++) { + MIRRORED_LEFT_RIGHT[i] = (i / 8) * 8 + 7 - (i & 7); + } + } + + public static final int[] MIRRORED_UP_DOWN = new int[64]; + static { + for (int i = 0; i < 64; i++) { + MIRRORED_UP_DOWN[i] = (7 - i / 8) * 8 + (i & 7); + } + } + static { // fix white arrays - for(int piece = ChessConstants.PAWN; piece<=ChessConstants.KING; piece++){ + for (int piece = ChessConstants.PAWN; piece <= ChessConstants.KING; piece++){ Util.reverse(PSQT_MG[piece][ChessConstants.WHITE]); Util.reverse(PSQT_EG[piece][ChessConstants.WHITE]); } // create black arrays - for(int piece=ChessConstants.PAWN; piece<=ChessConstants.KING; piece++){ + for (int piece = ChessConstants.PAWN; piece <= ChessConstants.KING; piece++){ for (int i = 0; i < 64; i++) { - PSQT_MG[piece][BLACK][i] = -PSQT_MG[piece][WHITE][63 - i]; - PSQT_EG[piece][BLACK][i] = -PSQT_EG[piece][WHITE][63 - i]; + PSQT_MG[piece][BLACK][i] = -PSQT_MG[piece][WHITE][MIRRORED_UP_DOWN[i]]; + PSQT_EG[piece][BLACK][i] = -PSQT_EG[piece][WHITE][MIRRORED_UP_DOWN[i]]; } } diff --git a/src/main/java/nl/s22k/chess/eval/EvalUtil.java b/src/main/java/nl/s22k/chess/eval/EvalUtil.java index f073c35..1a23e82 100644 --- a/src/main/java/nl/s22k/chess/eval/EvalUtil.java +++ b/src/main/java/nl/s22k/chess/eval/EvalUtil.java @@ -45,7 +45,7 @@ public static int getScore(final ChessBoard cb) { public static int calculateScore(final ChessBoard cb) { int score = 0; - if (!cb.isDrawByMaterial()) { + if (!MaterialUtil.isDrawByMaterial(cb)) { score = taperedEval(cb); } @@ -53,33 +53,28 @@ public static int calculateScore(final ChessBoard cb) { /* draw-by-material */ if (score > 25) { - if (cb.isDrawByMaterial(WHITE)) { - if (Statistics.ENABLED) { - Statistics.drawByMaterialCount++; - } + if (!MaterialUtil.hasMatingMaterial(cb, WHITE)) { score = EvalConstants.SCORE_DRAW; } else if (cb.isDrawishByMaterial(WHITE)) { if (Statistics.ENABLED) { Statistics.drawishByMaterialCount++; } - // keep king out of the corners - score = Bitboard.manhattanCenterDistance(cb.kingIndex[BLACK]) * 10; + // drive king in the corner + score = EndGameEvaluator.getDrawishScore(cb, WHITE); } } else if (score < -25) { - if (cb.isDrawByMaterial(BLACK)) { - if (Statistics.ENABLED) { - Statistics.drawByMaterialCount++; - } + if (!MaterialUtil.hasMatingMaterial(cb, BLACK)) { score = EvalConstants.SCORE_DRAW; } else if (cb.isDrawishByMaterial(BLACK)) { if (Statistics.ENABLED) { Statistics.drawishByMaterialCount++; } - // keep king out of the corners - score = -Bitboard.manhattanCenterDistance(cb.kingIndex[WHITE]) * 10; + // drive king in the corner + score = -EndGameEvaluator.getDrawishScore(cb, BLACK); } } + score *= ChessConstants.COLOR_FACTOR[cb.colorToMove]; if (EngineConstants.TEST_EVAL_CACHES) { final int cachedScore = EvalCache.getScore(cb.zobristKey); if (cachedScore != ChessConstants.CACHE_MISS) { @@ -89,7 +84,6 @@ public static int calculateScore(final ChessBoard cb) { } } - score *= ChessConstants.COLOR_FACTOR[cb.colorToMove]; EvalCache.addValue(cb.zobristKey, score); if (EngineConstants.TEST_EVAL_VALUES) { @@ -104,6 +98,13 @@ public static int calculateScore(final ChessBoard cb) { } private static int taperedEval(final ChessBoard cb) { + + if (MaterialUtil.isKBNK(cb.materialKey)) { + return EndGameEvaluator.calculateKBKNScore(cb); + } else if (MaterialUtil.isKRKP(cb.materialKey)) { + // return EndGameEvaluator.isKRKPDraw(cb); + } + final int pawnScore = getPawnScores(cb); final int mgEgScore = calculateMobilityScoresAndSetAttackBoards(cb) + PassedPawnEval.calculatePassedPawnScores(cb) + calculateThreats(cb) + calculatePawnShieldBonus(cb); @@ -240,14 +241,14 @@ private static int calculatePawnScores(final ChessBoard cb) { cb.passedPawnsAndOutposts = 0; pawns = Bitboard.getWhitePawnAttacks(cb.pieces[WHITE][PAWN]) & ~cb.pieces[WHITE][PAWN] & ~cb.pieces[BLACK][PAWN]; while (pawns != 0) { - if ((ChessConstants.MASK_ADJACENT_FILE_UP[Long.numberOfTrailingZeros(pawns)] & cb.pieces[BLACK][PAWN]) == 0) { + if ((Bitboard.getWhiteAdjacentMask(Long.numberOfTrailingZeros(pawns)) & cb.pieces[BLACK][PAWN]) == 0) { cb.passedPawnsAndOutposts |= Long.lowestOneBit(pawns); } pawns &= pawns - 1; } pawns = Bitboard.getBlackPawnAttacks(cb.pieces[BLACK][PAWN]) & ~cb.pieces[WHITE][PAWN] & ~cb.pieces[BLACK][PAWN]; while (pawns != 0) { - if ((ChessConstants.MASK_ADJACENT_FILE_DOWN[Long.numberOfTrailingZeros(pawns)] & cb.pieces[WHITE][PAWN]) == 0) { + if ((Bitboard.getBlackAdjacentMask(Long.numberOfTrailingZeros(pawns)) & cb.pieces[WHITE][PAWN]) == 0) { cb.passedPawnsAndOutposts |= Long.lowestOneBit(pawns); } pawns &= pawns - 1; @@ -266,7 +267,7 @@ private static int calculatePawnScores(final ChessBoard cb) { } // backward pawns - else if ((ChessConstants.MASK_ADJACENT_FILE_DOWN[index + 8] & cb.pieces[WHITE][PAWN]) == 0) { + else if ((Bitboard.getBlackAdjacentMask(index + 8) & cb.pieces[WHITE][PAWN]) == 0) { if ((StaticMoves.PAWN_ATTACKS[WHITE][index + 8] & cb.pieces[BLACK][PAWN]) != 0) { if ((Bitboard.FILES[index & 7] & cb.pieces[BLACK][PAWN]) == 0) { score -= EvalConstants.PAWN_SCORES[EvalConstants.IX_PAWN_BACKWARD]; @@ -274,15 +275,20 @@ else if ((ChessConstants.MASK_ADJACENT_FILE_DOWN[index + 8] & cb.pieces[WHITE][P } } + // pawn defending 2 pawns + if (Long.bitCount(StaticMoves.PAWN_ATTACKS[WHITE][index] & cb.pieces[WHITE][PAWN]) == 2) { + score -= EvalConstants.PAWN_SCORES[EvalConstants.IX_PAWN_INVERSE]; + } + // set passed pawns - if ((ChessConstants.PASSED_PAWN_MASKS[WHITE][index] & cb.pieces[BLACK][PAWN]) == 0) { + if ((Bitboard.getWhitePassedPawnMask(index) & cb.pieces[BLACK][PAWN]) == 0) { cb.passedPawnsAndOutposts |= Long.lowestOneBit(pawns); } // candidate passed pawns (no pawns in front, more friendly pawns behind and adjacent than enemy pawns) else if (63 - Long.numberOfLeadingZeros((cb.pieces[WHITE][PAWN] | cb.pieces[BLACK][PAWN]) & Bitboard.FILES[index & 7]) == index) { - if (Long.bitCount(cb.pieces[WHITE][PAWN] & ChessConstants.MASK_ADJACENT_FILE_DOWN[index + 8]) >= Long - .bitCount(cb.pieces[BLACK][PAWN] & ChessConstants.MASK_ADJACENT_FILE_UP[index])) { + if (Long.bitCount(cb.pieces[WHITE][PAWN] & Bitboard.getBlackPassedPawnMask(index + 8)) >= Long + .bitCount(cb.pieces[BLACK][PAWN] & Bitboard.getWhitePassedPawnMask(index))) { score += EvalConstants.PASSED_CANDIDATE[index / 8]; } } @@ -301,7 +307,7 @@ else if (63 - Long.numberOfLeadingZeros((cb.pieces[WHITE][PAWN] | cb.pieces[BLAC } // backward pawns - else if ((ChessConstants.MASK_ADJACENT_FILE_UP[index - 8] & cb.pieces[BLACK][PAWN]) == 0) { + else if ((Bitboard.getWhiteAdjacentMask(index - 8) & cb.pieces[BLACK][PAWN]) == 0) { if ((StaticMoves.PAWN_ATTACKS[BLACK][index - 8] & cb.pieces[WHITE][PAWN]) != 0) { if ((Bitboard.FILES[index & 7] & cb.pieces[WHITE][PAWN]) == 0) { score += EvalConstants.PAWN_SCORES[EvalConstants.IX_PAWN_BACKWARD]; @@ -309,15 +315,20 @@ else if ((ChessConstants.MASK_ADJACENT_FILE_UP[index - 8] & cb.pieces[BLACK][PAW } } + // pawn defending 2 pawns + if (Long.bitCount(StaticMoves.PAWN_ATTACKS[BLACK][index] & cb.pieces[BLACK][PAWN]) == 2) { + score += EvalConstants.PAWN_SCORES[EvalConstants.IX_PAWN_INVERSE]; + } + // set passed pawns - if ((ChessConstants.PASSED_PAWN_MASKS[BLACK][index] & cb.pieces[WHITE][PAWN]) == 0) { + if ((Bitboard.getBlackPassedPawnMask(index) & cb.pieces[WHITE][PAWN]) == 0) { cb.passedPawnsAndOutposts |= Long.lowestOneBit(pawns); } // candidate passers else if (Long.numberOfTrailingZeros((cb.pieces[WHITE][PAWN] | cb.pieces[BLACK][PAWN]) & Bitboard.FILES[index & 7]) == index) { - if (Long.bitCount(cb.pieces[BLACK][PAWN] & ChessConstants.MASK_ADJACENT_FILE_UP[index - 8]) >= Long - .bitCount(cb.pieces[WHITE][PAWN] & ChessConstants.MASK_ADJACENT_FILE_DOWN[index])) { + if (Long.bitCount(cb.pieces[BLACK][PAWN] & Bitboard.getWhitePassedPawnMask(index - 8)) >= Long + .bitCount(cb.pieces[WHITE][PAWN] & Bitboard.getBlackPassedPawnMask(index))) { score -= EvalConstants.PASSED_CANDIDATE[7 - index / 8]; } } @@ -360,6 +371,10 @@ private static int calculateImbalances(final ChessBoard cb) { score += Long.bitCount(cb.pieces[WHITE][NIGHT]) * EvalConstants.NIGHT_PAWN[Long.bitCount(cb.pieces[WHITE][PAWN])]; score -= Long.bitCount(cb.pieces[BLACK][NIGHT]) * EvalConstants.NIGHT_PAWN[Long.bitCount(cb.pieces[BLACK][PAWN])]; + // rook bonus if there are no pawns + score += Long.bitCount(cb.pieces[WHITE][ROOK]) * EvalConstants.ROOK_PAWN[Long.bitCount(cb.pieces[WHITE][PAWN])]; + score -= Long.bitCount(cb.pieces[BLACK][ROOK]) * EvalConstants.ROOK_PAWN[Long.bitCount(cb.pieces[BLACK][PAWN])]; + // double bishop bonus if (Long.bitCount(cb.pieces[WHITE][BISHOP]) == 2) { score += EvalConstants.IMBALANCE_SCORES[EvalConstants.IX_BISHOP_DOUBLE]; @@ -477,12 +492,11 @@ public static int calculateThreats(final ChessBoard cb) { // knight fork // skip when testing eval values because we break the loop if any fork has been found - long pieces; if (!EngineConstants.TEST_EVAL_VALUES) { long forked; - pieces = cb.attacks[WHITE][NIGHT] & ~blackAttacks & cb.emptySpaces; - while (pieces != 0) { - forked = blacks & ~blackPawns & StaticMoves.KNIGHT_MOVES[Long.numberOfTrailingZeros(pieces)]; + piece = cb.attacks[WHITE][NIGHT] & ~blackAttacks & cb.emptySpaces; + while (piece != 0) { + forked = blacks & ~blackPawns & StaticMoves.KNIGHT_MOVES[Long.numberOfTrailingZeros(piece)]; if (Long.bitCount(forked) > 1) { if ((cb.pieces[BLACK][KING] & forked) == 0) { score += EvalConstants.THREATS[EvalConstants.IX_NIGHT_FORK]; @@ -491,11 +505,11 @@ public static int calculateThreats(final ChessBoard cb) { } break; } - pieces &= pieces - 1; + piece &= piece - 1; } - pieces = cb.attacks[BLACK][NIGHT] & ~whiteAttacks & cb.emptySpaces; - while (pieces != 0) { - forked = whites & ~whitePawns & StaticMoves.KNIGHT_MOVES[Long.numberOfTrailingZeros(pieces)]; + piece = cb.attacks[BLACK][NIGHT] & ~whiteAttacks & cb.emptySpaces; + while (piece != 0) { + forked = whites & ~whitePawns & StaticMoves.KNIGHT_MOVES[Long.numberOfTrailingZeros(piece)]; if (Long.bitCount(forked) > 1) { if ((cb.pieces[WHITE][KING] & forked) == 0) { score -= EvalConstants.THREATS[EvalConstants.IX_NIGHT_FORK]; @@ -504,7 +518,7 @@ public static int calculateThreats(final ChessBoard cb) { } break; } - pieces &= pieces - 1; + piece &= piece - 1; } } @@ -808,7 +822,7 @@ public static int calculatePawnShieldBonus(final ChessBoard cb) { int file; int whiteScore = 0; - long piece = cb.pieces[WHITE][PAWN] & cb.kingArea[WHITE]; + long piece = cb.pieces[WHITE][PAWN] & cb.kingArea[WHITE] & ~cb.attacks[BLACK][PAWN]; while (piece != 0) { file = Long.numberOfTrailingZeros(piece) & 7; whiteScore += EvalConstants.SHIELD_BONUS[Math.min(7 - file, file)][Long.numberOfTrailingZeros(piece) >>> 3]; @@ -819,7 +833,7 @@ public static int calculatePawnShieldBonus(final ChessBoard cb) { } int blackScore = 0; - piece = cb.pieces[BLACK][PAWN] & cb.kingArea[BLACK]; + piece = cb.pieces[BLACK][PAWN] & cb.kingArea[BLACK] & ~cb.attacks[WHITE][PAWN]; while (piece != 0) { file = (63 - Long.numberOfLeadingZeros(piece)) & 7; blackScore += EvalConstants.SHIELD_BONUS[Math.min(7 - file, file)][7 - (63 - Long.numberOfLeadingZeros(piece)) / 8]; diff --git a/src/main/java/nl/s22k/chess/eval/KingSafetyEval.java b/src/main/java/nl/s22k/chess/eval/KingSafetyEval.java index c6c0e29..be9739f 100644 --- a/src/main/java/nl/s22k/chess/eval/KingSafetyEval.java +++ b/src/main/java/nl/s22k/chess/eval/KingSafetyEval.java @@ -42,7 +42,7 @@ public static int calculateKingSafetyScores(final ChessBoard cb) { counter += checks(cb, kingColor); counter += EvalConstants.KS_DOUBLE_ATTACKS[Long - .bitCount(StaticMoves.KING_MOVES[cb.kingIndex[kingColor]] & cb.doubleAttacks[1 - kingColor] & ~cb.attacks[kingColor][PAWN])]; + .bitCount(StaticMoves.KING_MOVES[cb.kingIndex[kingColor]] & cb.doubleAttacks[enemyColor] & ~cb.attacks[kingColor][PAWN])]; if ((cb.checkingPieces & cb.friendlyPieces[enemyColor]) != 0) { counter++; @@ -105,17 +105,17 @@ private static int checks(final ChessBoard cb, final int kingColor) { final long possibleSquares = ~cb.friendlyPieces[enemyColor] & (~StaticMoves.KING_MOVES[kingIndex] | StaticMoves.KING_MOVES[kingIndex] & cb.doubleAttacks[enemyColor] & ~cb.doubleAttacks[kingColor]); - int counter = checkNight(cb, kingColor, StaticMoves.KNIGHT_MOVES[kingIndex] & possibleSquares & cb.attacks[1 - kingColor][NIGHT]); + int counter = checkNight(cb, kingColor, StaticMoves.KNIGHT_MOVES[kingIndex] & possibleSquares & cb.attacks[enemyColor][NIGHT]); long moves; long queenMoves = 0; if ((cb.pieces[enemyColor][QUEEN] | cb.pieces[enemyColor][BISHOP]) != 0) { - moves = MagicUtil.getBishopMoves(kingIndex, cb.allPieces) & possibleSquares; + moves = MagicUtil.getBishopMoves(kingIndex, cb.allPieces ^ cb.pieces[kingColor][QUEEN]) & possibleSquares; queenMoves = moves; counter += checkBishop(cb, kingColor, moves & cb.attacks[enemyColor][BISHOP]); } if ((cb.pieces[enemyColor][QUEEN] | cb.pieces[enemyColor][ROOK]) != 0) { - moves = MagicUtil.getRookMoves(kingIndex, cb.allPieces) & possibleSquares; + moves = MagicUtil.getRookMoves(kingIndex, cb.allPieces ^ cb.pieces[kingColor][QUEEN]) & possibleSquares; queenMoves |= moves; counter += checkRook(cb, kingColor, moves & cb.attacks[enemyColor][ROOK]); } @@ -141,8 +141,7 @@ private static int safeCheckQueenTouch(final ChessBoard cb, final int kingColor) } private static int safeCheckQueen(final ChessBoard cb, final int kingColor, final long safeQueenMoves) { - - if ((safeQueenMoves) != 0) { + if (safeQueenMoves != 0) { return EvalConstants.KS_CHECK_QUEEN[Long.bitCount(cb.friendlyPieces[kingColor])]; } @@ -150,8 +149,7 @@ private static int safeCheckQueen(final ChessBoard cb, final int kingColor, fina } private static int checkRook(final ChessBoard cb, final int kingColor, final long rookMoves) { - - if ((rookMoves) == 0) { + if (rookMoves == 0) { return 0; } @@ -172,8 +170,7 @@ private static int checkRook(final ChessBoard cb, final int kingColor, final lon } private static int checkBishop(final ChessBoard cb, final int kingColor, final long bishopMoves) { - - if ((bishopMoves) != 0) { + if (bishopMoves != 0) { if ((bishopMoves & ~cb.attacksAll[kingColor]) != 0) { return EvalConstants.KS_CHECK[BISHOP]; } else { @@ -184,7 +181,7 @@ private static int checkBishop(final ChessBoard cb, final int kingColor, final l } private static int checkNight(final ChessBoard cb, final int kingColor, final long nightMoves) { - if ((nightMoves) != 0) { + if (nightMoves != 0) { if ((nightMoves & ~cb.attacksAll[kingColor]) != 0) { return EvalConstants.KS_CHECK[NIGHT]; } else { diff --git a/src/main/java/nl/s22k/chess/eval/MaterialUtil.java b/src/main/java/nl/s22k/chess/eval/MaterialUtil.java index aaa3af6..08fc658 100644 --- a/src/main/java/nl/s22k/chess/eval/MaterialUtil.java +++ b/src/main/java/nl/s22k/chess/eval/MaterialUtil.java @@ -1,5 +1,7 @@ package nl.s22k.chess.eval; +import nl.s22k.chess.ChessBoard; + public class MaterialUtil { public static final int[][] VALUES = { @@ -10,39 +12,40 @@ public class MaterialUtil { public static final int[] SHIFT = { 0, 16 }; - private static final int MASK_NON_PAWNS_ALL = 0xfff0fff0; - private static final int MASK_NON_PAWNS_WHITE = 0xfff0; - private static final int MASK_NON_PAWNS_BLACK = 0xfff00000; - private static final int[] MASK_NON_PAWNS = { MASK_NON_PAWNS_WHITE, MASK_NON_PAWNS_BLACK }; - private static final int MASK_NIGHTS[] = { 0x70, 0x700000 }; + private static final int MASK_MINOR_MAJOR_ALL = 0xfff0fff0; + private static final int MASK_MINOR_MAJOR_WHITE = 0xfff0; + private static final int MASK_MINOR_MAJOR_BLACK = 0xfff00000; + private static final int[] MASK_MINOR_MAJOR = { MASK_MINOR_MAJOR_WHITE, MASK_MINOR_MAJOR_BLACK }; + private static final int[] MASK_NON_NIGHTS = { 0xff8f, 0xff8f0000 }; private static final int MASK_SINGLE_BISHOPS = 0x800080; private static final int MASK_SINGLE_BISHOP_NIGHT_WHITE = 0x90; private static final int MASK_SINGLE_BISHOP_NIGHT_BLACK = 0x900000; - private static final int MASK_PAWNS_QUEENS[] = { 0xe00f, 0xe00f0000 }; - private static final int MASK_SLIDING_PIECES[] = { 0xff80, 0xff800000 }; + private static final int[] MASK_PAWNS_QUEENS = { 0xe00f, 0xe00f0000 }; + private static final int[] MASK_SLIDING_PIECES = { 0xff80, 0xff800000 }; + private static final int[] MASK_MATING_MATERIAL = { 0xff6f, 0xff6f0000 }; public static boolean containsMajorPieces(final int material) { - return (material & MASK_NON_PAWNS_ALL) != 0; + return (material & MASK_MINOR_MAJOR_ALL) != 0; } public static boolean hasNonPawnPieces(final int material, final int color) { - return (material & MASK_NON_PAWNS[color]) != 0; + return (material & MASK_MINOR_MAJOR[color]) != 0; } public static boolean hasWhiteNonPawnPieces(final int material) { - return (material & MASK_NON_PAWNS_WHITE) != 0; + return (material & MASK_MINOR_MAJOR_WHITE) != 0; } public static boolean hasBlackNonPawnPieces(final int material) { - return (material & MASK_NON_PAWNS_BLACK) != 0; + return (material & MASK_MINOR_MAJOR_BLACK) != 0; } public static boolean oppositeBishops(final int material) { - return Long.bitCount(material & MASK_NON_PAWNS_ALL) == 2 && Long.bitCount(material & MASK_SINGLE_BISHOPS) == 2; + return Long.bitCount(material & MASK_MINOR_MAJOR_ALL) == 2 && Long.bitCount(material & MASK_SINGLE_BISHOPS) == 2; } public static boolean onlyWhitePawnsOrOneNightOrBishop(final int material) { - switch (Long.bitCount(material & MASK_NON_PAWNS_WHITE)) { + switch (Long.bitCount(material & MASK_MINOR_MAJOR_WHITE)) { case 0: return true; case 1: @@ -53,7 +56,7 @@ public static boolean onlyWhitePawnsOrOneNightOrBishop(final int material) { } public static boolean onlyBlackPawnsOrOneNightOrBishop(final int material) { - switch (Long.bitCount(material & MASK_NON_PAWNS_BLACK)) { + switch (Long.bitCount(material & MASK_MINOR_MAJOR_BLACK)) { case 0: return true; case 1: @@ -68,15 +71,64 @@ public static boolean hasPawnsOrQueens(final int material, final int color) { } public static boolean hasOnlyNights(final int material, final int color) { - return (material & MASK_NON_PAWNS[color]) == (material & MASK_NIGHTS[color]); + return (material & MASK_NON_NIGHTS[color]) == 0; } public static int getMajorPieces(final int material, final int color) { - return (material & MASK_NON_PAWNS[color]) >>> SHIFT[color]; + return (material & MASK_MINOR_MAJOR[color]) >>> SHIFT[color]; } public static boolean hasSlidingPieces(final int material, final int color) { return (material & MASK_SLIDING_PIECES[color]) != 0; } + public static boolean isKBNK(final int material) { + return material == 0x90 || material == 0x900000; + } + + public static boolean isKRKP(final int material) { + return material == 0x10400 || material == 0x4000001; + } + + public static boolean isDrawByMaterial(final ChessBoard cb) { + if (Long.bitCount(cb.allPieces) > 4) { + return false; + } + switch (cb.materialKey) { + case 0x0: // KK + case 0x10: // KNK + case 0x20: // KNNK + case 0x80: // KBK + case 0x100000: // KKN + case 0x100010: // KNKN + case 0x100080: // KNKB + case 0x200000: // KKNN + case 0x800000: // KKB + case 0x800010: // KBKN + case 0x800080: // KBKB + return true; + case 0x1: // KPK + case 0x10000: // KPK + return KPKBitbase.isDraw(cb); + case 0x81: // KBPK + case 0x810000: // KBPK + return EndGameEvaluator.isKBPKDraw(cb.pieces); + case 0x12000: // KQKP + case 0x20000001: // KQKP + return EndGameEvaluator.isKQKPDraw(cb); + } + return false; + + } + + public static boolean hasMatingMaterial(final ChessBoard cb, final int color) { + if (Long.bitCount(cb.friendlyPieces[color]) > 3) { + return true; + } + if (Long.bitCount(cb.friendlyPieces[color]) > 2) { + return !hasOnlyNights(cb.materialKey, color); + } + return (cb.materialKey & MASK_MATING_MATERIAL[color]) != 0; + } + } diff --git a/src/main/java/nl/s22k/chess/eval/PassedPawnEval.java b/src/main/java/nl/s22k/chess/eval/PassedPawnEval.java index d5c8ea9..9615d11 100644 --- a/src/main/java/nl/s22k/chess/eval/PassedPawnEval.java +++ b/src/main/java/nl/s22k/chess/eval/PassedPawnEval.java @@ -142,7 +142,7 @@ private static int getBlackPromotionDistance(final ChessBoard cb, final int inde } // check if own king is defending the promotion square (including square just below) - if ((StaticMoves.KING_MOVES[cb.kingIndex[BLACK]] & ChessConstants.PASSED_PAWN_MASKS[BLACK][index] & Bitboard.RANK_12) != 0) { + if ((StaticMoves.KING_MOVES[cb.kingIndex[BLACK]] & ChessConstants.KING_AREA[BLACK][index] & Bitboard.RANK_12) != 0) { promotionDistance--; } @@ -194,7 +194,7 @@ private static int getWhitePromotionDistance(final ChessBoard cb, final int inde // TODO maybe the enemy king can capture the pawn!! // check if own king is defending the promotion square (including square just below) - if ((StaticMoves.KING_MOVES[cb.kingIndex[WHITE]] & ChessConstants.PASSED_PAWN_MASKS[WHITE][index] & Bitboard.RANK_78) != 0) { + if ((StaticMoves.KING_MOVES[cb.kingIndex[WHITE]] & ChessConstants.KING_AREA[WHITE][index] & Bitboard.RANK_78) != 0) { promotionDistance--; } diff --git a/src/main/java/nl/s22k/chess/eval/SEEUtil.java b/src/main/java/nl/s22k/chess/eval/SEEUtil.java index af84a46..a52a8fd 100644 --- a/src/main/java/nl/s22k/chess/eval/SEEUtil.java +++ b/src/main/java/nl/s22k/chess/eval/SEEUtil.java @@ -30,20 +30,20 @@ private static int getSmallestAttackSeeMove(final long pieces[], final int color // pawn non-promotion attacks attackMove = StaticMoves.PAWN_ATTACKS[1 - colorToMove][toIndex] & pieces[PAWN] & allPieces & Bitboard.RANK_NON_PROMOTION[colorToMove]; if (attackMove != 0) { - return MoveUtil.createSeeAttackMove(Long.numberOfTrailingZeros(attackMove), PAWN); + return MoveUtil.createSeeAttackMove(attackMove, PAWN); } // knight attacks attackMove = pieces[NIGHT] & StaticMoves.KNIGHT_MOVES[toIndex] & allPieces; if (attackMove != 0) { - return MoveUtil.createSeeAttackMove(Long.numberOfTrailingZeros(attackMove), NIGHT); + return MoveUtil.createSeeAttackMove(attackMove, NIGHT); } // bishop attacks if ((pieces[BISHOP] & slidingMask) != 0) { attackMove = pieces[BISHOP] & MagicUtil.getBishopMoves(toIndex, allPieces) & allPieces; if (attackMove != 0) { - return MoveUtil.createSeeAttackMove(Long.numberOfTrailingZeros(attackMove), BISHOP); + return MoveUtil.createSeeAttackMove(attackMove, BISHOP); } } @@ -51,7 +51,7 @@ private static int getSmallestAttackSeeMove(final long pieces[], final int color if ((pieces[ROOK] & slidingMask) != 0) { attackMove = pieces[ROOK] & MagicUtil.getRookMoves(toIndex, allPieces) & allPieces; if (attackMove != 0) { - return MoveUtil.createSeeAttackMove(Long.numberOfTrailingZeros(attackMove), ROOK); + return MoveUtil.createSeeAttackMove(attackMove, ROOK); } } @@ -59,7 +59,7 @@ private static int getSmallestAttackSeeMove(final long pieces[], final int color if ((pieces[QUEEN] & slidingMask) != 0) { attackMove = pieces[QUEEN] & MagicUtil.getQueenMoves(toIndex, allPieces) & allPieces; if (attackMove != 0) { - return MoveUtil.createSeeAttackMove(Long.numberOfTrailingZeros(attackMove), QUEEN); + return MoveUtil.createSeeAttackMove(attackMove, QUEEN); } } @@ -74,7 +74,7 @@ private static int getSmallestAttackSeeMove(final long pieces[], final int color // king attacks attackMove = pieces[KING] & StaticMoves.KING_MOVES[toIndex]; if (attackMove != 0) { - return MoveUtil.createSeeAttackMove(Long.numberOfTrailingZeros(attackMove), KING); + return MoveUtil.createSeeAttackMove(attackMove, KING); } return 0; @@ -125,7 +125,7 @@ public static int getSeeCaptureScore(final ChessBoard cb, final int move) { final int index = MoveUtil.getToIndex(move); final long allPieces = cb.allPieces & ~Util.POWER_LOOKUP[MoveUtil.getFromIndex(move)]; - final long slidingMask = (MagicUtil.bishopMovesEmptyBoard[index] | MagicUtil.rookMovesEmptyBoard[index]) & allPieces; + final long slidingMask = MagicUtil.getQueenMovesEmptyBoard(index) & allPieces; // add score when promotion if (MoveUtil.isPromotion(move)) { diff --git a/src/main/java/nl/s22k/chess/maintests/BestMoveTest.java b/src/main/java/nl/s22k/chess/maintests/BestMoveTest.java index 2d7c0f1..6f2f904 100644 --- a/src/main/java/nl/s22k/chess/maintests/BestMoveTest.java +++ b/src/main/java/nl/s22k/chess/maintests/BestMoveTest.java @@ -8,8 +8,8 @@ import nl.s22k.chess.ChessBoardUtil; import nl.s22k.chess.Statistics; import nl.s22k.chess.engine.MainEngine; -import nl.s22k.chess.move.MagicUtil; import nl.s22k.chess.move.MoveWrapper; +import nl.s22k.chess.move.PV; import nl.s22k.chess.search.NegamaxUtil; import nl.s22k.chess.search.TimeUtil; @@ -21,7 +21,6 @@ public class BestMoveTest { @BeforeClass public static void init() { - MagicUtil.init(); MainEngine.noOutput = true; } @@ -36,7 +35,7 @@ private void doTest(String[] epdStrings) { TimeUtil.setSimpleTimeWindow(5000); NegamaxUtil.start(cb); - MoveWrapper bestMove = new MoveWrapper(Statistics.bestMove.move); + MoveWrapper bestMove = new MoveWrapper(PV.getBestMove()); if (epd.isBestMove()) { if (epd.moveEquals(bestMove)) { System.out.println(epd.getId() + " BM OK"); diff --git a/src/main/java/nl/s22k/chess/maintests/NodeCounter.java b/src/main/java/nl/s22k/chess/maintests/NodeCounter.java index 8d1d81a..47207a2 100644 --- a/src/main/java/nl/s22k/chess/maintests/NodeCounter.java +++ b/src/main/java/nl/s22k/chess/maintests/NodeCounter.java @@ -3,33 +3,33 @@ import nl.s22k.chess.ChessBoard; import nl.s22k.chess.ChessBoardUtil; import nl.s22k.chess.Statistics; +import nl.s22k.chess.engine.EngineConstants; import nl.s22k.chess.engine.MainEngine; -import nl.s22k.chess.move.MagicUtil; import nl.s22k.chess.search.NegamaxUtil; import nl.s22k.chess.search.TimeUtil; public class NodeCounter { - private static final int MAX_PLY = 14; + private static final int MAX_PLY = 11; private static final int NUMBER_OF_POSITIONS = 100; public static void main(String[] args) { - // setup - MagicUtil.init(); + MainEngine.maxDepth = MAX_PLY; TimeUtil.setInfiniteWindow(); MainEngine.noOutput = true; + EngineConstants.POWER_2_TT_ENTRIES = 2; long totalNodesSearched = 0; for (int index = 0; index < NUMBER_OF_POSITIONS; index++) { System.out.println(index); - String epdString = BestMoveTest.WAC_EPDS[index]; + String epdString = BestMoveTest.WAC_EPDS[index + 20]; Statistics.reset(); EPD epd = new EPD(epdString); ChessBoard cb = ChessBoardUtil.getNewCB(epd.getFen()); NegamaxUtil.start(cb); - totalNodesSearched += NegamaxUtil.getTotalMoveCount(); + totalNodesSearched += ChessBoard.getTotalMoveCount(); } System.out.println("Total " + totalNodesSearched); System.out.println("Average " + totalNodesSearched / NUMBER_OF_POSITIONS); diff --git a/src/main/java/nl/s22k/chess/maintests/Perft.java b/src/main/java/nl/s22k/chess/maintests/Perft.java index 655bd3a..2aa363f 100644 --- a/src/main/java/nl/s22k/chess/maintests/Perft.java +++ b/src/main/java/nl/s22k/chess/maintests/Perft.java @@ -7,7 +7,6 @@ import nl.s22k.chess.ChessBoard; import nl.s22k.chess.ChessBoardUtil; import nl.s22k.chess.engine.EngineConstants; -import nl.s22k.chess.move.MagicUtil; import nl.s22k.chess.move.MoveGenerator; import nl.s22k.chess.move.MoveWrapper; @@ -17,7 +16,6 @@ public class Perft { @BeforeClass public static void init() { - MagicUtil.init(); if (!EngineConstants.GENERATE_BR_PROMOTIONS) { throw new RuntimeException("Generation of underpromotions must be enabled"); } diff --git a/src/main/java/nl/s22k/chess/maintests/QPerft.java b/src/main/java/nl/s22k/chess/maintests/QPerft.java index e7edd38..b1c528a 100644 --- a/src/main/java/nl/s22k/chess/maintests/QPerft.java +++ b/src/main/java/nl/s22k/chess/maintests/QPerft.java @@ -6,7 +6,6 @@ import nl.s22k.chess.Assert; import nl.s22k.chess.ChessBoard; import nl.s22k.chess.ChessBoardUtil; -import nl.s22k.chess.move.MagicUtil; import nl.s22k.chess.move.MoveGenerator; import nl.s22k.chess.move.MoveWrapper; @@ -16,7 +15,6 @@ public class QPerft { @BeforeClass public static void init() { - MagicUtil.init(); System.out.println("Do not forget to enable bishop- and rook-promotions!"); } diff --git a/src/main/java/nl/s22k/chess/maintests/SeeTest.java b/src/main/java/nl/s22k/chess/maintests/SeeTest.java index b8f3561..e54895a 100644 --- a/src/main/java/nl/s22k/chess/maintests/SeeTest.java +++ b/src/main/java/nl/s22k/chess/maintests/SeeTest.java @@ -9,7 +9,6 @@ import nl.s22k.chess.Util; import nl.s22k.chess.eval.EvalUtil; import nl.s22k.chess.eval.SEEUtil; -import nl.s22k.chess.move.MagicUtil; import nl.s22k.chess.move.MoveGenerator; import nl.s22k.chess.move.MoveUtil; import nl.s22k.chess.texel.Tuner; @@ -24,9 +23,6 @@ public class SeeTest { public static void main(String[] args) { - // setup - MagicUtil.init(); - // read all fens, including score Map fens = Tuner.loadFens("d:\\backup\\chess\\epds\\violent.epd", false, true); System.out.println("Fens found : " + fens.size()); diff --git a/src/main/java/nl/s22k/chess/move/MagicUtil.java b/src/main/java/nl/s22k/chess/move/MagicUtil.java index 51d8904..0ca03c9 100644 --- a/src/main/java/nl/s22k/chess/move/MagicUtil.java +++ b/src/main/java/nl/s22k/chess/move/MagicUtil.java @@ -35,8 +35,6 @@ public final class MagicUtil { private static final long[][] bishopMagicMoves = new long[64][]; private static final int[] rookShifts = new int[64]; private static final int[] bishopShifts = new int[64]; - public static final long[] rookMovesEmptyBoard = new long[64]; - public static final long[] bishopMovesEmptyBoard = new long[64]; public static long getRookMoves(final int fromIndex, final long allPieces) { return rookMagicMoves[fromIndex][(int) ((allPieces & rookMovementMasks[fromIndex]) * rookMagicNumbers[fromIndex] >>> rookShifts[fromIndex])]; @@ -51,7 +49,19 @@ public static long getQueenMoves(final int fromIndex, final long allPieces) { | bishopMagicMoves[fromIndex][(int) ((allPieces & bishopMovementMasks[fromIndex]) * bishopMagicNumbers[fromIndex] >>> bishopShifts[fromIndex])]; } - public static void init() { + public static long getRookMovesEmptyBoard(final int fromIndex) { + return rookMagicMoves[fromIndex][0]; + } + + public static long getBishopMovesEmptyBoard(final int fromIndex) { + return bishopMagicMoves[fromIndex][0]; + } + + public static long getQueenMovesEmptyBoard(final int fromIndex) { + return bishopMagicMoves[fromIndex][0] | rookMagicMoves[fromIndex][0]; + } + + static { calculateBishopMovementMasks(); calculateRookMovementMasks(); generateShiftArrys(); @@ -59,15 +69,6 @@ public static void init() { long[][] rookOccupancyVariations = calculateVariations(rookMovementMasks); generateBishopMoveDatabase(bishopOccupancyVariations); generateRookMoveDatabase(rookOccupancyVariations); - fillSlidingMovesEmptyBoard(); - } - - private static void fillSlidingMovesEmptyBoard() { - for (int i = 0; i < 64; i++) { - bishopMovesEmptyBoard[i] = getBishopMoves(i, 0); - rookMovesEmptyBoard[i] = getRookMoves(i, 0); - } - } private static void generateShiftArrys() { diff --git a/src/main/java/nl/s22k/chess/move/MoveGenerator.java b/src/main/java/nl/s22k/chess/move/MoveGenerator.java index 30a955a..5c336c3 100644 --- a/src/main/java/nl/s22k/chess/move/MoveGenerator.java +++ b/src/main/java/nl/s22k/chess/move/MoveGenerator.java @@ -2,6 +2,7 @@ import static nl.s22k.chess.ChessConstants.BISHOP; import static nl.s22k.chess.ChessConstants.BLACK; +import static nl.s22k.chess.ChessConstants.KING; import static nl.s22k.chess.ChessConstants.NIGHT; import static nl.s22k.chess.ChessConstants.PAWN; import static nl.s22k.chess.ChessConstants.QUEEN; @@ -16,33 +17,49 @@ import nl.s22k.chess.ChessBoard; import nl.s22k.chess.ChessConstants; import nl.s22k.chess.engine.EngineConstants; +import nl.s22k.chess.engine.MainEngine; public final class MoveGenerator { + private static MoveGenerator[] instances; + static { + initInstances(MainEngine.nrOfThreads); + } + private final int[] moves = new int[1500]; + private final int[] moveScores = new int[1500]; private final int[] nextToGenerate = new int[EngineConstants.MAX_PLIES * 2]; private final int[] nextToMove = new int[EngineConstants.MAX_PLIES * 2]; private int currentPly; + private final int[][][] COUNTER_MOVES = new int[2][7][64]; + private final int[] KILLER_MOVE_1 = new int[EngineConstants.MAX_PLIES * 2]; private final int[] KILLER_MOVE_2 = new int[EngineConstants.MAX_PLIES * 2]; private final int[][] HH_MOVES = new int[2][64 * 64]; private final int[][] BF_MOVES = new int[2][64 * 64]; - public MoveGenerator() { - clearHeuristicTables(); + public static MoveGenerator getInstance(int instanceNumber) { + return instances[instanceNumber]; } - public void clearHeuristicTables() { + public static void initInstances(int nrOfInstances) { + instances = new MoveGenerator[nrOfInstances]; + for (int i = 0; i < instances.length; i++) { + instances[i] = new MoveGenerator(); + } + } - Arrays.fill(KILLER_MOVE_1, 0); - Arrays.fill(KILLER_MOVE_2, 0); + public MoveGenerator() { + clearHistoryHeuristics(); + } - Arrays.fill(HH_MOVES[ChessConstants.WHITE], 0); - Arrays.fill(HH_MOVES[ChessConstants.BLACK], 0); - Arrays.fill(BF_MOVES[ChessConstants.WHITE], 1); - Arrays.fill(BF_MOVES[ChessConstants.BLACK], 1); + public void clearHistoryHeuristics() { + Arrays.fill(HH_MOVES[WHITE], 0); + Arrays.fill(HH_MOVES[BLACK], 0); + Arrays.fill(BF_MOVES[WHITE], 1); + Arrays.fill(BF_MOVES[BLACK], 1); } public void addHHValue(final int color, final int move, final int depth) { @@ -63,13 +80,10 @@ public int getHHScore(final int color, final int fromToIndex) { if (!EngineConstants.ENABLE_HISTORY_HEURISTIC) { return 1; } - return Math.min(MoveUtil.SCORE_MAX, 100 * HH_MOVES[color][fromToIndex] / BF_MOVES[color][fromToIndex]); + return 100 * HH_MOVES[color][fromToIndex] / BF_MOVES[color][fromToIndex]; } public void addKillerMove(final int move, final int ply) { - if (EngineConstants.ASSERT) { - Assert.isTrue(move == MoveUtil.getCleanMove(move)); - } if (EngineConstants.ENABLE_KILLER_MOVES) { if (KILLER_MOVE_1[ply] != move) { KILLER_MOVE_2[ply] = KILLER_MOVE_1[ply]; @@ -78,6 +92,16 @@ public void addKillerMove(final int move, final int ply) { } } + public void addCounterMove(final int color, final int parentMove, final int counterMove) { + if (EngineConstants.ENABLE_COUNTER_MOVES) { + COUNTER_MOVES[color][MoveUtil.getSourcePieceIndex(parentMove)][MoveUtil.getToIndex(parentMove)] = counterMove; + } + } + + public int getCounter(final int color, final int parentMove) { + return COUNTER_MOVES[color][MoveUtil.getSourcePieceIndex(parentMove)][MoveUtil.getToIndex(parentMove)]; + } + public int getKiller1(final int ply) { return KILLER_MOVE_1[ply]; } @@ -100,8 +124,8 @@ public int next() { return moves[nextToMove[currentPly]++]; } - public int getNextScore() { - return MoveUtil.getScore(moves[nextToMove[currentPly]]); + public int getScore() { + return moveScores[nextToMove[currentPly] - 1]; } public int previous() { @@ -113,37 +137,35 @@ public boolean hasNext() { } public void addMove(final int move) { - - if (EngineConstants.ASSERT) { - Assert.isTrue(MoveUtil.getCleanMove(move) == move); - } - moves[nextToGenerate[currentPly]++] = move; } public void setMVVLVAScores() { for (int j = nextToMove[currentPly]; j < nextToGenerate[currentPly]; j++) { - moves[j] = MoveUtil.setScoredMove(moves[j], MoveUtil.getAttackedPieceIndex(moves[j]) * 6 - MoveUtil.getSourcePieceIndex(moves[j])); + moveScores[j] = MoveUtil.getAttackedPieceIndex(moves[j]) * 6 - MoveUtil.getSourcePieceIndex(moves[j]); } } public void setHHScores(final int colorToMove) { for (int j = nextToMove[currentPly]; j < nextToGenerate[currentPly]; j++) { - moves[j] = MoveUtil.setScoredMove(moves[j], getHHScore(colorToMove, MoveUtil.getFromToIndex(moves[j]))); + moveScores[j] = getHHScore(colorToMove, MoveUtil.getFromToIndex(moves[j])); } } public void sort() { final int left = nextToMove[currentPly]; for (int i = left, j = i; i < nextToGenerate[currentPly] - 1; j = ++i) { - final int ai = moves[i + 1]; - while (ai > moves[j]) { + final int score = moveScores[i + 1]; + final int move = moves[i + 1]; + while (score > moveScores[j]) { + moveScores[j + 1] = moveScores[j]; moves[j + 1] = moves[j]; if (j-- == left) { break; } } - moves[j + 1] = ai; + moveScores[j + 1] = score; + moves[j + 1] = move; } } @@ -238,12 +260,14 @@ private void generateOutOfSlidingCheckMoves(final ChessBoard cb) { // move king or block sliding piece final long inBetween = ChessConstants.IN_BETWEEN[cb.kingIndex[cb.colorToMove]][Long.numberOfTrailingZeros(cb.checkingPieces)]; + if (inBetween != 0) { + addNightMoves(cb.pieces[cb.colorToMove][NIGHT] & ~cb.pinnedPieces, inBetween); + addBishopMoves(cb.pieces[cb.colorToMove][BISHOP] & ~cb.pinnedPieces, cb.allPieces, inBetween); + addRookMoves(cb.pieces[cb.colorToMove][ROOK] & ~cb.pinnedPieces, cb.allPieces, inBetween); + addQueenMoves(cb.pieces[cb.colorToMove][QUEEN] & ~cb.pinnedPieces, cb.allPieces, inBetween); + addPawnMoves(cb.pieces[cb.colorToMove][PAWN] & ~cb.pinnedPieces, cb, inBetween); + } - addNightMoves(cb.pieces[cb.colorToMove][NIGHT] & ~cb.pinnedPieces, inBetween); - addBishopMoves(cb.pieces[cb.colorToMove][BISHOP] & ~cb.pinnedPieces, cb.allPieces, inBetween); - addRookMoves(cb.pieces[cb.colorToMove][ROOK] & ~cb.pinnedPieces, cb.allPieces, inBetween); - addQueenMoves(cb.pieces[cb.colorToMove][QUEEN] & ~cb.pinnedPieces, cb.allPieces, inBetween); - addPawnMoves(cb.pieces[cb.colorToMove][PAWN] & ~cb.pinnedPieces, cb, inBetween); addKingMoves(cb); } @@ -305,7 +329,7 @@ private void addPawnAttacksAndPromotions(final long pawns, final ChessBoard cb, if (cb.colorToMove == WHITE) { // non-promoting - long piece = pawns & Bitboard.RANK_NON_PROMOTION[WHITE] & Bitboard.getBlackPawnAttacks(cb.friendlyPieces[BLACK]); + long piece = pawns & Bitboard.RANK_NON_PROMOTION[WHITE] & Bitboard.getBlackPawnAttacks(enemies); while (piece != 0) { final int fromIndex = Long.numberOfTrailingZeros(piece); long moves = StaticMoves.PAWN_ATTACKS[WHITE][fromIndex] & enemies; @@ -334,7 +358,7 @@ private void addPawnAttacksAndPromotions(final long pawns, final ChessBoard cb, } } else { // non-promoting - long piece = pawns & Bitboard.RANK_NON_PROMOTION[BLACK] & Bitboard.getWhitePawnAttacks(cb.friendlyPieces[WHITE]); + long piece = pawns & Bitboard.RANK_NON_PROMOTION[BLACK] & Bitboard.getWhitePawnAttacks(enemies); while (piece != 0) { final int fromIndex = Long.numberOfTrailingZeros(piece); long moves = StaticMoves.PAWN_ATTACKS[BLACK][fromIndex] & enemies; @@ -496,7 +520,7 @@ private void addKingMoves(final ChessBoard cb) { final int fromIndex = cb.kingIndex[cb.colorToMove]; long moves = StaticMoves.KING_MOVES[fromIndex] & cb.emptySpaces; while (moves != 0) { - addMove(MoveUtil.createMove(fromIndex, Long.numberOfTrailingZeros(moves), ChessConstants.KING)); + addMove(MoveUtil.createMove(fromIndex, Long.numberOfTrailingZeros(moves), KING)); moves &= moves - 1; } @@ -519,7 +543,7 @@ private void addKingAttacks(final ChessBoard cb) { long moves = StaticMoves.KING_MOVES[fromIndex] & cb.friendlyPieces[cb.colorToMoveInverse]; while (moves != 0) { final int toIndex = Long.numberOfTrailingZeros(moves); - addMove(MoveUtil.createAttackMove(fromIndex, toIndex, ChessConstants.KING, cb.pieceIndexes[toIndex])); + addMove(MoveUtil.createAttackMove(fromIndex, toIndex, KING, cb.pieceIndexes[toIndex])); moves &= moves - 1; } } @@ -569,4 +593,5 @@ private void addPromotionAttacks(long moves, final int fromIndex, final int[] pi moves &= moves - 1; } } + } diff --git a/src/main/java/nl/s22k/chess/move/MoveUtil.java b/src/main/java/nl/s22k/chess/move/MoveUtil.java index 206cec8..ee7a891 100644 --- a/src/main/java/nl/s22k/chess/move/MoveUtil.java +++ b/src/main/java/nl/s22k/chess/move/MoveUtil.java @@ -1,11 +1,10 @@ package nl.s22k.chess.move; -import nl.s22k.chess.Assert; import nl.s22k.chess.ChessConstants; -import nl.s22k.chess.engine.EngineConstants; public class MoveUtil { + // move types public static final int TYPE_NORMAL = 0; public static final int TYPE_EP = 1; public static final int TYPE_PROMOTION_N = ChessConstants.NIGHT; @@ -14,32 +13,22 @@ public class MoveUtil { public static final int TYPE_PROMOTION_Q = ChessConstants.QUEEN; public static final int TYPE_CASTLING = 6; + // shifts // ///////////////////// FROM //6 bits private static final int SHIFT_TO = 6; // 6 private static final int SHIFT_SOURCE = 12; // 3 private static final int SHIFT_ATTACK = 15; // 3 private static final int SHIFT_MOVE_TYPE = 18; // 3 private static final int SHIFT_PROMOTION = 21; // 1 - private static final int SHIFT_SCORE = 22; // 10 or 11 + // masks private static final int MASK_3_BITS = 7; // 6 private static final int MASK_6_BITS = 0x3f; // 6 private static final int MASK_12_BITS = 0xfff; - private static final int MASK_FROM = 0x3f; // 6 - private static final int MASK_TO = 0x3f << 6; // 6 - private static final int MASK_SOURCE = 7 << 12; // 3 private static final int MASK_ATTACK = 7 << 15; // 3 - private static final int MASK_TYPE = 7 << 18; // 3 private static final int MASK_PROMOTION = 1 << 21; // 1 - private static final int MASK_SCORE = 1 << 22; // 10 or 11 - - public static final int MASK_QUIET = MASK_PROMOTION | MASK_ATTACK; - public static final int SEE_CAPTURE_DIVIDER = 6; - - public static final int SCORE_MAX = 511; - - private static final int CLEAN_MOVE_MASK = (1 << SHIFT_SCORE) - 1; + private static final int MASK_QUIET = MASK_PROMOTION | MASK_ATTACK; public static int getFromIndex(final int move) { return move & MASK_6_BITS; @@ -53,10 +42,6 @@ public static int getFromToIndex(final int move) { return move & MASK_12_BITS; } - public static int getCleanMove(final int move) { - return move & CLEAN_MOVE_MASK; - } - public static int getAttackedPieceIndex(final int move) { return move >>> SHIFT_ATTACK & MASK_3_BITS; } @@ -65,11 +50,6 @@ public static int getSourcePieceIndex(final int move) { return move >>> SHIFT_SOURCE & MASK_3_BITS; } - public static int getScore(final int move) { - // arithmetic shift! - return move >> SHIFT_SCORE; - } - public static int getMoveType(final int move) { return move >>> SHIFT_MOVE_TYPE & MASK_3_BITS; } @@ -102,8 +82,8 @@ public static int createAttackMove(final int fromIndex, final int toIndex, final return attackedPieceIndex << SHIFT_ATTACK | sourcePieceIndex << SHIFT_SOURCE | toIndex << SHIFT_TO | fromIndex; } - public static int createSeeAttackMove(final int fromIndex, final int sourcePieceIndex) { - return sourcePieceIndex << SHIFT_SOURCE | fromIndex; + public static int createSeeAttackMove(final long fromSquare, final int sourcePieceIndex) { + return sourcePieceIndex << SHIFT_SOURCE | Long.numberOfTrailingZeros(fromSquare); } public static int createPromotionAttack(final int promotionPiece, final int fromIndex, final int toIndex, final int attackedPieceIndex) { @@ -142,14 +122,6 @@ public static boolean isQuiet(final int move) { return (move & MASK_QUIET) == 0; } - public static int setScoredMove(final int move, final int score) { - if (EngineConstants.ASSERT) { - Assert.isTrue(move == getCleanMove(move)); - Assert.isTrue(score == getScore(move | score << SHIFT_SCORE)); - } - return move | score << SHIFT_SCORE; - } - public static boolean isNormalMove(final int move) { return getMoveType(move) == TYPE_NORMAL; } diff --git a/src/main/java/nl/s22k/chess/move/MoveWrapper.java b/src/main/java/nl/s22k/chess/move/MoveWrapper.java index 7bdf7bb..6fdedea 100644 --- a/src/main/java/nl/s22k/chess/move/MoveWrapper.java +++ b/src/main/java/nl/s22k/chess/move/MoveWrapper.java @@ -18,8 +18,7 @@ public class MoveWrapper { public int fromIndex; public int toIndex; public int move; - public int score; - + public int pieceIndex; public int pieceIndexAttacked; @@ -41,12 +40,10 @@ public MoveWrapper(int move) { toIndex = MoveUtil.getToIndex(move); toFile = (char) (104 - toIndex % 8); toRank = toIndex / 8 + 1; - + pieceIndex = MoveUtil.getSourcePieceIndex(move); pieceIndexAttacked = MoveUtil.getAttackedPieceIndex(move); - score = MoveUtil.getScore(move); - switch (MoveUtil.getMoveType(move)) { case MoveUtil.TYPE_NORMAL: break; diff --git a/src/main/java/nl/s22k/chess/move/PV.java b/src/main/java/nl/s22k/chess/move/PV.java new file mode 100644 index 0000000..ee3f216 --- /dev/null +++ b/src/main/java/nl/s22k/chess/move/PV.java @@ -0,0 +1,87 @@ +package nl.s22k.chess.move; + +import java.util.Arrays; + +import nl.s22k.chess.ChessBoard; +import nl.s22k.chess.ChessConstants.ScoreType; +import nl.s22k.chess.search.TTUtil; + +public class PV { + + private static final int MOVES_LENGTH = 10; + + private static int[] moves = new int[MOVES_LENGTH]; + private static int flag; + private static int score; + + public static void set(int bestMove, int alpha, int beta, int score, ChessBoard cb) { + + PV.score = score; + + if (score <= alpha) { + flag = TTUtil.FLAG_UPPER; + return; + } else if (score >= beta) { + flag = TTUtil.FLAG_LOWER; + } else { + flag = TTUtil.FLAG_EXACT; + } + + Arrays.fill(moves, 0); + moves[0] = bestMove; + cb.doMove(bestMove); + + for (int i = 1; i < MOVES_LENGTH; i++) { + long ttValue = TTUtil.getTTValue(cb.zobristKey); + if (ttValue == 0) { + break; + } + int move = TTUtil.getMove(ttValue); + moves[i] = move; + cb.doMove(move); + } + for (int i = MOVES_LENGTH - 1; i >= 0; i--) { + if (moves[i] == 0) { + continue; + } + cb.undoMove(moves[i]); + } + } + + public static ScoreType getScoreType() { + switch (flag) { + case TTUtil.FLAG_EXACT: + return ScoreType.EXACT; + case TTUtil.FLAG_LOWER: + return ScoreType.LOWER; + case TTUtil.FLAG_UPPER: + return ScoreType.UPPER; + } + throw new RuntimeException("Unknown flag " + flag); + } + + public static int getPonderMove() { + return moves[1]; + } + + public static int getBestMove() { + return moves[0]; + } + + public static int getScore() { + return score; + } + + public static String asString() { + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < MOVES_LENGTH; i++) { + int move = moves[i]; + if (move == 0) { + break; + } + sb.append(new MoveWrapper(move)).append(" "); + } + return sb.toString(); + } + +} diff --git a/src/main/java/nl/s22k/chess/move/TreeMove.java b/src/main/java/nl/s22k/chess/move/TreeMove.java deleted file mode 100644 index df6024f..0000000 --- a/src/main/java/nl/s22k/chess/move/TreeMove.java +++ /dev/null @@ -1,46 +0,0 @@ -package nl.s22k.chess.move; - -import nl.s22k.chess.ChessConstants.ScoreType; - -public class TreeMove { - - public int move; - public TreeMove nextMove; - public int score; - public ScoreType scoreType; - - public TreeMove(int move) { - this.move = move; - } - - public TreeMove(int move, int score, ScoreType scoreType) { - this.move = move; - this.score = score; - this.scoreType = scoreType; - } - - @Override - public String toString() { - StringBuffer result = new StringBuffer(new MoveWrapper(move).toString()); - TreeMove childMove = nextMove; - while (childMove != null) { - result.append(" " + new MoveWrapper(childMove.move)); - childMove = childMove.nextMove; - } - - return result.toString(); - } - - public void appendMove(TreeMove treeMove) { - TreeMove childMove = nextMove; - if (nextMove == null) { - nextMove = treeMove; - } else { - while (childMove.nextMove != null) { - childMove = childMove.nextMove; - } - childMove.nextMove = treeMove; - } - } - -} diff --git a/src/main/java/nl/s22k/chess/search/Mode.java b/src/main/java/nl/s22k/chess/search/Mode.java deleted file mode 100644 index e39ef34..0000000 --- a/src/main/java/nl/s22k/chess/search/Mode.java +++ /dev/null @@ -1,18 +0,0 @@ -package nl.s22k.chess.search; - -public class Mode { - - /** - * stop signal by GUI or no time left. abort all calculation - */ - public static final int STOP = 0; - - /** - * main thread or any slave thread ready - */ - public static final int STOP_SLAVES = 1; - - public static final int START = 2; - public static final int ANY_SLAVE_READY = 3; - -} diff --git a/src/main/java/nl/s22k/chess/search/NegamaxUtil.java b/src/main/java/nl/s22k/chess/search/NegamaxUtil.java index 5212e72..4fe00be 100644 --- a/src/main/java/nl/s22k/chess/search/NegamaxUtil.java +++ b/src/main/java/nl/s22k/chess/search/NegamaxUtil.java @@ -7,7 +7,6 @@ import nl.s22k.chess.ChessBoard; import nl.s22k.chess.ChessBoardUtil; import nl.s22k.chess.ChessConstants; -import nl.s22k.chess.ChessConstants.ScoreType; import nl.s22k.chess.Statistics; import nl.s22k.chess.Util; import nl.s22k.chess.engine.EngineConstants; @@ -19,6 +18,7 @@ import nl.s22k.chess.move.MoveGenerator; import nl.s22k.chess.move.MoveUtil; import nl.s22k.chess.move.MoveWrapper; +import nl.s22k.chess.move.PV; public final class NegamaxUtil { @@ -26,28 +26,30 @@ public final class NegamaxUtil { private static final int PHASE_ATTACKING = 1; private static final int PHASE_KILLER_1 = 2; private static final int PHASE_KILLER_2 = 3; - private static final int PHASE_QUIET = 4; + private static final int PHASE_COUNTER = 4; + private static final int PHASE_QUIET = 5; // Margins shamelessly stolen from Laser private static final int[] STATIC_NULLMOVE_MARGIN = { 0, 60, 130, 210, 300, 400, 510 }; private static final int[] RAZORING_MARGIN = { 0, 240, 280, 300 }; private static final int[] FUTILITY_MARGIN = { 0, 80, 170, 270, 380, 500, 630 }; - - public static AtomicInteger nrOfActiveSlaveThreads = new AtomicInteger(0); - public static AtomicInteger mode = new AtomicInteger(Mode.STOP); - - private static MoveGenerator[] moveGens = new MoveGenerator[EngineConstants.MAX_THREADS]; + private static final int[][] LMR_TABLE = new int[64][64]; static { - for (int i = 0; i < moveGens.length; i++) { - moveGens[i] = new MoveGenerator(); + // Ethereal LMR formula with depth and number of performed moves + for (int depth = 1; depth < 64; depth++) { + for (int moveNumber = 1; moveNumber < 64; moveNumber++) { + LMR_TABLE[depth][moveNumber] = (int) (0.5f + Math.log(depth) * Math.log(moveNumber * 1.2f) / 2.5f); + } } } - public static int calculateBestMove(final ChessBoard cb, final MoveGenerator moveGen, final int ply, int depth, int alpha, int beta, - final int nullMoveCounter) { + public static AtomicInteger nrOfActiveThreads = new AtomicInteger(0); + public static boolean isRunning = false; - if (mode.get() != Mode.START) { - return 0; + public static int calculateBestMove(final int threadNumber, final int ply, int depth, int alpha, int beta, final int nullMoveCounter) { + + if (!isRunning) { + return ChessConstants.SCORE_NOT_RUNNING; } if (EngineConstants.ASSERT) { @@ -56,19 +58,13 @@ public static int calculateBestMove(final ChessBoard cb, final MoveGenerator mov Assert.isTrue(beta >= Util.SHORT_MIN && beta <= Util.SHORT_MAX); } + final int alphaOrig = alpha; + final ChessBoard cb = ChessBoard.getInstance(threadNumber); + final MoveGenerator moveGen = MoveGenerator.getInstance(threadNumber); + // get extensions depth += extensions(cb, moveGen, ply); - // TODO JITWatch unpredictable branch - if (depth == 0) { - return QuiescenceUtil.calculateBestMove(cb, moveGen, alpha, beta); - } - - Statistics.maxDepth = Math.max(Statistics.maxDepth, ply); - if (Statistics.ENABLED) { - Statistics.abNodes++; - } - /* mate-distance pruning */ if (EngineConstants.ENABLE_MATE_DISTANCE_PRUNING) { alpha = Math.max(alpha, Util.SHORT_MIN + ply); @@ -81,20 +77,29 @@ public static int calculateBestMove(final ChessBoard cb, final MoveGenerator mov /* transposition-table */ long ttValue = TTUtil.getTTValue(cb.zobristKey); int score = TTUtil.getScore(ttValue, ply); - if (ttValue != 0 && ply != 0) { + if (ttValue != 0) { if (!EngineConstants.TEST_TT_VALUES) { - if (TTUtil.getDepth(ttValue) >= (depth)) { + if (TTUtil.getDepth(ttValue) >= depth) { switch (TTUtil.getFlag(ttValue)) { case TTUtil.FLAG_EXACT: + if (ply == 0 && threadNumber == 0) { + PV.set(TTUtil.getMove(ttValue), alpha, beta, score, cb); + } return score; case TTUtil.FLAG_LOWER: if (score >= beta) { + if (ply == 0 && threadNumber == 0) { + PV.set(TTUtil.getMove(ttValue), alpha, beta, score, cb); + } return score; } break; case TTUtil.FLAG_UPPER: if (score <= alpha) { + if (ply == 0 && threadNumber == 0) { + PV.set(TTUtil.getMove(ttValue), alpha, beta, score, cb); + } return score; } } @@ -102,6 +107,16 @@ public static int calculateBestMove(final ChessBoard cb, final MoveGenerator mov } } + // TODO JITWatch unpredictable branch + if (depth == 0) { + return QuiescenceUtil.calculateBestMove(cb, moveGen, alpha, beta); + } + + Statistics.maxDepth = Math.max(Statistics.maxDepth, ply); + if (Statistics.ENABLED) { + Statistics.abNodes++; + } + int eval = Util.SHORT_MIN; final boolean isPv = beta - alpha != 1; if (!isPv && cb.checkingPieces == 0) { @@ -109,7 +124,7 @@ public static int calculateBestMove(final ChessBoard cb, final MoveGenerator mov eval = EvalUtil.getScore(cb); /* use tt value as eval */ - if (ttValue != 0) { + if (EngineConstants.USE_TT_SCORE_AS_EVAL && ttValue != 0) { if (TTUtil.getFlag(ttValue) == TTUtil.FLAG_EXACT || TTUtil.getFlag(ttValue) == TTUtil.FLAG_UPPER && score < eval || TTUtil.getFlag(ttValue) == TTUtil.FLAG_LOWER && score > eval) { eval = score; @@ -117,40 +132,36 @@ public static int calculateBestMove(final ChessBoard cb, final MoveGenerator mov } /* static null move pruning */ - if (EngineConstants.ENABLE_STATIC_NULL_MOVE) { - if (depth < STATIC_NULLMOVE_MARGIN.length) { - if (eval - STATIC_NULLMOVE_MARGIN[depth] >= beta) { - if (Statistics.ENABLED) { - Statistics.staticNullMoved[depth]++; - } - return eval; + if (EngineConstants.ENABLE_STATIC_NULL_MOVE && depth < STATIC_NULLMOVE_MARGIN.length) { + if (eval - STATIC_NULLMOVE_MARGIN[depth] >= beta) { + if (Statistics.ENABLED) { + Statistics.staticNullMoved[depth]++; } + return eval; } } /* razoring */ - if (EngineConstants.ENABLE_RAZORING) { - if (depth < RAZORING_MARGIN.length && Math.abs(alpha) < EvalConstants.SCORE_MATE_BOUND) { - if (eval + RAZORING_MARGIN[depth] < alpha) { - final int q = QuiescenceUtil.calculateBestMove(cb, moveGen, alpha - RAZORING_MARGIN[depth], alpha - RAZORING_MARGIN[depth] + 1); - if (q + RAZORING_MARGIN[depth] <= alpha) { - if (Statistics.ENABLED) { - Statistics.razored[depth]++; - } - return q; + if (EngineConstants.ENABLE_RAZORING && depth < RAZORING_MARGIN.length && Math.abs(alpha) < EvalConstants.SCORE_MATE_BOUND) { + if (eval + RAZORING_MARGIN[depth] < alpha) { + score = QuiescenceUtil.calculateBestMove(cb, moveGen, alpha - RAZORING_MARGIN[depth], alpha - RAZORING_MARGIN[depth] + 1); + if (score + RAZORING_MARGIN[depth] <= alpha) { + if (Statistics.ENABLED) { + Statistics.razored[depth]++; } + return score; } } } /* null-move */ if (EngineConstants.ENABLE_NULL_MOVE) { - if (nullMoveCounter < 2 && MaterialUtil.hasNonPawnPieces(cb.materialKey, cb.colorToMove)) { + if (nullMoveCounter < 2 && eval >= beta && MaterialUtil.hasNonPawnPieces(cb.materialKey, cb.colorToMove)) { cb.doNullMove(); // TODO less reduction if stm (other side) has only 1 major piece - final int reduction = 2 + depth / 3; + final int reduction = depth / 4 + 3 + Math.min((eval - beta) / 80, 3); score = depth - reduction <= 0 ? -QuiescenceUtil.calculateBestMove(cb, moveGen, -beta, -beta + 1) - : -calculateBestMove(cb, moveGen, ply + 1, depth - reduction, -beta, -beta + 1, nullMoveCounter + 1); + : -calculateBestMove(threadNumber, ply + 1, depth - reduction, -beta, -beta + 1, nullMoveCounter + 1); cb.undoNullMove(); if (score >= beta) { if (Statistics.ENABLED) { @@ -165,12 +176,13 @@ public static int calculateBestMove(final ChessBoard cb, final MoveGenerator mov } } - final int alphaOrig = alpha; final boolean wasInCheck = cb.checkingPieces != 0; + final int parentMove = ply == 0 ? 0 : moveGen.previous(); int bestMove = 0; - int bestScore = Util.SHORT_MIN; + int bestScore = Util.SHORT_MIN - 1; int ttMove = 0; + int counterMove = 0; int killer1Move = 0; int killer2Move = 0; int movesPerformed = 0; @@ -188,7 +200,7 @@ public static int calculateBestMove(final ChessBoard cb, final MoveGenerator mov if (Statistics.ENABLED) { Statistics.iidCount++; } - calculateBestMove(cb, moveGen, ply, depth - EngineConstants.IID_REDUCTION - 1, alpha, beta, 0); + calculateBestMove(threadNumber, ply, depth - EngineConstants.IID_REDUCTION - 1, alpha, beta, 0); ttValue = TTUtil.getTTValue(cb.zobristKey); } } @@ -216,14 +228,27 @@ public static int calculateBestMove(final ChessBoard cb, final MoveGenerator mov killer1Move = moveGen.getKiller1(ply); if (killer1Move != 0 && killer1Move != ttMove && cb.isValidQuietMove(killer1Move) && cb.isLegal(killer1Move)) { moveGen.addMove(killer1Move); + break; + } else { + phase++; } - break; case PHASE_KILLER_2: killer2Move = moveGen.getKiller2(ply); if (killer2Move != 0 && killer2Move != ttMove && cb.isValidQuietMove(killer2Move) && cb.isLegal(killer2Move)) { moveGen.addMove(killer2Move); + break; + } else { + phase++; + } + case PHASE_COUNTER: + counterMove = moveGen.getCounter(cb.colorToMove, parentMove); + if (counterMove != 0 && counterMove != ttMove && counterMove != killer1Move && counterMove != killer2Move && cb.isValidQuietMove(counterMove) + && cb.isLegal(counterMove)) { + moveGen.addMove(counterMove); + break; + } else { + phase++; } - break; case PHASE_QUIET: moveGen.generateMoves(cb); moveGen.setHHScores(cb.colorToMove); @@ -231,12 +256,10 @@ public static int calculateBestMove(final ChessBoard cb, final MoveGenerator mov } while (moveGen.hasNext()) { - - final int moveScore = moveGen.getNextScore(); - final int move = MoveUtil.getCleanMove(moveGen.next()); + final int move = moveGen.next(); if (phase == PHASE_QUIET) { - if (move == ttMove || move == killer1Move || move == killer2Move || !cb.isLegal(move)) { + if (move == ttMove || move == killer1Move || move == killer2Move || move == counterMove || !cb.isLegal(move)) { continue; } } else if (phase == PHASE_ATTACKING) { @@ -246,44 +269,37 @@ public static int calculateBestMove(final ChessBoard cb, final MoveGenerator mov } // pruning allowed? - if (!isPv && cb.checkingPieces == 0 && movesPerformed > 0 && moveScore < 100 && !cb.isDiscoveredMove(MoveUtil.getFromIndex(move))) { + if (!isPv && !wasInCheck && movesPerformed > 0 && moveGen.getScore() < 100 && !cb.isDiscoveredMove(MoveUtil.getFromIndex(move))) { if (MoveUtil.isQuiet(move)) { /* late move pruning */ - if (EngineConstants.ENABLE_LMP) { - if (depth <= 4 && movesPerformed >= depth * 3 + 3) { - if (Statistics.ENABLED) { - Statistics.lmped[depth]++; - } - continue; + if (EngineConstants.ENABLE_LMP && depth <= 4 && movesPerformed >= depth * 3 + 3) { + if (Statistics.ENABLED) { + Statistics.lmped[depth]++; } + continue; } /* futility pruning */ - if (EngineConstants.ENABLE_FUTILITY_PRUNING) { + if (EngineConstants.ENABLE_FUTILITY_PRUNING && depth < FUTILITY_MARGIN.length) { if (!MoveUtil.isPawnPush78(move)) { - if (depth < FUTILITY_MARGIN.length) { - if (eval == Util.SHORT_MIN) { - eval = EvalUtil.getScore(cb); - } - final int futilityValue = eval + FUTILITY_MARGIN[depth]; - if (futilityValue <= alpha) { - if (Statistics.ENABLED) { - Statistics.futile[depth]++; - } - if (futilityValue > bestScore) { - bestScore = futilityValue; - } - continue; + if (eval == Util.SHORT_MIN) { + eval = EvalUtil.getScore(cb); + } + if (eval + FUTILITY_MARGIN[depth] <= alpha) { + if (Statistics.ENABLED) { + Statistics.futile[depth]++; } + continue; } } } } /* SEE Pruning */ - else if (depth <= 6 && phase == PHASE_ATTACKING && SEEUtil.getSeeCaptureScore(cb, move) < -20 * depth * depth) { + else if (EngineConstants.ENABLE_SEE_PRUNING && depth <= 6 && phase == PHASE_ATTACKING + && SEEUtil.getSeeCaptureScore(cb, move) < -20 * depth * depth) { continue; } } @@ -292,7 +308,7 @@ else if (depth <= 6 && phase == PHASE_ATTACKING && SEEUtil.getSeeCaptureScore(cb movesPerformed++; /* draw check */ - if (cb.isRepetition() || cb.isDrawByMaterial()) { + if (cb.isRepetition(move) || MaterialUtil.isDrawByMaterial(cb)) { score = EvalConstants.SCORE_DRAW; } else { score = alpha + 1; // initial is above alpha @@ -303,61 +319,69 @@ else if (depth <= 6 && phase == PHASE_ATTACKING && SEEUtil.getSeeCaptureScore(cb cb.changeSideToMove(); } - if (EngineConstants.ENABLE_LMR && moveScore < 40 && movesPerformed > 2 && depth > 3 && MoveUtil.isQuiet(move) && cb.checkingPieces == 0 - && !wasInCheck && !MoveUtil.isPawnPush678(move, cb.colorToMoveInverse)) { - /* LMR */ - final int reduction = move != killer1Move && moveScore < 20 && movesPerformed > 6 ? Math.min(depth - 1, 2 + depth / 6) : 2; - score = -calculateBestMove(cb, moveGen, ply + 1, depth - reduction, -alpha - 1, -alpha, 0); - if (score > alpha) { - score = -calculateBestMove(cb, moveGen, ply + 1, depth - 1, -alpha - 1, -alpha, 0); - if (Statistics.ENABLED) { - Statistics.lmrMoveMiss++; - } - } else if (Statistics.ENABLED) { - Statistics.lmrMoveHit++; + int reduction = 1; + if (depth > 2 && movesPerformed > 1 && MoveUtil.isQuiet(move) && !MoveUtil.isPawnPush78(move)) { + + reduction = LMR_TABLE[Math.min(depth, 63)][Math.min(movesPerformed, 63)]; + if (moveGen.getScore() > 40) { + reduction -= 1; } - } else if (EngineConstants.ENABLE_PVS && movesPerformed > 1) { - /* PVS */ - score = -calculateBestMove(cb, moveGen, ply + 1, depth - 1, -alpha - 1, -alpha, 0); - if (Statistics.ENABLED) { - if (score > alpha) { - Statistics.pvsMoveMiss++; - } else { - Statistics.pvsMoveHit++; - } + if (move == killer1Move || move == counterMove) { + reduction -= 1; + } + if (!isPv) { + reduction += 1; } + reduction = Math.min(depth - 1, Math.max(reduction, 1)); } - if (score > alpha) { - score = -calculateBestMove(cb, moveGen, ply + 1, depth - 1, -beta, -alpha, 0); + + /* LMR */ + if (EngineConstants.ENABLE_LMR && reduction != 1) { + score = -calculateBestMove(threadNumber, ply + 1, depth - reduction, -alpha - 1, -alpha, 0); + } + + /* PVS */ + if (EngineConstants.ENABLE_PVS && score > alpha && movesPerformed > 1) { + score = -calculateBestMove(threadNumber, ply + 1, depth - 1, -alpha - 1, -alpha, 0); } + /* normal bounds */ + if (score > alpha) { + score = -calculateBestMove(threadNumber, ply + 1, depth - 1, -beta, -alpha, 0); + } } cb.undoMove(move); - if (mode.get() != Mode.START) { + if (!isRunning) { moveGen.endPly(); - return 0; + return ChessConstants.SCORE_NOT_RUNNING; } if (score > bestScore) { bestScore = score; bestMove = move; - alpha = Math.max(alpha, score); - } - if (alpha >= beta) { - if (Statistics.ENABLED) { - Statistics.failHigh[Math.min(movesPerformed - 1, Statistics.failHigh.length - 1)]++; + if (ply == 0 && threadNumber == 0) { + PV.set(bestMove, alphaOrig, beta, bestScore, cb); } - /* killer and history */ - if (MoveUtil.isQuiet(move)) { - moveGen.addKillerMove(move, ply); - moveGen.addHHValue(cb.colorToMove, move, depth); - } + alpha = Math.max(alpha, score); + if (alpha >= beta) { - phase += 10; - break; + if (Statistics.ENABLED) { + Statistics.failHigh[Math.min(movesPerformed - 1, Statistics.failHigh.length - 1)]++; + } + + /* killer and history */ + if (MoveUtil.isQuiet(move) && cb.checkingPieces == 0) { + moveGen.addCounterMove(cb.colorToMove, parentMove, move); + moveGen.addKillerMove(move, ply); + moveGen.addHHValue(cb.colorToMove, move, depth); + } + + phase += 10; + break; + } } if (MoveUtil.isQuiet(move)) { @@ -395,56 +419,16 @@ else if (depth <= 6 && phase == PHASE_ATTACKING && SEEUtil.getSeeCaptureScore(cb flag = TTUtil.FLAG_UPPER; } - TTUtil.addValue(cb.zobristKey, bestScore, ply, depth, flag, bestMove); + if (isRunning) { + TTUtil.addValue(cb.zobristKey, bestScore, ply, depth, flag, bestMove); + } - /* update statistics */ if (Statistics.ENABLED) { - if (flag == TTUtil.FLAG_LOWER) { - Statistics.cutNodes++; - } else if (flag == TTUtil.FLAG_UPPER) { - Statistics.allNodes++; - } else { - Statistics.pvNodes++; - } - if (bestMove == ttMove) { - if (TTUtil.getFlag(ttValue) == TTUtil.FLAG_LOWER) { - Statistics.bestMoveTTLower++; - } else if (TTUtil.getFlag(ttValue) == TTUtil.FLAG_UPPER) { - Statistics.bestMoveTTUpper++; - } else { - Statistics.bestMoveTT++; - } - } else if (MoveUtil.isPromotion(bestMove)) { - Statistics.bestMovePromotion++; - } else if (MoveUtil.getAttackedPieceIndex(bestMove) != 0) { - // slow but disabled when statistics are disabled - if (SEEUtil.getSeeCaptureScore(cb, bestMove) < 0) { - Statistics.bestMoveLosingCapture++; - } else { - Statistics.bestMoveWinningCapture++; - } - } else if (bestMove == killer1Move && cb.checkingPieces == 0) { - Statistics.bestMoveKiller1++; - } else if (bestMove == killer2Move && cb.checkingPieces == 0) { - Statistics.bestMoveKiller2++; - } else if (bestMove == killer1Move && cb.checkingPieces != 0) { - Statistics.bestMoveKillerEvasive1++; - } else if (bestMove == killer2Move && cb.checkingPieces != 0) { - Statistics.bestMoveKillerEvasive2++; - } else { - Statistics.bestMoveOther++; - } + Statistics.setBestMove(cb, bestMove, ttMove, ttValue, flag, counterMove, killer1Move, killer2Move); } if (EngineConstants.TEST_TT_VALUES) { - if (ttValue != 0 && TTUtil.getDepth(ttValue) == depth) { - if (TTUtil.getFlag(ttValue) == TTUtil.FLAG_EXACT && flag == TTUtil.FLAG_EXACT) { - score = TTUtil.getScore(ttValue, ply); - if (score != bestScore) { - throw new RuntimeException(String.format("Error: TT-score %s, bestScore %s", score, bestScore)); - } - } - } + SearchTestUtil.testTTValues(score, bestScore, depth, bestMove, flag, ttValue, ply); } return bestScore; @@ -472,157 +456,37 @@ private static int extensions(final ChessBoard cb, final MoveGenerator moveGen, return 0; } - public static void start(ChessBoard cb) { - start(cb, 1); - } + public static void start(final ChessBoard cb) { - public static void start(final ChessBoard chessBoard, final int nrOfThreads) { + isRunning = true; + cb.moveCount = 0; + TTUtil.init(false); - try { + if (MainEngine.nrOfThreads == 1) { + SearchThread thread = new SearchThread(0); + NegamaxUtil.nrOfActiveThreads.incrementAndGet(); + thread.run(); + isRunning = false; + } else { - mode.set(Mode.START); - chessBoard.moveCount = 0; - moveGens[0].clearHeuristicTables(); - if (nrOfThreads > 1) { - ChessBoard.initInstances(nrOfThreads - 1); - for (int i = 0; i < nrOfThreads - 1; i++) { - ChessBoardUtil.copy(chessBoard, ChessBoard.getInstance(i)); - moveGens[i + 1].clearHeuristicTables(); - } + // start slave threads + for (int i = 1; i < MainEngine.nrOfThreads; i++) { + ChessBoardUtil.copy(cb, ChessBoard.getInstance(i)); + NegamaxUtil.nrOfActiveThreads.incrementAndGet(); + new SearchThread(i).start(); } - TTUtil.init(false); - - int depth = 0; - int alpha = Util.SHORT_MIN; - int beta = Util.SHORT_MAX; - int score = Util.SHORT_MIN; - boolean panic = false; - - while (depth != MainEngine.maxDepth + 1 && mode.get() != Mode.STOP) { - - depth++; - Statistics.depth = depth; - - int delta = EngineConstants.ASPIRATION_WINDOW_DELTA; - - while (mode.get() != Mode.STOP) { - - // stop if depth!=1 and no time is left - if (depth != 1 && !TimeUtil.isTimeLeft()) { - if (panic) { - panic = false; - } else { - break; - } - } - - final int previousScore = score; - if (nrOfThreads == 1 || depth < 8) { - score = calculateBestMove(chessBoard, moveGens[0], 0, depth, alpha, beta, 0); - } else { - // start slave threads - for (int i = 1; i < nrOfThreads; i++) { - if (mode.get() == Mode.ANY_SLAVE_READY) { - break; - } - NegamaxUtil.nrOfActiveSlaveThreads.incrementAndGet(); - new SearchThread(ChessBoard.getInstance(i - 1), moveGens[i], depth + (i % 2), alpha, beta).start(); - } - // long now = System.currentTimeMillis(); - Thread.sleep(1); - while (nrOfActiveSlaveThreads.get() != nrOfThreads - 1 && mode.get() != Mode.ANY_SLAVE_READY && mode.get() != Mode.STOP) { - // System.out.println("Wait till all slave threads have started or any slave is ready. - // Mode=" + mode.get()); - Thread.yield(); - } - // System.out.println((System.currentTimeMillis() - now) + " starting slaves time"); - - // start main thread - if (mode.get() == Mode.START) { - score = calculateBestMove(chessBoard, moveGens[0], 0, depth, alpha, beta, 0); - mode.compareAndSet(Mode.START, Mode.STOP_SLAVES); - } - - // wait for all slave threads to be ready - // now = System.currentTimeMillis(); - while (nrOfActiveSlaveThreads.get() != 0) { - // System.out.println("Wait till all slave threads have stopped"); - Thread.yield(); - } - // System.out.println((System.currentTimeMillis() - now) + " stopping slaves time"); - mode.compareAndSet(Mode.STOP_SLAVES, Mode.START); - - // use slave score if finished sooner - if (mode.compareAndSet(Mode.ANY_SLAVE_READY, Mode.START)) { - // System.out.println("Slave threads already finished"); - score = TTUtil.getScore(TTUtil.getTTValue(chessBoard.zobristKey), 0); - } - } - - if (depth > 8 && score + 100 < previousScore && Math.abs(score) < EvalConstants.SCORE_MATE_BOUND) { - if (Statistics.ENABLED) { - Statistics.panic = true; - } - panic = true; - } + // start main thread + SearchThread thread = new SearchThread(0); + NegamaxUtil.nrOfActiveThreads.incrementAndGet(); + thread.run(); + isRunning = false; - if (score <= alpha) { - if (score < -EvalConstants.SCORE_MATE_BOUND) { - alpha = Util.SHORT_MIN; - beta = Util.SHORT_MAX; - } else { - alpha = Math.max(alpha - delta, Util.SHORT_MIN); - } - delta *= 2; - TTUtil.setScoreInStatistics(chessBoard); - if (!TimeUtil.isTimeLeft()) { - if (Statistics.ENABLED) { - Statistics.panic = true; - } - panic = true; - } - MainEngine.sendPlyInfo(); - } else if (score >= beta) { - if (score > EvalConstants.SCORE_MATE_BOUND) { - alpha = Util.SHORT_MIN; - beta = Util.SHORT_MAX; - } else { - beta = Math.min(beta + delta, Util.SHORT_MAX); - } - delta *= 2; - TTUtil.setBestMoveInStatistics(chessBoard, ScoreType.BETA); - MainEngine.sendPlyInfo(); - } else { - if (EngineConstants.ENABLE_ASPIRATION) { - if (Math.abs(score) > EvalConstants.SCORE_MATE_BOUND) { - alpha = Util.SHORT_MIN; - beta = Util.SHORT_MAX; - } else { - alpha = Math.max(score - delta, Util.SHORT_MIN); - beta = Math.min(score + delta, Util.SHORT_MAX); - } - } - TTUtil.setBestMoveInStatistics(chessBoard, ScoreType.EXACT); - MainEngine.sendPlyInfo(); - break; - } - } + // wait for all slave threads to be ready + while (nrOfActiveThreads.get() != 0) { + Thread.yield(); } - - mode.set(Mode.STOP); - - } catch (InterruptedException e) { - throw new RuntimeException(e.getMessage(), e); - } - } - - public static long getTotalMoveCount() { - long totalMoveCount = ChessBoard.getInstance().moveCount; - for (int i = 0; i < MainEngine.nrOfThreads - 1; i++) { - totalMoveCount += ChessBoard.getInstance(i).moveCount; } - return totalMoveCount; } } diff --git a/src/main/java/nl/s22k/chess/search/QuiescenceUtil.java b/src/main/java/nl/s22k/chess/search/QuiescenceUtil.java index 63d816a..bc645c0 100644 --- a/src/main/java/nl/s22k/chess/search/QuiescenceUtil.java +++ b/src/main/java/nl/s22k/chess/search/QuiescenceUtil.java @@ -3,7 +3,9 @@ import nl.s22k.chess.Assert; import nl.s22k.chess.CheckUtil; import nl.s22k.chess.ChessBoard; +import nl.s22k.chess.ChessConstants; import nl.s22k.chess.Statistics; +import nl.s22k.chess.Util; import nl.s22k.chess.engine.EngineConstants; import nl.s22k.chess.eval.EvalConstants; import nl.s22k.chess.eval.EvalUtil; @@ -13,7 +15,7 @@ public class QuiescenceUtil { - private static final int FUTILITY_MARGIN = 100; + private static final int FUTILITY_MARGIN = 200; public static int calculateBestMove(final ChessBoard cb, final MoveGenerator moveGen, int alpha, final int beta) { @@ -21,18 +23,20 @@ public static int calculateBestMove(final ChessBoard cb, final MoveGenerator mov Statistics.qNodes++; } - if (NegamaxUtil.mode.get() != Mode.START) { - return 0; + if (!NegamaxUtil.isRunning) { + return ChessConstants.SCORE_NOT_RUNNING; } /* stand-pat check */ - int score = EvalUtil.getScore(cb); - if (score >= beta) { - return score; + int eval = Util.SHORT_MIN; + if (cb.checkingPieces == 0) { + eval = EvalUtil.getScore(cb); + if (eval >= beta) { + return eval; + } + alpha = Math.max(alpha, eval); } - alpha = Math.max(alpha, score); - moveGen.startPly(); moveGen.generateAttacks(cb); moveGen.setMVVLVAScores(); @@ -41,22 +45,23 @@ public static int calculateBestMove(final ChessBoard cb, final MoveGenerator mov while (moveGen.hasNext()) { final int move = moveGen.next(); - if (!cb.isLegal(move)) { - continue; - } - // skip under promotions if (MoveUtil.isPromotion(move)) { if (MoveUtil.getMoveType(move) != MoveUtil.TYPE_PROMOTION_Q) { continue; } - } else if (score + FUTILITY_MARGIN + EvalConstants.MATERIAL[MoveUtil.getAttackedPieceIndex(move)] < alpha) { + } else if (EngineConstants.ENABLE_Q_FUTILITY_PRUNING + && eval + FUTILITY_MARGIN + EvalConstants.MATERIAL[MoveUtil.getAttackedPieceIndex(move)] < alpha) { // futility pruning continue; } + if (!cb.isLegal(move)) { + continue; + } + // skip bad-captures - if (EngineConstants.ENABLE_Q_PRUNE_BAD_CAPTURES && SEEUtil.getSeeCaptureScore(cb, move) <= 0) { + if (EngineConstants.ENABLE_Q_PRUNE_BAD_CAPTURES && !cb.isDiscoveredMove(MoveUtil.getFromIndex(move)) && SEEUtil.getSeeCaptureScore(cb, move) <= 0) { continue; } @@ -68,7 +73,7 @@ public static int calculateBestMove(final ChessBoard cb, final MoveGenerator mov cb.changeSideToMove(); } - score = -calculateBestMove(cb, moveGen, -beta, -alpha); + final int score = -calculateBestMove(cb, moveGen, -beta, -alpha); cb.undoMove(move); diff --git a/src/main/java/nl/s22k/chess/search/SearchTestUtil.java b/src/main/java/nl/s22k/chess/search/SearchTestUtil.java new file mode 100644 index 0000000..7aa46ac --- /dev/null +++ b/src/main/java/nl/s22k/chess/search/SearchTestUtil.java @@ -0,0 +1,49 @@ +package nl.s22k.chess.search; + +import nl.s22k.chess.move.MoveWrapper; + +public class SearchTestUtil { + + private static final int MARGIN = 500; + + public static void testTTValues(int score, int bestScore, int depth, int bestMove, int flag, long ttValue, int ply) { + if (ttValue != 0 && TTUtil.getDepth(ttValue) == depth) { + score = TTUtil.getScore(ttValue, ply); + if (TTUtil.getFlag(ttValue) == TTUtil.FLAG_EXACT && flag == TTUtil.FLAG_EXACT) { + if (score != bestScore) { + System.out.println(String.format("exact-exact: TT-score %s != bestScore %s", score, bestScore)); + } + int move = TTUtil.getMove(ttValue); + if (move != bestMove) { + throw new RuntimeException(String.format("Error: TT-move %s != bestMove %s", new MoveWrapper(move), new MoveWrapper(bestMove))); + } + } else if (TTUtil.getFlag(ttValue) == TTUtil.FLAG_LOWER && flag == TTUtil.FLAG_EXACT) { + if (score - MARGIN > bestScore) { + System.out.println(String.format("lower-exact: TT-score %s > bestScore %s", score, bestScore)); + } + } else if (TTUtil.getFlag(ttValue) == TTUtil.FLAG_UPPER && flag == TTUtil.FLAG_EXACT) { + if (score + MARGIN < bestScore) { + System.out.println(String.format("upper-exact: TT-score %s < bestScore %s", score, bestScore)); + } + } else if (TTUtil.getFlag(ttValue) == TTUtil.FLAG_EXACT && flag == TTUtil.FLAG_LOWER) { + if (score + MARGIN < bestScore) { + System.out.println(String.format("exact-lower: TT-score %s < bestScore %s", score, bestScore)); + } + } else if (TTUtil.getFlag(ttValue) == TTUtil.FLAG_EXACT && flag == TTUtil.FLAG_UPPER) { + if (score - MARGIN > bestScore) { + System.out.println(String.format("exact-upper: TT-score %s > bestScore %s", score, bestScore)); + } + } else if (TTUtil.getFlag(ttValue) == TTUtil.FLAG_UPPER && flag == TTUtil.FLAG_LOWER) { + if (score + MARGIN < bestScore) { + System.out.println(String.format("upper-lower: TT-score %s < bestScore %s", score, bestScore)); + } + } else if (TTUtil.getFlag(ttValue) == TTUtil.FLAG_LOWER && flag == TTUtil.FLAG_UPPER) { + if (score - MARGIN > bestScore) { + System.out.println(String.format("lower-upper: TT-score %s > bestScore %s", score, bestScore)); + } + } + } + + } + +} diff --git a/src/main/java/nl/s22k/chess/search/SearchThread.java b/src/main/java/nl/s22k/chess/search/SearchThread.java index 46f0131..a95f745 100644 --- a/src/main/java/nl/s22k/chess/search/SearchThread.java +++ b/src/main/java/nl/s22k/chess/search/SearchThread.java @@ -1,31 +1,111 @@ package nl.s22k.chess.search; -import nl.s22k.chess.ChessBoard; +import nl.s22k.chess.Statistics; +import nl.s22k.chess.Util; +import nl.s22k.chess.engine.EngineConstants; +import nl.s22k.chess.engine.MainEngine; +import nl.s22k.chess.eval.EvalConstants; import nl.s22k.chess.move.MoveGenerator; public class SearchThread extends Thread { - private ChessBoard cb; - private MoveGenerator moveGen; - private int depth; - private int alpha; - private int beta; - - public SearchThread(ChessBoard cb, MoveGenerator moveGen, int depth, int alpha, int beta) { - this.cb = cb; - this.moveGen = moveGen; - this.depth = depth; - this.alpha = alpha; - this.beta = beta; + private int threadNumber; + + // Laser based SMP skip + private int[] SMP_SKIP_DEPTHS = { 1, 1, 2, 2, 2, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4 }; + private int[] SMP_SKIP_AMOUNT = { 1, 2, 1, 2, 3, 1, 2, 3, 4, 5, 1, 2, 3, 4, 5, 6 }; + private int SMP_MAX_CYCLES = SMP_SKIP_AMOUNT.length; + + public SearchThread(final int threadNumber) { + this.threadNumber = threadNumber; } @Override public void run() { - // System.out.println("Starting slave thread. depth=" + depth + " alpha=" + alpha + " beta=" + beta); - NegamaxUtil.calculateBestMove(cb, moveGen, 0, depth, alpha, beta, 0); - NegamaxUtil.mode.compareAndSet(Mode.START, Mode.ANY_SLAVE_READY); - NegamaxUtil.nrOfActiveSlaveThreads.decrementAndGet(); - // System.out.println("Slave thread done"); - } + Thread.currentThread().setName("search " + threadNumber); + int cycleIndex = (threadNumber - 1) % SMP_MAX_CYCLES; + + int depth = 0; + int alpha = Util.SHORT_MIN; + int beta = Util.SHORT_MAX; + int score = Util.SHORT_MIN; + int previousScore; + boolean panic = false; + + MoveGenerator.getInstance(threadNumber).clearHistoryHeuristics(); + + while (depth < MainEngine.maxDepth && NegamaxUtil.isRunning) { + + depth++; + + if (threadNumber == 0) { + Statistics.depth = depth; + } else { + if ((depth + cycleIndex) % SMP_SKIP_DEPTHS[cycleIndex] == 0) { + depth += SMP_SKIP_AMOUNT[cycleIndex]; + } + } + + int delta = EngineConstants.ASPIRATION_WINDOW_DELTA; + + while (NegamaxUtil.isRunning) { + + if (threadNumber == 0 && depth != 1 && !TimeUtil.isTimeLeft()) { + if (panic) { + // only panic once + panic = false; + } else { + break; + } + } + + previousScore = score; + + // System.out.println("start " + threadNumber + " " + depth); + score = NegamaxUtil.calculateBestMove(threadNumber, 0, depth, alpha, beta, 0); + // System.out.println("done " + threadNumber + " " + depth); + + if (threadNumber == 0) { + MainEngine.sendPlyInfo(); + } + if (score + 100 < previousScore && Math.abs(score) < EvalConstants.SCORE_MATE_BOUND) { + panic = true; + } + if (score <= alpha && alpha != Util.SHORT_MIN) { + if (!TimeUtil.isTimeLeft()) { + panic = true; + } + if (score < -1000) { + alpha = Util.SHORT_MIN; + beta = Util.SHORT_MAX; + } else { + alpha = Math.max(alpha - delta, Util.SHORT_MIN); + } + delta *= 2; + } else if (score >= beta && beta != Util.SHORT_MAX) { + if (score > 1000) { + alpha = Util.SHORT_MIN; + beta = Util.SHORT_MAX; + } else { + beta = Math.min(beta + delta, Util.SHORT_MAX); + } + delta *= 2; + } else { + if (EngineConstants.ENABLE_ASPIRATION && depth > 5) { + if (Math.abs(score) > 1000) { + alpha = Util.SHORT_MIN; + beta = Util.SHORT_MAX; + } else { + delta = (delta + EngineConstants.ASPIRATION_WINDOW_DELTA) / 2; + alpha = Math.max(score - delta, Util.SHORT_MIN); + beta = Math.min(score + delta, Util.SHORT_MAX); + } + } + break; + } + } + } + NegamaxUtil.nrOfActiveThreads.decrementAndGet(); + } } diff --git a/src/main/java/nl/s22k/chess/search/TTUtil.java b/src/main/java/nl/s22k/chess/search/TTUtil.java index 5ae7b4b..6f13422 100644 --- a/src/main/java/nl/s22k/chess/search/TTUtil.java +++ b/src/main/java/nl/s22k/chess/search/TTUtil.java @@ -1,31 +1,25 @@ package nl.s22k.chess.search; -import java.util.ArrayList; import java.util.Arrays; -import java.util.List; import nl.s22k.chess.Assert; -import nl.s22k.chess.ChessBoard; -import nl.s22k.chess.ChessConstants.ScoreType; +import nl.s22k.chess.ChessConstants; import nl.s22k.chess.Statistics; import nl.s22k.chess.Util; import nl.s22k.chess.engine.EngineConstants; import nl.s22k.chess.eval.EvalConstants; import nl.s22k.chess.move.MoveUtil; import nl.s22k.chess.move.MoveWrapper; -import nl.s22k.chess.move.TreeMove; public class TTUtil { private static int keyShifts; public static int maxEntries; - private static long[] alwaysReplaceKeys; - private static long[] alwaysReplaceValues; - private static long[] depthReplaceKeys; - private static long[] depthReplaceValues; + private static long[] keys; + private static long[] values; - public static long usageCounter; + private static long usageCounter; public static final int FLAG_EXACT = 0; public static final int FLAG_UPPER = 1; @@ -33,23 +27,20 @@ public class TTUtil { public static long halfMoveCounter = 0; - // ///////////////////// DEPTH //8 bits - private static final int FLAG = 8; // 2 - private static final int MOVE = 10; // 22 - private static final int HALF_MOVE_COUNTER = 32; // 16 + // ///////////////////// DEPTH //12 bits + private static final int FLAG = 12; // 2 + private static final int MOVE = 14; // 22 private static final int SCORE = 48; // 16 public static boolean isInitialized = false; public static void init(final boolean force) { if (force || !isInitialized) { - keyShifts = 64 - EngineConstants.POWER_2_TT_ENTRIES + 1; - maxEntries = (int) Util.POWER_LOOKUP[EngineConstants.POWER_2_TT_ENTRIES - 1]; + keyShifts = 64 - EngineConstants.POWER_2_TT_ENTRIES; + maxEntries = (int) Util.POWER_LOOKUP[EngineConstants.POWER_2_TT_ENTRIES] + 3; - alwaysReplaceKeys = new long[maxEntries]; - alwaysReplaceValues = new long[maxEntries]; - depthReplaceKeys = new long[maxEntries]; - depthReplaceValues = new long[maxEntries]; + keys = new long[maxEntries]; + values = new long[maxEntries]; usageCounter = 0; isInitialized = true; @@ -60,10 +51,8 @@ public static void clearValues() { if (!isInitialized) { return; } - Arrays.fill(alwaysReplaceKeys, 0); - Arrays.fill(alwaysReplaceValues, 0); - Arrays.fill(depthReplaceKeys, 0); - Arrays.fill(depthReplaceValues, 0); + Arrays.fill(keys, 0); + Arrays.fill(values, 0); usageCounter = 0; } @@ -71,32 +60,19 @@ public static long getTTValue(final long key) { final int index = getIndex(key); - final long alwaysValue = alwaysReplaceValues[index]; - final long depthValue = depthReplaceValues[index]; - - if ((alwaysReplaceKeys[index] ^ alwaysValue) == key) { - if (Statistics.ENABLED) { - Statistics.ttHits++; - } - - if ((depthReplaceKeys[index] ^ depthValue) == key && getDepth(depthValue) > getDepth(alwaysValue)) { - return depthValue; - } - - return alwaysValue; - } - - if ((depthReplaceKeys[index] ^ depthValue) == key) { - if (Statistics.ENABLED) { - Statistics.ttHits++; + for (int i = 0; i < 4; i++) { + long value = values[index + i]; + if ((keys[index + i] ^ value) == key) { + if (Statistics.ENABLED) { + Statistics.ttHits++; + } + return value; } - return depthValue; } if (Statistics.ENABLED) { Statistics.ttMisses++; } - return 0; } @@ -104,72 +80,44 @@ private static int getIndex(final long key) { return (int) (key >>> keyShifts); } - public static void setScoreInStatistics(ChessBoard cb) { - if (NegamaxUtil.mode.get() == Mode.STOP) { - return; - } - - final long key = alwaysReplaceKeys[getIndex(cb.zobristKey)]; - final long value = alwaysReplaceValues[getIndex(cb.zobristKey)]; - - if ((key ^ value) != cb.zobristKey) { - // throw new RuntimeException("No best-move found"); - System.out.println("No bestmove found. SMP race condition?!"); - return; - } - Statistics.bestMove.score = getScore(value, 0); - Statistics.bestMove.scoreType = ScoreType.ALPHA; - } - - public static void setBestMoveInStatistics(ChessBoard cb, ScoreType scoreType) { - if (NegamaxUtil.mode.get() == Mode.STOP) { - return; - } - - final long key = alwaysReplaceKeys[getIndex(cb.zobristKey)]; - long value = alwaysReplaceValues[getIndex(cb.zobristKey)]; + public static void addValue(final long key, int score, final int ply, final int depth, final int flag, final int move) { - if ((key ^ value) != cb.zobristKey) { - // throw new RuntimeException("No best-move found"); - System.out.println("No bestmove found. SMP race condition?!"); - return; + if (EngineConstants.ASSERT) { + Assert.isTrue(depth >= 1); + Assert.isTrue(move != 0); + Assert.isTrue(score >= Util.SHORT_MIN && score <= Util.SHORT_MAX); + Assert.isTrue(MoveUtil.getSourcePieceIndex(move) != 0); + Assert.isTrue(score != ChessConstants.SCORE_NOT_RUNNING); } - int move = getMove(value); - List moves = new ArrayList(12); - moves.add(move); - TreeMove bestMove = new TreeMove(move, getScore(value, 0), scoreType); - cb.doMove(move); + final int index = getIndex(key); + int replacedDepth = Integer.MAX_VALUE; + int replacedIndex = index; + for (int i = index; i < index + 4; i++) { - for (int i = 0; i <= 10; i++) { - value = getTTValue(cb.zobristKey); - if (value == 0) { + if (keys[i] == 0) { + if (Statistics.ENABLED) { + usageCounter++; + } + replacedIndex = i; break; } - move = getMove(value); - moves.add(move); - bestMove.appendMove(new TreeMove(move)); - cb.doMove(move); - } - for (int i = moves.size() - 1; i >= 0; i--) { - cb.undoMove(moves.get(i)); - } - - Statistics.bestMove = bestMove; - } - - public static void addValue(final long key, int score, final int ply, final int depth, final int flag, final int cleanMove) { - if (NegamaxUtil.mode.get() != Mode.START) { - return; - } + long currentValue = values[i]; + int currentDepth = getDepth(currentValue); + if ((keys[i] ^ currentValue) == key) { + if (currentDepth > depth && flag != FLAG_EXACT) { + return; + } + replacedIndex = i; + break; + } - if (EngineConstants.ASSERT) { - Assert.isTrue(depth >= 1); - Assert.isTrue(cleanMove != 0); - Assert.isTrue(score >= Util.SHORT_MIN && score <= Util.SHORT_MAX); - Assert.isTrue(MoveUtil.getCleanMove(cleanMove) == cleanMove); - Assert.isTrue(MoveUtil.getSourcePieceIndex(cleanMove) != 0); + // replace the lowest depth + if (currentDepth < replacedDepth) { + replacedIndex = i; + replacedDepth = currentDepth; + } } // correct mate-score @@ -180,37 +128,13 @@ public static void addValue(final long key, int score, final int ply, final int // Math.max because of qsearch score = Math.max(score - ply, Util.SHORT_MIN); } - if (EngineConstants.ASSERT) { Assert.isTrue(score >= Util.SHORT_MIN && score <= Util.SHORT_MAX); } - final int index = getIndex(key); - final long value = createValue(score, cleanMove, flag, depth); - - if (Statistics.ENABLED) { - if (alwaysReplaceKeys[index] == 0) { - usageCounter++; - } - } - - if (depth > getDepth(depthReplaceValues[index]) || halfMoveCounter != getHalfMoveCounter(depthReplaceValues[index])) { - if (Statistics.ENABLED) { - if (depthReplaceKeys[index] == 0) { - usageCounter++; - } - } - depthReplaceKeys[index] = key ^ value; - depthReplaceValues[index] = value; - } - - if (NegamaxUtil.mode.get() != Mode.START) { - return; - } - - // TODO do not store if already stored in depth-TT? - alwaysReplaceKeys[index] = key ^ value; - alwaysReplaceValues[index] = value; + final long value = createValue(score, move, flag, depth); + keys[replacedIndex] = key ^ value; + values[replacedIndex] = value; } public static int getScore(final long value, final int ply) { @@ -230,12 +154,8 @@ public static int getScore(final long value, final int ply) { return score; } - public static int getHalfMoveCounter(final long value) { - return (int) (value >>> HALF_MOVE_COUNTER & 0xffff); - } - public static int getDepth(final long value) { - return (int) (value & 0xff); + return (int) ((value & 0xff) - halfMoveCounter); } public static int getFlag(final long value) { @@ -247,13 +167,12 @@ public static int getMove(final long value) { } // SCORE,HALF_MOVE_COUNTER,MOVE,FLAG,DEPTH - public static long createValue(final long score, final long cleanMove, final long flag, final long depth) { + public static long createValue(final long score, final long move, final long flag, final long depth) { if (EngineConstants.ASSERT) { - Assert.isTrue(cleanMove == MoveUtil.getCleanMove((int) cleanMove)); Assert.isTrue(score >= Util.SHORT_MIN && score <= Util.SHORT_MAX); Assert.isTrue(depth <= 255); } - return score << SCORE | halfMoveCounter << HALF_MOVE_COUNTER | cleanMove << MOVE | flag << FLAG | depth; + return score << SCORE | move << MOVE | flag << FLAG | (depth + halfMoveCounter); } public static String toString(long ttValue) { @@ -261,4 +180,35 @@ public static String toString(long ttValue) { + TTUtil.getFlag(ttValue); } + public static void setSizeMB(int value) { + switch (value) { + case 1: + case 2: + case 4: + case 8: + case 16: + case 32: + case 64: + case 128: + case 256: + case 512: + case 1024: + case 2048: + case 4096: + case 8192: + case 16384: + int power2Entries = (int) (Math.log(value) / Math.log(2) + 16); + if (EngineConstants.POWER_2_TT_ENTRIES != power2Entries) { + EngineConstants.POWER_2_TT_ENTRIES = power2Entries; + init(true); + } + break; + default: + throw new RuntimeException("Hash-size must be between 1-16384 mb and a multiple of 2"); + } + } + + public static long getUsagePercentage() { + return usageCounter * 1000 / TTUtil.maxEntries; + } } diff --git a/src/main/java/nl/s22k/chess/texel/EvalEvaluator.java b/src/main/java/nl/s22k/chess/texel/EvalEvaluator.java index 83cf0cc..4909494 100644 --- a/src/main/java/nl/s22k/chess/texel/EvalEvaluator.java +++ b/src/main/java/nl/s22k/chess/texel/EvalEvaluator.java @@ -13,7 +13,6 @@ import nl.s22k.chess.ChessBoard; import nl.s22k.chess.eval.MaterialCache; import nl.s22k.chess.eval.PawnEvalCache; -import nl.s22k.chess.move.MagicUtil; public class EvalEvaluator { @@ -22,8 +21,6 @@ public class EvalEvaluator { private static ExecutorService executor = Executors.newFixedThreadPool(numberOfThreads); public static void main(String[] args) { - // setup - MagicUtil.init(); // read all fens, including score Map fens = Tuner.loadFens("d:\\backup\\chess\\epds\\quiet-labeled.epd", true, false); diff --git a/src/main/java/nl/s22k/chess/texel/LargestErrorCalculator.java b/src/main/java/nl/s22k/chess/texel/LargestErrorCalculator.java index 3afad36..1cc1b31 100644 --- a/src/main/java/nl/s22k/chess/texel/LargestErrorCalculator.java +++ b/src/main/java/nl/s22k/chess/texel/LargestErrorCalculator.java @@ -6,7 +6,6 @@ import nl.s22k.chess.ChessBoard; import nl.s22k.chess.ChessBoardUtil; import nl.s22k.chess.eval.EvalUtil; -import nl.s22k.chess.move.MagicUtil; public class LargestErrorCalculator { @@ -15,9 +14,6 @@ public class LargestErrorCalculator { public static void main(String[] args) { - // setup - MagicUtil.init(); - Map fens = Tuner.loadFens("d:\\backup\\chess\\epds\\quiet-labeled.epd", true, false); System.out.println(fens.size() + " fens found"); diff --git a/src/main/java/nl/s22k/chess/texel/PsqtTuning.java b/src/main/java/nl/s22k/chess/texel/PsqtTuning.java index dc75b55..c962a17 100644 --- a/src/main/java/nl/s22k/chess/texel/PsqtTuning.java +++ b/src/main/java/nl/s22k/chess/texel/PsqtTuning.java @@ -1,6 +1,7 @@ package nl.s22k.chess.texel; import nl.s22k.chess.ChessConstants; +import nl.s22k.chess.eval.EvalConstants; public class PsqtTuning extends Tuning { @@ -52,13 +53,13 @@ public void addStep(int i) { psqtValues[ChessConstants.WHITE][i] += step; // add to white mirrored - psqtValues[ChessConstants.WHITE][TexelConstants.MIRRORED_LEFT_RIGHT[i]] += step; + psqtValues[ChessConstants.WHITE][EvalConstants.MIRRORED_LEFT_RIGHT[i]] += step; // add to black - psqtValues[ChessConstants.BLACK][TexelConstants.MIRRORED_UP_DOWN[i]] -= step; + psqtValues[ChessConstants.BLACK][EvalConstants.MIRRORED_UP_DOWN[i]] -= step; // add to black mirrored - psqtValues[ChessConstants.BLACK][TexelConstants.MIRRORED_LEFT_RIGHT[TexelConstants.MIRRORED_UP_DOWN[i]]] -= step; + psqtValues[ChessConstants.BLACK][EvalConstants.MIRRORED_LEFT_RIGHT[EvalConstants.MIRRORED_UP_DOWN[i]]] -= step; } public void removeStep(int i) { @@ -66,13 +67,13 @@ public void removeStep(int i) { psqtValues[ChessConstants.WHITE][i] -= step; // remove from white mirrored - psqtValues[ChessConstants.WHITE][TexelConstants.MIRRORED_LEFT_RIGHT[i]] -= step; + psqtValues[ChessConstants.WHITE][EvalConstants.MIRRORED_LEFT_RIGHT[i]] -= step; // remove from black - psqtValues[ChessConstants.BLACK][TexelConstants.MIRRORED_UP_DOWN[i]] += step; + psqtValues[ChessConstants.BLACK][EvalConstants.MIRRORED_UP_DOWN[i]] += step; // remove from black mirrored - psqtValues[ChessConstants.BLACK][TexelConstants.MIRRORED_LEFT_RIGHT[TexelConstants.MIRRORED_UP_DOWN[i]]] += step; + psqtValues[ChessConstants.BLACK][EvalConstants.MIRRORED_LEFT_RIGHT[EvalConstants.MIRRORED_UP_DOWN[i]]] += step; } public boolean scoreIsZero(int i) { @@ -84,14 +85,14 @@ public int numberOfParameters() { } public boolean skip(int i) { - return skipValues.contains(i) || (i & 7) > 3; + return skipValues.contains(i); } public static String getArrayFriendlyFormatted(int[] values) { StringBuilder sb = new StringBuilder("\n"); for (int i = 7; i >= 0; i--) { sb.append(" "); - for (int j = 0; j < 8; j++) { + for (int j = 7; j >= 0; j--) { sb.append(String.format("%3s", values[i * 8 + j])).append(","); } sb.append("\n"); diff --git a/src/main/java/nl/s22k/chess/texel/TestSetStatistics.java b/src/main/java/nl/s22k/chess/texel/TestSetStatistics.java index 24f0ae4..453b16f 100644 --- a/src/main/java/nl/s22k/chess/texel/TestSetStatistics.java +++ b/src/main/java/nl/s22k/chess/texel/TestSetStatistics.java @@ -5,7 +5,6 @@ import nl.s22k.chess.ChessBoard; import nl.s22k.chess.ChessBoardUtil; -import nl.s22k.chess.move.MagicUtil; public class TestSetStatistics { @@ -13,7 +12,6 @@ public class TestSetStatistics { public static void main(String[] args) { - MagicUtil.init(); Map fens = Tuner.loadFens("d:\\backup\\chess\\epds\\quiet-labeled.epd", true, false); System.out.println(fens.size() + " fens found"); diff --git a/src/main/java/nl/s22k/chess/texel/TexelConstants.java b/src/main/java/nl/s22k/chess/texel/TexelConstants.java deleted file mode 100644 index cab3682..0000000 --- a/src/main/java/nl/s22k/chess/texel/TexelConstants.java +++ /dev/null @@ -1,19 +0,0 @@ -package nl.s22k.chess.texel; - -public class TexelConstants { - - public static final int[] MIRRORED_LEFT_RIGHT = new int[64]; - static { - for (int i = 0; i < 64; i++) { - MIRRORED_LEFT_RIGHT[i] = (i / 8) * 8 + 7 - (i & 7); - } - } - - public static final int[] MIRRORED_UP_DOWN = new int[64]; - static { - for (int i = 0; i < 64; i++) { - MIRRORED_UP_DOWN[i] = (7 - i / 8) * 8 + (i & 7); - } - } - -} diff --git a/src/main/java/nl/s22k/chess/texel/Tuner.java b/src/main/java/nl/s22k/chess/texel/Tuner.java index baa6427..9af9b29 100644 --- a/src/main/java/nl/s22k/chess/texel/Tuner.java +++ b/src/main/java/nl/s22k/chess/texel/Tuner.java @@ -20,7 +20,6 @@ import nl.s22k.chess.eval.EvalConstants; import nl.s22k.chess.eval.MaterialCache; import nl.s22k.chess.eval.PawnEvalCache; -import nl.s22k.chess.move.MagicUtil; import nl.s22k.chess.move.MoveGenerator; public class Tuner { @@ -59,6 +58,7 @@ public static List getTuningObjects() { // tunings.add(new Tuning(EvalConstants.ROOK_TRAPPED, STEP, "Rook trapped")); // tunings.add(new Tuning(EvalConstants.ONLY_MAJOR_DEFENDERS, STEP, "Only major defenders", 0)); // tunings.add(new Tuning(EvalConstants.NIGHT_PAWN, STEP, "Night pawn")); + // tunings.add(new Tuning(EvalConstants.ROOK_PAWN, STEP, "Rook pawn")); // tunings.add(new Tuning(EvalConstants.BISHOP_PAWN, STEP, "Bishop pawn")); // tunings.add(new Tuning(EvalConstants.SPACE, 1, "Space", 0, 1, 2, 3, 4)); // @@ -123,9 +123,6 @@ public static List getTuningObjects() { } public static void main(String[] args) { - // setup - MagicUtil.init(); - // read all fens, including score Map fens = loadFens("d:\\backup\\chess\\epds\\quiet-labeled.epd", true, false); System.out.println("Fens found : " + fens.size()); diff --git a/src/main/java/nl/s22k/chess/texel/Tuning.java b/src/main/java/nl/s22k/chess/texel/Tuning.java index 5207142..d9da464 100644 --- a/src/main/java/nl/s22k/chess/texel/Tuning.java +++ b/src/main/java/nl/s22k/chess/texel/Tuning.java @@ -13,10 +13,6 @@ public class Tuning { public int tunedValues; public boolean showAverage; - public Tuning(int[] values, int step, String name) { - this(values, step, name, false, -1); - } - public Tuning(int[] values, int step, String name, Integer... skipValues) { this(values, step, name, false, skipValues); } diff --git a/src/main/java/nl/s22k/chess/unittests/CheckTest.java b/src/main/java/nl/s22k/chess/unittests/CheckTest.java index eac2766..cf3ed65 100644 --- a/src/main/java/nl/s22k/chess/unittests/CheckTest.java +++ b/src/main/java/nl/s22k/chess/unittests/CheckTest.java @@ -1,22 +1,15 @@ package nl.s22k.chess.unittests; -import org.junit.BeforeClass; import org.junit.Test; import nl.s22k.chess.ChessBoard; import nl.s22k.chess.ChessBoardUtil; import nl.s22k.chess.ChessConstants; import nl.s22k.chess.Statistics; -import nl.s22k.chess.move.MagicUtil; import nl.s22k.chess.move.MoveUtil; public class CheckTest { - @BeforeClass - public static void init() { - MagicUtil.init(); - } - // @Test // public void mateInOneTest() { // Statistics.reset(); diff --git a/src/main/java/nl/s22k/chess/unittests/DrawTest.java b/src/main/java/nl/s22k/chess/unittests/DrawTest.java index 892832e..e9e38d7 100644 --- a/src/main/java/nl/s22k/chess/unittests/DrawTest.java +++ b/src/main/java/nl/s22k/chess/unittests/DrawTest.java @@ -1,21 +1,14 @@ package nl.s22k.chess.unittests; -import org.junit.BeforeClass; import org.junit.Test; import nl.s22k.chess.ChessBoard; import nl.s22k.chess.ChessBoardUtil; import nl.s22k.chess.Statistics; -import nl.s22k.chess.move.MagicUtil; import nl.s22k.chess.search.NegamaxUtil; public class DrawTest { - @BeforeClass - public static void init() { - MagicUtil.init(); - } - @Test public void insufficientMaterialTest() { System.out.println("insufficientMaterialTest"); diff --git a/src/main/java/nl/s22k/chess/unittests/EvalTest.java b/src/main/java/nl/s22k/chess/unittests/EvalTest.java index 137e63e..3d1cf0c 100644 --- a/src/main/java/nl/s22k/chess/unittests/EvalTest.java +++ b/src/main/java/nl/s22k/chess/unittests/EvalTest.java @@ -1,25 +1,18 @@ package nl.s22k.chess.unittests; -import org.junit.BeforeClass; import org.junit.Test; import nl.s22k.chess.ChessBoard; import nl.s22k.chess.ChessBoardUtil; import nl.s22k.chess.ChessConstants; import nl.s22k.chess.eval.EvalUtil; -import nl.s22k.chess.move.MagicUtil; import nl.s22k.chess.move.MoveUtil; public class EvalTest { - @BeforeClass - public static void init() { - MagicUtil.init(); - } - @Test public void test() { - ChessBoard cb = ChessBoardUtil.getNewCB("rn1qkbnr/pp1b3p/4ppp1/3p3Q/3p4/2PBP2P/PP3PP1/RNB2KNR w kq - 0 1 "); + ChessBoard cb = ChessBoardUtil.getNewCB("8/2k5/5PK1/8/8/8/8/1r6 w -"); EvalUtil.calculateScore(cb); } diff --git a/src/main/java/nl/s22k/chess/unittests/KPKTest.java b/src/main/java/nl/s22k/chess/unittests/KPKTest.java index 0573cc5..1abef0b 100644 --- a/src/main/java/nl/s22k/chess/unittests/KPKTest.java +++ b/src/main/java/nl/s22k/chess/unittests/KPKTest.java @@ -9,15 +9,12 @@ import nl.s22k.chess.ChessBoardUtil; import nl.s22k.chess.ChessConstants; import nl.s22k.chess.eval.KPKBitbase; -import nl.s22k.chess.move.MagicUtil; import nl.s22k.chess.texel.Tuner; public class KPKTest { @Test public void test() { - // setup - MagicUtil.init(); // read all fens, including score Map fens = Tuner.loadFens("d:\\backup\\chess\\epds\\quiet-labeled.epd", true, true); diff --git a/src/main/java/nl/s22k/chess/unittests/KingSafetyTest.java b/src/main/java/nl/s22k/chess/unittests/KingSafetyTest.java index f8f160d..b8f904b 100644 --- a/src/main/java/nl/s22k/chess/unittests/KingSafetyTest.java +++ b/src/main/java/nl/s22k/chess/unittests/KingSafetyTest.java @@ -1,21 +1,14 @@ package nl.s22k.chess.unittests; -import org.junit.BeforeClass; import org.junit.Test; import nl.s22k.chess.ChessBoard; import nl.s22k.chess.ChessBoardUtil; import nl.s22k.chess.eval.EvalUtil; import nl.s22k.chess.eval.KingSafetyEval; -import nl.s22k.chess.move.MagicUtil; public class KingSafetyTest { - @BeforeClass - public static void init() { - MagicUtil.init(); - } - @Test public void onePieceAttackingTest() { System.out.println("One piece attacking test"); diff --git a/src/main/java/nl/s22k/chess/unittests/MagicTest.java b/src/main/java/nl/s22k/chess/unittests/MagicTest.java index 1400f69..b74e977 100644 --- a/src/main/java/nl/s22k/chess/unittests/MagicTest.java +++ b/src/main/java/nl/s22k/chess/unittests/MagicTest.java @@ -8,8 +8,6 @@ public class MagicTest { @Test public void test() { - MagicUtil.init(); - System.out.println("Performing board tests"); // Rival chess diff --git a/src/main/java/nl/s22k/chess/unittests/MainTest.java b/src/main/java/nl/s22k/chess/unittests/MainTest.java index f29855c..4e5df92 100644 --- a/src/main/java/nl/s22k/chess/unittests/MainTest.java +++ b/src/main/java/nl/s22k/chess/unittests/MainTest.java @@ -3,7 +3,6 @@ import nl.s22k.chess.ChessBoard; import nl.s22k.chess.ChessBoardUtil; import nl.s22k.chess.Statistics; -import nl.s22k.chess.move.MagicUtil; import nl.s22k.chess.search.NegamaxUtil; import nl.s22k.chess.search.TimeUtil; @@ -31,8 +30,6 @@ public class MainTest { public static void main(String[] args) { - MagicUtil.init(); - ChessBoard cb = ChessBoardUtil.getNewCB(FEN_STANDARD_OPENING); TimeUtil.setSimpleTimeWindow(5000); // NegamaxUtil.maxDepth = 20; diff --git a/src/main/java/nl/s22k/chess/unittests/RepetitionTest.java b/src/main/java/nl/s22k/chess/unittests/RepetitionTest.java deleted file mode 100644 index dd929d2..0000000 --- a/src/main/java/nl/s22k/chess/unittests/RepetitionTest.java +++ /dev/null @@ -1,43 +0,0 @@ -package nl.s22k.chess.unittests; - -import org.junit.BeforeClass; -import org.junit.Test; - -import nl.s22k.chess.ChessBoard; -import nl.s22k.chess.ChessBoardUtil; -import nl.s22k.chess.ChessConstants; -import nl.s22k.chess.Statistics; -import nl.s22k.chess.move.MagicUtil; -import nl.s22k.chess.move.MoveUtil; - -public class RepetitionTest { - - @BeforeClass - public static void init() { - MagicUtil.init(); - } - - @Test - public void doRepetitionTest() { - Statistics.reset(); - ChessBoard cb = ChessBoardUtil.getNewCB("8/8/4k3/5p2/5K2/8/8/8 w - - 0 63 "); - - int move1 = MoveUtil.createMove(26, 19, ChessConstants.KING); - int move2 = MoveUtil.createMove(43, 36, ChessConstants.KING); - int move3 = MoveUtil.createMove(19, 26, ChessConstants.KING); - int move4 = MoveUtil.createMove(36, 43, ChessConstants.KING); - - System.out.println(cb.zobristKey); - cb.doMove(move1); - System.out.println(cb.zobristKey); - cb.doMove(move2); - System.out.println(cb.zobristKey); - cb.doMove(move3); - System.out.println(cb.zobristKey); - cb.doMove(move4); - System.out.println(cb.zobristKey); - - System.out.println(cb.isRepetition()); - } - -} diff --git a/src/main/java/nl/s22k/chess/unittests/TTTest.java b/src/main/java/nl/s22k/chess/unittests/TTTest.java index 2a30203..dd725f8 100644 --- a/src/main/java/nl/s22k/chess/unittests/TTTest.java +++ b/src/main/java/nl/s22k/chess/unittests/TTTest.java @@ -8,7 +8,6 @@ import nl.s22k.chess.ChessBoard; import nl.s22k.chess.ChessBoardUtil; import nl.s22k.chess.ChessConstants; -import nl.s22k.chess.move.MagicUtil; import nl.s22k.chess.move.MoveUtil; import nl.s22k.chess.search.TTUtil; @@ -17,7 +16,6 @@ public class TTTest { @BeforeClass public static void init() { TTUtil.init(true); - MagicUtil.init(); } @Test @@ -36,7 +34,6 @@ public void testKey() { System.out.println("depth: " + TTUtil.getDepth(value)); System.out.println("flag: " + TTUtil.getFlag(value)); System.out.println("move: " + TTUtil.getMove(value)); - System.out.println("counter: " + TTUtil.getHalfMoveCounter(value)); Random r = new Random(); long zk = r.nextLong(); @@ -47,7 +44,6 @@ public void testKey() { System.out.println("depth: " + TTUtil.getDepth(ttValue)); System.out.println("flag: " + TTUtil.getFlag(ttValue)); System.out.println("move: " + TTUtil.getMove(ttValue)); - System.out.println("counter: " + TTUtil.getHalfMoveCounter(value)); } @Test diff --git a/src/test/java/nl/s22k/test/chess/CoverageTest.java b/src/test/java/nl/s22k/test/chess/CoverageTest.java index f5f9b02..87d089a 100644 --- a/src/test/java/nl/s22k/test/chess/CoverageTest.java +++ b/src/test/java/nl/s22k/test/chess/CoverageTest.java @@ -4,7 +4,6 @@ import nl.s22k.chess.ChessBoard; import nl.s22k.chess.ChessBoardUtil; -import nl.s22k.chess.move.MagicUtil; import nl.s22k.chess.move.MoveWrapper; import nl.s22k.chess.search.NegamaxUtil; import nl.s22k.chess.search.TimeUtil; @@ -14,8 +13,6 @@ public class CoverageTest { @Test public void doTest() { - MagicUtil.init(); - ChessBoard cb = ChessBoardUtil.getNewCB(MainTest.FEN_STANDARD_MIDDLEGAME); /* time-managed */ @@ -25,7 +22,6 @@ public void doTest() { // @Test public void doTestMovesPerformed() { - MagicUtil.init(); ChessBoard cb = ChessBoardUtil.getNewCB(); final String moves = "d2d4 d7d5 c2c4 c7c6 g1f3 g8f6 b1c3 d5c4 a2a4 b8a6 e2e3 c8g4 f1c4 e7e6 h2h3 g4h5 "