-
Notifications
You must be signed in to change notification settings - Fork 0
/
uci_commands.go
147 lines (124 loc) · 3.83 KB
/
uci_commands.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
package engine
import (
"bufio"
"fmt"
"os"
"strconv"
"strings"
"github.com/gabtar/aconcagua/board"
"github.com/gabtar/aconcagua/search"
)
type UciCommand func(en *Engine, stdout chan string, params ...string)
var uciCommands map[string]UciCommand = map[string]UciCommand{
"uci": uciCommand,
"isready": isReadyCommand,
"position": positionCommand,
"go": goCommand,
"d": printBoardCommand,
"perft": perftCommand,
"divide": divideCommand,
}
func (en *Engine) execute(command string, stdout chan string, params ...string) {
comm, exists := uciCommands[command]
if !exists {
stdout <- "invalid command"
return
}
comm(en, stdout, params...)
}
// uciCommand takes care of uciok command from gui
func uciCommand(en *Engine, stdout chan string, params ...string) {
// TODO: add more data/options of the engine. Checkout the stockfish's uciok implementation
stdout <- "id aconcagua"
stdout <- "author gabtar"
stdout <- "uciok"
}
// positionCommand implements the position uci command
func positionCommand(en *Engine, stdout chan string, params ...string) {
if params[0] == "startpos" {
engine.pos = *board.From("rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1")
} else if params[0] == "fen" {
fen := strings.Join(params[1:], " ")
en.pos = *board.From(fen)
} else {
stdout <- "invalid command"
return
}
movesIndex := findParam(params, "moves")
if movesIndex != -1 {
for _, move := range params[movesIndex:] {
for _, legalMove := range engine.pos.LegalMoves(engine.pos.Turn) {
if legalMove.ToUci() == move {
engine.pos.MakeMove(&legalMove)
}
}
}
}
}
// isReadyCommand responses to the gui whenever the engine is ready
func isReadyCommand(en *Engine, stdout chan string, params ...string) {
stdout <- "readyok"
}
// goCommand starts looking for the best move in the current position
func goCommand(en *Engine, stdout chan string, params ...string) {
// TODO: implement remaining 'go' uci options
depth := 5 // Default depth
dIndex := findParam(params, "depth")
if dIndex != -1 {
depth, _ = strconv.Atoi(params[dIndex+1])
}
score, bestMove := search.Search(&engine.pos, depth, stdout)
stdout <- "info score cp " + strconv.Itoa(score)
stdout <- "bestmove " + bestMove[0].ToUci()
}
// readStdin reads strings from standard input (from GUI to engine)
func readStdin(input chan string) {
scanner := bufio.NewScanner(os.Stdin)
for scanner.Scan() {
input <- scanner.Text()
if err := scanner.Err(); err != nil {
fmt.Fprintln(os.Stderr, "info string error reading standard input:", err)
os.Exit(1)
}
}
}
// writeStdout writes strings to standard output (form engine to GUI)
func writeStdout(output <-chan string) {
for cmd := range output {
fmt.Println(cmd)
}
}
// findParam returns the index if the passed params slice contains the searched param string or -1 if not
func findParam(params []string, param string) int {
for i, p := range params {
if p == param {
return i
}
}
return -1
}
// NOTE: Non uci commands, for debug only!
// printBoardCommand prtints the board on the terminal
func printBoardCommand(en *Engine, stdout chan string, params ...string) {
stdout <- en.pos.String()
}
// perftCommand returns the number of moves up to the passed depth for the current position
func perftCommand(en *Engine, stdout chan string, params ...string) {
depth, err := strconv.Atoi(params[0])
if err != nil {
stdout <- "invalid command"
return
}
stdout <- "nodes " + strconv.FormatUint(engine.pos.Perft(depth), 10)
}
// divide returns the number of moves up to the depth passed for each move of the current position
func divideCommand(en *Engine, stdout chan string, params ...string) {
depth, err := strconv.Atoi(params[0])
if err != nil {
stdout <- "invalid command"
return
}
for _, perft := range strings.Split(engine.pos.Divide(depth), ",") {
stdout <- perft
}
}