feat: katago

main v1.3.6
NoahLan 11 months ago
parent 5420a6e4dc
commit 217f715f23

@ -1,109 +0,0 @@
package ngochess
//
//var (
// analStartupDecidedFunc cmdn.LineFunc = func(sb *strings.Builder, line string) bool {
// return strings.Contains(line, "Started, ready to begin handling requests")
// }
// analEndLineDecidedFunc cmdn.LineFunc = func(sb *strings.Builder, line string) bool {
// return nstr.IsJSON(sb.String())
// }
// analReadIDFunc cmdn.ReadIDFunc = func(serializer ndef.Serializer, data string) (string, error) {
// var s struct {
// ID string `json:"id"`
// }
// err := serializer.Unmarshal([]byte(data), &s)
// if err != nil {
// return "", err
// }
// return s.ID, nil
// }
//)
//
//type AnalysisEngine struct {
// *KatagoEngine
//}
//
//// NewAnalysisEngine 创建Analysis引擎
//func NewAnalysisEngine(name string, args ...string) (*AnalysisEngine, error) {
// engine := &AnalysisEngine{KatagoEngine: &KatagoEngine{
// Processor: cmdn.NewProcessor(
// false,
// cmdn.WithSerializer(njson.NewJsonSerializer()),
// cmdn.WithStartupDecidedFunc(analStartupDecidedFunc),
// cmdn.WithEndLineDecidedFunc(analEndLineDecidedFunc),
// cmdn.WithReadIDFunc(analReadIDFunc),
// ),
// Name: name,
// Mode: EngineAnalysis,
// }}
// err := engine.Processor.Run(name, args...)
// return engine, err
//}
//
//// Query 分析查询
//func (eng *AnalysisEngine) Query(req *req.AnalQueryReq, callback func(queryResp *resp.AnalQueryResp)) error {
// if len(req.ID) == 0 {
// req.ID = nrandom.SnowflakeIdStr()
// }
// return eng.Processor.Exec(req, func(serializer ndef.Serializer, data string) {
// var tmp resp.AnalQueryResp
// tmp.Err = serializer.Unmarshal([]byte(data), &tmp)
//
// callback(&tmp)
// })
//}
//
//// QueryVersion 查询版本号
//func (eng *AnalysisEngine) QueryVersion(callback func(versionResp *resp.AnalQueryVersionResp)) error {
// return eng.Processor.Exec(&req.AnalQueryActionReq{
// ID: nrandom.SnowflakeIdStr(),
// Action: req.AnalActionQueryVersion,
// }, func(serializer ndef.Serializer, data string) {
// var tmp resp.AnalQueryVersionResp
// tmp.Err = serializer.Unmarshal([]byte(data), &tmp)
// callback(&tmp)
// })
//}
//
//// ClearCache 清除缓存
//func (eng *AnalysisEngine) ClearCache(callback func(cacheResp *resp.AnalClearCacheResp)) error {
// return eng.Processor.Exec(&req.AnalQueryActionReq{
// ID: nrandom.SnowflakeIdStr(),
// Action: req.AnalActionClearCache,
// }, func(serializer ndef.Serializer, data string) {
// var tmp resp.AnalClearCacheResp
// tmp.Err = serializer.Unmarshal([]byte(data), &tmp)
// callback(&tmp)
// })
//}
//
//// Terminate 终止某次分析
//// terminateId: 分析轮次ID
//// turnNumbers: 需要终止分析的回合数(手数),可空:所有回合
//func (eng *AnalysisEngine) Terminate(terminateId string, turnNumbers []int32, callback func(terminateResp *resp.AnalTerminateResp)) error {
// return eng.Processor.Exec(&req.AnalQueryActionReq{
// ID: nrandom.SnowflakeIdStr(),
// Action: req.AnalActionTerminate,
// TerminateId: terminateId,
// TurnNumbers: turnNumbers,
// }, func(serializer ndef.Serializer, data string) {
// var tmp resp.AnalTerminateResp
// tmp.Err = serializer.Unmarshal([]byte(data), &tmp)
// callback(&tmp)
// })
//}
//
//// TerminateAll 终止所有分析
//// turnNumbers: 需要终止所有分析轮次的回合数,可空:所有回合
//func (eng *AnalysisEngine) TerminateAll(turnNumbers []int32, callback func(allResp *resp.AnalTerminateAllResp)) error {
// return eng.Processor.Exec(&req.AnalQueryActionReq{
// ID: nrandom.SnowflakeIdStr(),
// Action: req.AnalActionTerminateAll,
// TurnNumbers: turnNumbers,
// }, func(serializer ndef.Serializer, data string) {
// var tmp resp.AnalTerminateAllResp
// tmp.Err = serializer.Unmarshal([]byte(data), &tmp)
// callback(&tmp)
// })
//}

