Skip to content

Commit e019e9c

Browse files
committed
Qs and See implemented. Renamed all 'pc' to pt (piece type) and 'p12' to 'pc'
1 parent 3b0244d commit e019e9c

File tree

14 files changed

+461
-315
lines changed

14 files changed

+461
-315
lines changed

.vscode/settings.json

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,10 @@
1212
"diverese": true,
1313
"pm.txt": true,
1414
"GoBit.exe~": true,
15-
".gitignore": true,
1615
"london.txt": true,
17-
"engine-interface.txt": true
16+
"engine-interface.txt": true,
17+
"testresults.txt": true,
18+
".gitignore": true
1819
},
1920
"cSpell.words": [
2021
"Pval",

castlings.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ const (
1717
type castlOptions struct {
1818
short uint // flag
1919
long uint // flag
20-
rook int // rook p12 (wR/bR)
20+
rook int // rook pc (wR/bR)
2121
kingPos int // king pos
2222
rookSh uint // rook pos short
2323
rookL uint // rook pos long

engine.go

Lines changed: 142 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@ const (
1414

1515
var cntNodes uint64
1616

17-
//TODO search limits: start clock and testing for movetime
1817
//TODO search limits: counting nodes and testing for limit.nodes
1918
//TODO search limits: limit.depth
2019

@@ -95,8 +94,6 @@ func engine() (toEngine chan bool, frEngine chan string) {
9594
return
9695
}
9796

98-
//TODO root: Iterative Depening
99-
10097
//TODO root: Aspiration search
10198
func root(toEngine chan bool, frEngine chan string) {
10299
var depth int
@@ -152,8 +149,8 @@ func root(toEngine chan bool, frEngine chan string) {
152149
}
153150
}
154151

155-
//TODO search: generate all moves and put captures first (temporary)
156-
//TODO search: qs
152+
//TODO search: qs with SEE
153+
157154
//TODO search: hash table/transposition table
158155
//TODO search: history table and maybe counter move table
159156
//TODO search: move generation. More fast and accurate
@@ -217,6 +214,146 @@ func search(alpha, beta, depth, ply int, pv *pvList, b *boardStruct) int {
217214
return bs
218215
}
219216

217+
func initQS(ml *moveList, b *boardStruct){
218+
ml.clear()
219+
b.genAllCaptures(ml)
220+
}
221+
func qs(beta int, b *boardStruct) int{
222+
ev := signEval(b.stm, evaluate(b))
223+
if ev >= beta {
224+
// we are good. No need to try captures
225+
return ev
226+
}
227+
bs := ev
228+
229+
qsList := make(moveList,0,60)
230+
initQS(&qsList,b) // create attacks
231+
232+
done:=bitBoard(0)
233+
234+
// move loop
235+
for _,mv := range qsList{
236+
fr:=mv.fr()
237+
to:=mv.to()
238+
239+
// This works because we pick lower value pieces first
240+
if done.test(to) {// Don't do the same to-sw again
241+
continue
242+
}
243+
done.set(to)
244+
245+
see := see(fr,to,b)
246+
247+
if see < 0 {
248+
continue // equal captures not interesting
249+
}
250+
if see == 0 && mv.cp() == empty {
251+
// must be a promotion that didn't captureand was not captured
252+
see = pieceVal[wQ] - pieceVal[wP]
253+
}
254+
255+
sc := ev+see
256+
if sc > bs {
257+
bs = sc
258+
if sc >= beta {
259+
return sc
260+
}
261+
}
262+
}
263+
264+
return bs
265+
}
266+
// see (Static Echange Evaluation)
267+
// Start with the capture fr-to and find out all the other captures to to-sq
268+
func see(fr, to int, b *boardStruct) int {
269+
pc := b.sq[fr]
270+
cp := b.sq[to]
271+
cnt := 1
272+
us := pcColor(pc)
273+
them := us.opp()
274+
275+
// All the attackers to the to-sq, but first remove the moving piece and use X-ray to the to-sq
276+
occ := b.allBB()
277+
occ.clr(fr)
278+
attackingBB := mRookTab[to].atks(occ)&(b.pieceBB[Rook]|b.pieceBB[Queen]) |
279+
mBishopTab[to].atks(occ)&(b.pieceBB[Bishop]|b.pieceBB[Queen]) |
280+
(atksKnights[to] & b.pieceBB[Knight]) |
281+
(atksKings[to] & b.pieceBB[King]) |
282+
(b.wPawnAtksFr(to) & b.pieceBB[Pawn] & b.wbBB[BLACK]) |
283+
(b.bPawnAtksFr(to) & b.pieceBB[Pawn] & b.wbBB[WHITE])
284+
attackingBB &= occ
285+
286+
if (attackingBB & b.wbBB[them]) == 0 { // 'they' have no attackers - good bye
287+
return abs(pieceVal[cp]) // always return score from 'our' point of view
288+
}
289+
290+
// Now we continue to keep track of the material gain/loss for each capture
291+
// Always remove the last attacker and use x-ray to find possible new attackers
292+
293+
lastAtkVal := abs(pieceVal[pc]) // save attacker piece value for later use
294+
var captureList [32]int
295+
n := 1
296+
297+
stm := them // change side to move
298+
var pt int
299+
var BB bitBoard
300+
301+
for {
302+
cnt++
303+
switch { // select the least valuable attacker
304+
case (attackingBB & b.pieceBB[Pawn] & b.wbBB[stm]) != 0:
305+
pt = Pawn
306+
case (attackingBB & b.pieceBB[Knight] & b.wbBB[stm]) != 0:
307+
pt = Knight
308+
case (attackingBB & b.pieceBB[Bishop] & b.wbBB[stm]) != 0:
309+
pt = Bishop
310+
case (attackingBB & b.pieceBB[Rook] & b.wbBB[stm]) != 0:
311+
pt = Rook
312+
case (attackingBB & b.pieceBB[Queen] & b.wbBB[stm]) != 0:
313+
pt = Queen
314+
case (attackingBB & b.pieceBB[King] & b.wbBB[stm]) != 0:
315+
pt = King
316+
default:
317+
panic("Don't come here in see! ")
318+
}
319+
320+
// now remove the pt above from the attackingBB and scan for new attackers by possible x-ray
321+
BB = attackingBB & (attackingBB & b.pieceBB[pt] & b.wbBB[stm])
322+
occ ^= (BB & -BB) // turn off the rightmost bit from BB in occ
323+
324+
// pick sliding attacks again (do it from to-sq)
325+
attackingBB |= mRookTab[to].atks(occ)&(b.pieceBB[Rook]|b.pieceBB[Queen]) |
326+
mBishopTab[to].atks(occ)&(b.pieceBB[Bishop]|b.pieceBB[Queen])
327+
attackingBB &= occ // but only attacking pieces
328+
329+
captureList[n] = -captureList[n-1] + lastAtkVal
330+
n++
331+
332+
// save the value of tha capturing piece to be used later
333+
lastAtkVal = abs(pieceVal[pt2pc(pt, stm)])
334+
stm = stm.opp() // next side to move
335+
336+
if pt == King && (attackingBB&b.pieceBB[King]&b.wbBB[stm]) != 0 {
337+
// if king capture and 'they' are atting we have to stop
338+
captureList[n] = pieceVal[wK]
339+
n++
340+
break
341+
}
342+
343+
if attackingBB&b.wbBB[stm] == 0 { // if no more attackers
344+
break
345+
}
346+
347+
}
348+
349+
// find the optimal capture sequence and 'our' material value will be on top
350+
for n--; n != 0; n-- {
351+
captureList[n-1] = min(-captureList[n], captureList[n-1])
352+
}
353+
354+
return captureList[0]
355+
}
356+
220357
func genAndSort(b *boardStruct, ml *moveList) {
221358
ml.clear()
222359
b.genAllLegals(ml)

engine_test.go

Lines changed: 30 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ func Test_genAndSort(t *testing.T) {
3232
}
3333

3434
func Benchmark_root(b *testing.B) {
35-
/* initFenSq2Int()
35+
/* initFen2Sq()
3636
initMagic()
3737
initAtksKings()
3838
initAtksKnights()
@@ -52,52 +52,48 @@ func Benchmark_root(b *testing.B) {
5252
/* only a/b nothing else
5353
Benchmark_root-4: 192.911s 1 192848735500 ns/op 13250269384 B/op 28233303 allocs/op
5454
*/
55-
/* a/b sort captures first, followed by killers
55+
/* a/b sort captures first, followed by killers
5656
Benchmark_root-4 10.313s 1 10248174300 ns/op 4391864352 B/op 9352403 allocs/op
5757
*/
5858
/* a/b only killers first no other sorting
5959
Benchmark_root-4 241.245s 1 241180949700 ns/op 79804119512 B/op 169935345 allocs/op
6060
*/
6161

62-
63-
64-
func fakeRoot(){
62+
func fakeRoot() {
6563
var pv pvList
6664
var childPV pvList
6765
childPV.new()
6866
b := &board
6967
ml := make(moveList, 0, 60)
70-
limits.startTime, limits.nextTime = time.Now(), time.Now()
71-
alpha, beta := minEval, maxEval
72-
bm, bs := noMove, noScore
73-
depth := limits.depth
74-
cntNodes = 0
75-
killersx.clearx()
76-
ml.clear()
77-
genAndSort( b, &ml)
68+
limits.startTime, limits.nextTime = time.Now(), time.Now()
69+
alpha, beta := minEval, maxEval
70+
bm, bs := noMove, noScore
71+
depth := limits.depth
72+
cntNodes = 0
73+
killersx.clearx()
74+
ml.clear()
75+
genAndSort(b, &ml)
7876

79-
for ix := range ml {
80-
mv := &ml[ix]
81-
childPV.clear()
77+
for ix := range ml {
78+
mv := &ml[ix]
79+
childPV.clear()
8280

83-
b.move(*mv)
84-
tell("info currmove ", mv.String())
85-
score := -search(-beta, -alpha, depth-1, 1, &childPV, b)
86-
b.unmove(*mv)
87-
mv.packEval(signEval(b.stm, score))
88-
if score > bs {
89-
bs = score
90-
pv.clear()
91-
pv.catenate(*mv, &childPV)
81+
b.move(*mv)
82+
tell("info currmove ", mv.String())
83+
score := -search(-beta, -alpha, depth-1, 1, &childPV, b)
84+
b.unmove(*mv)
85+
mv.packEval(signEval(b.stm, score))
86+
if score > bs {
87+
bs = score
88+
pv.clear()
89+
pv.catenate(*mv, &childPV)
9290

93-
bm = *mv
94-
alpha = score
95-
tell(fmt.Sprintf("info score cp %v depth %v nodes %v pv ", bs, depth, cntNodes), pv.String())
96-
}
91+
bm = *mv
92+
alpha = score
93+
tell(fmt.Sprintf("info score cp %v depth %v nodes %v pv ", bs, depth, cntNodes), pv.String())
9794
}
98-
ml.sort()
99-
tell(fmt.Sprintf("info score cp %v depth %v nodes %v pv ", bm.eval(), depth, cntNodes), pv.String())
100-
fmt.Printf("bestmove %v%v", sq2Fen[ml[0].fr()], sq2Fen[ml[0].to()])
10195
}
102-
103-
96+
ml.sort()
97+
tell(fmt.Sprintf("info score cp %v depth %v nodes %v pv ", bm.eval(), depth, cntNodes), pv.String())
98+
fmt.Printf("bestmove %v%v", sq2Fen[ml[0].fr()], sq2Fen[ml[0].to()])
99+
}

evaluate.go

Lines changed: 22 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,13 @@
11
package main
2+
23
const (
3-
maxEval = +10000
4-
minEval = -maxEval
5-
mateEval= maxEval+1
6-
noScore = minEval-1
4+
maxEval = +10000
5+
minEval = -maxEval
6+
mateEval = maxEval + 1
7+
noScore = minEval - 1
78
)
89

9-
var pieceVal = [16]int{100, -100, 325, -325, 350, -350, 500, -500, 950, -950, 10000, -10000,0,0,0,0}
10+
var pieceVal = [16]int{100, -100, 325, -325, 350, -350, 500, -500, 950, -950, 10000, -10000, 0, 0, 0, 0}
1011

1112
var knightFile = [8]int{-4, -3, -2, +2, +2, 0, -2, -4}
1213
var knightRank = [8]int{-15, 0, +5, +6, +7, +8, +2, -4}
@@ -15,6 +16,7 @@ var kingFile = [8]int{+1, +2, 0, -2, -2, 0, +2, +1}
1516
var kingRank = [8]int{+1, 0, -2, -4, -6, -8, -10, -12}
1617
var pawnRank = [8]int{0, 0, 0, 0, +2, +6, +25, 0}
1718
var pawnFile = [8]int{0, 0, +1, +10, +10, +8, +10, +8}
19+
1820
const longDiag = 10
1921

2022
// Piece Square Table
@@ -25,35 +27,34 @@ var pSqTab [12][64]int
2527
//TODO: pawn structures. isolated, backward, duo, passed (guarded and not), double and more...
2628
//TODO: bishop pair
2729
//TODO: King safety. pawn shelter, guarding pieces
28-
//TODO: King attack. Attacking area surrounding the enemy king, closeness to the enemy king
30+
//TODO: King attack. Attacking area surrounding the enemy king, closeness to the enemy king
2931
//TODO: space, center control, knight outposts, connected rooks, 7th row and more
3032
//TODO: combine middle game and end game values
3133

3234
// evaluate returns score from white pov
33-
func evaluate(b *boardStruct) int {
35+
func evaluate(b *boardStruct) int {
3436
ev := 0
3537
for sq := A1; sq <= H8; sq++ {
36-
p12 := b.sq[sq]
37-
if p12 == empty {
38+
pc := b.sq[sq]
39+
if pc == empty {
3840
continue
3941
}
40-
ev += pieceVal[p12]
41-
ev += pSqScore(p12, sq)
42+
ev += pieceVal[pc]
43+
ev += pcSqScore(pc, sq)
4244
}
4345
return ev
4446
}
4547

46-
4748
// Score returns the piece square table value for a given piece on a given square. Stage = MG/EG
48-
func pSqScore(p12, sq int) int {
49-
return pSqTab[p12][sq]
49+
func pcSqScore(pc, sq int) int {
50+
return pSqTab[pc][sq]
5051
}
5152

5253
// PstInit intits the pieces-square-tables when the program starts
53-
func pSqInit() {
54-
for p12 := 0; p12 < 12; p12++ {
54+
func pcSqInit() {
55+
for pc := 0; pc < 12; pc++ {
5556
for sq := 0; sq < 64; sq++ {
56-
pSqTab[p12][sq] = 0
57+
pSqTab[pc][sq] = 0
5758
}
5859
}
5960

@@ -86,14 +87,14 @@ func pSqInit() {
8687
}
8788

8889
// for Black
89-
for pc := Pawn; pc <= King; pc++ {
90+
for pt := Pawn; pt <= King; pt++ {
9091

91-
wP12 := pc2P12(pc, WHITE)
92-
bP12 := pc2P12(pc, BLACK)
92+
wPiece := pt2pc(pt, WHITE)
93+
bPiece := pt2pc(pt, BLACK)
9394

9495
for bSq := 0; bSq < 64; bSq++ {
9596
wSq := oppRank(bSq)
96-
pSqTab[bP12][bSq] = -pSqTab[wP12][wSq]
97+
pSqTab[bPiece][bSq] = -pSqTab[wPiece][wSq]
9798
}
9899
}
99100
}

magic_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import (
55
)
66

77
func Test_sMagic_atks(t *testing.T) {
8-
initFenSq2Int()
8+
initFen2Sq()
99
initMagic()
1010
handleNewgame()
1111
tests := []struct {

0 commit comments

Comments
 (0)