1
0
forked from sent/waves
waves/public/assets/g/chess/other-implementations/example3/js/search.js
2025-04-09 17:11:14 -05:00

360 lines
6.6 KiB
JavaScript

var SearchController = {};
SearchController.nodes;
SearchController.fh;
SearchController.fhf;
SearchController.depth;
SearchController.time;
SearchController.start;
SearchController.stop;
SearchController.best;
SearchController.thinking;
function PickNextMove(MoveNum) {
var index = 0;
var bestScore = -1;
var bestNum = MoveNum;
for(index = MoveNum; index < GameBoard.moveListStart[GameBoard.ply+1]; ++index) {
if(GameBoard.moveScores[index] > bestScore) {
bestScore = GameBoard.moveScores[index];
bestNum = index;
}
}
if(bestNum != MoveNum) {
var temp = 0;
temp = GameBoard.moveScores[MoveNum];
GameBoard.moveScores[MoveNum] = GameBoard.moveScores[bestNum];
GameBoard.moveScores[bestNum] = temp;
temp = GameBoard.moveList[MoveNum];
GameBoard.moveList[MoveNum] = GameBoard.moveList[bestNum];
GameBoard.moveList[bestNum] = temp;
}
}
function ClearPvTable() {
for(index = 0; index < PVENTRIES; index++) {
GameBoard.PvTable[index].move = NOMOVE;
GameBoard.PvTable[index].posKey = 0;
}
}
function CheckUp() {
if (( $.now() - SearchController.start ) > SearchController.time) {
SearchController.stop = BOOL.TRUE;
}
}
function IsRepetition() {
var index = 0;
for(index = GameBoard.hisPly - GameBoard.fiftyMove; index < GameBoard.hisPly - 1; ++index) {
if(GameBoard.posKey == GameBoard.history[index].posKey) {
return BOOL.TRUE;
}
}
return BOOL.FALSE;
}
function Quiescence(alpha, beta) {
if ((SearchController.nodes & 2047) == 0) {
CheckUp();
}
SearchController.nodes++;
if( (IsRepetition() || GameBoard.fiftyMove >= 100) && GameBoard.ply != 0) {
return 0;
}
if(GameBoard.ply > MAXDEPTH -1) {
return EvalPosition();
}
var Score = EvalPosition();
if(Score >= beta) {
return beta;
}
if(Score > alpha) {
alpha = Score;
}
GenerateCaptures();
var MoveNum = 0;
var Legal = 0;
var OldAlpha = alpha;
var BestMove = NOMOVE;
var Move = NOMOVE;
for(MoveNum = GameBoard.moveListStart[GameBoard.ply]; MoveNum < GameBoard.moveListStart[GameBoard.ply + 1]; ++MoveNum) {
PickNextMove(MoveNum);
Move = GameBoard.moveList[MoveNum];
if(MakeMove(Move) == BOOL.FALSE) {
continue;
}
Legal++;
Score = -Quiescence( -beta, -alpha);
TakeMove();
if(SearchController.stop == BOOL.TRUE) {
return 0;
}
if(Score > alpha) {
if(Score >= beta) {
if(Legal == 1) {
SearchController.fhf++;
}
SearchController.fh++;
return beta;
}
alpha = Score;
BestMove = Move;
}
}
if(alpha != OldAlpha) {
StorePvMove(BestMove);
}
return alpha;
}
function AlphaBeta(alpha, beta, depth) {
if(depth <= 0) {
return Quiescence(alpha, beta);
}
if ((SearchController.nodes & 2047) == 0) {
CheckUp();
}
SearchController.nodes++;
if( (IsRepetition() || GameBoard.fiftyMove >= 100) && GameBoard.ply != 0) {
return 0;
}
if(GameBoard.ply > MAXDEPTH -1) {
return EvalPosition();
}
var InCheck = SqAttacked(GameBoard.pList[PCEINDEX(Kings[GameBoard.side],0)], GameBoard.side^1);
if(InCheck == BOOL.TRUE) {
depth++;
}
var Score = -INFINITE;
GenerateMoves();
var MoveNum = 0;
var Legal = 0;
var OldAlpha = alpha;
var BestMove = NOMOVE;
var Move = NOMOVE;
var PvMove = ProbePvTable();
if(PvMove != NOMOVE) {
for(MoveNum = GameBoard.moveListStart[GameBoard.ply]; MoveNum < GameBoard.moveListStart[GameBoard.ply + 1]; ++MoveNum) {
if(GameBoard.moveList[MoveNum] == PvMove) {
GameBoard.moveScores[MoveNum] = 2000000;
break;
}
}
}
for(MoveNum = GameBoard.moveListStart[GameBoard.ply]; MoveNum < GameBoard.moveListStart[GameBoard.ply + 1]; ++MoveNum) {
PickNextMove(MoveNum);
Move = GameBoard.moveList[MoveNum];
if(MakeMove(Move) == BOOL.FALSE) {
continue;
}
Legal++;
Score = -AlphaBeta( -beta, -alpha, depth-1);
TakeMove();
if(SearchController.stop == BOOL.TRUE) {
return 0;
}
if(Score > alpha) {
if(Score >= beta) {
if(Legal == 1) {
SearchController.fhf++;
}
SearchController.fh++;
if((Move & MFLAGCAP) == 0) {
GameBoard.searchKillers[MAXDEPTH + GameBoard.ply] =
GameBoard.searchKillers[GameBoard.ply];
GameBoard.searchKillers[GameBoard.ply] = Move;
}
return beta;
}
if((Move & MFLAGCAP) == 0) {
GameBoard.searchHistory[GameBoard.pieces[FROMSQ(Move)] * BRD_SQ_NUM + TOSQ(Move)]
+= depth * depth;
}
alpha = Score;
BestMove = Move;
}
}
if(Legal == 0) {
if(InCheck == BOOL.TRUE) {
return -MATE + GameBoard.ply;
} else {
return 0;
}
}
if(alpha != OldAlpha) {
StorePvMove(BestMove);
}
return alpha;
}
function ClearForSearch() {
var index = 0;
var index2 = 0;
for(index = 0; index < 14 * BRD_SQ_NUM; ++index) {
GameBoard.searchHistory[index] = 0;
}
for(index = 0; index < 3 * MAXDEPTH; ++index) {
GameBoard.searchKillers[index] = 0;
}
ClearPvTable();
GameBoard.ply = 0;
SearchController.nodes = 0;
SearchController.fh = 0;
SearchController.fhf = 0;
SearchController.start = $.now();
SearchController.stop = BOOL.FALSE;
}
function SearchPosition() {
var bestMove = NOMOVE;
var bestScore = -INFINITE;
var Score = -INFINITE;
var currentDepth = 0;
var line;
var PvNum;
var c;
ClearForSearch();
for( currentDepth = 1; currentDepth <= SearchController.depth; ++currentDepth) {
Score = AlphaBeta(-INFINITE, INFINITE, currentDepth);
if(SearchController.stop == BOOL.TRUE) {
break;
}
bestScore = Score;
bestMove = ProbePvTable();
line = 'D:' + currentDepth + ' Best:' + PrMove(bestMove) + ' Score:' + bestScore +
' nodes:' + SearchController.nodes;
PvNum = GetPvLine(currentDepth);
line += ' Pv:';
for( c = 0; c < PvNum; ++c) {
line += ' ' + PrMove(GameBoard.PvArray[c]);
}
if(currentDepth!=1) {
line += (" Ordering:" + ((SearchController.fhf/SearchController.fh)*100).toFixed(2) + "%");
}
console.log(line);
}
SearchController.best = bestMove;
SearchController.thinking = BOOL.FALSE;
UpdateDOMStats(bestScore, currentDepth);
}
function UpdateDOMStats(dom_score, dom_depth) {
var scoreText = "Score: " + (dom_score / 100).toFixed(2);
if(Math.abs(dom_score) > MATE - MAXDEPTH) {
scoreText = "Score: Mate In " + (MATE - (Math.abs(dom_score))-1) + " moves";
}
$("#OrderingOut").text("Ordering: " + ((SearchController.fhf/SearchController.fh)*100).toFixed(2) + "%");
$("#DepthOut").text("Depth: " + dom_depth);
$("#ScoreOut").text(scoreText);
$("#NodesOut").text("Nodes: " + SearchController.nodes);
$("#TimeOut").text("Time: " + (($.now()-SearchController.start)/1000).toFixed(1) + "s");
$("#BestOut").text("BestMove: " + PrMove(SearchController.best));
}