pedrojdm2021 wrote: ↑Wed Jun 30, 2021 6:49 am
Hello, i am really happy with the results that i'm getting so far, but the speed is
still not that great
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;
}
you can see is a pretty simple scoring for MVV/LVA move ordering, the "only" problem is that the moved piece in a move is not begin encoded into the int move, instead i need to search for the piece from the board using the square index.
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;
}
in my point of view, i see it ok, i don't know if keeping an array of pieces in squares would be better, but it can slow the move generator as i have tested
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.