Skip to content

Commit

Permalink
Add a [224, 32, 1] NN to augment the existing Pawn King evaluation.
Browse files Browse the repository at this point in the history
This network has 224 inputs, mapped to white King bb, the white Pawn bb (minus the promotion ranks), the black King bb, and the black Pawn bb (minus the promotion ranks). We incrementally update the 1st layer of Neurons. Due to Pawn Hashing, Eval Caching, and the TT, we very rarely actually have to perform the computation to move from the 1st layer to the output Neuron.

The training code for these networks is a private implementation using the PyTorch framework. The goal of the trainer is to train to output a centipawn value, which is then put through a sigmoid after being offset by a static evaluation of the position.

This work could not have been done without the work of @KierenP , the author of https://github.com/KierenP/Halogen . Halogen offers some generic NN structure code which made trying out new nets quick and easy. While developing the networks, I used a c++ fork of Ethereal which contained Halogen's NN code.

The final code does not contain anything from Halogen, nor does it contain anything from Stockfish or the Leela projects. The code is entirely new, and is not based in full or in part upon the work on the Stockfish project's NNUE. This network _augments_ the existing evaluation. This network does _not_ replace the existing evaluation in any way, shape, or form.

ELO   | 25.81 +- 10.38 (95%)
SPRT  | 10.0+0.1s Threads=1 Hash=8MB
LLR   | 2.95 (-2.94, 2.94) [0.00, 5.00]
Games | N: 1888 W: 484 L: 344 D: 1060
http://chess.grantnet.us/test/7422/

ELO   | 23.39 +- 8.71 (95%)
SPRT  | 60.0+0.6s Threads=1 Hash=64MB
LLR   | 2.95 (-2.94, 2.94) [0.00, 5.00]
Games | N: 1904 W: 362 L: 234 D: 1308
http://chess.grantnet.us/test/7423/

BENCH : 4,679,412
  • Loading branch information
AndyGrant committed Sep 19, 2020
1 parent 4e89430 commit 513a93c
Show file tree
Hide file tree
Showing 16 changed files with 492 additions and 151 deletions.
22 changes: 16 additions & 6 deletions src/evaluate.c
Expand Up @@ -25,11 +25,14 @@
#include "board.h"
#include "evalcache.h"
#include "evaluate.h"
#include "move.h"
#include "masks.h"
#include "network.h"
#include "thread.h"
#include "transposition.h"
#include "types.h"

extern PKNetwork PKNN;
EvalTrace T, EmptyTrace;
int PSQT[32][SQUARE_NB];

