Skip to content

Commit

Permalink
1.10
Browse files Browse the repository at this point in the history
  • Loading branch information
Senders authored and Senders committed Jul 9, 2018
1 parent a53cb3c commit fa9497e
Show file tree
Hide file tree
Showing 50 changed files with 1,692 additions and 1,934 deletions.
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,13 +19,14 @@ Score is about 3000 elo.
- aspiration window
- evaluation parameters tuned using the Texel's tuning method
- tapered eval
- lazy SMP
- no openingbook or endgame tablebases
- no pondering

## Future
- multi-threading
- singular extensions
- improved king safety
- ...


_"Simplicity is the soul of efficiency"_ - Austin Freeman -
Expand Down
2 changes: 1 addition & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
<!-- artifactId>${engineName}</artifactId -->
<artifactId>chess22k</artifactId>

<version>1.9</version>
<version>1.10</version>

<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
Expand Down
6 changes: 6 additions & 0 deletions release-notes.txt
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@

1.10 - 09-07-2018 - 3000 elo
- lazy SMP
- several simplifications and small updates


1.9 - 23-04-2018 - 3000 elo
- fixed some crashes
- improved sorting efficiency
Expand Down
17 changes: 17 additions & 0 deletions src/main/java/nl/s22k/chess/Assert.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package nl.s22k.chess;