@ -1,168 +0,0 @@
package ngochess
//
//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
//}

@ -177,9 +177,6 @@ func (e *GtpEngine) handleMessage() {
e.Cmd.Stdout = nil e.Cmd.Stdout = nil
continue continue
} }
//if e.DevMode {
// nlog.Debugf("接收单条消息: %s", line)
//}
resp, ok := e.serializer.Unmarshal(line) resp, ok := e.serializer.Unmarshal(line)
if !ok { if !ok {

@ -0,0 +1,266 @@
package katago
import (
"errors"
"git.noahlan.cn/noahlan/ntool/ncmd"
"git.noahlan.cn/noahlan/ntool/nlog"
"git.noahlan.cn/noahlan/ntool/nstr"
"git.noahlan.cn/noahlan/ntool/nsys/atomic"
"sync"
"time"
)
var (
DefaultMaxMessageId uint64 = 9999999
DefaultTimeout = 10 * time.Minute
)
var ErrTimeout = errors.New("timeout")
type (
Options struct {
DevMode bool `json:",default=false"` // 开发模式
MaxMessageId uint64 `json:",default=9999999"` // 最大消息ID
Timeout time.Duration `json:""` // 单条命令最大等待时间默认10min
}
Option func(options *Options)
pendingMsg struct {
chWait chan struct{}
respJson string
}
AnalysisEngine struct {
*Options
Cmd *ncmd.Cmd
serializer *Serializer
mid *atomic.AtomicInt64
pendingMsg map[string]*pendingMsg
mu sync.RWMutex
}
)
func NewKatagoAnalysisEngine(opts ...Option) *AnalysisEngine {
serializer := NewSerializer()
ret := &AnalysisEngine{
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
}
func (e *AnalysisEngine) Start(name string, args ...string) <-chan ncmd.Status {
statusChan := e.Cmd.Start(name, args...)
go e.handleMessage()
return statusChan
}
func (e *AnalysisEngine) 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 *AnalysisEngine) Send(data BaseMsg, resp any) error {
pMsg := &pendingMsg{
chWait: make(chan struct{}, 1),
}
id := e.nextId()
data.SetMessageId(id)
e.mu.Lock()
e.pendingMsg[id] = pMsg
e.mu.Unlock()
err := e.Cmd.Send(data)
if err != nil {
return 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 ErrTimeout
case <-pMsg.chWait:
ok, err := e.serializer.Unmarshal(pMsg.respJson, resp)
if err != nil {
return err
}
if !ok {
return errors.New("消息不完整")
}
return nil
}
}
func (e *AnalysisEngine) nextId() string {
idInt := e.mid.IncrementAndGet()
if idInt > int64(e.MaxMessageId) {
e.mid.Reset()
idInt = e.mid.IncrementAndGet()
}
return nstr.SafeString(idInt)
}
func (e *AnalysisEngine) 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)
//}
var baseResp BaseResp
ok, err := e.serializer.Unmarshal(line, &baseResp)
if err != nil {
nlog.Errorf("解释json失败 %+v", err)
continue
}
if e.DevMode {
nlog.Debugf("接收完整消息: %+v", e.serializer.contentBuf.String())
}
e.mu.RLock()
pMsg, ok := e.pendingMsg[baseResp.ID]
e.mu.RUnlock()
if !ok {
continue
}
pMsg.respJson = e.serializer.LastJson()
pMsg.chWait <- struct{}{}
case line, open := <-e.Cmd.Stderr:
if !open {
e.Cmd.Stdout = nil
continue
}
nlog.Errorf("错误消息: %s\n", line)
}
}
}
func (e *AnalysisEngine) check(err error, req any) bool {
if err != nil {
nlog.Errorf("发送命令[%+v]失败 %v", req, err)
return false
}
return true
}
////////////////////////////////
// QueryVersion 查询版本号
func (e *AnalysisEngine) QueryVersion(req *QueryActionReq) (*QueryVersionResp, error) {
var resp QueryVersionResp
err := e.Send(req, &resp)
if !e.check(err, req) {
return nil, err
}
return &resp, nil
}
// Query 分析查询
func (e *AnalysisEngine) Query(req *QueryReq) (*QueryResp, error) {
var resp QueryResp
err := e.Send(req, &resp)
if !e.check(err, req) {
return nil, err
}
return &resp, nil
}
// ClearCache 清除缓存
func (e *AnalysisEngine) ClearCache() (*ClearCacheResp, error) {
var resp ClearCacheResp
req := &QueryActionReq{
Action: AnalActionClearCache,
}
err := e.Send(req, &resp)
if !e.check(err, req) {
return nil, err
}
return &resp, nil
}
// Terminate 终止某次分析
// terminateId: 分析轮次ID
// turnNumbers: 需要终止分析的回合数(手数),可空:所有回合
func (e *AnalysisEngine) Terminate(terminateId string, turnNumbers []int32) (*TerminateResp, error) {
var resp TerminateResp
req := &QueryActionReq{
Action: AnalActionTerminate,
TerminateId: terminateId,
TurnNumbers: turnNumbers,
}
err := e.Send(req, &resp)
if !e.check(err, req) {
return nil, err
}
return &resp, nil
}
// TerminateAll 终止所有分析
// turnNumbers: 需要终止所有分析轮次的回合数,可空:所有回合
func (e *AnalysisEngine) TerminateAll(turnNumbers []int32) (*TerminateAllResp, error) {
var resp TerminateAllResp
req := &QueryActionReq{
Action: AnalActionTerminateAll,
TurnNumbers: turnNumbers,
}
err := e.Send(req, &resp)
if !e.check(err, req) {
return nil, err
}
return &resp, nil
}

