Home

Awesome

@sabaki/gtp CI

A Node.js module for handling GTP engines.

Installation

Use npm to install:

$ npm install @sabaki/gtp

Usage

Controller Usage

Use the Controller class to interact with an engine:

const {StreamController, Controller, Command, Response} = require('@sabaki/gtp')

async function main() {
  let leela = new Controller('./path/to/leela', ['--gtp', '--noponder'])
  leela.start()

  let response = null

  try {
    response = await leela.sendCommand({name: 'genmove', args: ['B']})
  } catch (err) {
    throw new Error('Failed to send command!')
  }

  if (response.error) {
    throw new Error('Command not understood by Leela!')
  }

  console.log(response.content)
  await leela.stop()
}

main().catch(err => console.log(`Error: ${err}`))

Engine Usage

Use the Engine class to create an engine:

const {Engine} = require('@sabaki/gtp')

let testEngine = new Engine('Test Engine', '0.1')

testEngine.command('play', (command, out) => {
  if (command.args.length === 0) return out.err('player not specified')
  out.send('playing for ' + command.args[0])
})

testEngine.start()

API

Command

A GTP command is represented by an object of the following form:

{
  id?: <Integer> | null,
  name: <String>,
  args?: <String[]>
}

Command.fromString(input)

Returns a Command object, representing input.

Command.toString(command)

Returns a GTP command string represented by command to be sent to an engine.


Response

A response from a GTP engine is represented by an object of the following form:

{
  id?: <Integer> | null,
  content: <String>,
  error?: <Boolean>
}

Response.fromString(input)

Returns a Response object, representing input.

Response.toString(response)

Returns a GTP response string represented by response, something that an engine might send.


class StreamController extends EventEmitter

Use this class to control GTP engines on arbitrary communication channels. To spawn engine processes automatically, use Controller.

new StreamController(input, output)

Event: command-sent

This event is emitted when a command is sent to the engine. Using the subscribe function you can get updates every time the engine responds with a new line, see streamController.sendCommand().

Event: response-received

This event is emitted when the engine finishes sending a response.

streamController.input

<Writable> - The input stream of the GTP engine.

streamController.output

<Readable> - The output stream of the GTP engine.

streamController.commands

<Command[]> - The command queue.

streamController.busy

<Boolean> - Indicates whether the controller is waiting for an engine response or not.

async streamController.sendCommand(command[, subscriber])

Sends a command to the engine and returns a response object. You can pass a subscriber function to get updates every time the engine responds with a new line. subscriber is called with an object evt with the following properties:

streamController.close()

Cleans up listeners.


class Controller extends EventEmitter

Use this class to spawn GTP engine processes and control them over stdin and stdout.

new Controller(path[, args[, spawnOptions]])

Event: started

This event is emitted when the engine process starts.

Event: stopped

This event is emitted after the engine process ends.

Event: stderr

This event is emitted when the engine process finishes printing a line on stderr.

Event: command-sent

See corresponding event in StreamController.

Event: response-received

See corresponding event in StreamController.

controller.path

<String> - The path to an executable file, the GTP engine.

controller.args

<String[]> - Additional arguments that are passed to the engine when started.

controller.spawnOptions

<Object> - See Node.js documentation.

controller.process

<ChildProcess> | null - The GTP engine process.

controller.commands

<Command[]> - The command queue.

controller.busy

<Boolean> - Indicates whether the controller is waiting for an engine response or not.

controller.start()

Spawns a process of the engine if necessary.

async controller.stop([timeout])

Sends a quit command to the engine. If engine doesn't respond, it will be killed after timeout ms.

async controller.kill()

Kills the engine process.

async controller.sendCommand(command[, subscriber])

See corresponding function in StreamController.


class ControllerStateTracker

Use this class to keep track of the state of an engine. This class is also able to synchronize the state of an engine to a given state.

EngineState

The state of an engine is represented by an object of the following structure:

{
  komi: <number>,
  boardsize: <[number, number]>,
  timeSettings: {
    mainTime: <number>,
    byoyomiTime: <number>,
    byoyomiStones: <number>
  },
  history: <Command[]>
}

new ControllerStateTracker(controller)

ControllerStateTracker.fromStreamController(input, output)

Equivalent to new ControllerStateTracker(new StreamController(input, output)).

ControllerStateTracker.fromController(path[, args[, spawnOptions]])

Equivalent to new ControllerStateTracker(new Controller(path, args, spawnOptions)).

stateTracker.controller

<StreamController> or <Controller> - The controller of the engine that we're tracking the state of.

stateTracker.state

<EngineState> - The state of the engine controlled by stateTracker.controller.

stateTracker.syncing

<Boolean> - Indicates whether the controller is performing a sync right now.

async stateTracker.knowsCommand(commandName)

Returns a boolean whether the engine supports the given command.

async stateTracker.queueCommand(command)

Sends the given command to the engine after all ongoing syncs have finished and returns the response.

async stateTracker.sync(state)

Tries to sync the engine to the given state. Omit keys or set values to null in the state object if you do not want to change the engine state for particular keys.


class Engine extends EventEmitter

Use this class to create a GTP engine using the communication channels of your choice.

new Engine([name[, version]])

The following GTP commands have a default implementation:

Event: started

This event is emitted when the engine has started.

Event: stopped

This event is emitted when the engine has stopped.

Event: command-received

This event is emitted after a command has been received.

Event: command-processing

This event is emitted when a command is about to be processed.

Event: command-processed

This event is emitted after a command has been processed.

engine.handlers

<Object> - An object with the command names as keys, and handler functions as values.

engine.commands

<Command[]> - The command queue.

engine.busy

<Boolean> - If true, a command is being processed right now.

engine.command(name, handler)

Sets a handler for the given command. handler will be called with the following arguments:

handler can be an async function or return a Promise. In this case, you don't need to call out.end() explicitly.

You can also pass a string as handler to immediately return a response.

engine.start([options])

Starts listening to commands.

engine.stop()

Stops listening.