katago.go (2771B)
1 package main 2 3 import ( 4 "log" 5 "os/exec" 6 "encoding/json" 7 "bufio" 8 "fmt" 9 "io" 10 "sync" 11 ) 12 13 type Katago struct { 14 Process *exec.Cmd 15 InChan io.WriteCloser 16 ChanMap map[string]chan<- Response 17 RCount uint 18 WMutex sync.Mutex 19 } 20 21 type Request struct { 22 Id string `json:"id"` 23 Moves [][]string `json:"moves"` 24 InitialStones [][]string `json:"initialStones"` 25 Rules string `json:"rules"` 26 Komi float64 `json:"komi"` 27 XSize int `json:"boardXSize"` 28 YSize int `json:"boardYSize"` 29 Report float64 `json:"reportDuringSearchEvery,omitempty"` 30 // TODO: add the many other options 31 } 32 33 type Response struct { 34 Id string `json:"id"` 35 IsDuringSearch bool `json:"isDuringSearch"` 36 TurnNumber int `json:"turnNumber"` 37 moveInfos []any 38 rootInfo any 39 ownership any 40 ownershipStdev any 41 policy any 42 } 43 44 func (k *Katago) request(r Request) (<-chan Response, error) { 45 r.Id = fmt.Sprint("req", k.RCount) 46 k.RCount++ 47 48 channel := make(chan Response) 49 k.ChanMap[r.Id] = channel 50 51 bytes, err := json.Marshal(r) 52 if err != nil { 53 return nil, err 54 } 55 56 k.WMutex.Lock() 57 _, err = k.InChan.Write(bytes) 58 if err != nil { 59 return nil, err 60 } 61 62 _, err = k.InChan.Write([]byte("\n")) 63 if err != nil { 64 return nil, err 65 } 66 k.WMutex.Unlock() 67 68 return channel, nil 69 } 70 71 func newKatago() (*Katago, error) { 72 cmd := exec.Command("./katago/katago", "analysis", "-config", "katago/analysis_example.cfg") 73 74 stdinpipe, err := cmd.StdinPipe() 75 if err != nil { 76 log.Fatal(err) 77 } 78 79 stdoutpipe, err := cmd.StdoutPipe() 80 if err != nil { 81 log.Fatal(err) 82 } 83 84 stderrpipe, err := cmd.StderrPipe() 85 if err != nil { 86 log.Fatal(err) 87 } 88 89 if err = cmd.Start(); err != nil { 90 return nil, err 91 } 92 log.Println("KataGo started") 93 94 ret := new(Katago) 95 ret.Process = cmd 96 ret.ChanMap = make(map[string](chan<- Response)) 97 ret.InChan = stdinpipe 98 ret.WMutex = sync.Mutex{} 99 100 // Begin listening on stdout 101 go func() { 102 jsonreader := json.NewDecoder(stdoutpipe) 103 for { 104 var resp Response 105 log.Println("----------------------- Listening to KataGo") 106 107 err := jsonreader.Decode(&resp) 108 109 if err != nil { 110 log.Fatal(err) 111 } else if err == io.EOF { 112 break 113 } 114 115 channel, ok := ret.ChanMap[resp.Id] 116 117 if !ok { 118 log.Println("No corresponding channel ", resp.Id) 119 continue 120 } 121 122 channel <- resp 123 log.Print("Sent resp ", resp) 124 // TODO: close channel after final response 125 // if !resp.IsDuringSearch { 126 // close(channel) 127 // ret.ChanMap[resp.Id] = nil 128 // } 129 } 130 log.Println("Finished") 131 }() 132 133 go func() { 134 errscanner := bufio.NewScanner(stderrpipe) 135 log.Println("Listening to KataGo stderr") 136 for errscanner.Scan() { 137 txt := errscanner.Text() 138 log.Println("KataGo Err ", txt) 139 } 140 }() 141 // 142 // ret.InChan.Write([]byte("{\"id\":\"foo\",\"action\":\"query_version\"}\n")) 143 144 return ret, nil 145 }