@ -0,0 +1,72 @@
package katago
import "git.noahlan.cn/noahlan/ntool/njson"
type BaseMsg interface {
SetMessageId(id string)
MessageId() string
}
type Stone struct {
Player string // 玩家 "B" / "W"
Location string // "C4" GTP标准协议或 "CC" 行列协议
}
func (s Stone) MarshalJSON() ([]byte, error) {
ret := []string{s.Player, s.Location}
return njson.Marshal(ret)
}
type QueryReq struct {
Id string `json:"id"` // ID
Moves []Stone `json:"moves"` // moves
InitialStones []Stone `json:"initialStones,omitempty,optional"` // 初始化棋子(让子棋,棋谱等等)
Rules string `json:"rules"` // 规则, chinese / japanese / tromp-taylor .etc...
Komi float32 `json:"komi"` // 贴目中国规则一般7.5目
BoardXSize int64 `json:"boardXSize"` // 棋盘X大小
BoardYSize int64 `json:"boardYSize"` // 棋盘Y大小
AnalyzeTurns []int32 `json:"analyzeTurns,omitempty,optional"` // 需要分析的回合数与moves对应若不指定则分析最后一回合
InitialPlayer string `json:"initialPlayer,omitempty,optional"` // 指定第一手的玩家 "B" / "W"当moves为空时很有用
/// .etc...
}
func (r *QueryReq) SetMessageId(id string) {
r.Id = id
}
func (r *QueryReq) MessageId() string {
return r.Id
}
func (r *QueryReq) AddMove(player, location string) {
r.Moves = append(r.Moves, Stone{Player: player, Location: location})
}
func (r *QueryReq) AddInitialStone(player, location string) {
r.InitialStones = append(r.InitialStones, Stone{Player: player, Location: location})
}
const (
AnalActionQueryVersion = "query_version"
AnalActionClearCache = "clear_cache"
AnalActionTerminate = "terminate"
AnalActionTerminateAll = "terminate_all"
)
type QueryActionReq struct {
Id string `json:"id"` // ID
Action string `json:"action"` // 查询动作
// terminate
TerminateId string `json:"terminateId,omitempty,optional"`
// terminate | terminate_all
TurnNumbers []int32 `json:"turnNumbers,omitempty,optional"`
}
func (r *QueryActionReq) SetMessageId(id string) {
r.Id = id
}
func (r *QueryActionReq) MessageId() string {
return r.Id
}

