Skip to content

Commit 49604a4

Browse files
committed
19 Look Who is Playing
1 parent dde4efa commit 49604a4

File tree

5 files changed

+217
-34
lines changed

5 files changed

+217
-34
lines changed

.vscode/settings.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,8 @@
1414
"pm.txt": true,
1515
"GoBit.exe~": true,
1616
".gitignore": true,
17-
"temp.go": true,
18-
"engine-interface.txt": true
17+
"engine-interface.txt": true,
18+
"temp.go": true
1919
},
2020
"cSpell.words": [
2121
"Pval",

engine.go

Lines changed: 62 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,12 @@ import (
55
"math"
66
)
77

8+
9+
//TODO search limits: start clock and testing for movetime
10+
//TODO search limits: counting nodes and testing for limit.nodes
11+
//TODO search limits: depth and other limits
12+
//TODO search limits: time per game w/wo increments
13+
//TODO search limits: time per x moves and after x moves w/wo increments
814
type searchLimits struct {
915
depth int
1016
nodes uint64
@@ -40,26 +46,72 @@ func engine() (toEngine chan bool, frEngine chan string) {
4046
fmt.Println("info string Hello from engine")
4147
frEngine = make(chan string)
4248
toEngine = make(chan bool)
43-
go rootx(toEngine, frEngine)
49+
go root(toEngine, frEngine)
4450

4551
return
4652
}
4753

54+
//TODO root: Iterative Depening
55+
//TODO root: Aspiration search
4856
func root(toEngine chan bool, frEngine chan string) {
57+
b := &board
58+
ml := moveList{}
4959
for _ = range toEngine {
50-
tell("info string engine got go!")
51-
// genAllMoves
52-
// evaluate and sort
53-
// for each move{
54-
// score := search()
55-
// store score in move
56-
//}
57-
// reply to GUI with the best move
60+
tell("info string engine got go! X")
61+
ml = moveList{}
62+
genAndSort(b, &ml)
63+
64+
for _, mv := range ml {
65+
b.move(mv)
66+
score := -search(b)
67+
b.unmove(mv)
68+
69+
mv.packEval(adjEval(b, score))
70+
}
71+
ml.sort()
72+
tell("info score cp ", fmt.Sprintf("%v", ml[0].eval()), " depth 1 pv ", ml[0].String())
73+
frEngine <- fmt.Sprintf("bestmove %v%v", sq2Fen[ml[0].fr()], sq2Fen[ml[0].to()])
5874
}
5975
}
6076

77+
//TODO search: the 'stop' command
78+
//TODO search: time handling basic movetime
79+
//TODO search: generate all moves and put captures first (temporary)
80+
//TODO search: alpha Beta
81+
//TODO search: qs
82+
//TODO search: killer moves
83+
//TODO search: hash table
84+
//TODO search: history table or counter move table
85+
//TODO search: move generation. More fast and accurate
86+
//TODO search: Null Move
87+
//TODO search: Late Move Reduction
88+
//TODO search: Internal Iterative Depening
89+
//TODO search: Delta Pruning
90+
//TODO search: more complicated time handling schemes
91+
//TODO search: other reductions and extensions
92+
6193
func search(b *boardStruct) int {
6294

63-
return b.evaluate()
95+
return evaluate(b)
6496
}
6597

98+
func genAndSort(b *boardStruct, ml *moveList) {
99+
b.genAllMoves(ml)
100+
101+
for ix, mv := range *ml {
102+
b.move(mv)
103+
v := evaluate(b)
104+
b.unmove(mv)
105+
v = adjEval(b, v)
106+
(*ml)[ix].packEval(v)
107+
}
108+
109+
ml.sort()
110+
}
111+
112+
func adjEval(b *boardStruct, ev int) int {
113+
if b.stm == BLACK {
114+
return -ev
115+
}
116+
return ev
117+
}

engine_test.go

Lines changed: 17 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,35 +1,30 @@
11
package main
22

3-
import "testing"
3+
import (
4+
"testing"
5+
)
46

5-
func Test_evaluate(t *testing.T) {
7+
func Test_genAndSort(t *testing.T) {
68
tests := []struct {
79
name string
8-
pos string
9-
want int
10+
pos string
11+
want1Mv string
12+
want1Ev int
1013
}{
11-
{"", "position startpos", 0},
12-
{"e4", "position startpos moves e2e4", 24},
13-
{"Nf3", "position startpos moves g1f3", 22},
14-
{"d4", "position startpos moves d2d4", 20},
15-
{"c4", "position startpos moves c2c4", 11},
16-
{"Nc3", "position startpos moves b1c3", 21},
17-
{"e4+d4", "position startpos moves e2e4 e7e5 d2d4",20},
18-
{"e4+Nf3", "position startpos moves e2e4 e7e5 g1f3", 22},
19-
{"e4+Be2", "position startpos moves e2e4 e7e5 f1e2", 15},
20-
{"e4+Bd3", "position startpos moves e2e4 e7e5 f1d3", 17},
21-
{"e4+Bc4", "position startpos moves e2e4 e7e5 f1c4", 18},
22-
{"e4+Bb5", "position startpos moves e2e4 e7e5 f1b5", 17},
23-
{"e4+Ba6", "position startpos moves e2e4 e7e5 f1a6", 8},
14+
{"","position startpos ","e2e4",24},
2415
}
2516

26-
pSqInit()
2717
for _, tt := range tests {
28-
handlePosition(tt.pos)
2918
t.Run(tt.name, func(t *testing.T) {
30-
if got := board.evaluate(); got != tt.want {
31-
t.Errorf("%v: evaluate() = %v, want %v", tt.name, got, tt.want)
32-
}
19+
var ml moveList
20+
genAndSort(&board, &ml)
21+
if tt.want1Mv != trim(ml[0].String()) {
22+
t.Errorf("%v: %#v should be best move. Got %#v", tt.name, tt.want1Mv, trim(ml[0].String()))
23+
}
24+
if tt.want1Ev != ml[0].eval() {
25+
t.Errorf("%v: %v should be best score. Got %v", tt.name, tt.want1Ev, ml[0].eval())
26+
}
27+
3328
})
3429
}
3530
}

evaluate.go

Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
package main
2+
3+
var pieceVal = [12]int{100, -100, 325, -325, 350, -350, 500, -500, 950, -950, 10000, -10000}
4+
5+
var knightFile = [8]int{-4, -3, -2, +2, +2, 0, -2, -4}
6+
var knightRank = [8]int{-15, 0, +5, +6, +7, +8, +2, -4}
7+
var centerFile = [8]int{-8, -1, 0, +1, +1, 0, -1, -3}
8+
var kingFile = [8]int{+1, +2, 0, -2, -2, 0, +2, +1}
9+
var kingRank = [8]int{+1, 0, -2, -4, -6, -8, -10, -12}
10+
var pawnRank = [8]int{0, 0, 0, 0, +2, +6, +25, 0}
11+
var pawnFile = [8]int{0, 0, +1, +10, +10, +8, +10, +8}
12+
const longDiag = 10
13+
14+
// Piece Square Table
15+
var pSqTab [12][64]int
16+
17+
//TODO: eval hash
18+
//TODO: pawn hash
19+
//TODO: pawn structures. isolated, backward, duo, passed (guarded and not), double and more...
20+
//TODO: bishop pair
21+
//TODO: King safety. pawn shelter, guarding pieces
22+
//TODO: King attack. Attacking area surrounding the enemy king, closeness to the enemy king
23+
//TODO: space, center control, knight outposts, connected rooks, 7th row and more
24+
//TODO: combine middle game and end game values
25+
26+
// evaluate returns score from white pov
27+
func evaluate(b *boardStruct) int {
28+
ev := 0
29+
for sq := A1; sq <= H8; sq++ {
30+
p12 := b.sq[sq]
31+
if p12 == empty {
32+
continue
33+
}
34+
ev += pieceVal[p12]
35+
ev += pSqScore(p12, sq)
36+
}
37+
return ev
38+
}
39+
40+
41+
// Score returns the piece square table value for a given piece on a given square. Stage = MG/EG
42+
func pSqScore(p12, sq int) int {
43+
return pSqTab[p12][sq]
44+
}
45+
46+
// PstInit intits the pieces-square-tables when the program starts
47+
func pSqInit() {
48+
tell("info string pStInit startar")
49+
for p12 := 0; p12 < 12; p12++ {
50+
for sq := 0; sq < 64; sq++ {
51+
pSqTab[p12][sq] = 0
52+
}
53+
}
54+
55+
for sq := 0; sq < 64; sq++ {
56+
57+
fl := sq % 8
58+
rk := sq / 8
59+
60+
pSqTab[wP][sq] = pawnFile[fl] + pawnRank[rk]
61+
62+
pSqTab[wN][sq] = knightFile[fl] + knightRank[rk]
63+
pSqTab[wB][sq] = centerFile[fl] + centerFile[rk]*2
64+
65+
pSqTab[wR][sq] = centerFile[fl] * 5
66+
67+
pSqTab[wQ][sq] = centerFile[fl] + centerFile[rk]
68+
69+
pSqTab[wK][sq] = (kingFile[fl] + kingRank[rk]) * 8
70+
}
71+
72+
// bonus for e4 d5 and c4
73+
pSqTab[wP][E2], pSqTab[wP][D2], pSqTab[wP][E3], pSqTab[wP][D3], pSqTab[wP][E4], pSqTab[wP][D4], pSqTab[wP][C4] = 0, 0, 6, 6, 24, 20, 12
74+
75+
// long diagonal
76+
for sq := A1; sq <= H8; sq += NE {
77+
pSqTab[wB][sq] += longDiag - 2
78+
}
79+
for sq := H1; sq <= A8; sq += NW {
80+
pSqTab[wB][sq] += longDiag
81+
}
82+
83+
// for Black
84+
for pc := Pawn; pc <= King; pc++ {
85+
86+
wP12 := pc2P12(pc, WHITE)
87+
bP12 := pc2P12(pc, BLACK)
88+
89+
for bSq := 0; bSq < 64; bSq++ {
90+
wSq := oppRank(bSq)
91+
pSqTab[bP12][bSq] = -pSqTab[wP12][wSq]
92+
}
93+
}
94+
}
95+
96+
// mirror the rank_sq
97+
func oppRank(sq int) int {
98+
fl := sq % 8
99+
rk := sq / 8
100+
rk = 7 - rk
101+
return rk*8 + fl
102+
}

evaluate_test.go

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
package main
2+
3+
import "testing"
4+
5+
func Test_evaluate(t *testing.T) {
6+
tests := []struct {
7+
name string
8+
pos string
9+
want int
10+
}{
11+
{"", "position startpos", 0},
12+
{"e4", "position startpos moves e2e4", 24},
13+
{"Nf3", "position startpos moves g1f3", 22},
14+
{"d4", "position startpos moves d2d4", 20},
15+
{"c4", "position startpos moves c2c4", 11},
16+
{"Nc3", "position startpos moves b1c3", 21},
17+
{"e4+d4", "position startpos moves e2e4 e7e5 d2d4",20},
18+
{"e4+Nf3", "position startpos moves e2e4 e7e5 g1f3", 22},
19+
{"e4+Be2", "position startpos moves e2e4 e7e5 f1e2", 15},
20+
{"e4+Bd3", "position startpos moves e2e4 e7e5 f1d3", 17},
21+
{"e4+Bc4", "position startpos moves e2e4 e7e5 f1c4", 18},
22+
{"e4+Bb5", "position startpos moves e2e4 e7e5 f1b5", 17},
23+
{"e4+Ba6", "position startpos moves e2e4 e7e5 f1a6", 8},
24+
}
25+
26+
for _, tt := range tests {
27+
handlePosition(tt.pos)
28+
t.Run(tt.name, func(t *testing.T) {
29+
if got := evaluate(&board); got != tt.want {
30+
t.Errorf("%v: evaluate() = %v, want %v", tt.name, got, tt.want)
31+
}
32+
})
33+
}
34+
}

0 commit comments

Comments
 (0)