github.com/gorgonia/agogo@v0.1.1/internal/gtp/gtp.go (about)

     1  package gtp
     2  
     3  import (
     4  	"fmt"
     5  	"strconv"
     6  	"strings"
     7  
     8  	"github.com/gorgonia/agogo/game"
     9  	"github.com/pkg/errors"
    10  )
    11  
    12  var known_commands = []string{
    13  	"protocol_version",
    14  	"name",
    15  	"version",
    16  	"known_command",
    17  	"list_commands",
    18  	"quit",
    19  
    20  	// setup
    21  	"boardsize",
    22  	"clear_board",
    23  	"komi",
    24  	"fixed_handicap",
    25  	"place_free_handicap",
    26  	"set_free_handicap",
    27  
    28  	// play
    29  	"play",
    30  	"genmove",
    31  	"undo",
    32  }
    33  
    34  type Engine struct {
    35  	g game.State
    36  
    37  	known map[string]Command
    38  
    39  	ch  chan string
    40  	ret chan string
    41  
    42  	Generate      func(g game.State) game.PlayerMove
    43  	New           func(m, n int) game.State
    44  	name, version string
    45  }
    46  
    47  func New(g game.State, name, version string, known map[string]Command) *Engine {
    48  	if known == nil {
    49  		known = StandardLib()
    50  	}
    51  	return &Engine{
    52  		g:       g,
    53  		known:   known,
    54  		name:    name,
    55  		version: version,
    56  	}
    57  }
    58  
    59  func (e *Engine) Start() (input, output chan string) {
    60  	e.ch = make(chan string)
    61  	e.ret = make(chan string)
    62  	go e.start()
    63  	return e.ch, e.ret
    64  }
    65  
    66  func (e *Engine) State() game.State { return e.g }
    67  
    68  func (e *Engine) start() {
    69  	for cmd := range e.ch {
    70  		id, x, args, err := e.parse(cmd)
    71  		if x == nil && err == nil {
    72  			continue //
    73  		}
    74  		if err != nil {
    75  			e.ret <- handleErr(id, err)
    76  			continue
    77  		}
    78  		id, result, err := x.Do(id, args, e)
    79  		e.ret <- handleResult(id, result, err)
    80  	}
    81  }
    82  
    83  // refer to this
    84  // https://www.lysator.liu.se/%7Egunnar/gtp/gtp2-spec-draft2/gtp2-spec.html#SECTION00030000000000000000
    85  func (e *Engine) parse(cmd string) (id int, x Command, args []string, err error) {
    86  	cmd = preprocess(cmd)
    87  	tokens := strings.Fields(cmd)
    88  	if id, err = strconv.Atoi(tokens[0]); err == nil {
    89  		// we've consumed ID
    90  		tokens = tokens[1:]
    91  	} else {
    92  		// set err to nil because ID is optional
    93  		err = nil
    94  		id = -1
    95  	}
    96  
    97  	if len(tokens) == 0 {
    98  		return id, nil, nil, nil // GNUGo some how does nothing when there are no tokens left. An ID may be passed in but it'll be ignored
    99  	}
   100  
   101  	var ok bool
   102  	if x, ok = e.known[tokens[0]]; !ok {
   103  		return id, nil, nil, errors.Errorf("Unknown command %q", tokens[0])
   104  	}
   105  	if len(tokens) > 1 {
   106  		args = tokens[1:]
   107  	}
   108  	return
   109  }
   110  
   111  func preprocess(a string) string {
   112  	return strings.ToLower(strings.TrimSpace(a))
   113  }
   114  
   115  func sqrt(a int) int {
   116  	if a == 0 || a == 1 {
   117  		return a
   118  	}
   119  	start := 1
   120  	end := a / 2
   121  	var retVal int
   122  	for start <= end {
   123  		mid := (start + end) / 2
   124  		sq := mid * mid
   125  		if sq == a {
   126  			return mid
   127  		}
   128  		if sq < a {
   129  			start = mid + 1
   130  			retVal = mid
   131  		} else {
   132  			end = mid - 1
   133  		}
   134  	}
   135  	return retVal
   136  }
   137  
   138  func handleErr(id int, err error) string {
   139  	if id != -1 {
   140  		return fmt.Sprintf("? %d %v\n\n", id, err)
   141  	}
   142  	return fmt.Sprintf("? %v\n\n", err)
   143  }
   144  
   145  func handleResult(id int, result string, err error) string {
   146  	if err != nil {
   147  		return handleErr(id, err)
   148  	}
   149  
   150  	if id != -1 {
   151  		return fmt.Sprintf("= %d %v\n\n", id, result)
   152  	}
   153  	return fmt.Sprintf("= %v\n\n", result)
   154  }