|  |  | package gtp
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  | import (
 | 
						
						
						
							|  |  | 	"errors"
 | 
						
						
						
							|  |  | 	"fmt"
 | 
						
						
						
							|  |  | 	"git.noahlan.cn/noahlan/ntool/narr"
 | 
						
						
						
							|  |  | 	"git.noahlan.cn/noahlan/ntool/ncmd"
 | 
						
						
						
							|  |  | 	"git.noahlan.cn/noahlan/ntool/nlog"
 | 
						
						
						
							|  |  | 	"git.noahlan.cn/noahlan/ntool/nmath"
 | 
						
						
						
							|  |  | 	"git.noahlan.cn/noahlan/ntool/nstr"
 | 
						
						
						
							|  |  | 	"git.noahlan.cn/noahlan/ntool/nsys/atomic"
 | 
						
						
						
							|  |  | 	"strings"
 | 
						
						
						
							|  |  | 	"sync"
 | 
						
						
						
							|  |  | 	"time"
 | 
						
						
						
							|  |  | )
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  | var (
 | 
						
						
						
							|  |  | 	DefaultMaxMessageId uint = 999
 | 
						
						
						
							|  |  | 	DefaultTimeout           = 3 * time.Second
 | 
						
						
						
							|  |  | )
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  | var ErrTimeout = errors.New("timeout")
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  | type (
 | 
						
						
						
							|  |  | 	Options struct {
 | 
						
						
						
							|  |  | 		DevMode      bool          `json:",default=false"` // 开发模式
 | 
						
						
						
							|  |  | 		MaxMessageId uint          `json:",default=999"`   // 最大消息ID
 | 
						
						
						
							|  |  | 		Timeout      time.Duration `json:""`               // 单条命令最大等待时间,默认3s
 | 
						
						
						
							|  |  | 	}
 | 
						
						
						
							|  |  | 	Option func(options *Options)
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  | 	pendingMsg struct {
 | 
						
						
						
							|  |  | 		chWait chan struct{}
 | 
						
						
						
							|  |  | 		resp   *GTPResponse
 | 
						
						
						
							|  |  | 	}
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  | 	GtpEngine struct {
 | 
						
						
						
							|  |  | 		*Options
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  | 		Cmd        *ncmd.Cmd
 | 
						
						
						
							|  |  | 		serializer *GTPSerializer
 | 
						
						
						
							|  |  | 		mid        *atomic.AtomicInt64
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  | 		pendingMsg map[string]*pendingMsg
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  | 		mu sync.RWMutex
 | 
						
						
						
							|  |  | 	}
 | 
						
						
						
							|  |  | )
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  | func NewGtpEngine(opts ...Option) *GtpEngine {
 | 
						
						
						
							|  |  | 	serializer := NewGTPSerializer()
 | 
						
						
						
							|  |  | 	ret := &GtpEngine{
 | 
						
						
						
							|  |  | 		Options: &Options{
 | 
						
						
						
							|  |  | 			DevMode: false,
 | 
						
						
						
							|  |  | 		},
 | 
						
						
						
							|  |  | 		mid:        atomic.NewAtomicInt64(),
 | 
						
						
						
							|  |  | 		pendingMsg: make(map[string]*pendingMsg),
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  | 		mu: sync.RWMutex{},
 | 
						
						
						
							|  |  | 	}
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  | 	for _, opt := range opts {
 | 
						
						
						
							|  |  | 		opt(ret.Options)
 | 
						
						
						
							|  |  | 	}
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  | 	if ret.MaxMessageId == 0 {
 | 
						
						
						
							|  |  | 		ret.MaxMessageId = DefaultMaxMessageId
 | 
						
						
						
							|  |  | 	}
 | 
						
						
						
							|  |  | 	if ret.Timeout == 0 {
 | 
						
						
						
							|  |  | 		ret.Timeout = DefaultTimeout
 | 
						
						
						
							|  |  | 	}
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  | 	ret.serializer = serializer
 | 
						
						
						
							|  |  | 	ret.Cmd = ncmd.NewCmd(ncmd.WithOptions(&ncmd.Options{
 | 
						
						
						
							|  |  | 		Marshaler:      serializer,
 | 
						
						
						
							|  |  | 		Buffered:       false,
 | 
						
						
						
							|  |  | 		CombinedOutput: false,
 | 
						
						
						
							|  |  | 		Streaming:      true,
 | 
						
						
						
							|  |  | 		LineBufferSize: ncmd.DEFAULT_LINE_BUFFER_SIZE,
 | 
						
						
						
							|  |  | 		DevMode:        ret.DevMode,
 | 
						
						
						
							|  |  | 	}))
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  | 	return ret
 | 
						
						
						
							|  |  | }
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  | // Bind 绑定实例到id
 | 
						
						
						
							|  |  | func (e *GtpEngine) Bind(id int64) {
 | 
						
						
						
							|  |  | 	e.Cmd.Session.SetId(id)
 | 
						
						
						
							|  |  | 	// Set in_use flag
 | 
						
						
						
							|  |  | 	e.Cmd.Session.SetAttribute(KeyInUse, true)
 | 
						
						
						
							|  |  | }
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  | // Release 释放示例,以便其它调用
 | 
						
						
						
							|  |  | func (e *GtpEngine) Release() {
 | 
						
						
						
							|  |  | 	e.Cmd.Session.SetId(0)
 | 
						
						
						
							|  |  | 	e.Cmd.Session.SetAttribute(KeyInUse, false)
 | 
						
						
						
							|  |  | }
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  | func (e *GtpEngine) Session() *ncmd.Session {
 | 
						
						
						
							|  |  | 	return e.Cmd.Session
 | 
						
						
						
							|  |  | }
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  | func (e *GtpEngine) StartSafe(name string, args ...string) <-chan ncmd.Status {
 | 
						
						
						
							|  |  | 	ret := e.Cmd.StatusChan()
 | 
						
						
						
							|  |  | 	if !e.Cmd.Started() {
 | 
						
						
						
							|  |  | 		if !e.Cmd.Stopped() {
 | 
						
						
						
							|  |  | 			ret = e.Start(name, args...)
 | 
						
						
						
							|  |  | 		} else {
 | 
						
						
						
							|  |  | 			// 被停止
 | 
						
						
						
							|  |  | 			e.Cmd = e.Cmd.Clone()
 | 
						
						
						
							|  |  | 			ret = e.Start(name, args...)
 | 
						
						
						
							|  |  | 		}
 | 
						
						
						
							|  |  | 	}
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  | 	return ret
 | 
						
						
						
							|  |  | }
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  | func (e *GtpEngine) Start(name string, args ...string) <-chan ncmd.Status {
 | 
						
						
						
							|  |  | 	statusChan := e.Cmd.Start(name, args...)
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  | 	go e.handleMessage()
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  | 	return statusChan
 | 
						
						
						
							|  |  | }
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  | func (e *GtpEngine) Send(id, command string, args ...string) (*GTPResponse, error) {
 | 
						
						
						
							|  |  | 	if len(id) == 0 || len(id) > 3 {
 | 
						
						
						
							|  |  | 		id = e.nextId()
 | 
						
						
						
							|  |  | 	}
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  | 	pMsg := &pendingMsg{
 | 
						
						
						
							|  |  | 		chWait: make(chan struct{}, 1),
 | 
						
						
						
							|  |  | 	}
 | 
						
						
						
							|  |  | 	e.mu.Lock()
 | 
						
						
						
							|  |  | 	e.pendingMsg[id] = pMsg
 | 
						
						
						
							|  |  | 	e.mu.Unlock()
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  | 	err := e.Cmd.Send(>PCommand{
 | 
						
						
						
							|  |  | 		ID:   id,
 | 
						
						
						
							|  |  | 		Cmd:  command,
 | 
						
						
						
							|  |  | 		Args: args,
 | 
						
						
						
							|  |  | 	})
 | 
						
						
						
							|  |  | 	if err != nil {
 | 
						
						
						
							|  |  | 		return nil, err
 | 
						
						
						
							|  |  | 	}
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  | 	// waiting pending message with timeout
 | 
						
						
						
							|  |  | 	timer := time.NewTimer(e.Timeout)
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  | 	select {
 | 
						
						
						
							|  |  | 	case <-timer.C:
 | 
						
						
						
							|  |  | 		// error
 | 
						
						
						
							|  |  | 		e.mu.Lock()
 | 
						
						
						
							|  |  | 		delete(e.pendingMsg, id)
 | 
						
						
						
							|  |  | 		e.mu.Unlock()
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  | 		return nil, ErrTimeout
 | 
						
						
						
							|  |  | 	case <-pMsg.chWait:
 | 
						
						
						
							|  |  | 		return pMsg.resp, nil
 | 
						
						
						
							|  |  | 	}
 | 
						
						
						
							|  |  | }
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  | func (e *GtpEngine) nextId() string {
 | 
						
						
						
							|  |  | 	idInt := e.mid.IncrementAndGet()
 | 
						
						
						
							|  |  | 	if idInt > int64(e.MaxMessageId) {
 | 
						
						
						
							|  |  | 		e.mid.Reset()
 | 
						
						
						
							|  |  | 		idInt = e.mid.IncrementAndGet()
 | 
						
						
						
							|  |  | 	}
 | 
						
						
						
							|  |  | 	return nstr.SafeString(idInt)
 | 
						
						
						
							|  |  | }
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  | func (e *GtpEngine) handleMessage() {
 | 
						
						
						
							|  |  | 	for e.Cmd.Stdout != nil || e.Cmd.Stderr != nil {
 | 
						
						
						
							|  |  | 		select {
 | 
						
						
						
							|  |  | 		case line, open := <-e.Cmd.Stdout:
 | 
						
						
						
							|  |  | 			if !open {
 | 
						
						
						
							|  |  | 				e.Cmd.Stdout = nil
 | 
						
						
						
							|  |  | 				continue
 | 
						
						
						
							|  |  | 			}
 | 
						
						
						
							|  |  | 			//if e.DevMode {
 | 
						
						
						
							|  |  | 			//	nlog.Debugf("接收单条消息: %s", line)
 | 
						
						
						
							|  |  | 			//}
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  | 			resp, ok := e.serializer.Unmarshal(line)
 | 
						
						
						
							|  |  | 			if !ok {
 | 
						
						
						
							|  |  | 				continue
 | 
						
						
						
							|  |  | 			}
 | 
						
						
						
							|  |  | 			if e.DevMode {
 | 
						
						
						
							|  |  | 				nlog.Debugf("接收完整消息: %+v", resp)
 | 
						
						
						
							|  |  | 			}
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  | 			e.mu.RLock()
 | 
						
						
						
							|  |  | 			pMsg, ok := e.pendingMsg[resp.ID]
 | 
						
						
						
							|  |  | 			e.mu.RUnlock()
 | 
						
						
						
							|  |  | 			if !ok {
 | 
						
						
						
							|  |  | 				continue
 | 
						
						
						
							|  |  | 			}
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  | 			pMsg.resp = resp
 | 
						
						
						
							|  |  | 			pMsg.chWait <- struct{}{}
 | 
						
						
						
							|  |  | 		case line, open := <-e.Cmd.Stderr:
 | 
						
						
						
							|  |  | 			if !open {
 | 
						
						
						
							|  |  | 				e.Cmd.Stdout = nil
 | 
						
						
						
							|  |  | 				continue
 | 
						
						
						
							|  |  | 			}
 | 
						
						
						
							|  |  | 			nlog.Errorf("错误消息: %s\n", line)
 | 
						
						
						
							|  |  | 		}
 | 
						
						
						
							|  |  | 	}
 | 
						
						
						
							|  |  | }
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  | // Name 查看gtp软件名称
 | 
						
						
						
							|  |  | func (e *GtpEngine) Name() string {
 | 
						
						
						
							|  |  | 	cmd := "name"
 | 
						
						
						
							|  |  | 	resp, err := e.Send(e.nextId(), cmd)
 | 
						
						
						
							|  |  | 	if !e.Check(resp, err, cmd) {
 | 
						
						
						
							|  |  | 		return "unknown"
 | 
						
						
						
							|  |  | 	}
 | 
						
						
						
							|  |  | 	return resp.Content
 | 
						
						
						
							|  |  | }
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  | // Version 查看gtp软件版本
 | 
						
						
						
							|  |  | func (e *GtpEngine) Version() string {
 | 
						
						
						
							|  |  | 	cmd := "version"
 | 
						
						
						
							|  |  | 	resp, err := e.Send(e.nextId(), cmd)
 | 
						
						
						
							|  |  | 	if !e.Check(resp, err, cmd) {
 | 
						
						
						
							|  |  | 		return "0"
 | 
						
						
						
							|  |  | 	}
 | 
						
						
						
							|  |  | 	return resp.Content
 | 
						
						
						
							|  |  | }
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  | // ProtocolVersion 查看gtp协议版本
 | 
						
						
						
							|  |  | func (e *GtpEngine) ProtocolVersion() string {
 | 
						
						
						
							|  |  | 	cmd := "protocol_version"
 | 
						
						
						
							|  |  | 	resp, err := e.Send(e.nextId(), cmd)
 | 
						
						
						
							|  |  | 	if !e.Check(resp, err, cmd) {
 | 
						
						
						
							|  |  | 		return "1"
 | 
						
						
						
							|  |  | 	}
 | 
						
						
						
							|  |  | 	return resp.Content
 | 
						
						
						
							|  |  | }
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  | // ListCommands 列举gtp软件可用命令列表
 | 
						
						
						
							|  |  | func (e *GtpEngine) ListCommands() []string {
 | 
						
						
						
							|  |  | 	cmd := "list_commands"
 | 
						
						
						
							|  |  | 	resp, err := e.Send(e.nextId(), cmd)
 | 
						
						
						
							|  |  | 	if !e.Check(resp, err, cmd) {
 | 
						
						
						
							|  |  | 		return []string{}
 | 
						
						
						
							|  |  | 	}
 | 
						
						
						
							|  |  | 	return strings.Split(resp.Content, "\n")
 | 
						
						
						
							|  |  | }
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  | // KnowCommand 判断命令是否支持
 | 
						
						
						
							|  |  | func (e *GtpEngine) KnowCommand(cmd string) bool {
 | 
						
						
						
							|  |  | 	command := fmt.Sprintf("known_command %s", cmd)
 | 
						
						
						
							|  |  | 	resp, err := e.Send(e.nextId(), command)
 | 
						
						
						
							|  |  | 	if !e.Check(resp, err, command) {
 | 
						
						
						
							|  |  | 		return false
 | 
						
						
						
							|  |  | 	}
 | 
						
						
						
							|  |  | 	if strings.ToLower(strings.TrimSpace(resp.Content)) != "true" {
 | 
						
						
						
							|  |  | 		return false
 | 
						
						
						
							|  |  | 	}
 | 
						
						
						
							|  |  | 	return true
 | 
						
						
						
							|  |  | }
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  | // Komi 设置贴目
 | 
						
						
						
							|  |  | func (e *GtpEngine) Komi(komi float64) bool {
 | 
						
						
						
							|  |  | 	cmd := fmt.Sprintf("komi %.1f", komi)
 | 
						
						
						
							|  |  | 	resp, err := e.Send(e.nextId(), cmd)
 | 
						
						
						
							|  |  | 	if !e.Check(resp, err, cmd) {
 | 
						
						
						
							|  |  | 		return false
 | 
						
						
						
							|  |  | 	}
 | 
						
						
						
							|  |  | 	return true
 | 
						
						
						
							|  |  | }
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  | // BoardSize 设置棋盘大小
 | 
						
						
						
							|  |  | func (e *GtpEngine) BoardSize(size int) bool {
 | 
						
						
						
							|  |  | 	cmd := fmt.Sprintf("boardsize %d", size)
 | 
						
						
						
							|  |  | 	resp, err := e.Send(e.nextId(), cmd)
 | 
						
						
						
							|  |  | 	if !e.Check(resp, err, cmd) {
 | 
						
						
						
							|  |  | 		return false
 | 
						
						
						
							|  |  | 	}
 | 
						
						
						
							|  |  | 	return true
 | 
						
						
						
							|  |  | }
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  | // ClearBoard 清理棋盘
 | 
						
						
						
							|  |  | func (e *GtpEngine) ClearBoard() bool {
 | 
						
						
						
							|  |  | 	cmd := "clear_board"
 | 
						
						
						
							|  |  | 	resp, err := e.Send(e.nextId(), cmd)
 | 
						
						
						
							|  |  | 	if !e.Check(resp, err, cmd) {
 | 
						
						
						
							|  |  | 		return false
 | 
						
						
						
							|  |  | 	}
 | 
						
						
						
							|  |  | 	return true
 | 
						
						
						
							|  |  | }
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  | // Play 下棋 color: B/W vex: A1
 | 
						
						
						
							|  |  | func (e *GtpEngine) Play(color, vex string) bool {
 | 
						
						
						
							|  |  | 	cmd := fmt.Sprintf("play %s %s", color, vex)
 | 
						
						
						
							|  |  | 	resp, err := e.Send(e.nextId(), cmd)
 | 
						
						
						
							|  |  | 	if !e.Check(resp, err, cmd) {
 | 
						
						
						
							|  |  | 		return false
 | 
						
						
						
							|  |  | 	}
 | 
						
						
						
							|  |  | 	return true
 | 
						
						
						
							|  |  | }
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  | // GenMove 生成一手棋 color: B/W
 | 
						
						
						
							|  |  | func (e *GtpEngine) GenMove(color string) string {
 | 
						
						
						
							|  |  | 	cmd := fmt.Sprintf("genmove %s", color)
 | 
						
						
						
							|  |  | 	resp, err := e.Send(e.nextId(), cmd)
 | 
						
						
						
							|  |  | 	if !e.Check(resp, err, cmd) {
 | 
						
						
						
							|  |  | 		return ""
 | 
						
						
						
							|  |  | 	}
 | 
						
						
						
							|  |  | 	return resp.Content
 | 
						
						
						
							|  |  | }
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  | // SetFreeHandicap 设置自由的让子点位
 | 
						
						
						
							|  |  | func (e *GtpEngine) SetFreeHandicap(vexArr ...string) bool {
 | 
						
						
						
							|  |  | 	if len(vexArr) < 2 {
 | 
						
						
						
							|  |  | 		return true
 | 
						
						
						
							|  |  | 	}
 | 
						
						
						
							|  |  | 	cmd := fmt.Sprintf("set_free_handicap %s", strings.Join(vexArr, " "))
 | 
						
						
						
							|  |  | 	resp, err := e.Send(e.nextId(), cmd)
 | 
						
						
						
							|  |  | 	if !e.Check(resp, err, cmd) {
 | 
						
						
						
							|  |  | 		return false
 | 
						
						
						
							|  |  | 	}
 | 
						
						
						
							|  |  | 	return true
 | 
						
						
						
							|  |  | }
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  | // Sync 同步
 | 
						
						
						
							|  |  | // handicaps: points of all handicaps
 | 
						
						
						
							|  |  | // plays: [][2]string -> [["B","A1"]]
 | 
						
						
						
							|  |  | func (e *GtpEngine) Sync(size int, komi float64, level int, handicaps []string, plays [][]string) bool {
 | 
						
						
						
							|  |  | 	// 1. komi
 | 
						
						
						
							|  |  | 	// 2. boardsize
 | 
						
						
						
							|  |  | 	// 3. set_level
 | 
						
						
						
							|  |  | 	// 4. clear_board
 | 
						
						
						
							|  |  | 	// 5. set_free_handicap pos1 pos2 ...
 | 
						
						
						
							|  |  | 	// 6~n. play X XX
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  | 	playAllFn := func() bool {
 | 
						
						
						
							|  |  | 		return narr.Every(plays, func(_ int, v []string) bool {
 | 
						
						
						
							|  |  | 			if len(v) < 2 {
 | 
						
						
						
							|  |  | 				return false
 | 
						
						
						
							|  |  | 			}
 | 
						
						
						
							|  |  | 			color, pos := v[0], v[1]
 | 
						
						
						
							|  |  | 			return e.Play(color, pos)
 | 
						
						
						
							|  |  | 		})
 | 
						
						
						
							|  |  | 	}
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  | 	return e.Komi(komi) &&
 | 
						
						
						
							|  |  | 		e.BoardSize(size) &&
 | 
						
						
						
							|  |  | 		e.SetLevel(level) &&
 | 
						
						
						
							|  |  | 		e.ClearBoard() &&
 | 
						
						
						
							|  |  | 		e.SetFreeHandicap(handicaps...) &&
 | 
						
						
						
							|  |  | 		playAllFn()
 | 
						
						
						
							|  |  | }
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  | // LoadSgf 加载SGF文件
 | 
						
						
						
							|  |  | func (e *GtpEngine) LoadSgf(file string) bool {
 | 
						
						
						
							|  |  | 	cmd := fmt.Sprintf("loadsgf %s", file)
 | 
						
						
						
							|  |  | 	resp, err := e.Send(e.nextId(), cmd)
 | 
						
						
						
							|  |  | 	if !e.Check(resp, err, cmd) {
 | 
						
						
						
							|  |  | 		return false
 | 
						
						
						
							|  |  | 	}
 | 
						
						
						
							|  |  | 	return true
 | 
						
						
						
							|  |  | }
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  | // FinalStatusList 获取当前盘面形势判断
 | 
						
						
						
							|  |  | func (e *GtpEngine) FinalStatusList(cmd string) string {
 | 
						
						
						
							|  |  | 	command := fmt.Sprintf("final_status_list %s", cmd)
 | 
						
						
						
							|  |  | 	resp, err := e.Send(e.nextId(), command)
 | 
						
						
						
							|  |  | 	if !e.Check(resp, err, command) {
 | 
						
						
						
							|  |  | 		return ""
 | 
						
						
						
							|  |  | 	}
 | 
						
						
						
							|  |  | 	return resp.Content
 | 
						
						
						
							|  |  | }
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  | // SetLevel 设置AI级别
 | 
						
						
						
							|  |  | func (e *GtpEngine) SetLevel(level int) bool {
 | 
						
						
						
							|  |  | 	cmd := fmt.Sprintf("level %d", level)
 | 
						
						
						
							|  |  | 	resp, err := e.Send(e.nextId(), cmd)
 | 
						
						
						
							|  |  | 	if !e.Check(resp, err, cmd) {
 | 
						
						
						
							|  |  | 		return false
 | 
						
						
						
							|  |  | 	}
 | 
						
						
						
							|  |  | 	return true
 | 
						
						
						
							|  |  | }
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  | // SetRandomSeed 设置AI随机数
 | 
						
						
						
							|  |  | func (e *GtpEngine) SetRandomSeed(seed int) bool {
 | 
						
						
						
							|  |  | 	cmd := fmt.Sprintf("set_random_seed %d", seed)
 | 
						
						
						
							|  |  | 	resp, err := e.Send(e.nextId(), cmd)
 | 
						
						
						
							|  |  | 	if !e.Check(resp, err, cmd) {
 | 
						
						
						
							|  |  | 		return false
 | 
						
						
						
							|  |  | 	}
 | 
						
						
						
							|  |  | 	return true
 | 
						
						
						
							|  |  | }
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  | // ShowBoard 显示棋盘
 | 
						
						
						
							|  |  | func (e *GtpEngine) ShowBoard() string {
 | 
						
						
						
							|  |  | 	cmd := "showboard"
 | 
						
						
						
							|  |  | 	resp, err := e.Send(e.nextId(), cmd)
 | 
						
						
						
							|  |  | 	if !e.Check(resp, err, cmd) {
 | 
						
						
						
							|  |  | 		return ""
 | 
						
						
						
							|  |  | 	}
 | 
						
						
						
							|  |  | 	return resp.Content
 | 
						
						
						
							|  |  | }
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  | // PrintSgf 打印SGF
 | 
						
						
						
							|  |  | func (e *GtpEngine) PrintSgf() string {
 | 
						
						
						
							|  |  | 	cmd := "printsgf"
 | 
						
						
						
							|  |  | 	resp, err := e.Send(e.nextId(), cmd)
 | 
						
						
						
							|  |  | 	if !e.Check(resp, err, cmd) {
 | 
						
						
						
							|  |  | 		return ""
 | 
						
						
						
							|  |  | 	}
 | 
						
						
						
							|  |  | 	return resp.Content
 | 
						
						
						
							|  |  | }
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  | // TimeSetting 设置时间规则
 | 
						
						
						
							|  |  | func (e *GtpEngine) TimeSetting(baseTime, byoTime, byoStones int) bool {
 | 
						
						
						
							|  |  | 	cmd := fmt.Sprintf("time_settings %d %d %d", baseTime, byoTime, byoStones)
 | 
						
						
						
							|  |  | 	resp, err := e.Send(e.nextId(), cmd)
 | 
						
						
						
							|  |  | 	if !e.Check(resp, err, cmd) {
 | 
						
						
						
							|  |  | 		return false
 | 
						
						
						
							|  |  | 	}
 | 
						
						
						
							|  |  | 	return true
 | 
						
						
						
							|  |  | }
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  | // KGSTimeSetting 设置KGS time
 | 
						
						
						
							|  |  | func (e *GtpEngine) KGSTimeSetting(mainTime, readTime, readLimit int) bool {
 | 
						
						
						
							|  |  | 	cmd := fmt.Sprintf("kgs-time_settings byoyomi %d %d %d", mainTime, readTime, readLimit)
 | 
						
						
						
							|  |  | 	resp, err := e.Send(e.nextId(), cmd)
 | 
						
						
						
							|  |  | 	if !e.Check(resp, err, cmd) {
 | 
						
						
						
							|  |  | 		return false
 | 
						
						
						
							|  |  | 	}
 | 
						
						
						
							|  |  | 	return true
 | 
						
						
						
							|  |  | }
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  | // FinalScore 获取结果
 | 
						
						
						
							|  |  | // returns winner, score, ok
 | 
						
						
						
							|  |  | func (e *GtpEngine) FinalScore() (string, float64, bool) {
 | 
						
						
						
							|  |  | 	cmd := "final_score"
 | 
						
						
						
							|  |  | 	resp, err := e.Send(e.nextId(), cmd)
 | 
						
						
						
							|  |  | 	if !e.Check(resp, err, cmd) {
 | 
						
						
						
							|  |  | 		return "", 0, false
 | 
						
						
						
							|  |  | 	}
 | 
						
						
						
							|  |  | 	splited := strings.Split(resp.Content, "+")
 | 
						
						
						
							|  |  | 	if len(splited) < 2 {
 | 
						
						
						
							|  |  | 		return "", 0, false
 | 
						
						
						
							|  |  | 	}
 | 
						
						
						
							|  |  | 	score, _ := nmath.Float(splited[1])
 | 
						
						
						
							|  |  | 	return splited[0], score, true
 | 
						
						
						
							|  |  | }
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  | // Undo 悔棋
 | 
						
						
						
							|  |  | func (e *GtpEngine) Undo() bool {
 | 
						
						
						
							|  |  | 	cmd := "undo"
 | 
						
						
						
							|  |  | 	resp, err := e.Send(e.nextId(), cmd)
 | 
						
						
						
							|  |  | 	if !e.Check(resp, err, cmd) {
 | 
						
						
						
							|  |  | 		return false
 | 
						
						
						
							|  |  | 	}
 | 
						
						
						
							|  |  | 	return true
 | 
						
						
						
							|  |  | }
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  | // TimeLeft 设置时间
 | 
						
						
						
							|  |  | func (e *GtpEngine) TimeLeft(color string, mainTime, stones int) bool {
 | 
						
						
						
							|  |  | 	cmd := fmt.Sprintf("time_left %s %d %d", color, mainTime, stones)
 | 
						
						
						
							|  |  | 	resp, err := e.Send(e.nextId(), cmd)
 | 
						
						
						
							|  |  | 	if !e.Check(resp, err, cmd) {
 | 
						
						
						
							|  |  | 		return false
 | 
						
						
						
							|  |  | 	}
 | 
						
						
						
							|  |  | 	return true
 | 
						
						
						
							|  |  | }
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  | // Quit 退出
 | 
						
						
						
							|  |  | func (e *GtpEngine) Quit() bool {
 | 
						
						
						
							|  |  | 	cmd := "quit"
 | 
						
						
						
							|  |  | 	resp, err := e.Send(e.nextId(), cmd)
 | 
						
						
						
							|  |  | 	if !e.Check(resp, err, cmd) {
 | 
						
						
						
							|  |  | 		return false
 | 
						
						
						
							|  |  | 	}
 | 
						
						
						
							|  |  | 	_ = e.Cmd.Stop()
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  | 	return true
 | 
						
						
						
							|  |  | }
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  | func (e *GtpEngine) Check(resp *GTPResponse, err error, cmd string) bool {
 | 
						
						
						
							|  |  | 	if err != nil {
 | 
						
						
						
							|  |  | 		nlog.Errorf("发送命令[%s]失败 %v", cmd, err)
 | 
						
						
						
							|  |  | 		return false
 | 
						
						
						
							|  |  | 	}
 | 
						
						
						
							|  |  | 	if resp.Err != nil {
 | 
						
						
						
							|  |  | 		nlog.Errorf("接收到GTP错误消息 %v", resp.Err)
 | 
						
						
						
							|  |  | 		return false
 | 
						
						
						
							|  |  | 	}
 | 
						
						
						
							|  |  | 	return true
 | 
						
						
						
							|  |  | }
 |