Some weeks ago i started programming a chess engine for the fun of it. I jumped right into the cold water only knowing about the negamax algorithm. however, after 3 re-writes, it looks like i'm slowly getting a grip onto things.
there is one issue that i seem not to be able to resolve: due to a too high branching factor in my engine, i'm not able to search past ply 8. even searching the initial position to ply 8 takes nearly 2 minutes.
i found out that my branching factor is way too high. (i'm using alpha-beta, PV search and aspiration windows). it's something around 7! the crappy presorting of my moves is responsible for that, it also renders the PV search useless (too many re-searches).
i could try looking at other engines' source and copy the move pre-evaluation, but i'd really hate to do so since i like to figure things out myself.
maybe you guys could help me out. i would appreciate it if you could tell me which approaches you use for move presorting, and what your average branching factors are (without considering things like null-move and quiescene).
maybe you even can tell me if i have some major bug in my code i overlooked.
my method is the following:
the score used for pre-sorting the moves score is an integer based on a value of 100 for a pawn.
i'm iterating through each move before doing the actual search. each move has a estimated score already stored during move generation.
the score is the captured piece's value if the move captures a piece. if the move leaves the piece on a threatened square, 1/10th of the piece's value is subtracted from the score.
if the move is one of my 2 killer moves i keep for each ply, it's score is set to something around 1000. if it's no killer move, but the best move in any previous search (found via TT), it's also set to an artificial maximum.
if the move is neither found in the TT nor a killer move, the move's prescore which was calculated during move generation is used. a score from the history heuristics is added. and, yes, it gets a high bonus if it checks the enemy's king.
the code goes here:
- Code: Select all
// CALCULATE PRESCORES
for (ctr = MoveStacks[depth].top - 1; ctr >= 0; ctr--) {
Move = &MoveStacks[depth].Stack[ctr];
used[depth][ctr] = false;
if (Move->UID == KillerMove[depth][0].UID) {
Move->prescore = KillerMove[depth][0].value + 1000;
} else if (Move->UID == KillerMove[depth][1].UID) {
Move->prescore = KillerMove[depth][1].value + 1000;
} else if (TranspositionTable[numPlayer].lookupBestMove(boardKey, boardLock) == Move->UID) {
Move->prescore = PRESCORE_PV;
} else {
Move->makeMove(ChessBoard, &Players[numPlayer], &Players[1 - numPlayer]);
if (chessRulesV2::isCheck(ChessBoard, &Players[1 - numPlayer])) Move->prescore += PRESCORE_GIVE_CHECK;
Move->undoMove(ChessBoard, &Players[numPlayer], &Players[1 - numPlayer]);
Move->prescore += HistoryHeuristic[Move->startIndex][Move->endIndex];
}
}
i know it could be optimized code-wise, but at the moment, i'm more interested in getting the branching factor down.
this still leaves me with a branching factor of around seven. i'm slowly getting mad about this
thanks in advance!