igoui

Log | Files | Refs

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 }