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
+ 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 "