Not so happy with this code. It's short but not that easy to understand. Also CollectCaptures is one of most expensive operations.
So maybe better not use LINQ here. Although might be that performance can not be improved because
sq.Moves(sort, Board.Occupiers) is bottleneck and these magic bitboard operations can hardly be improved.
Looks like MD5 giving best performance. That is key of 128 bits.
If I would use Sha512 I might as well store all bitboards in the key giving 8 x 64 = 512 bits.
Maybe that would be best for now I have to verify if move from hashtable is invalid because of collisions.
class ChessBoardBits
....
public byte[] ComputeHashKey(HashAlgorithm hashAlgorithm)
{
const int ByteCountBB = sizeof(ulong);
const int nBitBooards = (int)None_Kind + 2;
var arr = new byte[ByteCountBB * nBitBooards];
int k = 0;
for (var j = Pawn_Kind; j <= King_Kind; j++)
{
foreach (var b in GetBytes(GetBits(j)))
{
arr[k++] = b;
}
}
foreach (var b in GetBytes(GetBits(White)))
{
arr[k++] = b;
}
foreach (var b in GetBytes(Other))
{
arr[k++] = b;
}
return hashAlgorithm.ComputeHash(arr);
}
Henk wrote: ↑Wed Aug 26, 2020 4:29 pm
Looks like MD5 giving best performance. That is key of 128 bits.
If I would use Sha512 I might as well store all bitboards in the key giving 8 x 64 = 512 bits.
Maybe that would be best for now I have to verify if move from hashtable is invalid because of collisions.
[url][/url]
class ChessBoardBits
....
public byte[] ComputeHashKey(HashAlgorithm hashAlgorithm)
{
const int ByteCountBB = sizeof(ulong);
const int nBitBooards = (int)None_Kind + 2;
var arr = new byte[ByteCountBB * nBitBooards];
int k = 0;
for (var j = Pawn_Kind; j <= King_Kind; j++)
{
foreach (var b in GetBytes(GetBits(j)))
{
arr[k++] = b;
}
}
foreach (var b in GetBytes(GetBits(White)))
{
arr[k++] = b;
}
foreach (var b in GetBytes(Other))
{
arr[k++] = b;
}
return hashAlgorithm.ComputeHash(arr);
}
Hi Henk, you could easily compress your bitboard representation to 256 bit using quad bit boards https://www.chessprogramming.org/Quad-Bitboards. You could also represent enpassant pawn and rooks that can castle in that representation.
Don't know. Looks bit slower when making a move for you have to access 4 bitboards instead of 3.
But may be comparable. I don't know.
By the way maybe I go back and use Zobrist key. If I use MD5 I might as well use Zobrist key. Although more code because MD5 is a hash algorithm available in .NET .
After all these rewrites. This is first time in months Skipper winning from Fairy-max in a normal way.
Hmm appears to be lucky win. Fairy-max almost playing with a knight less (Nh5).
Henk wrote: ↑Fri Aug 28, 2020 12:13 pm
O wait just won for second time. So this version is one of the better ones. Maybe I should save it before I ruin it (again).
One of the biggest things (imho) for making a strong engine is proper version control and standardised testing. I’d highly recommend using GitHub or something similar in order to be able to “go back” to a previous version in future.
As for testing, it pays to be able to test each change you make by playing the “new” version against the “previous” version. Some people disagree with this and think gauntlet testing is better but self play testing is what the top engines do (stockfish etc)
Henk wrote: ↑Thu Sep 17, 2020 1:41 pm
Functions no longer than twenty lines of code.
No more than three arguments.
Food for rewrite:
Exactly!
Functions that take 4 or less arguments fit into register and that is processed really fast.
Functions should do just one thing. And _ONE_ thing only.
Somehow in chess programming it's ok to have a search function that's 1000+ lines long.
Quiescence check -> Tablebase probe -> Razoring -> Futility -> Null Move ...
I have 9 small search functions. You could fit them into one single function.