But before I do that, I needed to implement triple repetition draw detection, since my engine will often just shuffle a rook back and forth even when it's in a clearly winning position, since it thinks it's doing well maintaining its material advantage.
To do this, and for general speed-ups, I took a crack at making a transposition table using Zobrist hashing. I tested the function which produces the Zobrist keys pretty well, and I made sure I tested that the initialize hash was being updated correctly when my engine made and unmade a move. But for some reason, I have two big problems:
- I'm getting incorrect perft numbers, which is the biggest problem
- Using even this buggy transposition table shows hardly any speed-ups whatsoever, which is pretty disappointing. I do realize that perft speed doesn't really play a huge factor in a lower ELO created engine, but I do want transposition tables to improve the speed of my minimax implementation
Here is where I initialize the 12*64 random numbers:
Code: Select all
const (
ZobristRandomNumbersSize = 12 * 64
TranspositionTableSize = 1000
)
var TranspositionTable [TranspositionTableSize]Entry
var ZobristTable [ZobristRandomNumbersSize]uint64
func init() {
for index := 0; index < ZobristRandomNumbersSize; index++ {
ZobristTable[index] = rand.Uint64()
}
TranspositionTable = [TranspositionTableSize]Entry{}
}Code: Select all
const (
WhitePawnChunkStart = 0
WhiteKnightChunkStart = 1
)
var hash uint64
for index := 0; index < 64; index++ {
if board.Pieces[index] != 0 {
if board.Pieces[index]&Pawn != 0 && board.Pieces[index]&White != 0 {
hash ^= ZobristTable[WhitePawnChunkStart*64+index]
} else if board.Pieces[index]&Knight != 0 && board.Pieces[index]&White != 0 {
hash ^= ZobristTable[WhiteKnightChunkStart*64+index]
...
}Here is my code for putting an entry in the TranspositionTable:
Code: Select all
type Entry struct {
Hash uint64
Depth int
NodeCount uint64
}
func getHashEntry(board *Board, depth int) Entry {
entry := TranspositionTable[board.InitZobristHash%TranspositionTableSize]
if entry.Hash == board.InitZobristHash && entry.Depth >= depth {
return entry
}
return Entry{Depth: NoEntry}
}
func setHashEntry(board *Board, depth int, nodeCount uint64) {
entry := Entry{Hash: board.InitZobristHash, Depth: depth, NodeCount: nodeCount}
TranspositionTable[board.InitZobristHash%TranspositionTableSize] = entry
}Code: Select all
func DividePerft(board *Board, depth, divideAt int) uint64 {
if depth == 0 {
return 1
}
entry := getHashEntry(board, depth)
if entry.Depth != NoEntry {
return entry.NodeCount
}
pseduoMoves := computePseduolegalMoves(board)
var nodes uint64
for _, move := range pseduoMoves {
board.DoMove(&move, true)
if KingIsSafe(board) {
moveNodes := DividePerft(board, depth-1, divideAt)
nodes += moveNodes
if depth == divideAt {
fmt.Printf("%v: %v\n", move, moveNodes)
}
}
board.UndoMove(&move)
}
setHashEntry(board, depth, nodes)
return nodes
}Code: Select all
perft(5) from startpos:
b1-c3: 234656
b1-a3: 198572
g1-h3: 198502
g1-f3: 233491
a2-a3: 181046
a2-a4: 217832
b2-b3: 215255
b2-b4: 216142
c2-c3: 223341
c2-c4: 240080
d2-d3: 328511
d2-d4: 361790
e2-e3: 402988
e2-e4: 405383
f2-f3: 178889
f2-f4: 198469
g2-g3: 217210
g2-g4: 214049
h2-h3: 181044
h2-h4: 218830
Nodes: 4866080