@ -0,0 +1,68 @@
package katago
type BaseResp struct {
ID string `json:"id"` // ID 必有
Action string `json:"action,omitempty,optional"` // Action 动作,不是必填值
// 其它东西,通过组合
Err error
}
type QueryResp struct {
BaseResp
IsDuringSearch bool `json:"isDuringSearch"` // 是否是搜索的过程结果,发送时指定 reportDuringSearchEvery则会在过程中进行回复
TurnNumber int32 `json:"turnNumber"` // 正在分析的回合数(手数)
MoveInfos []struct {
Move string `json:"move"` // 正在分析的移动,通常为棋盘坐标
Visits int32 `json:"visits"` // 访问次数(神经网络)
WinRate float64 `json:"winrate"` // 胜率 [0~1] [0%~100%] 越高越好
ScoreMean float64 `json:"scoreMean"` // 预计平均分
ScoreLead float64 `json:"scoreLead"` // 预计平均分 与 ScoreMean 一致
ScoreStdev float64 `json:"scoreStdev"` // 预计平均分标准差由于MCTS机制该分值可能偏高
ScoreSelfplay float64 `json:"scoreSelfplay"` // 自我对弈时的预计分数,该值目前偏高
Prior float64 `json:"prior"` // 策略优先级 [0~1],通常会选优先级高的下棋(直觉)
Utility float64 `json:"utility"` // 综合效用值结合winrate和score值域 [-C,C]
Lcb float64 `json:"lcb"` // LCB胜率但值可能有误 [0~1] 可能会越界
UtilityLcb float64 `json:"utilityLcb"` // LCB综合效用值
Weight float64 `json:"weight"` // Visits 的平均权重
Order int32 `json:"order"` // Katago神经网络中的直觉优先级值[0~max]越低,优先级越高
PV []string `json:"pv"` // 此次移动之后预测之后的移动列表
// PvVisits
// pvEdgeVisits
// ownership
// ownershipStdev
} `json:"moveInfos"` // 移动信息
RootInfo struct {
ThisHash string `json:"thisHash"` // 本次移动的唯一编码
SymHash string `json:"symHash"` //
CurrentPlayer string `json:"currentPlayer"` // "B" or "W"
// RawStWrError
// rawStScoreError
// rawVarTimeLeft
Visits int32 `json:"visits"` // 访问次数(神经网络)
WinRate float64 `json:"winrate"` // 胜率 [0~1] [0%~100%] 越高越好
ScoreLead float64 `json:"scoreLead"` // 预计平均分 与 ScoreMean 一致
ScoreSelfplay float64 `json:"scoreSelfplay"` // 自我对弈时的预计分数,该值目前偏高
Utility float64 `json:"utility"` // 综合效用值结合winrate和score值域 [-C,C]
} `json:"rootInfo"` // 根信息
}
type QueryVersionResp struct {
BaseResp
GitHash string `json:"git_hash"` // GitHash
Version string `json:"version"` // 版本号
}
type ClearCacheResp struct {
BaseResp
}
type TerminateResp struct {
BaseResp
IsDuringSearch bool `json:"isDuringSearch"` // 是否是搜索的过程结果,发送时指定 reportDuringSearchEvery则会在过程中进行回复
TurnNumber int64 `json:"turnNumber"` // 本次停止的回合数
}
type TerminateAllResp struct {
BaseResp
TurnNumbers []int64 `json:"turnNumbers"` // 本次停止的回合数
}

