Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Threat history #167

Open
wants to merge 11 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions src/main/java/com/kelseyde/calvin/board/Bits.java
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,10 @@ public static long of(int sq) {
return 1L << sq;
}

public static boolean contains(long bb, int sq) {
return (bb & of(sq)) != 0;
}

public static long north(long board) {
return board << 8;
}
Expand Down
40 changes: 40 additions & 0 deletions src/main/java/com/kelseyde/calvin/movegen/MoveGenerator.java
Original file line number Diff line number Diff line change
Expand Up @@ -580,6 +580,46 @@ public void calculatePins(Board board, boolean white) {

}

/**
* Calculate which squares are attacked by the specified side.
*/
public long calculateThreats(Board board, boolean white) {

long threats = 0L;
long occ = board.getOccupied();

long knights = board.getKnights(white);
while (knights != 0) {
final int square = Bits.next(knights);
threats |= Attacks.knightAttacks(square);
knights = Bits.pop(knights);
}

long bishops = board.getBishops(white) | board.getQueens(white);
while (bishops != 0) {
final int square = Bits.next(bishops);
threats |= Attacks.bishopAttacks(square, occ);
bishops = Bits.pop(bishops);
}

long rooks = board.getRooks(white) | board.getQueens(white);
while (rooks != 0) {
final int square = Bits.next(rooks);
threats |= Attacks.rookAttacks(square, occ);
rooks = Bits.pop(rooks);
}

long pawns = board.getPawns(white);
threats |= Attacks.pawnAttacks(pawns, white);

long king = board.getKing(white);
final int square = Bits.next(king);
threats |= Attacks.kingAttacks(square);

return threats;

}

