test(search): add transposition table and draw detection tests
This commit is contained in:
1 parent
707174a420
commit
a38a065f6a
2 files changed
+122
No files matched your search
@@ -0,0 +1,46 @@
|
|||||||
|
package gambit
|
||||||
|
|
||||||
|
class DrawTest extends munit.FunSuite:
|
||||||
|
|
||||||
|
test("insufficient material for bare kings and lone minor") {
|
||||||
|
assert(Draw.isInsufficientMaterial(Fen.parse("4k3/8/8/8/8/8/8/4K3 w - - 0 1")))
|
||||||
|
assert(Draw.isInsufficientMaterial(Fen.parse("4k3/8/8/8/8/8/5B2/4K3 w - - 0 1")))
|
||||||
|
assert(Draw.isInsufficientMaterial(Fen.parse("4k3/8/8/8/8/8/5N2/4K3 w - - 0 1")))
|
||||||
|
assert(Draw.isInsufficientMaterial(Fen.parse("4k3/8/5b2/8/8/8/8/4K3 b - - 0 1")))
|
||||||
|
}
|
||||||
|
|
||||||
|
test("sufficient material is not flagged") {
|
||||||
|
assert(!Draw.isInsufficientMaterial(Fen.parse("4k3/8/8/8/8/8/5R2/4K3 w - - 0 1")))
|
||||||
|
assert(!Draw.isInsufficientMaterial(Fen.parse("4k3/8/8/8/8/8/4PP2/4K3 w - - 0 1")))
|
||||||
|
assert(!Draw.isInsufficientMaterial(Fen.parse("4k3/5b2/8/8/8/8/5B2/4K3 w - - 0 1")))
|
||||||
|
assert(!Draw.isInsufficientMaterial(Fen.parse(Fen.startPosition)))
|
||||||
|
}
|
||||||
|
|
||||||
|
test("threefold repetition counts the current position") {
|
||||||
|
val h = Fen.parse(Fen.startPosition).hash
|
||||||
|
val other = Fen.parse("rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR b KQkq - 0 1").hash
|
||||||
|
assert(Draw.isThreefold(Vector(h, other, h, other, h), h))
|
||||||
|
assert(!Draw.isThreefold(Vector(h, other, h, other), h))
|
||||||
|
}
|
||||||
|
|
||||||
|
test("fifty-move rule is a draw in search despite winning material") {
|
||||||
|
val stalled = Fen.parse("7k/8/8/8/8/8/8/KQ6 w - - 100 1")
|
||||||
|
assertEquals(new Searcher().search(stalled, 3, 0).score, 0)
|
||||||
|
val fresh = Fen.parse("7k/8/8/8/8/8/8/KQ6 w - - 0 1")
|
||||||
|
assert(new Searcher().search(fresh, 3, 0).score > 0, "with a live clock the queen must count")
|
||||||
|
}
|
||||||
|
|
||||||
|
test("repetition detection sees a matching ancestor at the right distance") {
|
||||||
|
val pos = Fen.parse("R6k/8/8/8/8/8/8/6K1 w - - 6 30")
|
||||||
|
val decoy = Fen.parse("R6k/8/8/8/8/8/8/5K2 w - - 6 30").hash
|
||||||
|
assert(new Searcher().wouldRepeat(pos, Array(pos.hash, decoy)))
|
||||||
|
assert(!new Searcher().wouldRepeat(pos, Array(decoy, decoy)))
|
||||||
|
assert(!new Searcher().wouldRepeat(pos, Array(decoy, pos.hash)))
|
||||||
|
}
|
||||||
|
|
||||||
|
test("self-play from a dead endgame is adjudicated as a draw") {
|
||||||
|
val insufficient = Game.selfPlay("4k3/8/8/8/8/8/5B2/4K3 w - - 0 1", 20, 4, 0)
|
||||||
|
assertEquals(insufficient.result, "1/2-1/2 (insufficient material)")
|
||||||
|
val fifty = Game.selfPlay("4k3/8/8/3p4/3P4/8/8/4K3 w - - 98 1", 200, 3, 0)
|
||||||
|
assertEquals(fifty.result, "1/2-1/2 (fifty-move rule)")
|
||||||
|
}
|
||||||
@@ -0,0 +1,76 @@
|
|||||||
|
package gambit
|
||||||
|
|
||||||
|
class TTTest extends munit.FunSuite:
|
||||||
|
|
||||||
|
test("store then probe returns exact score") {
|
||||||
|
val tt = new TranspositionTable(16)
|
||||||
|
tt.store(0x1234L, 5, 123, TT.Exact, 42, 0)
|
||||||
|
assertEquals(tt.probe(0x1234L, 5, -1000, 1000, 0), 123)
|
||||||
|
assertEquals(tt.probeMove(0x1234L), 42)
|
||||||
|
}
|
||||||
|
|
||||||
|
test("lower bound only usable at or above beta") {
|
||||||
|
val tt = new TranspositionTable(16)
|
||||||
|
tt.store(0x55L, 5, 200, TT.Lower, 7, 0)
|
||||||
|
assertEquals(tt.probe(0x55L, 5, -1000, 150, 0), 200)
|
||||||
|
assertEquals(tt.probe(0x55L, 5, -1000, 300, 0), TT.Unknown)
|
||||||
|
}
|
||||||
|
|
||||||
|
test("upper bound only usable at or below alpha") {
|
||||||
|
val tt = new TranspositionTable(16)
|
||||||
|
tt.store(0x66L, 5, 50, TT.Upper, 8, 0)
|
||||||
|
assertEquals(tt.probe(0x66L, 5, 100, 1000, 0), 50)
|
||||||
|
assertEquals(tt.probe(0x66L, 5, 0, 1000, 0), TT.Unknown)
|
||||||
|
}
|
||||||
|
|
||||||
|
test("shallow entry is not usable for deeper probe") {
|
||||||
|
val tt = new TranspositionTable(16)
|
||||||
|
tt.store(0x77L, 3, 10, TT.Exact, 0, 0)
|
||||||
|
assertEquals(tt.probe(0x77L, 6, -1000, 1000, 0), TT.Unknown)
|
||||||
|
}
|
||||||
|
|
||||||
|
test("depth preferred replacement keeps the deeper entry") {
|
||||||
|
val tt = new TranspositionTable(16)
|
||||||
|
tt.store(0x88L, 8, 300, TT.Exact, 1, 0)
|
||||||
|
tt.store(0x88L, 4, 999, TT.Exact, 2, 0)
|
||||||
|
assertEquals(tt.probe(0x88L, 8, -1000, 1000, 0), 300)
|
||||||
|
}
|
||||||
|
|
||||||
|
test("mate score is stored relative to root distance") {
|
||||||
|
val tt = new TranspositionTable(16)
|
||||||
|
val mateAtPly4 = Searcher.MateValue - 4
|
||||||
|
tt.store(0x99L, 5, mateAtPly4, TT.Exact, 0, 4)
|
||||||
|
assertEquals(tt.probe(0x99L, 5, -Searcher.Infinity, Searcher.Infinity, 4), mateAtPly4)
|
||||||
|
assertEquals(tt.probe(0x99L, 5, -Searcher.Infinity, Searcher.Infinity, 2), Searcher.MateValue - 2)
|
||||||
|
}
|
||||||
|
|
||||||
|
private val positions = List(
|
||||||
|
Fen.startPosition,
|
||||||
|
"r3k2r/p1ppqpb1/bn2pnp1/3PN3/1p2P3/2N2Q1p/PPPBBPPP/R3K2R w KQkq - 0 1",
|
||||||
|
"8/2p5/3p4/KP5r/1R3p1k/8/4P1P1/8 w - - 0 1",
|
||||||
|
"rnbqkbnr/ppp1pppp/8/3pP3/8/8/PPPP1PPP/RNBQKBNR w KQkq d6 0 3"
|
||||||
|
)
|
||||||
|
|
||||||
|
test("transposition table does not change the search score") {
|
||||||
|
for fen <- positions do
|
||||||
|
val withTt = new Searcher
|
||||||
|
withTt.useTt = true
|
||||||
|
val noTt = new Searcher
|
||||||
|
noTt.useTt = false
|
||||||
|
assertEquals(
|
||||||
|
withTt.search(Fen.parse(fen), 5, 0).score,
|
||||||
|
noTt.search(Fen.parse(fen), 5, 0).score,
|
||||||
|
fen
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
test("transposition table reduces the node count") {
|
||||||
|
for fen <- positions do
|
||||||
|
val withTt = new Searcher
|
||||||
|
withTt.useTt = true
|
||||||
|
val noTt = new Searcher
|
||||||
|
noTt.useTt = false
|
||||||
|
val ttNodes = withTt.search(Fen.parse(fen), 5, 0).nodes
|
||||||
|
val plainNodes = noTt.search(Fen.parse(fen), 5, 0).nodes
|
||||||
|
assert(ttNodes < plainNodes, s"$fen: expected TT to prune, got $ttNodes vs $plainNodes")
|
||||||
|
}
|
||||||
Reference in new issue
Block a user