Feature
- A custom client for Tenhou implemented in node
- Nearly full coverage for client / server events
- Lobby stats and tournament mode events are currently not supported
Usage
npm install --save tenhou-client
Running the simple AI demo:
cd node_modules/tenhou-client
node examples/simple-ai-controller.js
simple-ai-controller
is a menzen bot that connects to Tenhou and plays a game in L0.
Code Example
Most of the time you'll want to listen to the events emitted from TenhouClient.
At the very least, you should listen to onTileDraw
and onTileDiscard
in order for the bot to not freeze the game.
const TenhouClient = require('tenhou-client');
const client = new TenhouClient();
const onTileDraw = (who, tile, actions) => {
if (who === 0) {
if (actions) {
client.pass();
}
client.discard(tile); // tsumogiri
}
};
const onTileDiscard = (who, tile, actions, isTedashi) => {
if (actions) {
client.pass();
}
};
client.registerListener('onTileDraw', onTileDraw);
client.registerListener('onTileDiscard', onTileDiscard);
Once you set up the event handlers, you can connect to the Tenhou server and queue for a game:
client.connect()
.then(() => {
return client.login('NoName'); // Login with NoName
})
.then(() => {
return client.switchTaku(1); // Switch to Ippan
})
.then(() => {
return client.joinLobby('0'); // Join a lobby
})
.then(() => {
client.queue(1); // Queue for a game
});
The GameState
is an object containing a snapshot of the current state of the game. At any given point of the game, you may query the game state like so:
const onNaki = (nakiData, who) => {
const gameState = client.getGameState(); // Get a copy of the gameState
const hand = gameState.self.hand;
// ... Do stuff
};
Game state object
{
self, // Player Object
player2, // Player Object
player3, // Player Object
player4, // Player Object
type: 0, // Game Type ID
lobby: 0, // Lobby ID
gpid: '', // Game ID
log: '', // Log ID
ba: 0, // Current round
honba: 0, // Number of repeat deals
riichiSticks: 0, // Number of unclaimed riichi sticks
dice1: 0, // Value of die 1
dice2: 0, // Value of die 2
oya: 0, // Who is dealer
wall: 70, // Number of tiles remaining in the wall
doraList: [], // List of dora
unseenTileList: [], // List of tiles that haven't been revealed yet
globalDiscardList: [], // List of all players' discards
};
Player object
{
name: '', // Player name
dan: 0, // Player dan
rating: 0.00, // Player rating
connected: true, // Connected to game
points: 0, // Current points
riichi: false, // Declared riichi
riichiTile: null, // Tile the player dealt to declare riichi
fuuroList: [], // List of naki
discardList: [], // List of discarded tiles
tedashiList: [], // List of discarded tiles (exclude tsumogiri tiles)
}
Events
Listening and telling the client to respond to events is critical for interacting with the game
Event Name | Param 1 | Param 2 | Param 3 | Description |
---|---|---|---|---|
'onHandShake' |
Received handshake from tenhou | |||
'onAuthenticated' |
Auth token accepted | |||
'onLobbyBroadcast' |
Received lobby broadcast message containing lobby status. Currently incomplete. | |||
'onLobbyJoined' |
newLobby ID of the new lobby |
Lobby changed | ||
'onPlayerChat' |
uname Username of the message's author |
message The message that was received |
Lobby chat recieved | |
'onGameFound' |
gameData An object containing information of the game joined. Such as gpid and game type |
A game is found | ||
'onPlayerReconnect' |
player The player who reconnected. Can only be 1, 2 or 3. |
A player reconnected | ||
'onPlayersInfo' |
gameData An object containing player information such as name, dan, rating etc. |
Player info received | ||
'onGameInit' |
gameData An object containing information about the new round |
A new round is starting | ||
'onTaikyoku' |
gameData This object contains two pieces of information. oya is the initial dealer and log is the ID of the log |
A taikyoku is about to start. This is received together with onGameFound | ||
'onAgari' |
agariData An object containing information about the agari. Such as point changes, the winning hand, etc. |
Triggered when someone scores an agari | ||
'onGameEnd' |
The game has ended and everyone should exit | |||
'onRyuukyoku' |
ryuukyukuData An object containing information about the ryuukyuku. Similar to onAgari |
The current round ended by ryuukyoku | ||
'onDora' |
doraTileId Tile ID of the new dora indicator |
A new dora indicator is being revealed | ||
'onPlayerDisconnect' |
player The player who reconnected. Can only be 1, 2 or 3. |
Someone disconnected from game | ||
'onRiichiStepOne' |
player The player who declared riichi |
Someone pressed the 'riichi' button | ||
'onRiichiStepTwo' |
gameData Object containing information about points changes |
player The player who declared riichi |
The riichi is acknowledged | |
'onNaki' |
nakiData Object containing information about the naki |
player The player who called |
Someone called a tile | |
'onTileDraw' |
player Player who drew a tile |
Someone drew a tile | ||
'onTileDiscard' |
player Player who discarded the tile |
tileId ID of the tile discarded |
actions A value representing the available actions |
Someone discarded a tile |
License
MIT
Copyright 2017 Zefiris (Mai)
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.