@ -0,0 +1,51 @@
package katago
import (
"bytes"
"encoding/json"
"git.noahlan.cn/noahlan/ntool/njson"
)
type Serializer struct {
contentBuf *bytes.Buffer
lastJson string
}
func NewSerializer() *Serializer {
ret := &Serializer{
contentBuf: bytes.NewBuffer([]byte{}),
}
return ret
}
func (s *Serializer) reset() {
s.contentBuf.Reset()
}
func (s *Serializer) LastJson() string {
return s.lastJson
}
func (s *Serializer) Marshal(v any) ([]byte, error) {
return njson.Marshal(v)
}
func (s *Serializer) Unmarshal(line string, v any) (bool, error) {
s.contentBuf.WriteString(line)
if json.Valid(s.contentBuf.Bytes()) {
defer func() {
s.lastJson = s.contentBuf.String()
s.reset()
}()
err := njson.Unmarshal(s.contentBuf.Bytes(), v)
if err != nil {
return false, err
}
return true, nil
} else {
return false, nil
}
}

@ -1,67 +0,0 @@
package req
//
//import (
// "encoding/json"
// "git.noahlan.cn/noahlan/ntool/nsys/cmdn"
//)
//
//type Stone struct {
// Player string // 玩家 "B" / "W"
// Location string // "C4" GTP标准协议或 "CC" 行列协议
//}
//
//func (s Stone) MarshalJSON() ([]byte, error) {
// ret := []string{s.Player, s.Location}
// return json.Marshal(ret)
//}
//
//var _ cmdn.ICommand = (*AnalQueryReq)(nil)
//
//type AnalQueryReq struct {
// ID string `json:"id"` // ID
// Moves []Stone `json:"moves"` // moves
// InitialStones []Stone `json:"initialStones,omitempty,optional"` // 初始化棋子(让子棋,棋谱等等)
// Rules string `json:"rules"` // 规则, chinese / japanese / tromp-taylor .etc...
// Komi float32 `json:"komi"` // 贴目中国规则一般7.5目
// BoardXSize int64 `json:"boardXSize"` // 棋盘X大小
// BoardYSize int64 `json:"boardYSize"` // 棋盘Y大小
// AnalyzeTurns []int32 `json:"analyzeTurns,omitempty,optional"` // 需要分析的回合数与moves对应若不指定则分析最后一回合
// InitialPlayer string `json:"initialPlayer,omitempty,optional"` // 指定第一手的玩家 "B" / "W"当moves为空时很有用
// /// .etc...
//}
//
//func (r *AnalQueryReq) MessageID() string {
// return r.ID
//}
//
//func (r *AnalQueryReq) AddMove(player, location string) {
// r.Moves = append(r.Moves, Stone{Player: player, Location: location})
//}
//
//func (r *AnalQueryReq) AddInitialStone(player, location string) {
// r.InitialStones = append(r.InitialStones, Stone{Player: player, Location: location})
//}
//
//var _ cmdn.ICommand = (*AnalQueryActionReq)(nil)
//
//const (
// AnalActionQueryVersion = "query_version"
// AnalActionClearCache = "clear_cache"
// AnalActionTerminate = "terminate"
// AnalActionTerminateAll = "terminate_all"
//)
//
//type AnalQueryActionReq struct {
// ID string `json:"id"` // ID
// Action string `json:"action"` // 查询动作
//
// // terminate
// TerminateId string `json:"terminateId,omitempty,optional"`
// // terminate | terminate_all
// TurnNumbers []int32 `json:"turnNumbers,omitempty,optional"`
//}
//
//func (r *AnalQueryActionReq) MessageID() string {
// return r.ID
//}