Expand Down Expand Up @@ -426,16 +429,24 @@ int evaluateBoard(Thread *thread, Board *board) {
EvalInfo ei;
int phase, factor, eval, pkeval, hashed;

// We can recognize positions we just evaluated
if (thread->moveStack[thread->height-1] == NULL_MOVE)
return -thread->evalStack[thread->height-1] + 2 * Tempo;

// Check for this evaluation being cached already
if (!TRACE && getCachedEvaluation(thread, board, &hashed))
return hashed;

initEvalInfo(thread, board, &ei);
eval = evaluatePieces(&ei, board);
eval = evaluatePieces(&ei, board);

pkeval = ei.pkeval[WHITE] - ei.pkeval[BLACK];
eval += pkeval + board->psqtmat + thread->contempt;
eval += evaluateClosedness(&ei, board);
eval += evaluateComplexity(&ei, board, eval);
if (ei.pkentry == NULL)
pkeval += partiallyComputePKNetwork(thread);

eval += pkeval + board->psqtmat + thread->contempt;
eval += evaluateClosedness(&ei, board);
eval += evaluateComplexity(&ei, board, eval);

// Calculate the game phase based on remaining material (Fruit Method)
phase = 24 - 4 * popcount(board->pieces[QUEEN ])
Expand All @@ -458,8 +469,7 @@ int evaluateBoard(Thread *thread, Board *board) {
storeCachedPawnKingEval(thread, board, ei.passedPawns, pkeval);

// Factor in the Tempo after interpolation and scaling, so that
// in the search we can assume that if a null move is made, then
// then `eval = last_eval + 2 * Tempo`
// if a null move is made, then we know eval = last_eval + 2 * Tempo
return Tempo + (board->turn == WHITE ? eval : -eval);
}

Expand Down
54 changes: 27 additions & 27 deletions src/history.c
Expand Up @@ -25,25 +25,25 @@
#include "thread.h"
#include "types.h"

void updateHistoryHeuristics(Thread *thread, uint16_t *moves, int length, int height, int depth) {
void updateHistoryHeuristics(Thread *thread, uint16_t *moves, int length, int depth) {

int entry, bonus, colour = thread->board.turn;
uint16_t bestMove = moves[length-1];

// Extract information from last move
uint16_t counter = thread->moveStack[height-1];
int cmPiece = thread->pieceStack[height-1];
uint16_t counter = thread->moveStack[thread->height-1];
int cmPiece = thread->pieceStack[thread->height-1];
int cmTo = MoveTo(counter);

// Extract information from two moves ago
uint16_t follow = thread->moveStack[height-2];
int fmPiece = thread->pieceStack[height-2];
uint16_t follow = thread->moveStack[thread->height-2];
int fmPiece = thread->pieceStack[thread->height-2];
int fmTo = MoveTo(follow);

// Update Killer Moves (Avoid duplicates)
if (thread->killers[height][0] != bestMove) {
thread->killers[height][1] = thread->killers[height][0];
thread->killers[height][0] = bestMove;
if (thread->killers[thread->height][0] != bestMove) {
thread->killers[thread->height][1] = thread->killers[thread->height][0];
thread->killers[thread->height][0] = bestMove;
}

// Update Counter Moves (BestMove refutes the previous move)
Expand Down Expand Up @@ -88,13 +88,13 @@ void updateHistoryHeuristics(Thread *thread, uint16_t *moves, int length, int he
}
}

void updateKillerMoves(Thread *thread, int height, uint16_t move) {
void updateKillerMoves(Thread *thread, uint16_t move) {

// Avoid saving the same Killer Move twice
if (thread->killers[height][0] == move) return;
if (thread->killers[thread->height][0] == move) return;

thread->killers[height][1] = thread->killers[height][0];
thread->killers[height][0] = move;
thread->killers[thread->height][1] = thread->killers[thread->height][0];
thread->killers[thread->height][0] = move;
}


Expand Down Expand Up @@ -150,21 +150,21 @@ void getCaptureHistories(Thread *thread, uint16_t *moves, int *scores, int start
}


void getHistory(Thread *thread, uint16_t move, int height, int *hist, int *cmhist, int *fmhist) {
void getHistory(Thread *thread, uint16_t move, int *hist, int *cmhist, int *fmhist) {

// Extract information from this move
int to = MoveTo(move);
int from = MoveFrom(move);
int piece = pieceType(thread->board.squares[from]);

// Extract information from last move
uint16_t counter = thread->moveStack[height-1];
int cmPiece = thread->pieceStack[height-1];
uint16_t counter = thread->moveStack[thread->height-1];
int cmPiece = thread->pieceStack[thread->height-1];
int cmTo = MoveTo(counter);

// Extract information from two moves ago
uint16_t follow = thread->moveStack[height-2];
int fmPiece = thread->pieceStack[height-2];
uint16_t follow = thread->moveStack[thread->height-2];
int fmPiece = thread->pieceStack[thread->height-2];
int fmTo = MoveTo(follow);

// Set basic Butterfly history
Expand All @@ -179,16 +179,16 @@ void getHistory(Thread *thread, uint16_t move, int height, int *hist, int *cmhis
else *fmhist = thread->continuation[1][fmPiece][fmTo][piece][to];
}

void getHistoryScores(Thread *thread, uint16_t *moves, int *scores, int start, int length, int height) {
void getHistoryScores(Thread *thread, uint16_t *moves, int *scores, int start, int length) {

// Extract information from last move
uint16_t counter = thread->moveStack[height-1];
int cmPiece = thread->pieceStack[height-1];
uint16_t counter = thread->moveStack[thread->height-1];
int cmPiece = thread->pieceStack[thread->height-1];
int cmTo = MoveTo(counter);

// Extract information from two moves ago
uint16_t follow = thread->moveStack[height-2];
int fmPiece = thread->pieceStack[height-2];
uint16_t follow = thread->moveStack[thread->height-2];
int fmPiece = thread->pieceStack[thread->height-2];
int fmTo = MoveTo(follow);

for (int i = start; i < start + length; i++) {
Expand All @@ -211,16 +211,16 @@ void getHistoryScores(Thread *thread, uint16_t *moves, int *scores, int start, i
}
}

void getRefutationMoves(Thread *thread, int height, uint16_t *killer1, uint16_t *killer2, uint16_t *counter) {
void getRefutationMoves(Thread *thread, uint16_t *killer1, uint16_t *killer2, uint16_t *counter) {

// Extract information from last move
uint16_t previous = thread->moveStack[height-1];
int cmPiece = thread->pieceStack[height-1];
uint16_t previous = thread->moveStack[thread->height-1];
int cmPiece = thread->pieceStack[thread->height-1];
int cmTo = MoveTo(previous);

// Set Killer Moves by height
*killer1 = thread->killers[height][0];
*killer2 = thread->killers[height][1];
*killer1 = thread->killers[thread->height][0];
*killer2 = thread->killers[thread->height][1];

// Set Counter Move if one exists
if (previous == NONE_MOVE || previous == NULL_MOVE) *counter = NONE_MOVE;
Expand Down
10 changes: 5 additions & 5 deletions src/history.h
Expand Up @@ -26,12 +26,12 @@ static const int HistoryMax = 400;
static const int HistoryMultiplier = 32;
static const int HistoryDivisor = 512;

void updateHistoryHeuristics(Thread *thread, uint16_t *moves, int length, int height, int depth);
void updateKillerMoves(Thread *thread, int height, uint16_t move);
void updateHistoryHeuristics(Thread *thread, uint16_t *moves, int length, int depth);
void updateKillerMoves(Thread *thread, uint16_t move);

void updateCaptureHistories(Thread *thread, uint16_t best, uint16_t *moves, int length, int depth);
void getCaptureHistories(Thread *thread, uint16_t *moves, int *scores, int start, int length);

void getHistory(Thread *thread, uint16_t move, int height, int *hist, int *cmhist, int *fmhist);
void getHistoryScores(Thread *thread, uint16_t *moves, int *scores, int start, int length, int height);
void getRefutationMoves(Thread *thread, int height, uint16_t *killer1, uint16_t *killer2, uint16_t *counter);
void getHistory(Thread *thread, uint16_t move, int *hist, int *cmhist, int *fmhist);
void getHistoryScores(Thread *thread, uint16_t *moves, int *scores, int start, int length);
void getRefutationMoves(Thread *thread, uint16_t *killer1, uint16_t *killer2, uint16_t *counter);
59 changes: 41 additions & 18 deletions src/move.c
Expand Up @@ -48,36 +48,51 @@ int castleRookTo(int king, int rook) {
return square(rankOf(king), (rook > king) ? 5 : 3);
}

int apply(Thread *thread, Board *board, uint16_t move, int height) {

int apply(Thread *thread, Board *board, uint16_t move) {

// NULL moves are only tried when legal
if (move == NULL_MOVE) {
thread->moveStack[height] = NULL_MOVE;
applyNullMove(board, &thread->undoStack[height]);
return 1;
thread->moveStack[thread->height] = NULL_MOVE;
applyNullMove(board, &thread->undoStack[thread->height]);
}

// Track some move information for history lookups
thread->moveStack[height] = move;
thread->pieceStack[height] = pieceType(board->squares[MoveFrom(move)]);
else {

// Track some move information for history lookups
thread->moveStack[thread->height] = move;
thread->pieceStack[thread->height] = pieceType(board->squares[MoveFrom(move)]);

// Apply the move and reject if illegal
applyMove(board, move, &thread->undoStack[thread->height]);
if (!moveWasLegal(board))
return revertMove(board, move, &thread->undoStack[thread->height]), 0;
}

// Advance the Stack before updating
thread->height++;

// Apply the move and reject if illegal
applyMove(board, move, &thread->undoStack[height]);
if (!moveWasLegal(board))
return revertMove(board, move, &thread->undoStack[height]), 0;
// Update the collected [PKNETWORK_LAYERS1] Neurons
updatePKNetworkAfterMove(thread, move);

return 1;
}

void applyLegal(Thread *thread, Board *board, uint16_t move, int height) {
void applyLegal(Thread *thread, Board *board, uint16_t move) {

// Track some move information for history lookups
thread->moveStack[height] = move;
thread->pieceStack[height] = pieceType(board->squares[MoveFrom(move)]);
thread->moveStack[thread->height] = move;
thread->pieceStack[thread->height] = pieceType(board->squares[MoveFrom(move)]);

// Assumed that this move is legal
applyMove(board, move, &thread->undoStack[height]);
applyMove(board, move, &thread->undoStack[thread->height]);
assert(moveWasLegal(board));

// Advance the Stack before updating
thread->height++;

// Update the collected [PKNETWORK_LAYERS1] Neurons
updatePKNetworkAfterMove(thread, move);
}

void applyMove(Board *board, uint16_t move, Undo *undo) {
Expand Down Expand Up @@ -330,9 +345,16 @@ void applyNullMove(Board *board, Undo *undo) {
}
}

void revert(Thread *thread, Board *board, uint16_t move, int height) {
if (move == NULL_MOVE) revertNullMove(board, &thread->undoStack[height]);
else revertMove(board, move, &thread->undoStack[height]);

void revert(Thread *thread, Board *board, uint16_t move) {

if (move == NULL_MOVE)
revertNullMove(board, &thread->undoStack[--thread->height]);
else
revertMove(board, move, &thread->undoStack[--thread->height]);

if (thread->pknnchanged[thread->height])
thread->pknndepth -= 1;
}

void revertMove(Board *board, uint16_t move, Undo *undo) {
Expand Down Expand Up @@ -438,6 +460,7 @@ void revertNullMove(Board *board, Undo *undo) {
board->numMoves--;
}


int legalMoveCount(Board * board) {

// Count of the legal number of moves for a given position
Expand Down
6 changes: 3 additions & 3 deletions src/move.h
Expand Up @@ -40,16 +40,16 @@ enum {
int castleKingTo(int king, int rook);
int castleRookTo(int king, int rook);

int apply(Thread *thread, Board *board, uint16_t move, int height);
void applyLegal(Thread *thread, Board *board, uint16_t move, int height);
int apply(Thread *thread, Board *board, uint16_t move);
void applyLegal(Thread *thread, Board *board, uint16_t move);
void applyMove(Board *board, uint16_t move, Undo *undo);
void applyNormalMove(Board *board, uint16_t move, Undo *undo);
void applyCastleMove(Board *board, uint16_t move, Undo *undo);
void applyEnpassMove(Board *board, uint16_t move, Undo *undo);
void applyPromotionMove(Board *board, uint16_t move, Undo *undo);
void applyNullMove(Board *board, Undo *undo);

void revert(Thread *thread, Board *board, uint16_t move, int height);
void revert(Thread *thread, Board *board, uint16_t move);
void revertMove(Board *board, uint16_t move, Undo *undo);
void revertNullMove(Board *board, Undo *undo);

Expand Down
12 changes: 5 additions & 7 deletions src/movepicker.c
Expand Up @@ -45,26 +45,25 @@ static int getBestMoveIndex(MovePicker *mp, int start, int end) {
}


void initMovePicker(MovePicker *mp, Thread *thread, uint16_t ttMove, int height) {
void initMovePicker(MovePicker *mp, Thread *thread, uint16_t ttMove) {

// Start with the table move
mp->stage = STAGE_TABLE;
mp->tableMove = ttMove;

// Lookup our refutations (killers and counter moves)
getRefutationMoves(thread, height, &mp->killer1, &mp->killer2, &mp->counter);
getRefutationMoves(thread, &mp->killer1, &mp->killer2, &mp->counter);

// General housekeeping
mp->threshold = 0;
mp->thread = thread;
mp->height = height;
mp->type = NORMAL_PICKER;
}

void initSingularMovePicker(MovePicker *mp, Thread *thread, uint16_t ttMove, int height) {
void initSingularMovePicker(MovePicker *mp, Thread *thread, uint16_t ttMove) {

// Simply skip over the TT move
initMovePicker(mp, thread, ttMove, height);
initMovePicker(mp, thread, ttMove);
mp->stage = STAGE_GENERATE_NOISY;

}
Expand All @@ -80,7 +79,6 @@ void initNoisyMovePicker(MovePicker *mp, Thread *thread, int threshold) {
// General housekeeping
mp->threshold = threshold;
mp->thread = thread;
mp->height = 0;
mp->type = NOISY_PICKER;
}

Expand Down Expand Up @@ -194,7 +192,7 @@ uint16_t selectNextMove(MovePicker *mp, Board *board, int skipQuiets) {
// Generate and evaluate all quiet moves when not skipping them
if (!skipQuiets) {
mp->quietSize = genAllQuietMoves(board, mp->moves + mp->split);
getHistoryScores(mp->thread, mp->moves, mp->values, mp->split, mp->quietSize, mp->height);
getHistoryScores(mp->thread, mp->moves, mp->values, mp->split, mp->quietSize);
}

mp->stage = STAGE_QUIET;
Expand Down
6 changes: 3 additions & 3 deletions src/movepicker.h
Expand Up @@ -33,14 +33,14 @@ enum {

struct MovePicker {
int split, noisySize, quietSize;
int stage, height, type, threshold;
int stage, type, threshold;
int values[MAX_MOVES];
uint16_t moves[MAX_MOVES];
uint16_t tableMove, killer1, killer2, counter;
Thread *thread;
};

void initMovePicker(MovePicker *mp, Thread *thread, uint16_t ttMove, int height);
void initSingularMovePicker(MovePicker *mp, Thread *thread, uint16_t ttMove, int height);
void initMovePicker(MovePicker *mp, Thread *thread, uint16_t ttMove);
void initSingularMovePicker(MovePicker *mp, Thread *thread, uint16_t ttMove);
void initNoisyMovePicker(MovePicker *mp, Thread *thread, int threshold);
uint16_t selectNextMove(MovePicker *mp, Board *board, int skipQuiets);

0 comments on commit 513a93c

Please sign in to comment.