-
Notifications
You must be signed in to change notification settings - Fork 14
/
utils.go
234 lines (197 loc) · 5.89 KB
/
utils.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
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
// Copyright (c) 2014-2018 by Michael Dvorkin. All Rights Reserved.
// Use of this source code is governed by a MIT-style license that can
// be found in the LICENSE file.
//
// I am making my contributions/submissions to this project solely in my
// personal capacity and am not conveying any rights to any intellectual
// property of any third parties.
package donna
import (
`fmt`
`time`
)
// Returns row number in 0..7 range for the given square.
func row(square int) int {
return square >> 3
}
// Returns column number in 0..7 range for the given square.
func col(square int) int {
return square & 7
}
// Returns both row and column numbers for the given square.
func coordinate(square int) (int, int) {
return row(square), col(square)
}
// Returns relative rank for the square in 0..7 range. For example E2 is rank 1
// for white and rank 6 for black.
func rank(color int, square int) int {
return row(square) ^ (color * 7)
}
// Returns 0..63 square number for the given row/column coordinate.
func square(row, column int) int {
return (row << 3) + column
}
// Poor man's ternary. Works best with scalar yes and no.
func let(ok bool, yes, no int) int {
if ok {
return yes
}
return no
}
// Flips the square verically for white (ex. E2 becomes E7).
func flip(color int, square int) int {
if color == White {
return square ^ 56
}
return square
}
// Returns a bitmask with light or dark squares set matching the color of the
// square.
func same(square int) Bitmask {
if (bit[square] & maskDark).any() {
return maskDark
}
return ^maskDark
}
// Returns a distance between current node and the root one.
func ply() int {
return node - rootNode
}
// Returns a score of getting mated in given number of plies.
func matedIn(ply int) int {
return ply - Checkmate
}
// Returns a score of mating an opponent in given number of plies.
func matingIn(ply int) int {
return Checkmate - ply
}
// Adjusts values of alpha and beta based on how close we are
// to checkmate or be checkmated.
func mateDistance(alpha, beta, ply int) (int, int) {
return max(matedIn(ply), alpha), min(matingIn(ply + 1), beta)
}
func isMate(score int) bool {
return abs(score) >= Checkmate - MaxPly
}
// Integer version of math/abs.
func abs(n int) int {
if n < 0 {
return -n
}
return n
}
func min(x, y int) int {
if x < y {
return x
}
return y
}
func max(x, y int) int {
if x > y {
return x
}
return y
}
func min64(x, y int64) int64 {
if x < y {
return x
}
return y
}
func max64(x, y int64) int64 {
if x > y {
return x
}
return y
}
// Returns time in milliseconds elapsed since the given start time.
func since(start time.Time) int64 {
return time.Since(start).Nanoseconds() / 1000000
}
// Returns nodes per second search speed for the given time duration.
func nps(duration int64) int64 {
nodes := int64(game.nodes + game.qnodes) * 1000
if duration != 0 {
return nodes / duration
}
return nodes
}
// Returns a number of used items in a sample of 1000 cache entries.
func hashfull() int {
count := 0
if cacheSize := len(game.cache); cacheSize > 1000 {
start := (game.nodes + game.qnodes) % (cacheSize - 1000) // 0 <= start < cacheSize - 1000.
for i := start; i < start + 1000; i++ {
if game.cache[i].token() == game.token {
count++
}
}
}
return count
}
// Formats time duration in milliseconds in human readable form (MM:SS.XXX).
func ms(duration int64) string {
mm := duration / 1000 / 60
ss := duration / 1000 % 60
xx := duration - mm * 1000 * 60 - ss * 1000
return fmt.Sprintf(`%02d:%02d.%03d`, mm, ss, xx)
}
func C(color int) string {
return [2]string{`white`, `black`}[color]
}
func Summary(metrics map[string]interface{}) {
phase := metrics[`Phase`].(int)
tally := metrics[`PST`].(Score)
material := metrics[`Imbalance`].(Score)
final := metrics[`Final`].(Score)
units := float32(onePawn)
fmt.Println()
fmt.Printf("Metric MidGame | EndGame | Blended\n")
fmt.Printf(" W B W-B | W B W-B | (%d) \n", phase)
fmt.Printf("-----------------------------------+-----------------------+--------\n")
fmt.Printf("%-12s - - %5.2f | - - %5.2f > %5.2f\n", `PST`,
float32(tally.midgame)/units, float32(tally.endgame)/units, float32(tally.blended(phase))/units)
fmt.Printf("%-12s - - %5.2f | - - %5.2f > %5.2f\n", `Imbalance`,
float32(material.midgame)/units, float32(material.endgame)/units, float32(material.blended(phase))/units)
for _, tag := range([]string{`Tempo`, `Center`, `Threats`, `Pawns`, `Passers`, `Mobility`, `+Pieces`, `-Knights`, `-Bishops`, `-Rooks`, `-Queens`, `+King`, `-Cover`, `-Safety`}) {
white := metrics[tag].(Total).white
black := metrics[tag].(Total).black
var score Score
score.add(white).sub(black)
if tag[0:1] == `+` {
tag = tag[1:]
} else if tag[0:1] == `-` {
tag = ` ` + tag[1:]
}
fmt.Printf("%-12s %5.2f %5.2f %5.2f | %5.2f %5.2f %5.2f > %5.2f\n", tag,
float32(white.midgame)/units, float32(black.midgame)/units, float32(score.midgame)/units,
float32(white.endgame)/units, float32(black.endgame)/units, float32(score.endgame)/units,
float32(score.blended(phase))/units)
}
fmt.Printf("%-12s - - %5.2f | - - %5.2f > %5.2f\n\n", `Final Score`,
float32(final.midgame)/units, float32(final.endgame)/units, float32(final.blended(phase))/units)
}
// Logging wrapper around fmt.Printf() that could be turned on as needed. Typical
// usage is Log(); defer Log() in tests.
func Log(args ...interface{}) {
switch len(args) {
case 0:
// Calling Log() with no arguments flips the logging setting.
engine.log = !engine.log
engine.fancy = !engine.fancy
case 1:
switch args[0].(type) {
case bool:
engine.log = args[0].(bool)
engine.fancy = args[0].(bool)
default:
if engine.log {
fmt.Println(args...)
}
}
default:
if engine.log {
fmt.Printf(args[0].(string), args[1:]...)
}
}
}