goban-engine
Version:
This contains the built Go engine that is used by the Goban package. There are no display components in this package, only the logic for playing the game of Go, making it suitable for usage in node.js or other server-side environments.
806 lines (805 loc) • 28.2 kB
TypeScript
import type { JGOFMove, JGOFPlayerClock, JGOFSealingIntersection } from "../formats/JGOF";
import type { ReviewMessage } from "../GobanEngine";
import type { ConditionalMoveResponse } from "../ConditionalMoveTree";
/** Messages that clients send, regardless of target server */
export interface ClientToServerBase {
/** Authenticate with the server.
*
* Prior to authentication, you should perform a GET request to
* `/api/v1/ui/config`
* to get the current configuration. Within the returned JSON
* you will find all of the necessary fields to authenticate.
*/
"authenticate": (data: {
/** The JSON Web Token (`user_jwt` field) from `/api/v1/ui/config`. If
* connecting as a guest, send "" */
jwt: string;
/** Client generated unique id for the device. */
device_id?: string;
/** Browser user agent (or websocket library) */
user_agent?: string;
/** ISO 639-1 language code used on this device. */
language?: string;
/** The version of the translation dictionary. */
language_version?: string;
/** Client name (your application name) */
client?: string;
/** Client version string. */
client_version?: string;
/** Bot username connecting, if applicable */
bot_username?: string;
/** Bot API key, if applicable */
bot_apikey?: string;
}) => {
id: number;
username: string;
} | undefined;
/** Sends a ping to the server. This message should be
* sent regularly. The default interval is 10 seconds.
* This keeps the connection alive and allows a client
* to measure clock drift and latency, both of which
* are vital to adjusting the client's game clock displays.
*/
"net/ping": (data: {
/** Client timestamp - milliseconds since epoch */
client: number;
/** Last clock drift measurement, or `0` */
drift: number;
/** Last latency measurement, or `0` */
latency: number;
}) => void;
}
/** This is an exhaustive list of the messages that the client can send
* to the server.
*
* This documentation is generated from the official typescript interface.
* To interpret it, you will every message organized as the name of the
* message followed by a function taking the message data parameters and
* returning what you can expect tor receive back.
*
* For example, the authentication message documentation looks like this:
*
* ```typescript
* authenticate: ((data: {
* bot_apikey?: string;
* bot_username?: string;
* client?: string;
* client_version?: string;
* device_id?: string;
* jwt: string;
* language?: string;
* language_version?: string;
* user_agent?: string;
* }) => undefined | {
* id: number;
* username: string;
* })
* ```
*
* The command you will send is `authenticate`, the data you send will be an object with the following format:
* ```typescript
* {
* bot_apikey?: string;
* bot_username?: string;
* client?: string;
* client_version?: string;
* device_id?: string;
* jwt: string;
* language?: string;
* language_version?: string;
* user_agent?: string;
* }
* ```
*
* and you can expect to receive back either `undefined` or `{id: number, username: string}`
*
*/
export interface ClientToServer extends ClientToServerBase {
/** Get active automatch entries for the current user */
"automatch/list": (data: {}) => void;
/** Message to let the server know the client is still interested
* in the specified blitz or live challenge. These should be sent
* about once a second to prevent the server from canceling the challenge.
*/
"challenge/keepalive": (data: {
challenge_id: number;
game_id: number;
}) => void;
/** Connect to a game. Once connected, the client will receive game
* updates relevant to the game. */
"game/connect": (data: {
/** The game id to connect to */
game_id: number;
/** If true, the client will receive the game chat log and new chat events */
chat?: boolean;
}) => void;
/** Disconnect from a game. This will stop game updates for a particular game. */
"game/disconnect": (data: {
game_id: number;
}) => void;
/** Sets removed stones in the stone removal phase. */
"game/removed_stones/set": (data: {
/** The game id */
game_id: number;
/** True if the stones should be marked as removed (or intersections marked
* as dame if there is no stone there), false if they should be marked as
* not removed / open area. */
removed: boolean;
/** List of intersections that are to be removed. */
stones: JGOFMove[] | string;
/** List of intersections that need to be sealed before the game can be
* correctly scored. Note, if this is undefined, the value will not
* be changed on the server side. To specify there are no more intersections
* that need to be cleared, set it to `[]` specifically.
*/
needs_sealing?: JGOFSealingIntersection[];
/** Japanese rules technically have some special scoring rules about
* whether territory in seki should be counted or not. This is supported
* by the backend but the official client no longer displays this as an
* option to the user as it was very largely unused and was a large
* source of confusion. This field is deprecated and will likely be
* removed in the future.*/
strict_seki_mode?: boolean;
}) => void;
/** Rejects the removed stones and resumes the game from the stone removal phase */
"game/removed_stones/reject": (data: {
/** The game id */
game_id: number;
}) => void;
/** Accepts the stones as removed. Once both players have accepted the same
* stones, the stone removal phase will conclude and the game will finish. */
"game/removed_stones/accept": (data: {
/** The game id */
game_id: number;
/** All of the stones that are accepted as removed, and all
* intersections marked as dame */
stones: string;
/** Japanese rules technically have some special scoring rules about
* whether territory in seki should be counted or not. This is supported
* by the backend but clients should always set this to false in this
* era of the game, the official client no longer displays this as an
* option to the user as it was very largely unused and was a large
* source of confusion. */
strict_seki_mode: boolean;
}) => void;
/** Submit a move for a game */
"game/move": (data: {
/** The game id */
game_id: number;
/** The move number to play at */
move: string;
/** Maximum number of milliseconds the client was out of focus between
* the last move and this move */
blur?: number;
/** Clock according to the client. If this is within the margin of
* error of the server's clock, the server will accept the new
* clock value. If not provided, the server clock will be used. */
clock?: JGOFPlayerClock;
}) => void;
/** Requests an undo */
"game/undo/request": (data: {
/** The game id */
game_id: number;
/** The current move number */
move_number: number;
}) => void;
/** Accepts an undo */
"game/undo/accept": (data: {
/** The game id */
game_id: number;
/** The current move number */
move_number: number;
}) => void;
/** Cancels an undo request */
"game/undo/cancel": (data: {
/** The game id */
game_id: number;
/** The current move number */
move_number: number;
}) => void;
/** Pauses the game clocks */
"game/pause": (data: {
/** The game id */
game_id: number;
}) => void;
/** Resumes the game clocks */
"game/resume": (data: {
/** The game id */
game_id: number;
}) => void;
/** Resigns from the game */
"game/resign": (data: {
/** The game id */
game_id: number;
}) => void;
"game/delayed_resign": (data: {
/** The game id */
game_id: number;
}) => void;
"game/clear_delayed_resign": (data: {
/** The game id */
game_id: number;
}) => void;
/** Cancels a game. This is effectively the same as resign, except the
* game will not be ranked. This is only allowed within the first few
* moves of the game. (See GobanEngine.gameCanBeCancelled for cancellation ) */
"game/cancel": (data: {
/** The game id */
game_id: number;
}) => void;
/** In Japanese rules, if the game is found to be repeating, the players
* may opt to annul the entire game and start over.
*
* This is largely undesired in an online setting and support for this
* will probably be removed in the future, dont' bother implementing
* this.
*/
"game/annul": (data: {
/** The game id */
game_id: number;
}) => void;
/** Request the server end a game that is being stalled by one of the
* players. This will only work if the server agrees in the outcome. */
"game/prevent_stalling": (data: {
/** The game id */
game_id: number;
/** The proposed winner */
winner: "black" | "white";
}) => void;
/** Request the server end a game that someone has left without resigning
* from */
"game/prevent_escaping": (data: {
/** The game id */
game_id: number;
/** The proposed winner */
winner: "black" | "white";
/** Request that the game be annulled or not */
annul: boolean;
}) => void;
/** Inform the server that the client believes it's clock has timed out
* and the game should be ended in a timeout. This is not strictly necessary
* to implement as the server will also timeout games, however there is
* a grace period to account for network latency, so well behaved clients
* can (and should) send this message to be very exact with timeouts. */
"game/timed_out": (data: {
/** The game id */
game_id: number;
}) => void;
/** Sets conditional moves to be made on behalf of the player in response
* to a move by the opponent. */
"game/conditional_moves/set": (data: {
/** The game id */
game_id: number;
/** The move number from which the conditional moves are rooted in */
move_number: number;
/** The conditional moves. The top level should be an array that looks
* like `[null, { ... }]` where the second element contains the responses
* to the opponent's move. */
conditional_moves: ConditionalMoveResponse;
}) => void;
/** Sends a chat message to a game */
"game/chat": (data: {
/** The game id */
game_id: number;
/** The type of chat message being sent */
type: "main" | "malkovich" | "moderator" | "hidden" | "personal";
/** The move number currently being viewed */
move_number: number;
/** The chat message */
body: string | GameChatTranslatedMessage | GameChatAnalysisMessage | GameChatReviewMessage;
}) => void;
/** Update your latency information for a particular game. This is used
* for clock synchronization. It is not strictly required, however strongly
* suggested for live games. */
"game/latency": (data: {
/** The game id */
game_id: number;
/** Network latency, measured in milliseconds. See net/ping to measure this. */
latency: number;
}) => void;
/** Connects to a review */
"review/connect": (data: {
/** The review id */
review_id: number;
}) => void;
/** Disconnects from a review */
"review/disconnect": (data: {
/** The review id */
review_id: number;
}) => void;
/** Append a review action to the review log. */
"review/append": (data: ReviewMessage) => void;
/** Sends a chat message to a review */
"review/chat": (data: {
/** The review id */
review_id: number;
/** The root of the branch the user is viewing */
from: number;
/** The analysis branch the user is viewing */
moves: string;
/** The chat message */
body: string;
}) => void;
/** Request the number of unique authenticated players
* online within the given interval */
"stats/online": (data: {
/** Interval in seconds */
interval: number;
}) => number;
/** Deletes a notification */
"notification/delete": (data: {
notification_id: string;
}) => void;
/** Connects to the game list count.
* Once connected you'll start receiving `gamelist-count` or
* `gamelist-count-${channel}` messages.
*/
"gamelist/count/subscribe": (data: {
/** The group or tournament channel to subscribe to. If no
* channel is provided, the global server counts will be
* sent */
channel?: string;
}) => void;
/** Disconnects from the game list count */
"gamelist/count/unsubscribe": (data: {
/** The group or tournament channel to unsubscribe from. If no
* channel is provided, the global server counts will be
* unsubscribed from */
channel?: string;
}) => void;
/** Queries the server for a list of games */
"gamelist/query": (data: {
list: "live" | "corr" | "kidsgo";
sort_by: "rank";
/** Filtering options */
where: GameListWhere;
/** The number of games to skip before returning results */
from: number;
/** Number of games to return, between 1 and 300 */
limit: number;
/** The group or tournament channel to query */
channel?: string;
}) => undefined | {
list: string;
by: string;
size: number;
where: GameListWhere;
from: number;
limit: number;
results: GameListEntry[];
};
/** Returns an event log for the given game. This is primarily
* for moderation purposes, although the endpoint is generally
* available to all users. */
"game/log": (data: {
game_id: number;
}) => {
timestamp: string;
event: string;
data: any;
}[];
/** Subscribes to online status updates for the given player ids */
"user/monitor": (data: {
user_ids: number[];
}) => void;
/** Sends an "Inter Tab Communication" message to all other connected
* clients for the current user. This includes other devices, so the
* "Tab" part is a bit of a misnomer. */
"itc": (data: {
/** User defined event string */
event: string;
/** User defined data */
data: any;
}) => void;
/** Set the given key in the remote storage system for this user
*
* For more details on the remote storage replication system see:
* https://github.com/online-go/online-go.com/blob/devel/src/lib/data.ts
*/
"remote_storage/set": (data: {
key: string;
value: any;
replication: RemoteStorageReplication;
}) => {
error?: string;
retry?: boolean;
} | {
success: true;
};
/** Remove the given key from remote storage system for this user
*
* For more details on the remote storage replication system see:
* https://github.com/online-go/online-go.com/blob/devel/src/lib/data.ts
*/
"remote_storage/remove": (data: {
key: string;
replication: RemoteStorageReplication;
}) => {
error?: string;
retry?: boolean;
} | {
success: true;
};
/** Requests all updated key/value pairs for this user since the
* provided timestamp (as as ISO 8601 string).
*
* For more details on the remote storage replication system see:
* https://github.com/online-go/online-go.com/blob/devel/src/lib/data.ts
*/
"remote_storage/sync": (data: {
/** ISO 8601 timestamp. Updates made after this timestamp will be sent to the client. */
since: string;
}) => {
error?: string;
retry?: boolean;
} | {
success: true;
};
/** Sets a channel topic */
"chat/topic": (data: {
channel: string;
topic: string;
}) => void;
/** Sends a chat message to the given channel */
"chat/send": (data: {
/** Channel to send the message to */
channel: string;
/** ID for the message */
uuid: string;
/** Message text */
message: string;
}) => void;
/** Join a chat channel */
"chat/join": (data: {
/** Channel to join */
channel: string;
}) => void;
/** Leave a channel */
"chat/part": (data: {
/** Channel to leave */
channel: string;
}) => void;
/** Subscribes to UI related push event messages sent to a particular channel */
"ui-pushes/subscribe": (data: {
channel: string;
}) => void;
/** Un-Subscribes to UI related push event messages sent to a particular channel */
"ui-pushes/unsubscribe": (data: {
channel: string;
}) => void;
/** Subscribes to the seek graph events. The channel is required to be "global"
* for now and the foreseeable future. */
"seek_graph/connect": (data: {
channel: "global";
}) => void;
/** Un-Subscribes to the seek graph events. The channel is required to be "global"
* for now and the foreseeable future. */
"seek_graph/disconnect": (data: {
channel: "global";
}) => void;
/** Send a private message to another user */
"chat/pm": (data: {
/** Player ID of the recipient */
player_id: number;
/** Username of the recipient */
username: string;
/** UUID for the message */
uid: string;
/** Message text */
message: string;
/** Moderator option to send the chat from the system not from their personal PM */
as_system?: true;
}) => undefined | {
from: User;
to: {
id: number;
username: string;
};
message: {
i: string;
t: number;
m: string;
};
};
/** Loads the current user's private message session history with the given player id */
"chat/pm/load": (data: {
player_id: number;
}) => void;
/** Closes the current user's private message session with the given player id */
"chat/pm/close": (data: {
player_id: number;
}) => void;
/** Begins a "super chat" session with the given player id, which creates an
* unclosable dialog if enable is true, and makes the dialog closable again
* if enable is false. This is only available to moderators. */
"chat/pm/superchat": (data: {
player_id: number;
/** Username of the recipient */
username: string;
/** Set to true if you want the modal to be unclosable, false if you want
* the modal to be closable again */
enable: boolean;
}) => void;
/** Moderator only command to remove all chat messages for a given player */
"chat/remove_all": (data: {
/** Player id to remove all messages for */
player_id: number;
}) => void;
/** Moderator only command to remove a single chat message */
"chat/remove": (data: {
uuid: string;
}) => void;
/** Moderator only command to remove a single chat message from a game */
"game/chat/remove": (data: {
game_id: number;
channel: string;
chat_id: string;
}) => void;
/** Moderator only command to remove a single chat message from a game */
"review/chat/remove": (data: {
review_id: number;
channel: string;
chat_id: string;
}) => void;
/** Retrieve host information for the termination server you are connected to */
"hostinfo": (data: {}) => {
"hostname": string;
"clients": number;
"uptime": number;
"ggs-version": string;
};
/** Request a match via the automatch system */
"automatch/find_match": (data: AutomatchPreferences) => void;
/** Cancel a match request */
"automatch/cancel": (data: {
uuid: string;
}) => void;
/** Subscribe to automatch offers */
"automatch/available/subscribe": () => void;
/** Unsubscribe from automatch offers */
"automatch/available/unsubscribe": () => void;
/** Updates the config for the bot */
"bot/config": (config: BotConfig) => void;
/** Update the number of games that the bot is currently playing */
"bot/status": (data: {
ongoing_blitz_count: number;
ongoing_live_count: number;
ongoing_correspondence_count: number;
}) => void;
}
export interface BotAllowedClockSettingsV1 {
simple?: {
per_move_time_range: [number, number];
};
byoyomi?: {
main_time_range: [number, number];
period_time_range: [number, number];
periods_range: [number, number];
};
fischer?: {
max_time_range: [number, number];
time_increment_range: [number, number];
};
concurrent_games?: number;
}
export interface BotAllowedClockSettingsV2 {
simple?: {
per_move_time_range: [number, number];
};
byoyomi?: {
main_time_range: [number, number];
period_time_range: [number, number];
periods_range: [number, number];
};
fischer?: {
initial_time_range: [number, number];
max_time_range: [number, number];
time_increment_range: [number, number];
};
concurrent_games?: number;
}
export interface BotConfigV0 {
_config_version: 0;
}
export interface BotConfigV1 {
_config_version: 1;
hidden: boolean;
bot_id: number;
username: string;
allowed_time_control_systems: ("simple" | "byoyomi" | "fischer")[];
allowed_board_sizes: number[] | "all" | "square" | number;
allowed_blitz_settings?: BotAllowedClockSettingsV1;
allowed_rapid_settings?: BotAllowedClockSettingsV1;
allowed_live_settings?: BotAllowedClockSettingsV1;
allowed_correspondence_settings?: BotAllowedClockSettingsV1;
allow_ranked: boolean;
allow_unranked: boolean;
allowed_rank_range: [string, string];
allow_ranked_handicap: boolean;
allow_unranked_handicap: boolean;
allowed_komi_range: [number, number];
decline_new_challenges: boolean;
min_move_time: number;
max_games_per_player: number;
}
export interface BotConfigV2 {
_config_version: 2;
hidden: boolean;
bot_id: number;
username: string;
allowed_time_control_systems: ("simple" | "byoyomi" | "fischer")[];
allowed_board_sizes: number[] | "all" | "square" | number;
allowed_blitz_settings?: BotAllowedClockSettingsV2;
allowed_rapid_settings?: BotAllowedClockSettingsV2;
allowed_live_settings?: BotAllowedClockSettingsV2;
allowed_correspondence_settings?: BotAllowedClockSettingsV2;
allow_ranked: boolean;
allow_unranked: boolean;
allowed_rank_range: [string, string];
allow_ranked_handicap: boolean;
allow_unranked_handicap: boolean;
allowed_komi_range: [number, number];
decline_new_challenges: boolean;
min_move_time: number;
max_games_per_player: number;
}
export type BotConfig = BotConfigV0 | BotConfigV1 | BotConfigV2;
export type Speed = "blitz" | "rapid" | "live" | "correspondence";
export type Size = "9x9" | "13x13" | "19x19";
export type AutomatchCondition = "required" | "preferred" | "no-preference";
export type RuleSet = "japanese" | "chinese" | "aga" | "korean" | "nz" | "ing";
export interface AutomatchPreferences {
uuid: string;
size_speed_options: Array<{
size: Size;
speed: Speed;
system: "fischer" | "byoyomi";
}>;
timestamp?: number;
lower_rank_diff: number;
upper_rank_diff: number;
rules: {
condition: AutomatchCondition;
value: "japanese" | "chinese" | "aga" | "korean" | "nz" | "ing";
};
handicap: {
condition: AutomatchCondition;
value: "enabled" | "disabled";
};
}
/** This enum defines the various replication strategies for the remote storage
* system. For more details on the remote storage replication system see:
* https://github.com/online-go/online-go.com/blob/devel/src/lib/data.ts
*/
export declare enum RemoteStorageReplication {
/** No replication of this change */
NONE = 0,
/** Locally set data will overwrite remotely set data, but if not
* set will default to remotely set data */
LOCAL_OVERWRITES_REMOTE = 1,
/** Remotely set data will overwrite locally set data */
REMOTE_OVERWRITES_LOCAL = 2,
/** Remotely set data, but do not update our local value */
REMOTE_ONLY = 4
}
/** Parameters for the `gamelist/query` message */
export interface GameListWhere {
hide_ranked?: boolean;
hide_unranked?: boolean;
rengo_only?: boolean;
hide_19x19?: boolean;
hide_9x9?: boolean;
hide_13x13?: boolean;
hide_other?: boolean;
hide_tournament?: boolean;
hide_ladder?: boolean;
hide_open?: boolean;
hide_handicap?: boolean;
hide_even?: boolean;
hide_bot_games?: boolean;
hide_beginning?: boolean;
hide_middle?: boolean;
hide_end?: boolean;
players?: Array<number>;
tournament_id?: number;
ladder_id?: number;
malk_only?: boolean;
}
interface GameListPlayer {
username: string;
id: number;
rank: number;
professional: boolean;
accepted: boolean;
ratings: {
version: number;
overall: {
rating: number;
deviation: number;
volatility: number;
};
};
}
export interface GameListEntry {
id: number;
group_ids?: Array<number>;
group_ids_map?: {
[]: boolean;
};
kidsgo_game?: boolean;
phase: string;
name: string;
player_to_move: number;
width: number;
height: number;
move_number: number;
paused: boolean;
private: boolean;
black: GameListPlayer;
white: GameListPlayer;
rengo: boolean;
rengo_teams: {
black: Array<User>;
white: Array<User>;
};
dropped_player: number;
rengo_casual_mode: boolean;
_participants?: Array<number>;
time_per_move: number;
clock_expiration: number;
bot_game?: boolean;
ranked?: boolean;
handicap?: number;
tournament_id?: number;
ladder_id?: number;
komi?: number;
socket_id?: any;
in_beginning?: boolean;
in_middle?: boolean;
in_end?: boolean;
malkovich_present?: boolean;
}
export interface User {
id: number;
username: string;
ratings?: {
[]: Glicko2;
};
ranking?: number;
professional?: boolean;
country?: string;
ui_class?: string;
}
export interface Glicko2 {
rating: number;
deviation: number;
volatility: number;
games_played?: number;
}
export interface GameChatTranslatedMessage {
type: "translated";
en: string;
[]: string;
}
export interface GameChatAnalysisMessage {
type: "analysis";
name?: string;
branch_move?: number;
from?: number;
moves?: string;
marks?: {
[]: string;
};
pen_marks?: unknown[];
engine_analysis?: {
win_rate: number;
score?: number;
visits?: number;
[]: number | undefined;
};
}
export interface GameChatReviewMessage {
type: "review";
review_id: number;
}
export {};