Skip to content

Commit 20646ed

Browse files
committed
28 LMR and a better way to comute ebf
1 parent e03249a commit 20646ed

File tree

6 files changed

+166
-103
lines changed

6 files changed

+166
-103
lines changed

.vscode/settings.json

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,11 @@
1313
"GoBit.exe~": true,
1414
"london.txt": true,
1515
"engine-interface.txt": true,
16-
"README.md": true,
16+
1717
"perft_tests.txt": true,
18-
"temp.goo": true
18+
"*.goo": true,
19+
".gitignore": true,
20+
"diverse": true
1921
},
2022
"cSpell.words": [
2123
"Pval",

engine.go

Lines changed: 95 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -97,17 +97,17 @@ func (e *ebfStruct) add(nodes uint64) {
9797
func (e *ebfStruct) clear() {
9898
*e = (*e)[:0]
9999
}
100-
func (e *ebfStruct) ebf() float64 {
100+
func (e *ebfStruct) ebf(depth int) float64 {
101101
if len(*e) < 4 {
102102
return 0
103103
}
104104
ebf := 0.0
105-
prevNodes1 := float64((*e)[len(*e)-2])
106-
prevNodes2 := float64((*e)[len(*e)-3])
107-
prevNodes3 := float64((*e)[len(*e)-4])
105+
nodes := float64((*e)[len(*e)-1]) // the last depth
106+
prevNodes := float64((*e)[len(*e)-2])
108107

109-
if prevNodes2 > 0.0 && prevNodes3 > 0.0 {
110-
ebf = (prevNodes2/prevNodes3 + prevNodes1/prevNodes2) / 2
108+
if nodes > 0.0 && prevNodes > 0.0 {
109+
// ebf = (prevNodes2/prevNodes3 + prevNodes1/prevNodes2) / 2
110+
ebf = nodes/prevNodes
111111
}
112112
fmt.Printf("ebf: %0.2f age=%v Used=%v Stored: %v Tried: %v Found: %v Prunes: %v Best: %v\n", ebf, trans.age, trans.cntUsed, trans.cStores, trans.cTried, trans.cFound, trans.cPrune, trans.cBest)
113113
return ebf
@@ -144,32 +144,38 @@ func root(toEngine chan bool, frEngine chan string) {
144144
trans.initSearch() // incr age coounters=0
145145

146146
genAndSort(0, b, &ml)
147-
bm := ml[0]
148-
bs := noScore
149147
depth = 0
150148

151149
transDepth := 0
152-
150+
inCheck := b.isAttacked(b.King[b.stm], b.stm.opp())
151+
bm := ml[0]
152+
bs := noScore // bm keeps the best from prev iteration in case of immediate stop before first is done in this iteration
153153
for depth = 1; depth <= limits.depth && !limits.stop; depth++ {
154154
ml.sort()
155155
bs = noScore // bm keeps the best from prev iteration in case of immediate stop before first is done in this iterastion
156156
alpha, beta = minEval, maxEval
157-
for ix, mv := range ml {
157+
for ix, mv := range ml { // root move loop
158158
childPV.clear()
159+
159160
b.move(mv)
160-
tell("info depth ", strconv.Itoa(depth), " currmove ", mv.String(), " currmovenumber ", strconv.Itoa(ix+1))
161-
//fmt.Printf("alpha=%v beta=%v\n",alpha,beta)
162-
score := -search(-beta, -alpha, depth-1, 1, &childPV, b)
161+
tell(fmt.Sprintf("info depth %v currmove %v currmovenumber %v", depth, mv.String(), ix+1))
162+
lmrRed := 0
163+
ext := 0 // TODO: make extension function
164+
if ext == 0 {
165+
lmrRed = lmr(mv, inCheck, depth, ix+1, ix, b)
166+
}
167+
score := noScore
168+
if ix == 0 {
169+
score = -search(-beta, -alpha, depth-1+ext, 1, &childPV, b) // full search
170+
} else {
171+
score = -search(-alpha-1, -alpha, depth-1+ext-lmrRed, 1, &childPV, b)
172+
if score > alpha && !limits.stop { // re-search due to PVS and/or lmr
173+
score = -search(-beta, -alpha, depth-1+ext, 1, &childPV, b)
174+
}
175+
}
163176

164177
b.unmove(mv)
165178

166-
////////////////////////////////////
167-
if !checkKey(b) {
168-
fmt.Println("fullkey=", b.fullKey(), "Key", b.key, mv.StringFull())
169-
fmt.Println("INVALID KEY AFTER UNMOVE ROOT")
170-
}
171-
///////////////////////////////////////
172-
173179
if limits.stop {
174180
break
175181
}
@@ -189,9 +195,11 @@ func root(toEngine chan bool, frEngine chan string) {
189195
tell(fmt.Sprintf("info score cp %v depth %v nodes %v time %v pv ", bm.eval(), depth, cntNodes, int(t1.Seconds()*1000)), pv.String())
190196
}
191197
}
198+
if !limits.stop {
199+
ebfTab.add(cntNodes)
200+
}
192201

193-
ebfTab.add(cntNodes)
194-
}
202+
} // end ID
195203
ml.sort()
196204

197205
trans.store(b.fullKey(), bm, transDepth, 0, bs, scoreType(bs, alpha, beta))
@@ -202,16 +210,16 @@ func root(toEngine chan bool, frEngine chan string) {
202210
if t1.Seconds() != 0 {
203211
nps = float64(cntNodes) / t1.Seconds()
204212
}
205-
ebfTab.ebf()
206-
tell(fmt.Sprintf("info score cp %v depth %v nodes %v time %v nps %v pv %v", bm.eval(), depth-1, cntNodes, int(t1.Seconds()*1000), uint(nps), pv.String()))
213+
ebfTab.ebf(transDepth)
214+
tell(fmt.Sprintf("info score cp %v depth %v nodes %v time %v nps %v pv %v", bm.eval(), transDepth, cntNodes, int(t1.Seconds()*1000), uint(nps), pv.String()))
207215
frEngine <- fmt.Sprintf("bestmove %v%v", sq2Fen[bm.fr()], sq2Fen[bm.to()])
208216
}
209217
}
210218

211219
//TODO search: Late Move Reduction
212220

213221
//TODO search: Internal Iterative Depening
214-
//TODO search: Delta Pruning
222+
//TODO search: Futility/Delta Pruning
215223
//TODO search: more complicated time handling schemes
216224
//TODO search: other reductions and extensions
217225
func search(alpha, beta, depth, ply int, pv *pvList, b *boardStruct) int {
@@ -274,7 +282,7 @@ func search(alpha, beta, depth, ply int, pv *pvList, b *boardStruct) int {
274282
if depth <= 3 { // static
275283
// if you don't beat me with 100 points,
276284
// then I think your position sucks
277-
sc = -qs(-beta+1, b)
285+
sc = -qs(-beta+1, b) // TODO: maybe 75-100 points bonus for opponent?
278286
} else { // dynamic
279287
sc = -search(-beta, -beta+1, depth-3-1, ply, &childPV, b)
280288
}
@@ -294,7 +302,7 @@ func search(alpha, beta, depth, ply int, pv *pvList, b *boardStruct) int {
294302
bm := noMove
295303

296304
var genInfo = genInfoStruct{sv: 0, ply: ply, transMove: transMove}
297-
cntMoves:=0
305+
cntMoves := 0
298306
next = nextNormal
299307
for mv, msg := next(&genInfo, b); mv != noMove; mv, msg = next(&genInfo, b) {
300308
_ = msg
@@ -304,17 +312,22 @@ func search(alpha, beta, depth, ply int, pv *pvList, b *boardStruct) int {
304312
}
305313

306314
childPV.clear()
307-
308-
if pvNode && bm != noMove {
309-
score = -search(-alpha-1, -alpha, depth-1, ply+1, &childPV, b)
315+
lmrRed := 0
316+
ext := 0 // TODO: make extension function
317+
if ext == 0 {
318+
lmrRed = lmr(mv, inCheck, depth, genInfo.sv, cntMoves, b)
319+
}
320+
if pvNode && cntMoves == 0 {
321+
score = -search(-beta, -alpha, depth-1+ext, ply+1, &childPV, b)
322+
} else {
323+
score = -search(-alpha-1, -alpha, depth-1+ext-lmrRed, ply+1, &childPV, b)
310324
if score > alpha {
311-
score = -search(-beta, -alpha, depth-1, ply+1, &childPV, b)
325+
score = -search(-beta, -alpha, depth-1+ext, ply+1, &childPV, b)
312326
}
313-
} else {
314-
score = -search(-beta, -alpha, depth-1, ply+1, &childPV, b)
315327
}
316-
cntMoves++
328+
317329
b.unmove(mv)
330+
cntMoves++
318331

319332
if score > bs {
320333
bs = score
@@ -362,10 +375,10 @@ func search(alpha, beta, depth, ply int, pv *pvList, b *boardStruct) int {
362375
}
363376
}
364377

365-
if cntMoves==0{ // whe didn't find any legal moves - either mate or stalemate
366-
sc:=0 // we could have a contempt value here instead
367-
if inCheck{ // must be a mate
368-
sc= -mateEval+ply+1
378+
if cntMoves == 0 { // we didn't find any legal moves - either mate or stalemate
379+
sc := 0 // we could have a contempt value here instead
380+
if inCheck { // must be a mate
381+
sc = -mateEval + ply + 1
369382
}
370383

371384
if useTT {
@@ -380,6 +393,22 @@ func search(alpha, beta, depth, ply int, pv *pvList, b *boardStruct) int {
380393
return bs
381394
}
382395

396+
// compute late move reduction
397+
func lmr(mv move, inCheck bool, depth, sv, cntMoves int, b *boardStruct) int {
398+
interesting := inCheck || mv.cp() != empty || mv.pr() != empty ||
399+
b.isAttacked(b.King[b.stm], b.stm.opp()) ||
400+
(b.stm == WHITE && mv.pc() == wP && mv.to() >= A6) ||
401+
(b.stm == BLACK && mv.pc() == bP && mv.to() <= H3) // even big threats? castling?
402+
red := 0
403+
if !interesting && depth >= 3 && sv >= nextFirstNonCp {
404+
red = 1
405+
if depth >= 5 && sv >= nextFirstNonCp {
406+
red = depth / 3
407+
}
408+
}
409+
return red
410+
}
411+
383412
// is this a position to avoid null move?
384413
func (b *boardStruct) isAntiNullMove() bool {
385414
if b.wbBB[b.stm] == b.pieceBB[King]&b.wbBB[b.stm] {
@@ -879,7 +908,7 @@ func nextNormal(genInfo *genInfoStruct, b *boardStruct) (move, string) {
879908
for ix := 0; ix < len(*ml); ix++ {
880909
if (*ml)[ix].cmpFrToP(genInfo.transMove) || (*ml)[ix].cmpFrToP(genInfo.counterMv) ||
881910
(*ml)[ix].cmpFrToP(killers[genInfo.ply].k1) || (*ml)[ix].cmpFrToP(killers[genInfo.ply].k2) {
882-
continue
911+
continue
883912
}
884913
sc := int(history.get((*ml)[ix].fr(), (*ml)[ix].to(), b.stm))
885914
if sc > bs {
@@ -939,13 +968,13 @@ func startPerft(depth int, bd *boardStruct) uint64 {
939968
continue
940969
}
941970
dbg := false
942-
/*
943-
/////////////////////////////////////////////////////////////
944-
if mv.fr() == D4 && mv.to() == F4 {
945-
dbg = true
946-
}
947-
/////////////////////////////////////////////////////////////
948-
*/
971+
/*
972+
/////////////////////////////////////////////////////////////
973+
if mv.fr() == D4 && mv.to() == F4 {
974+
dbg = true
975+
}
976+
/////////////////////////////////////////////////////////////
977+
*/
949978
count := perft(dbg, depth-1, 1, bd)
950979
totCount += count
951980
fmt.Printf("%2d: %v \t%v \t%v\n", ix+1, mv.String(), count, msg)
@@ -976,29 +1005,29 @@ func perft(dbg bool, depth, ply int, bd *boardStruct) uint64 {
9761005
}
9771006
_ = msg
9781007
deb := false
979-
/*
980-
////////////////////////////////////////////////////////////////
981-
if dbg && mv.fr() == F5 && mv.to() == F4 {
982-
deb = true
983-
}
984-
if dbg && mv.fr() == E2 && mv.to() == E4 {
985-
deb = true
986-
}
987-
////////////////////////////////////////////////////////////////
988-
*/
1008+
/*
1009+
////////////////////////////////////////////////////////////////
1010+
if dbg && mv.fr() == F5 && mv.to() == F4 {
1011+
deb = true
1012+
}
1013+
if dbg && mv.fr() == E2 && mv.to() == E4 {
1014+
deb = true
1015+
}
1016+
////////////////////////////////////////////////////////////////
1017+
*/
9891018
cnt := perft(deb, depth-1, ply+1, bd)
9901019
count += cnt
991-
/*
992-
/////////////////////////////////////////////
993-
if dbg && !deb {
994-
fmt.Println(ix+1, ":(e4) ", mv.String(), msg, "\t", cnt)
995-
if ix==1{
996-
fmt.Println("K1",killers[ply].k1.StringFull())
997-
fmt.Println("K2",killers[ply].k2.StringFull())
1020+
/*
1021+
/////////////////////////////////////////////
1022+
if dbg && !deb {
1023+
fmt.Println(ix+1, ":(e4) ", mv.String(), msg, "\t", cnt)
1024+
if ix==1{
1025+
fmt.Println("K1",killers[ply].k1.StringFull())
1026+
fmt.Println("K2",killers[ply].k2.StringFull())
1027+
}
9981028
}
999-
}
1000-
////////////////////////////////////////////
1001-
*/
1029+
////////////////////////////////////////////
1030+
*/
10021031
bd.unmove(mv)
10031032
ix++
10041033
}

engine_test.go

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -121,12 +121,12 @@ func Test_see(t *testing.T) {
121121
{"Bxf7", C4, F7, "rnbqk2r/pppp1pp1/5n1p/2b1p1N1/2B1P3/8/PPPP1PPP/RNBQK2R w KQkq - 0 5", 100},
122122

123123
// Mixed
124-
{"Bxh7", D3, H7, "r2q1rk1/pp1bbppp/2n1pn2/2pp2N1/3P1B2/2PBP3/PP1N1PPP/R2QK2R w KQ - 6 9", -250},
124+
{"Bxh7", D3, H7, "r2q1rk1/pp1bbppp/2n1pn2/2pp2N1/3P1B2/2PBP3/PP1N1PPP/R2QK2R w KQ - 6 9", -225},
125125
{"Nxh7", G5, H7, "r2q1rk1/pp1bbppp/2n1pn2/2pp2N1/3P1B2/2PBP3/PP1N1PPP/R2QK2R w KQ - 6 9", -225},
126126
{"Nxf7", G5, F7, "r2q1rk1/pp1bbppp/2n1pn2/2pp2N1/3P1B2/2PBP3/PP1N1PPP/R2QK2R w KQ - 6 9", -225},
127127
{"Nxe6", G5, E6, "r2q1rk1/pp1bbppp/2n1pn2/2pp2N1/3P1B2/2PBP3/PP1N1PPP/R2QK2R w KQ - 6 9", -225},
128-
{"d5xf3 a", D5, F3, "2kr1bnr/pb3ppp/1p2p3/2pqn3/8/P1PP1NP1/1P3PBP/RNBQ1RK1 b - - 0 10", -275},
129-
{"d5xf3 b", D5, F3, "r3kbnr/pb3ppp/1p2p3/2pqn3/8/2PP1NP1/PP3PBP/RNBQ1RK1 b kq - 4 9", -275},
128+
{"d5xf3 a", D5, F3, "2kr1bnr/pb3ppp/1p2p3/2pqn3/8/P1PP1NP1/1P3PBP/RNBQ1RK1 b - - 0 10", -300},
129+
{"d5xf3 b", D5, F3, "r3kbnr/pb3ppp/1p2p3/2pqn3/8/2PP1NP1/PP3PBP/RNBQ1RK1 b kq - 4 9", -300},
130130
{"d5xd3", D5, D3, "r3kbnr/pb3ppp/1p2p3/2pqn3/8/2PP1NP1/PP3PBP/RNBQ1RK1 b kq - 4 9", 100},
131131

132132
{"Dxg7", G4, G7, "r3kbnr/pb3ppp/1p2p3/2pqn3/3N2Q1/2PP2P1/PP3PBP/RNB2RK1 w kq - 4 3", -850},
@@ -140,7 +140,7 @@ func Test_see(t *testing.T) {
140140
{"w ungarded pawn", B5, C4, "rnbqkbnr/p1pppppp/8/1p6/2P5/8/PP1PPPPP/RNBQKBNR b KQkq - 0 2", 100},
141141

142142
{"f3xe5", F3, E5, "r3kbnr/pb3ppp/1p2p3/2pqn3/8/2PP1NP1/PP3PBP/RNBQ1RK1 w kq - 4 9", 0},
143-
{"Bxg5 a", C1, G5, "rnbqkbnr/pppp3p/4p3/5pp1/3PP3/8/PPP2PPP/RNBQKBNR w KQkq - 0 3", -250},
143+
{"Bxg5 a", C1, G5, "rnbqkbnr/pppp3p/4p3/5pp1/3PP3/8/PPP2PPP/RNBQKBNR w KQkq - 0 3", -225},
144144
{"Bxg5 b", C1, G5, "rnbqkbnr/ppppp2p/8/5pp1/3PP3/8/PPP2PPP/RNBQKBNR w KQkq - 0 3", 100},
145145
{"exf5", E4, F5, "rnbqkbnr/ppppp2p/8/5pp1/3PP3/8/PPP2PPP/RNBQKBNR w KQkq - 0 3", 100},
146146
{"ungarded pawn", C4, B5, "rnbqkbnr/p1pppppp/8/1p6/2P5/8/PP1PPPPP/RNBQKBNR w KQkq - 0 2", 100},
@@ -237,17 +237,17 @@ func Test_QS(t *testing.T) {
237237
{"", "Pawn promotes with capture. B under. This special case is not working prperly", "position fen 3b1n2/1k2P3/8/q7/8/8/6K1/8 w - - 0 1", pieceVal[wP] - pieceVal[wB] - pieceVal[wN], 30},
238238

239239
// Knight
240-
{"", "White is up knight-pawn", "position fen rnbqkbnr/ppp1pppp/8/3p4/1N6/8/PP1PPPPP/RNBQKBNR w KQkq - 0 1", pieceVal[wN] - pieceVal[wP], 20},
241-
{"", "White is up knight and queen", "position fen rnb1kbnr/ppp1pppp/8/3n4/5N2/8/PP1PPPPP/RNBQKBNR w KQkq - 0 1", pieceVal[wN] + pieceVal[wQ], 20},
242-
{"", "Bl Knigh captures guarded W queen", "position fen rnbqkbnr/ppp1pppp/8/3n4/5N2/8/PP1PPPPP/RNBQKBNR b KQkq - 0 1", pieceVal[wN], 20},
240+
{"WN-P", "White is up knight-pawn", "position fen rnbqkbnr/ppp1pppp/8/3p4/1N6/8/PP1PPPPP/RNBQKBNR w KQkq - 0 1", pieceVal[wN] - pieceVal[wP], 20},
241+
{"wN+q", "White is up knight and queen", "position fen rnb1kbnr/ppp1pppp/8/3n4/5N2/8/PP1PPPPP/RNBQKBNR w KQkq - 0 1", pieceVal[wN] + pieceVal[wQ], 20},
242+
{"bN capt", "Bl Knigh captures guarded W queen", "position fen rnbqkbnr/ppp1pppp/8/3n4/5N2/8/PP1PPPPP/RNBQKBNR b KQkq - 0 1", pieceVal[wN], 20},
243243
// Bishop
244-
{"", "White is up bishop and queen", "position fen rnb1kbnr/ppp1pppp/8/3p4/2B5/8/PP1PPPPP/RNBQKBNR w KQkq - 0 1", pieceVal[wB] + pieceVal[wQ], 30},
245-
{"", "Bishop captures guarded knight", "position fen rnbqkbnr/ppp1pppp/8/3n4/4B3/8/PP1PPPPP/RNBQKBNR w KQkq - 0 1", 25, 10},
244+
{"wB", "White is up bishop and queen", "position fen rnb1kbnr/ppp1pppp/8/3p4/2B5/8/PP1PPPPP/RNBQKBNR w KQkq - 0 1", pieceVal[wB] + pieceVal[wQ], 30},
245+
{"B capt N", "Bishop captures guarded knight", "position fen rnbqkbnr/ppp1pppp/8/3n4/4B3/8/PP1PPPPP/RNBQKBNR w KQkq - 0 1", 25, 10},
246246
{"", "Black is up a bishop", "position fen rnbqkbnr/ppp1pppp/8/3b4/4Q3/8/PP1PPPPP/RNBQKBNR b KQkq - 0 1", pieceVal[wB], 25},
247247
// Rook
248-
{"", "White is up Rook and queen", "position fen rnb1kbnr/ppp1pppp/8/3p4/3R4/8/PP1PPPPP/RNBQKBNR w KQkq - 0 1", pieceVal[wR] + pieceVal[wQ], 30},
249-
{"", "White is up Rook vs knight", "position fen rnbqkbnr/ppp1pppp/8/3n4/3R4/8/PP1PPPPP/RNBQKBNR w KQkq - 0 1", pieceVal[wR] - pieceVal[wN], 20},
250-
{"", "Black is up a Rook", "position fen rnbqkbnr/ppp1pppp/8/3r1B2/8/8/PP1PPPPP/RNBQKBNR b KQkq - 0 1", pieceVal[wR], 15},
248+
{"wR+Q", "White is up Rook and queen", "position fen rnb1kbnr/ppp1pppp/8/3p4/3R4/8/PP1PPPPP/RNBQKBNR w KQkq - 0 1", pieceVal[wR] + pieceVal[wQ], 30},
249+
{"wR+N", "White is up Rook vs knight", "position fen rnbqkbnr/ppp1pppp/8/3n4/3R4/8/PP1PPPPP/RNBQKBNR w KQkq - 0 1", pieceVal[wR] - pieceVal[wN], 20},
250+
{"bR up", "Black is up a Rook", "position fen rnbqkbnr/ppp1pppp/8/3r1B2/8/8/PP1PPPPP/RNBQKBNR b KQkq - 0 1", pieceVal[wR], 25},
251251
// Queen
252252
{"", "White is up a Queen vs a Pawn", "position fen rnbqkbnr/ppp1pppp/8/3p4/4Q3/8/PP1PPPPP/RNBQKBNR w KQkq - 0 1", pieceVal[wQ] - pieceVal[wP], 20},
253253
{"", "White is up 2 Queens", "position fen rnb1kbnr/ppp1pppp/8/3n4/2Q5/8/PP1PPPPP/RNBQKBNR w KQkq - 0 1", pieceVal[wQ] * 2, 50},

position_test.go

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -699,10 +699,8 @@ func Test_move_key(t *testing.T) {
699699
}{
700700
{"e4 no capt", "position startpos moves e2e4 d7d5", movStr{E4, E5, wP, empty, empty}},
701701
{"exd5 capt", "position startpos moves e2e4 d7d5", movStr{E4, D5, wP, bP, empty}},
702-
{"exd5 capt", "position fen r3k2r/p1ppqpb1/bn2pnp1/3PN3/1p2P3/2N2Q1p/PPPBBPPP/R3K2R w KQkq - moves e2e4 d7d5",
703-
movStr{E4, D5, wP, bP, empty}},
704-
705-
702+
{"Bxa6 capt", "position fen r3k2r/p1ppqpb1/bn2pnp1/3PN3/1p2P3/2N2Q1p/PPPBBPPP/R3K2R w KQkq -",
703+
movStr{E2, A6, wB, bB, empty}},
706704
}
707705

708706
for _, tt := range tests {

0 commit comments

Comments
 (0)