public class Assert {

public static void isTrue(boolean condition) {
if (!condition) {
throw new AssertionError();
}
}

public static void isTrue(boolean condition, String message) {
if (!condition) {
throw new AssertionError(message);
}
}

}
1 change: 1 addition & 0 deletions src/main/java/nl/s22k/chess/Bitboard.java
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,7 @@ public class Bitboard {
public static final long RANK_34567 = RANK_3 | RANK_4 | RANK_5 | RANK_6 | RANK_7;
public static final long RANK_PROMOTION[] = { RANK_7, RANK_2 };
public static final long RANK_NON_PROMOTION[] = { ~RANK_PROMOTION[0], ~RANK_PROMOTION[1] };
public static final long RANK_FIRST[] = { RANK_1, RANK_8 };

// files
public static final long FILE_A = A1 | A2 | A3 | A4 | A5 | A6 | A7 | A8;
Expand Down
18 changes: 9 additions & 9 deletions src/main/java/nl/s22k/chess/CastlingUtil.java
Original file line number Diff line number Diff line change
Expand Up @@ -143,8 +143,8 @@ public static void castleRookUpdateKeyAndPsqt(final ChessBoard cb, final int kin
cb.pieceIndexes[0] = EMPTY;
cb.pieceIndexes[2] = ROOK;
cb.zobristKey ^= ChessBoard.zkPieceValues[0][WHITE][ROOK] ^ ChessBoard.zkPieceValues[2][WHITE][ROOK];
cb.psqtScore += EvalConstants.PSQT_SCORES[ROOK][cb.colorToMove][2] - EvalConstants.PSQT_SCORES[ROOK][cb.colorToMove][0];
cb.psqtScoreEg += EvalConstants.PSQT_EG_SCORES[ROOK][cb.colorToMove][2] - EvalConstants.PSQT_EG_SCORES[ROOK][cb.colorToMove][0];
cb.psqtScore += EvalConstants.PSQT[ROOK][cb.colorToMove][2] - EvalConstants.PSQT[ROOK][cb.colorToMove][0];
cb.psqtScoreEg += EvalConstants.PSQT_EG[ROOK][cb.colorToMove][2] - EvalConstants.PSQT_EG[ROOK][cb.colorToMove][0];
return;
case 57:
// black rook from 56 to 58
Expand All @@ -153,8 +153,8 @@ public static void castleRookUpdateKeyAndPsqt(final ChessBoard cb, final int kin
cb.pieceIndexes[56] = EMPTY;
cb.pieceIndexes[58] = ROOK;
cb.zobristKey ^= ChessBoard.zkPieceValues[56][BLACK][ROOK] ^ ChessBoard.zkPieceValues[58][BLACK][ROOK];
cb.psqtScore += EvalConstants.PSQT_SCORES[ROOK][cb.colorToMove][58] - EvalConstants.PSQT_SCORES[ROOK][cb.colorToMove][56];
cb.psqtScoreEg += EvalConstants.PSQT_EG_SCORES[ROOK][cb.colorToMove][58] - EvalConstants.PSQT_EG_SCORES[ROOK][cb.colorToMove][56];
cb.psqtScore += EvalConstants.PSQT[ROOK][cb.colorToMove][58] - EvalConstants.PSQT[ROOK][cb.colorToMove][56];
cb.psqtScoreEg += EvalConstants.PSQT_EG[ROOK][cb.colorToMove][58] - EvalConstants.PSQT_EG[ROOK][cb.colorToMove][56];
return;
case 5:
// white rook from 7 to 4
Expand All @@ -163,8 +163,8 @@ public static void castleRookUpdateKeyAndPsqt(final ChessBoard cb, final int kin
cb.pieceIndexes[7] = EMPTY;
cb.pieceIndexes[4] = ROOK;
cb.zobristKey ^= ChessBoard.zkPieceValues[7][WHITE][ROOK] ^ ChessBoard.zkPieceValues[4][WHITE][ROOK];
cb.psqtScore += EvalConstants.PSQT_SCORES[ROOK][cb.colorToMove][4] - EvalConstants.PSQT_SCORES[ROOK][cb.colorToMove][7];
cb.psqtScoreEg += EvalConstants.PSQT_EG_SCORES[ROOK][cb.colorToMove][4] - EvalConstants.PSQT_EG_SCORES[ROOK][cb.colorToMove][7];
cb.psqtScore += EvalConstants.PSQT[ROOK][cb.colorToMove][4] - EvalConstants.PSQT[ROOK][cb.colorToMove][7];
cb.psqtScoreEg += EvalConstants.PSQT_EG[ROOK][cb.colorToMove][4] - EvalConstants.PSQT_EG[ROOK][cb.colorToMove][7];
return;
case 61:
// black rook from 63 to 60
Expand All @@ -173,8 +173,8 @@ public static void castleRookUpdateKeyAndPsqt(final ChessBoard cb, final int kin
cb.pieceIndexes[63] = EMPTY;
cb.pieceIndexes[60] = ROOK;
cb.zobristKey ^= ChessBoard.zkPieceValues[63][BLACK][ROOK] ^ ChessBoard.zkPieceValues[60][BLACK][ROOK];
cb.psqtScore += EvalConstants.PSQT_SCORES[ROOK][cb.colorToMove][60] - EvalConstants.PSQT_SCORES[ROOK][cb.colorToMove][63];
cb.psqtScoreEg += EvalConstants.PSQT_EG_SCORES[ROOK][cb.colorToMove][60] - EvalConstants.PSQT_EG_SCORES[ROOK][cb.colorToMove][63];
cb.psqtScore += EvalConstants.PSQT[ROOK][cb.colorToMove][60] - EvalConstants.PSQT[ROOK][cb.colorToMove][63];
cb.psqtScoreEg += EvalConstants.PSQT_EG[ROOK][cb.colorToMove][60] - EvalConstants.PSQT_EG[ROOK][cb.colorToMove][63];
return;
}
throw new RuntimeException("Incorrect king castling to-index: " + kingToIndex);
Expand All @@ -189,7 +189,7 @@ public static boolean isValidCastlingMove(final ChessBoard cb, final int fromInd
return false;
}

long kingIndexes = ChessConstants.ROOK_IN_BETWEEN[fromIndex][toIndex] | Util.POWER_LOOKUP[toIndex];
long kingIndexes = ChessConstants.IN_BETWEEN[fromIndex][toIndex] | Util.POWER_LOOKUP[toIndex];
while (kingIndexes != 0) {
// king does not move through a checked position?
if (CheckUtil.isInCheckIncludingKing(Long.numberOfTrailingZeros(kingIndexes), cb.colorToMove, cb.pieces[cb.colorToMoveInverse], cb.allPieces,
Expand Down
3 changes: 1 addition & 2 deletions src/main/java/nl/s22k/chess/CheckUtil.java
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,7 @@ public static long getCheckingPieces(final ChessBoard cb, final int sourcePieceI
case ROOK:
return cb.pieces[cb.colorToMoveInverse][ROOK] & MagicUtil.getRookMoves(cb.kingIndex[cb.colorToMove], cb.allPieces);
case QUEEN:
return cb.pieces[cb.colorToMoveInverse][QUEEN] & MagicUtil.getRookMoves(cb.kingIndex[cb.colorToMove], cb.allPieces) |
cb.pieces[cb.colorToMoveInverse][QUEEN] & MagicUtil.getBishopMoves(cb.kingIndex[cb.colorToMove], cb.allPieces);
return cb.pieces[cb.colorToMoveInverse][QUEEN] & MagicUtil.getQueenMoves(cb.kingIndex[cb.colorToMove], cb.allPieces);
default:
//king can never set the other king in check
return 0;
Expand Down
115 changes: 53 additions & 62 deletions src/main/java/nl/s22k/chess/ChessBoard.java
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@
import static nl.s22k.chess.ChessConstants.QUEEN;
import static nl.s22k.chess.ChessConstants.ROOK;
import static nl.s22k.chess.ChessConstants.WHITE;
import static org.junit.Assert.assertTrue;

import java.security.SecureRandom;
import java.util.Arrays;
Expand Down Expand Up @@ -49,31 +48,31 @@ public final class ChessBoard {
public long zobristKey, pawnZobristKey;
public long checkingPieces, pinnedPieces, discoveredPieces;

public long moveCount;

/** which piece is on which square */
public final int[] pieceIndexes = new int[64];
public final int[] kingIndex = new int[2];
public final long[] kingArea = new long[2];

public int moveCounter = 0;
private final int[] psqtScoreHistory = new int[EngineConstants.MAX_MOVES];
private final int[] psqtScoreEgHistory = new int[EngineConstants.MAX_MOVES];
private final int[] castlingHistory = new int[EngineConstants.MAX_MOVES];
private final int[] epIndexHistory = new int[EngineConstants.MAX_MOVES];
public final int[] psqtScoreHistory = new int[EngineConstants.MAX_MOVES];
public final int[] psqtScoreEgHistory = 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];
private final long[] pawnZobristKeyHistory = new long[EngineConstants.MAX_MOVES];
private final long[] checkingPiecesHistory = new long[EngineConstants.MAX_MOVES];
private final long[] pinnedPiecesHistory = new long[EngineConstants.MAX_MOVES];
private final long[] discoveredPiecesHistory = new long[EngineConstants.MAX_MOVES];
public final long[] pawnZobristKeyHistory = new long[EngineConstants.MAX_MOVES];
public final long[] checkingPiecesHistory = new long[EngineConstants.MAX_MOVES];
public final long[] pinnedPiecesHistory = new long[EngineConstants.MAX_MOVES];
public final long[] discoveredPiecesHistory = new long[EngineConstants.MAX_MOVES];

// attack boards
public final long[][] attacks = new long[2][7];
public final long[] attacksAll = new long[2];
public final long[] attacksWithoutKing = new long[2];
public final int[] kingAttackersFlag = new int[2];

public final int[] mobilityScore = new int[2];
public long passedPawns;
public final long[] outposts = new long[2];
public long passedPawnsAndOutposts;

public static ChessBoard getInstance() {
return instance;
Expand Down Expand Up @@ -135,7 +134,7 @@ public boolean isDrawishByMaterial(final int color) {

// material difference bigger than bishop + 50
// TODO do not include pawn score (why...?)
return EvalUtil.getImbalances(this) * ChessConstants.COLOR_FACTOR[color] < EvalConstants.MATERIAL_SCORES[BISHOP] + 50;
return EvalUtil.getImbalances(this) * ChessConstants.COLOR_FACTOR[color] < EvalConstants.MATERIAL[BISHOP] + 50;
}

public boolean isDrawByMaterial(final int color) {
Expand Down Expand Up @@ -271,7 +270,7 @@ public void undoNullMove() {

public void doMove(int move) {

Statistics.moveCount++;
moveCount++;

final int fromIndex = MoveUtil.getFromIndex(move);
final long fromMask = Util.POWER_LOOKUP[fromIndex];
Expand All @@ -282,8 +281,8 @@ public void doMove(int move) {
final int attackedPieceIndex = MoveUtil.getAttackedPieceIndex(move);

if (EngineConstants.ASSERT) {
assertTrue(attackedPieceIndex != KING);
assertTrue(attackedPieceIndex == 0 || (Util.POWER_LOOKUP[toIndex] & friendlyPieces[colorToMove]) == 0);
Assert.isTrue(attackedPieceIndex != KING);
Assert.isTrue(attackedPieceIndex == 0 || (Util.POWER_LOOKUP[toIndex] & friendlyPieces[colorToMove]) == 0);
}

pushHistoryValues();
Expand All @@ -296,9 +295,8 @@ public void doMove(int move) {
pieceIndexes[fromIndex] = EMPTY;
pieceIndexes[toIndex] = sourcePieceIndex;
pieces[colorToMove][sourcePieceIndex] ^= fromToMask;
psqtScore += EvalConstants.PSQT_SCORES[sourcePieceIndex][colorToMove][toIndex] - EvalConstants.PSQT_SCORES[sourcePieceIndex][colorToMove][fromIndex];
psqtScoreEg += EvalConstants.PSQT_EG_SCORES[sourcePieceIndex][colorToMove][toIndex]
- EvalConstants.PSQT_EG_SCORES[sourcePieceIndex][colorToMove][fromIndex];
psqtScore += EvalConstants.PSQT[sourcePieceIndex][colorToMove][toIndex] - EvalConstants.PSQT[sourcePieceIndex][colorToMove][fromIndex];
psqtScoreEg += EvalConstants.PSQT_EG[sourcePieceIndex][colorToMove][toIndex] - EvalConstants.PSQT_EG[sourcePieceIndex][colorToMove][fromIndex];

switch (sourcePieceIndex) {
case PAWN:
Expand All @@ -310,15 +308,13 @@ public void doMove(int move) {
pieces[colorToMove][MoveUtil.getMoveType(move)] |= toMask;
pieceIndexes[toIndex] = MoveUtil.getMoveType(move);
zobristKey ^= zkPieceValues[toIndex][colorToMove][PAWN] ^ zkPieceValues[toIndex][colorToMove][MoveUtil.getMoveType(move)];
psqtScore += EvalConstants.PSQT_SCORES[MoveUtil.getMoveType(move)][colorToMove][toIndex]
- EvalConstants.PSQT_SCORES[PAWN][colorToMove][toIndex];
psqtScoreEg += EvalConstants.PSQT_EG_SCORES[MoveUtil.getMoveType(move)][colorToMove][toIndex]
- EvalConstants.PSQT_EG_SCORES[PAWN][colorToMove][toIndex];
psqtScore += EvalConstants.PSQT[MoveUtil.getMoveType(move)][colorToMove][toIndex] - EvalConstants.PSQT[PAWN][colorToMove][toIndex];
psqtScoreEg += EvalConstants.PSQT_EG[MoveUtil.getMoveType(move)][colorToMove][toIndex] - EvalConstants.PSQT_EG[PAWN][colorToMove][toIndex];
} else {
pawnZobristKey ^= zkPieceValues[toIndex][colorToMove][PAWN];
// check 2-move
if (ChessConstants.ROOK_IN_BETWEEN[fromIndex][toIndex] != 0) {
epIndex = Long.numberOfTrailingZeros(ChessConstants.ROOK_IN_BETWEEN[fromIndex][toIndex]);
if (ChessConstants.IN_BETWEEN[fromIndex][toIndex] != 0) {
epIndex = Long.numberOfTrailingZeros(ChessConstants.IN_BETWEEN[fromIndex][toIndex]);
zobristKey ^= zkEPIndex[epIndex];
}
}
Expand Down Expand Up @@ -355,8 +351,8 @@ public void doMove(int move) {
pieceIndexes[toIndex] = EMPTY;
}
pawnZobristKey ^= zkPieceValues[toIndex][colorToMoveInverse][PAWN];
psqtScore -= EvalConstants.PSQT_SCORES[attackedPieceIndex][colorToMoveInverse][toIndex];
psqtScoreEg -= EvalConstants.PSQT_EG_SCORES[attackedPieceIndex][colorToMoveInverse][toIndex];
psqtScore -= EvalConstants.PSQT[attackedPieceIndex][colorToMoveInverse][toIndex];
psqtScoreEg -= EvalConstants.PSQT_EG[attackedPieceIndex][colorToMoveInverse][toIndex];
friendlyPieces[colorToMoveInverse] ^= toMask;
pieces[colorToMoveInverse][attackedPieceIndex] ^= toMask;
zobristKey ^= zkPieceValues[toIndex][colorToMoveInverse][attackedPieceIndex];
Expand All @@ -370,8 +366,8 @@ public void doMove(int move) {
}
default:
phase += EvalConstants.PHASE[attackedPieceIndex];
psqtScore -= EvalConstants.PSQT_SCORES[attackedPieceIndex][colorToMoveInverse][toIndex];
psqtScoreEg -= EvalConstants.PSQT_EG_SCORES[attackedPieceIndex][colorToMoveInverse][toIndex];
psqtScore -= EvalConstants.PSQT[attackedPieceIndex][colorToMoveInverse][toIndex];
psqtScoreEg -= EvalConstants.PSQT_EG[attackedPieceIndex][colorToMoveInverse][toIndex];
friendlyPieces[colorToMoveInverse] ^= toMask;
pieces[colorToMoveInverse][attackedPieceIndex] ^= toMask;
zobristKey ^= zkPieceValues[toIndex][colorToMoveInverse][attackedPieceIndex];
Expand Down Expand Up @@ -404,41 +400,28 @@ public void doMove(int move) {

public void setPinnedAndDiscoPieces() {

long enemyPiece;
int enemyColor;

pinnedPieces = 0;
discoveredPieces = 0;

for (int kingColor = ChessConstants.WHITE; kingColor <= ChessConstants.BLACK; kingColor++) {

enemyColor = 1 - kingColor;
int enemyColor = 1 - kingColor;

if (!MaterialUtil.hasSlidingPieces(materialKey, enemyColor)) {
continue;
}

// bishop and queen
enemyPiece = (pieces[enemyColor][BISHOP] | pieces[enemyColor][QUEEN]) & MagicUtil.bishopMovesEmptyBoard[kingIndex[kingColor]];
long enemyPiece = (pieces[enemyColor][BISHOP] | pieces[enemyColor][QUEEN]) & MagicUtil.bishopMovesEmptyBoard[kingIndex[kingColor]]
| (pieces[enemyColor][ROOK] | pieces[enemyColor][QUEEN]) & MagicUtil.rookMovesEmptyBoard[kingIndex[kingColor]];
while (enemyPiece != 0) {
final long checkedPiece = ChessConstants.BISHOP_IN_BETWEEN[kingIndex[kingColor]][Long.numberOfTrailingZeros(enemyPiece)] & allPieces;
final long checkedPiece = ChessConstants.IN_BETWEEN[kingIndex[kingColor]][Long.numberOfTrailingZeros(enemyPiece)] & allPieces;
if (Long.bitCount(checkedPiece) == 1) {
pinnedPieces |= checkedPiece & friendlyPieces[kingColor];
discoveredPieces |= checkedPiece & friendlyPieces[enemyColor];
}
enemyPiece &= enemyPiece - 1;
}

// rook and queen
enemyPiece = (pieces[enemyColor][ROOK] | pieces[enemyColor][QUEEN]) & MagicUtil.rookMovesEmptyBoard[kingIndex[kingColor]];
while (enemyPiece != 0) {
final long checkedPiece = ChessConstants.ROOK_IN_BETWEEN[kingIndex[kingColor]][Long.numberOfTrailingZeros(enemyPiece)] & allPieces;
if (Long.bitCount(checkedPiece) == 1) {
pinnedPieces |= checkedPiece & friendlyPieces[kingColor];
discoveredPieces |= checkedPiece & friendlyPieces[enemyColor];
}
enemyPiece &= enemyPiece - 1;
}
}
}

Expand Down Expand Up @@ -515,7 +498,8 @@ public void updateKingValues(final int kingColor, final int index) {

public boolean isLegal(final int move) {
if (MoveUtil.getSourcePieceIndex(move) == KING) {
return isLegalKingMove(MoveUtil.getFromIndex(move), MoveUtil.getToIndex(move));
return !CheckUtil.isInCheckIncludingKing(MoveUtil.getToIndex(move), colorToMove, pieces[colorToMoveInverse],
allPieces ^ Util.POWER_LOOKUP[MoveUtil.getFromIndex(move)], MaterialUtil.getMajorPieces(materialKey, colorToMoveInverse));
}

if (MoveUtil.getAttackedPieceIndex(move) != 0) {
Expand All @@ -526,22 +510,12 @@ public boolean isLegal(final int move) {
}

if (checkingPieces != 0) {
return isLegalEvasiveMove(MoveUtil.getFromIndex(move), MoveUtil.getToIndex(move));
return !CheckUtil.isInCheck(kingIndex[colorToMove], colorToMove, pieces[colorToMoveInverse],
allPieces ^ Util.POWER_LOOKUP[MoveUtil.getFromIndex(move)] ^ Util.POWER_LOOKUP[MoveUtil.getToIndex(move)]);
}
return true;
}

private boolean isLegalEvasiveMove(final int fromIndex, final int toIndex) {
// called when king is in check and by killer-move-validity-check
return !CheckUtil.isInCheck(kingIndex[colorToMove], colorToMove, pieces[colorToMoveInverse],
allPieces ^ Util.POWER_LOOKUP[fromIndex] ^ Util.POWER_LOOKUP[toIndex]);
}

private boolean isLegalKingMove(final int fromIndex, final int toIndex) {
return !CheckUtil.isInCheckIncludingKing(toIndex, colorToMove, pieces[colorToMoveInverse], allPieces ^ Util.POWER_LOOKUP[fromIndex],
MaterialUtil.getMajorPieces(materialKey, colorToMoveInverse));
}

private boolean isLegalEPMove(final int fromIndex) {

// do move, check if in check, undo move. slow but also not called very often
Expand Down Expand Up @@ -601,7 +575,7 @@ public boolean isValidQuietMove(int move) {
}
break;
case QUEEN:
if (((MagicUtil.getBishopMoves(fromIndex, allPieces) | MagicUtil.getRookMoves(fromIndex, allPieces)) & Util.POWER_LOOKUP[toIndex]) == 0) {
if ((MagicUtil.getQueenMoves(fromIndex, allPieces) & Util.POWER_LOOKUP[toIndex]) == 0) {
return false;
}
break;
Expand Down Expand Up @@ -655,6 +629,25 @@ public boolean isValidMove(int move) {
return true;
}

public boolean isRepetition() {

if (!EngineConstants.ENABLE_REPETITION_TABLE) {
return false;
}

// TODO same position but other side to move is also a draw?
for (int i = moveCounter - 1; i >= 0 && i > moveCounter - 50; i--) {
// TODO if move was an attacking-move or pawn move, no repetition!
if (zobristKey == zobristKeyHistory[i]) {
if (Statistics.ENABLED) {
Statistics.repetitions++;
}
return true;
}
}
return false;
}

public void clearHistoryValues() {
// history
Arrays.fill(psqtScoreHistory, 0);
Expand All @@ -667,8 +660,6 @@ public void clearHistoryValues() {
}

public void clearEvalAttacks() {
mobilityScore[EvalUtil.MG] = 0;
mobilityScore[EvalUtil.EG] = 0;
kingAttackersFlag[WHITE] = 0;
kingAttackersFlag[BLACK] = 0;
attacks[WHITE][NIGHT] = 0;
Expand Down
Loading

0 comments on commit fa9497e

Please sign in to comment.