For example in Kiwipete position:
[d]r3k2r/p1ppqpb1/bn2pnp1/3PN3/1p2P3/2N2Q1p/PPPBBPPP/R3K2R w KQkq -
it does a search of depth 9 with this results:
Nodes searched: 1286811
Search time: 12596ms ( ~13 seconds ) in a AMD FX 8350 @4ghz CPU
so it means that my engine AI it's searching ~100k positions per second, i think it is pretty bad speed
Features that i have so far inside the search:
- Transposition Tables (~20mb hash table of 875000 entries)
- Bitboards with magic bitboard move generator
- Null move pruning
- PVS (principal variation search)
- LMR (Late move reductions)
- Extended Futility Pruning
The engine is developed in C#, because i am making a chess application in unity engine, so i have to keep that language at least for now, maybe in a future it can be a good idea develop a the engine in a .dll plugin and link it into the C# layer, but that is not the case right now.
In the other hand i have a pretty "good" speed in the perft results:
for example in the same Kiwipete position in depth 4 it does the 4085603 nodes in only ~700ms, so clearly the issue comes from the AI search itself.
i have executed the visual studio debugger and it points to this function that have the most heavy cpu use:
Code: Select all
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private int GuessMoveScore(int _move)
{
int score = 0;
if (MoveUtility.Get_flag_capture(_move) > 0)
{
// apply lookup to the MVV/LVA Table
byte attacker = board.GetPieceTypeInSquare((byte)MoveUtility.Get_square_from(_move));
byte victim = board.GetPieceTypeInSquare((byte)MoveUtility.Get_square_to(_move), true);
score += AISettings.mvv_lva[attacker][victim] + 10000;
}else
{
// score first killer move
if (killer_moves[0][ply] == _move)
score += 9000;
// score second killer move
else if (killer_moves[1][ply] == _move)
score += 8000;
else
// score history move
score += history_moves[board.sideToMove][MoveUtility.Get_square_from(_move)][MoveUtility.Get_square_to(_move)];
}
// give a little bonus if this is a promotion
if (MoveUtility.Get_promotion(_move) > 0)
{
score += 325;
}
return score;
}
the search piece in the board function looks like this:
Code: Select all
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public byte GetPieceTypeInSquare(byte _square , bool _adversary = false)
{
int player = _adversary ? sideToMove ^ 1 : sideToMove;
if((1 & (bitboards[player][ChessPiece.Pawn] >> _square)) > 0) return ChessPiece.Pawn;
if((1 & (bitboards[player][ChessPiece.Bishop] >> _square)) > 0) return ChessPiece.Bishop;
if((1 & (bitboards[player][ChessPiece.Rook] >> _square)) > 0) return ChessPiece.Rook;
if((1 & (bitboards[player][ChessPiece.Knight] >> _square)) > 0) return ChessPiece.Knight;
if((1 & (bitboards[player][ChessPiece.Queen] >> _square)) > 0) return ChessPiece.Queen;
if((1 & (bitboards[player][ChessPiece.King] >> _square)) > 0) return ChessPiece.King;
return 0;
}
the move ordering is done very similar as it's done by the VICE engine:
The move ordering is done inside the search loop in the negamnax, and it calls ' OrderMoves(Moveindex, ref move_list); '
and then that function calls the "GuessMoveScore" function to sort the moves on the list based on the current index.
so, anyone have any ideas to improve my nps? or another pruning technique that i should apply?
Cheers, Pedro.