You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
168 lines
4.8 KiB
Go
168 lines
4.8 KiB
Go
package nkatago
|
|
|
|
import (
|
|
"fmt"
|
|
"git.noahlan.cn/noahlan/ntool-biz/nkatago/codec"
|
|
"git.noahlan.cn/noahlan/ntool-biz/nkatago/req"
|
|
"git.noahlan.cn/noahlan/ntool-biz/nkatago/resp"
|
|
"git.noahlan.cn/noahlan/ntool/ndef"
|
|
"git.noahlan.cn/noahlan/ntool/nsys/cmdn"
|
|
"strings"
|
|
)
|
|
|
|
var (
|
|
gtpStartupDecidedFunc cmdn.LineFunc = func(sb *strings.Builder, line string) bool {
|
|
return strings.Contains(line, "GTP ready")
|
|
}
|
|
gtpEndLineDecidedFunc cmdn.LineFunc = func(sb *strings.Builder, line string) bool {
|
|
return len(line) == 0
|
|
}
|
|
gtpReadIDFunc cmdn.ReadIDFunc = func(serializer ndef.Serializer, data string) (string, error) {
|
|
return "", nil
|
|
}
|
|
)
|
|
|
|
type GtpEngine struct {
|
|
*KatagoEngine
|
|
|
|
lastID string
|
|
}
|
|
|
|
// NewGtpEngine 创建GTP引擎
|
|
func NewGtpEngine(name string, args ...string) (*GtpEngine, error) {
|
|
engine := &GtpEngine{KatagoEngine: &KatagoEngine{
|
|
Processor: cmdn.NewProcessor(
|
|
true,
|
|
cmdn.WithSerializer(codec.NewGtpSerializer()),
|
|
cmdn.WithStartupDecidedFunc(gtpStartupDecidedFunc),
|
|
cmdn.WithEndLineDecidedFunc(gtpEndLineDecidedFunc),
|
|
cmdn.WithReadIDFunc(gtpReadIDFunc),
|
|
),
|
|
Name: name,
|
|
Mode: EngineGTP,
|
|
}}
|
|
err := engine.Processor.Run(name, args...)
|
|
return engine, err
|
|
}
|
|
|
|
func (eng *GtpEngine) ID(id string) {
|
|
eng.lastID = id
|
|
}
|
|
|
|
// Exec 同步方式自定义命令
|
|
func (eng *GtpEngine) Exec(id, cmd string) (*resp.GtpResponse, error) {
|
|
cmds := strings.Fields(cmd)
|
|
|
|
ret := resp.NewGtpResponse(cmds[0])
|
|
err := eng.Processor.ExecAsync(req.NewGtpReq(id, cmd, cmds[1:]...), func(serializer ndef.Serializer, data string) {
|
|
ret.Err = serializer.Unmarshal([]byte(data), ret)
|
|
})
|
|
return ret, err
|
|
}
|
|
|
|
// KnowCommand 判断命令是否支持
|
|
func (eng *GtpEngine) KnowCommand(cmd string) bool {
|
|
value, err := eng.Exec(eng.lastID, "known_command "+cmd)
|
|
if err != nil {
|
|
return false
|
|
}
|
|
if strings.ToLower(strings.TrimSpace(value.Content)) != "true" {
|
|
return false
|
|
}
|
|
return true
|
|
}
|
|
|
|
// Komi 设置贴目
|
|
func (eng *GtpEngine) Komi(komi float64) (*resp.GtpResponse, error) {
|
|
return eng.Exec(eng.lastID, fmt.Sprintf("komi %v", komi))
|
|
}
|
|
|
|
// BoardSize 设置棋盘大小
|
|
func (eng *GtpEngine) BoardSize(size int) (*resp.GtpResponse, error) {
|
|
return eng.Exec(eng.lastID, fmt.Sprintf("boardsize %v", size))
|
|
}
|
|
|
|
// GenMove AI落子
|
|
func (eng *GtpEngine) GenMove(color string) (*resp.GtpResponse, error) {
|
|
color = strings.ToUpper(color)
|
|
command := "genmove " + color
|
|
return eng.Exec(eng.lastID, command)
|
|
}
|
|
|
|
// Play 手工落子
|
|
func (eng *GtpEngine) Play(color, coor string) (*resp.GtpResponse, error) {
|
|
color = strings.ToUpper(color)
|
|
return eng.Exec(eng.lastID, fmt.Sprintf("play %s %s", color, coor))
|
|
}
|
|
|
|
// LoadSgf 加载SGF文件
|
|
func (eng *GtpEngine) LoadSgf(file string) (*resp.GtpResponse, error) {
|
|
command := fmt.Sprintf("loadsgf %s", file)
|
|
return eng.Exec(eng.lastID, command)
|
|
}
|
|
|
|
// FinalStatusList 获取当前盘面形势判断
|
|
func (eng *GtpEngine) FinalStatusList(cmd string) (*resp.GtpResponse, error) {
|
|
command := fmt.Sprintf("final_status_list %s", cmd)
|
|
return eng.Exec(eng.lastID, command)
|
|
}
|
|
|
|
// SetLevel 设置AI级别
|
|
func (eng *GtpEngine) SetLevel(seed int) (*resp.GtpResponse, error) {
|
|
command := fmt.Sprintf("level %d", seed)
|
|
return eng.Exec(eng.lastID, command)
|
|
}
|
|
|
|
// SetRandomSeed 设置AI随机数
|
|
func (eng *GtpEngine) SetRandomSeed(seed int) (*resp.GtpResponse, error) {
|
|
command := fmt.Sprintf("set_random_seed %d", seed)
|
|
return eng.Exec(eng.lastID, command)
|
|
}
|
|
|
|
// ShowBoard 显示棋盘
|
|
func (eng *GtpEngine) ShowBoard() (*resp.GtpResponse, error) {
|
|
return eng.Exec(eng.lastID, "showboard")
|
|
}
|
|
|
|
// ClearBoard 清空棋盘
|
|
func (eng *GtpEngine) ClearBoard() (*resp.GtpResponse, error) {
|
|
return eng.Exec(eng.lastID, "clear_board")
|
|
}
|
|
|
|
// PrintSgf 打印SGF
|
|
func (eng *GtpEngine) PrintSgf() (*resp.GtpResponse, error) {
|
|
return eng.Exec(eng.lastID, "printsgf")
|
|
}
|
|
|
|
// TimeSetting 设置时间规则
|
|
func (eng *GtpEngine) TimeSetting(baseTime, byoTime, byoStones int) (*resp.GtpResponse, error) {
|
|
return eng.Exec(eng.lastID, fmt.Sprintf("time_settings %d %d %d", baseTime, byoTime, byoStones))
|
|
}
|
|
|
|
// KGSTimeSetting 设置KGS time
|
|
func (eng *GtpEngine) KGSTimeSetting(mainTime, readTime, readLimit int) (*resp.GtpResponse, error) {
|
|
return eng.Exec(eng.lastID, fmt.Sprintf("kgs-time_settings byoyomi %d %d %d", mainTime, readTime, readLimit))
|
|
}
|
|
|
|
// FinalScore 获取结果
|
|
func (eng *GtpEngine) FinalScore() (*resp.GtpResponse, error) {
|
|
return eng.Exec(eng.lastID, "final_score")
|
|
}
|
|
|
|
// Undo 悔棋
|
|
func (eng *GtpEngine) Undo() (*resp.GtpResponse, error) {
|
|
return eng.Exec(eng.lastID, "undo")
|
|
}
|
|
|
|
// TimeLeft 设置时间
|
|
func (eng *GtpEngine) TimeLeft(color string, mainTime, stones int) (*resp.GtpResponse, error) {
|
|
return eng.Exec(eng.lastID, fmt.Sprintf("time_left %s %d %d", color, mainTime, stones))
|
|
}
|
|
|
|
// Quit 退出
|
|
func (eng *GtpEngine) Quit() (*resp.GtpResponse, error) {
|
|
r, err := eng.Exec(eng.lastID, "quit")
|
|
err = eng.Processor.Stop()
|
|
return r, err
|
|
}
|