Awesome
@sabaki/gtp
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)
input
<String>
- The GTP command as string, e.g.1 genmove B
Returns a Command
object, representing input
.
Command.toString(command)
command
<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)
input
<String>
- The GTP response as string, e.g.=1 ok
Returns a Response
object, representing input
.
Response.toString(response)
response
<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)
input
<Writable>
output
<Readable>
Event: command-sent
evt
<Object>
command
<Command>
subscribe(subscriber)
async getResponse()
<Response>
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
evt
<Object>
command
<Command>
response
<Response>
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])
command
<Command>
subscriber
<Function>
(optional)evt
<Object>
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:
line
<String>
- The contents of the incoming line.end
<Boolean>
-true
if incoming line is the last line of response.command
<Command>
- The command to which the response belongs.response
<Response>
- The partial response until now, including the incoming line with all the previous lines.
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]])
path
<String>
args
<String[]>
(optional)spawnOptions
<Object>
(optional) - See Node.js documentation.
Event: started
This event is emitted when the engine process starts.
Event: stopped
evt
<Object>
signal
<String>
- The signal by which the engine process was terminated.
This event is emitted after the engine process ends.
Event: stderr
evt
<Object>
content
<String>
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])
timeout
<number>
(optional) - Default:3000
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[]>
}
- Values will be
null
if we do not know the engine state. boardsize
contains the width and height of the board.history
is an array ofset_free_handicap
andplay
commands.
new ControllerStateTracker(controller)
controller
<StreamController>
or<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)
commandName
<String>
Returns a boolean whether the engine supports the given command.
async stateTracker.queueCommand(command)
command
<Command>
Sends the given command to the engine after all ongoing syncs have finished and returns the response.
async stateTracker.sync(state)
state
<EngineState>
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]])
name
<String>
(optional)version
<String>
(optional)
The following GTP commands have a default implementation:
protocol_version
name
version
list_commands
quit
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
evt
<Object>
command
<Command>
This event is emitted after a command has been received.
Event: command-processing
evt
<Object>
command
<Command>
This event is emitted when a command is about to be processed.
Event: command-processed
evt
<Object>
command
<Command>
response
<Response>
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)
name
<String>
- The command name.handler
<Function>
|<String>
Sets a handler for the given command. handler
will be called with the
following arguments:
command
<Command>
out
<Object>
send(content)
- Sends a successful response with the given content.err(content)
- Sends an error response with the given content.write(content)
- Writes given content to response.end()
- When usingwrite
, use this method to indicate end of response.
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])
options
<Object>
(optional)input
<Readable>
(optional) - Default:process.stdin
output
<Writable>
(optional) - Default:process.stdout
Starts listening to commands.
engine.stop()
Stops listening.