This document describes the JSON-based communication protocol used by Triangle.js adapters to interface with external engines. The protocol enables bidirectional communication between Triangle.js and external processes through a communication channel such as standard input/output or a WebSocket.
Transparency note: this document was AI-generated based on the Triangle.js adapter source code.
The adapter protocol is implemented by the AdapterIO
class and uses line-delimited JSON messages. Each message is a single JSON object. The protocol supports three main types of outgoing messages and two types of incoming messages.
info
messageconfig
message with game rules and settingsstate
updates and play
requests, receiving move
responsesThese messages are sent from Triangle.js to the external engine.
Sent once at the beginning to configure the game rules and settings.
{
"type": "config",
"boardWidth": 10,
"boardHeight": 20,
"kicks": "SRS",
"spins": {
"t": { "mini": 100, "full": 400 },
"all": { "mini": 100, "full": 400 }
},
"comboTable": [0, 0, 1, 1, 1, 2, 2, 3, 3, 4, 4, 4, 5],
"b2bCharing": false,
"b2bChargeAt": 0,
"b2bChargeBase": 0,
"b2bChaining": true,
"garbageMultiplier": 1.0,
"garbageCap": 8,
"garbageSpecialBonus": false,
"pcB2b": 0,
"pcGarbage": 10,
"queue": ["T", "S", "Z", "I", "O", "J", "L"],
"data": null
}
Fields:
type
: Always "config"
boardWidth
: Width of the game board (typically 10)boardHeight
: Height of the visible game board (typically 20)kicks
: Kick table name (e.g., "SRS", "TETR.IO")spins
: Spin detection settings for T-spins and other piece spinscomboTable
: Array defining combo multipliersb2bCharing
: Whether back-to-back bonus charging is enabledb2bChargeAt
: Frame count when B2B charging beginsb2bChargeBase
: Base value for B2B chargingb2bChaining
: Whether back-to-back chaining is enabledgarbageMultiplier
: Global garbage damage multipliergarbageCap
: Maximum garbage lines that can be sent at oncegarbageSpecialBonus
: Whether special attack bonuses are enabledpcB2b
: Back-to-back bonus for perfect clearspcGarbage
: Garbage lines sent for perfect clearsqueue
: Initial piece queue as array of piece symbolsdata
: Custom data field for engine-specific configurationSent to update the external process with the current game state.
{
"type": "state",
"board": [
...,
[null, null, null, null, null, null, null, null, null, null],
[null, null, null, null, null, null, null, null, null, null],
["T", "T", "T", null, null, null, null, null, null, null],
[null, "T", null, null, null, null, null, null, null, null]
],
"current": "I",
"hold": "O",
"queue": ["L", "J", "S", "Z", "T", ...],
"garbage": [4, 2, 1],
"combo": -1,
"b2b": -1,
"data": null
}
Fields:
type
: Always "state"
board
: 2D array representing the game board, where each cell is either null
(empty) or a piece symbol string. Row 0 is the bottom of the boardcurrent
: Symbol of the currently falling piecehold
: Symbol of the held piece, or null
if no piece is heldqueue
: Array of upcoming piece symbolsgarbage
: Array of incoming garbage amounts (in lines)combo
: Current combo countb2b
: Current back-to-back countdata
: Custom data field for engine-specific state informationSent when new pieces are added to the queue.
{
"type": "pieces",
"pieces": ["T", "S", "Z", "I", "O", "J", "L"],
"data": null
}
Fields:
type
: Always "pieces"
pieces
: Array of new piece symbolsdata
: Custom data field for engine-specific piece informationSent when requesting a move from the external process.
{
"type": "play",
"garbageMultiplier": 1.0,
"garbageCap": 8,
"data": null
}
Fields:
type
: Always "play"
garbageMultiplier
: Current garbage damage multipliergarbageCap
: Current garbage cap for this turndata
: Custom data field for engine-specific play parametersThese messages are received by Triangle.js from the external engine.
Sent once at startup to identify the external process.
{
"type": "info",
"name": "MyBot",
"version": "1.0.0",
"author": "Developer Name",
"data": null
}
Fields:
type
: Always "info"
name
: Display name of the bot/engineversion
: Version stringauthor
: Author/developer namedata
: Custom data field for engine-specific informationSent in response to a play
message to specify the desired move.
{
"type": "move",
"keys": ["moveLeft", "moveLeft", "rotateClockwise", "hardDrop"],
"data": null
}
Fields:
type
: Always "move"
keys
: Array of key actions to perform in sequencedata
: Custom data field for engine-specific move informationThe keys
array in move messages can contain the following actions:
Standard Game Keys:
moveLeft
- Move piece leftmoveRight
- Move piece rightsoftDrop
- Move piece downhardDrop
- Drop piece to bottomrotateClockwise
- Rotate piece clockwiserotateCounterclockwise
- Rotate piece counterclockwiserotate180
- Rotate piece 180 degreeshold
- Hold current pieceExtended Keys:
dasLeft
- Delayed Auto Shift left (continuous movement)dasRight
- Delayed Auto Shift right (continuous movement)Each message type includes a data
field that can be used to pass custom information specific to your adapter implementation. This field should be null
if not used, or can contain any JSON-serializable data structure.
To use custom data, extend the CustomMessageData
interface:
interface MyCustomData extends CustomMessageData {
info: { capabilities: string[] };
move: { confidence: number };
config: { difficulty: number };
state: { evaluation: number };
play: { timeLimit: number };
}
Here's a minimal external process implementation in Python:
import json
import sys
def send_message(msg):
print(json.dumps(msg), flush=True)
def receive_message():
line = sys.stdin.readline()
return json.loads(line.strip())
# Send info message
send_message({
"type": "info",
"name": "SimpleBot",
"version": "1.0.0",
"author": "Example"
})
# Main loop
while True:
msg = receive_message()
if msg["type"] == "config":
# Store configuration
pass
elif msg["type"] == "state":
# Update internal state
pass
elif msg["type"] == "play":
# Calculate and send move
send_message({
"type": "move",
"keys": ["hardDrop"]
})
play
requests to maintain smooth gameplayinfo
messageconfig
message with game settingsstate
and play
messagesThe adapter ensures proper cleanup of the external process and handles termination gracefully.