-
Notifications
You must be signed in to change notification settings - Fork 0
/
game_test.go
146 lines (115 loc) · 3.84 KB
/
game_test.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
package chess
import (
"bufio"
"flag"
"fmt"
"os"
"slices"
"strconv"
"strings"
"testing"
"time"
)
type PerftTest struct {
fen string
depthMap map[int]int
maxDepth int
}
var loadedPerftTests []PerftTest
var maxDepth int // Default: 1
// Each line of the file should contain only one test position, along with it's depth result.
// If you want to ignore a test, write # in the first char of the line.
// Example line-test, with FEN position, and depth with results from 1 to 6:
// rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1;D1 20;D2 400;D3 8902;D4 197281;D5 4865609;D6 119060324
var perftEdpFile string // Default: "perft_test.epd"
// If you wish to print each movement's nodes for each depth done, set this flag.
var positionVerbose bool // Default: false
func init() {
flag.IntVar(&maxDepth, "maxDepth", 1, "The max amount of depth to explore in a test")
flag.StringVar(&perftEdpFile, "epd", "perft_tests.epd", "The name of the file in which the tests are written. Check code for more information about the file format.")
flag.BoolVar(&positionVerbose, "positionVerbose", false, "If set, prints each movement's nodes, for each depth done. Note: The tests will be runned one by one instead of in parallel.")
}
func parsePerftFile() {
loadedPerftTests = make([]PerftTest, 0)
file, err := os.Open(perftEdpFile)
if err != nil {
if os.IsNotExist(err) {
panic(fmt.Errorf("The perft test file with the name \"%s\" was not found. Use -epd=<filename> to specify a perft test file.", perftEdpFile))
} else {
panic(err)
}
}
defer file.Close()
scanner := bufio.NewScanner(file)
for scanner.Scan() {
lineText := scanner.Text()
if strings.HasPrefix(lineText, "#") {
continue
}
parts := strings.SplitN(lineText, ";", 2)
fen := strings.TrimSpace(parts[0])
depths := strings.Split(strings.TrimSpace(parts[1]), ";")
depthMap := make(map[int]int)
maxDepthFound := 0
for _, depth := range depths {
depthParts := strings.Split(strings.TrimSpace(depth), " ")
if len(depthParts) != 2 {
panic(fmt.Errorf("There was an error parsing the depth \"%s\".", depth))
}
depthAmount, _ := strconv.Atoi(string(depthParts[0][1]))
nodeAmount, _ := strconv.Atoi(depthParts[1])
if depthAmount > maxDepthFound && depthAmount <= maxDepth {
maxDepthFound = depthAmount
}
depthMap[depthAmount] = nodeAmount
}
loadedPerftTests = append(loadedPerftTests, PerftTest{
fen: fen,
depthMap: depthMap,
maxDepth: maxDepthFound,
})
}
if err := scanner.Err(); err != nil {
panic(err)
}
// Sort Perft tests in increasing amount of nodes at max depth
slices.SortFunc(loadedPerftTests, func(a, b PerftTest) int {
if a.depthMap[a.maxDepth] < b.depthMap[a.maxDepth] {
return -1
}
return 1
})
fmt.Printf("Running %d Perfts at max-depth of %d\n", len(loadedPerftTests), maxDepth)
}
func TestPerft(t *testing.T) {
parsePerftFile()
start := time.Now()
totalNodes := 0
t.Run("Perft group test", func(t *testing.T) {
for i, perftTest := range loadedPerftTests {
i := i
perftTest := perftTest
t.Run(fmt.Sprintf("Test %d: '%s'", i, perftTest.fen), func(t *testing.T) {
if !positionVerbose {
t.Parallel()
}
game, _ := NewGame(perftTest.fen)
for depth := 1; depth <= perftTest.maxDepth; depth++ {
if positionVerbose {
fmt.Printf("Evaluation depth: %d\n", depth)
}
result := game.perft(depth, depth, "", positionVerbose)
totalNodes += result
if val, ok := perftTest.depthMap[depth]; ok && val != result {
t.Fatalf("\tTest_%d: failed at depth: %d. Expected nodes: %d, got: %d\n\n", i, depth, perftTest.depthMap[depth], result)
}
}
})
}
})
elapsed := time.Since(start)
if elapsed.Seconds() > 0.2 {
kNodesPerSecond := float64(totalNodes) / elapsed.Seconds() / 1000.0 //kN/s
fmt.Printf("Nodes per second: %fkN/s\n", kNodesPerSecond)
}
}