private List<Move> getPromotionMoves(int from, int to) {
return List.of(new Move(from, to, Move.PROMOTE_TO_QUEEN_FLAG),
new Move(from, to, Move.PROMOTE_TO_ROOK_FLAG),
Expand Down
4 changes: 2 additions & 2 deletions src/main/java/com/kelseyde/calvin/search/SearchHistory.java
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ public SearchHistory(EngineConfig config) {
}

public void updateHistory(
PlayedMove bestMove, boolean white, int depth, int ply, SearchStack ss, boolean failHigh) {
PlayedMove bestMove, boolean white, int depth, int ply, SearchStack ss, long threats, boolean failHigh) {

List<PlayedMove> playedMoves = ss.get(ply).searchedMoves;

Expand All @@ -54,7 +54,7 @@ public void updateHistory(

boolean good = bestMove.move.equals(playedMove.move);
if (good || failHigh) {
quietHistoryTable.update(playedMove.move, playedMove.piece, depth, white, good);
quietHistoryTable.update(playedMove.move, playedMove.piece, threats, depth, white, good);
for (int prevPly : CONT_HIST_PLIES) {
SearchStackEntry prevEntry = ss.get(ply - prevPly);
if (prevEntry != null && prevEntry.currentMove != null) {
Expand Down
11 changes: 7 additions & 4 deletions src/main/java/com/kelseyde/calvin/search/Searcher.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.kelseyde.calvin.search;

import com.kelseyde.calvin.board.Bits;
import com.kelseyde.calvin.board.Board;
import com.kelseyde.calvin.board.Move;
import com.kelseyde.calvin.board.Piece;
Expand Down Expand Up @@ -208,9 +209,10 @@ else if (depth <= config.ttExtensionDepth.value) {
ttMove = ttEntry.move();
}

final boolean inCheck = movegen.isCheck(board, board.isWhite());
final long threats = movegen.calculateThreats(board, !board.isWhite());
final boolean inCheck = Bits.contains(threats, Bits.next(board.getKing(board.isWhite())));

final MovePicker movePicker = new MovePicker(movegen, ss, history, board, ply, ttMove, inCheck);
final MovePicker movePicker = new MovePicker(movegen, ss, history, board, ply, ttMove, inCheck, threats);

// Check extension - https://www.chessprogramming.org/Check_Extension
// If we are in check then there if a forcing sequence, so we could benefit from searching one ply deeper to
Expand Down Expand Up @@ -471,7 +473,7 @@ else if (depth <= config.ttExtensionDepth.value) {
final PlayedMove best = sse.bestMove;
final int historyDepth = depth + (staticEval > alpha ? 1 : 0);
final boolean failHigh = bestScore >= beta;
history.updateHistory(best, board.isWhite(), historyDepth, ply, ss, failHigh);
history.updateHistory(best, board.isWhite(), historyDepth, ply, ss, threats, failHigh);
}

if (!inCheck
Expand Down Expand Up @@ -520,9 +522,10 @@ && isWithinBounds(ttEntry, alpha, beta)) {
ttMove = ttEntry.move();
}

final long threats = movegen.calculateThreats(board, !board.isWhite());
final boolean inCheck = movegen.isCheck(board, board.isWhite());

final QuiescentMovePicker movePicker = new QuiescentMovePicker(movegen, ss, history, board, ply, ttMove, inCheck);
final QuiescentMovePicker movePicker = new QuiescentMovePicker(movegen, ss, history, board, ply, ttMove, inCheck, threats);

// Re-use cached static eval if available. Don't compute static eval while in check.
int rawStaticEval = Integer.MIN_VALUE;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,19 +40,21 @@ public enum Stage {
Stage stage;
boolean skipQuiets;
boolean inCheck;
long threats;

int moveIndex;
ScoredMove[] moves;

public MovePicker(
MoveGenerator movegen, SearchStack ss, SearchHistory history, Board board, int ply, Move ttMove, boolean inCheck) {
MoveGenerator movegen, SearchStack ss, SearchHistory history, Board board, int ply, Move ttMove, boolean inCheck, long threats) {
this.movegen = movegen;
this.history = history;
this.board = board;
this.ss = ss;
this.ply = ply;
this.ttMove = ttMove;
this.inCheck = inCheck;
this.threats = threats;
this.stage = ttMove != null ? Stage.TT_MOVE : Stage.GEN_NOISY;
}

Expand Down Expand Up @@ -177,7 +179,7 @@ protected ScoredMove scoreQuiet(Board board, Move move, Piece piece, Piece captu
int killerScore = killerIndex >= 0 ? MoveType.KILLER_OFFSET * (KillerTable.KILLERS_PER_PLY - killerIndex) : 0;

// Get the history score for the move
int historyScore = history.getQuietHistoryTable().get(move, piece, white);
int historyScore = history.getQuietHistoryTable().get(move, piece, threats, white);

int contHistScore = 0;
// Get the continuation history score for the move
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@ public class QuiescentMovePicker extends MovePicker {
private MoveFilter filter;

public QuiescentMovePicker(
MoveGenerator movegen, SearchStack ss, SearchHistory history, Board board, int ply, Move ttMove, boolean inCheck) {
super(movegen, ss, history, board, ply, ttMove, inCheck);
MoveGenerator movegen, SearchStack ss, SearchHistory history, Board board, int ply, Move ttMove, boolean inCheck, long threats) {
super(movegen, ss, history, board, ply, ttMove, inCheck, threats);
this.skipQuiets = true;
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package com.kelseyde.calvin.tables.history;

import com.kelseyde.calvin.board.Bits;

public abstract class AbstractHistoryTable {

private final int bonusMax;
Expand Down Expand Up @@ -28,4 +30,10 @@ protected int gravity(int current, int update) {
return current + update - current * Math.abs(update) / scoreMax;
}

protected int threatIndex(int from, int to, long threats) {
int fromThreatened = Bits.contains(threats, from) ? 1 : 0;
int toThreatened = Bits.contains(threats, to) ? 1 : 0;
return fromThreatened << 1 | toThreatened;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@

public class QuietHistoryTable extends AbstractHistoryTable {

int[][][] table = new int[2][Piece.COUNT][Square.COUNT];
int[][][][] table = new int[2][Piece.COUNT][Square.COUNT][4];

public QuietHistoryTable(EngineConfig config) {
super(config.quietHistBonusMax.value,
Expand All @@ -18,30 +18,42 @@ public QuietHistoryTable(EngineConfig config) {
config.quietHistMaxScore.value);
}

public void update(Move move, Piece piece, int depth, boolean white, boolean good) {
public void update(Move move, Piece piece, long threats, int depth, boolean white, boolean good) {
int colourIndex = Colour.index(white);
int current = table[colourIndex][piece.index()][move.to()];
int bonus = good ? bonus(depth) : malus(depth);
int pieceIndex = piece.index();
int from = move.from();
int to = move.to();
int threatIndex = threatIndex(from, to, threats);
int current = table[colourIndex][pieceIndex][to][threatIndex];
int bonus = bonus(depth);
if (!good) bonus = -bonus;
int update = gravity(current, bonus);
table[colourIndex][piece.index()][move.to()] = update;
table[colourIndex][pieceIndex][to][threatIndex] = update;
}

public int get(Move move, Piece piece, boolean white) {
public int get(Move historyMove, Piece piece, long threats, boolean white) {
int colourIndex = Colour.index(white);
return table[colourIndex][piece.index()][move.to()];
int pieceIndex = piece.index();
int from = historyMove.from();
int to = historyMove.to();
int threatIndex = threatIndex(from, to, threats);
return table[colourIndex][pieceIndex][to][threatIndex];
}

public void ageScores(boolean white) {
int colourIndex = Colour.index(white);
for (int from = 0; from < Piece.COUNT; from++) {
for (int pieceIndex = 0; pieceIndex < Piece.COUNT; pieceIndex++) {
for (int to = 0; to < Square.COUNT; to++) {
table[colourIndex][from][to] /= 2;
table[colourIndex][pieceIndex][to][0] /= 2;
table[colourIndex][pieceIndex][to][1] /= 2;
table[colourIndex][pieceIndex][to][2] /= 2;
table[colourIndex][pieceIndex][to][3] /= 2;
}
}
}

public void clear() {
table = new int[2][Piece.COUNT][Square.COUNT];
table = new int[2][Piece.COUNT][Square.COUNT][4];
}

}
40 changes: 40 additions & 0 deletions src/test/java/com/kelseyde/calvin/movegen/ThreatsTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
package com.kelseyde.calvin.movegen;

import com.kelseyde.calvin.board.Bits;
import com.kelseyde.calvin.board.Board;
import com.kelseyde.calvin.board.Move;
import com.kelseyde.calvin.utils.Bench;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;

import java.util.List;

public class ThreatsTest {

private static final MoveGenerator MOVEGEN = new MoveGenerator();

// @Test
// public void testThreats() {
//
// for (String fen : Bench.FENS) {
//
// System.out.println("fen: " + fen);
// Board board = Board.from(fen);
//
// long threats = MOVEGEN.calculateThreats(board, !board.isWhite());
// board.setWhite(!board.isWhite());
// List<Move> moves = MOVEGEN.generateMoves(board);
// Bits.print(threats);
// for (Move move : moves) {
// System.out.println(Move.toUCI(move));
// Assertions.assertTrue(Bits.contains(threats, move.to()));
// }
//// Bits.print(expectedThreats);
//// Bits.print(threats);
//// Assertions.assertEquals(expectedThreats, threats);
//
// }
//
// }

}
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ public void testMoveOrder() {
history.getKillerTable().add(0, killer1);
history.getKillerTable().add(0, killer2);

MovePicker picker = new MovePicker(moveGenerator, ss, history, board, 0, ttMove, false);
MovePicker picker = new MovePicker(moveGenerator, ss, history, board, 0, ttMove, false, 0);

int maxIndex = -1;
int tried = 0;
Expand Down Expand Up @@ -88,7 +88,7 @@ public void testInCheckDoesNotGenerateMovesTwice() {
String fen = "rnbqkbnr/1p2pppp/p2p4/1Bp5/4P3/5N2/PPPP1PPP/RNBQK2R b KQkq - 0 1";
Board board = FEN.toBoard(fen);

MovePicker picker = new MovePicker(moveGenerator, new SearchStack(), new SearchHistory(new EngineConfig()), board, 0, null, true);
MovePicker picker = new MovePicker(moveGenerator, new SearchStack(), new SearchHistory(new EngineConfig()), board, 0, null, true, 0);

List<ScoredMove> moves = new ArrayList<>();
while (true) {
Expand Down