@ -1,70 +0,0 @@
package resp
//
//type AnalBaseResp struct {
// ID string `json:"id"` // ID 必有
// Action string `json:"action,omitempty,optional"` // Action 动作,不是必填值
// // 其它东西,通过组合
//
// Err error
//}
//
//type AnalQueryResp struct {
// AnalBaseResp
// IsDuringSearch bool `json:"isDuringSearch"` // 是否是搜索的过程结果,发送时指定 reportDuringSearchEvery则会在过程中进行回复
// TurnNumber int32 `json:"turnNumber"` // 正在分析的回合数(手数)
// MoveInfos []struct {
// Move string `json:"move"` // 正在分析的移动,通常为棋盘坐标
// Visits int32 `json:"visits"` // 访问次数(神经网络)
// WinRate float64 `json:"winrate"` // 胜率 [0~1] [0%~100%] 越高越好
// ScoreMean float64 `json:"scoreMean"` // 预计平均分
// ScoreLead float64 `json:"scoreLead"` // 预计平均分 与 ScoreMean 一致
// ScoreStdev float64 `json:"scoreStdev"` // 预计平均分标准差由于MCTS机制该分值可能偏高
// ScoreSelfplay float64 `json:"scoreSelfplay"` // 自我对弈时的预计分数,该值目前偏高
// Prior float64 `json:"prior"` // 策略优先级 [0~1],通常会选优先级高的下棋(直觉)
// Utility float64 `json:"utility"` // 综合效用值结合winrate和score值域 [-C,C]
// Lcb float64 `json:"lcb"` // LCB胜率但值可能有误 [0~1] 可能会越界
// UtilityLcb float64 `json:"utilityLcb"` // LCB综合效用值
// Weight float64 `json:"weight"` // Visits 的平均权重
// Order int32 `json:"order"` // Katago神经网络中的直觉优先级值[0~max]越低,优先级越高
// PV []string `json:"pv"` // 此次移动之后预测之后的移动列表
// // PvVisits
// // pvEdgeVisits
// // ownership
// // ownershipStdev
// } `json:"moveInfos"` // 移动信息
// RootInfo struct {
// ThisHash string `json:"thisHash"` // 本次移动的唯一编码
// SymHash string `json:"symHash"` //
// CurrentPlayer string `json:"currentPlayer"` // "B" or "W"
// // RawStWrError
// // rawStScoreError
// // rawVarTimeLeft
// Visits int32 `json:"visits"` // 访问次数(神经网络)
// WinRate float64 `json:"winrate"` // 胜率 [0~1] [0%~100%] 越高越好
// ScoreLead float64 `json:"scoreLead"` // 预计平均分 与 ScoreMean 一致
// ScoreSelfplay float64 `json:"scoreSelfplay"` // 自我对弈时的预计分数,该值目前偏高
// Utility float64 `json:"utility"` // 综合效用值结合winrate和score值域 [-C,C]
// } `json:"rootInfo"` // 根信息
//}
//
//type AnalQueryVersionResp struct {
// AnalBaseResp
// GitHash string `json:"git_hash"` // GitHash
// Version string `json:"version"` // 版本号
//}
//
//type AnalClearCacheResp struct {
// AnalBaseResp
//}
//
//type AnalTerminateResp struct {
// AnalBaseResp
// IsDuringSearch bool `json:"isDuringSearch"` // 是否是搜索的过程结果,发送时指定 reportDuringSearchEvery则会在过程中进行回复
// TurnNumber int64 `json:"turnNumber"` // 本次停止的回合数
//}
//
//type AnalTerminateAllResp struct {
// AnalBaseResp
// TurnNumbers []int64 `json:"turnNumbers"` // 本次停止的回合数
//}
Loading…
Cancel
Save