Riichi Mahjong Environments and Utilities
Project description
Jansou
Python library for Riichi Mahjong environments and utilities.
- Conversion between multiple tile notations (MPSZ, MJAI, 136)
- Hand analysis (shanten, wait, efficiency, yaku, fu/han, score)
- Game environment for 3/4 player game with configurable rules
- Game replay log in multiple formats (mjlog XML, mjai JSONL, Tenhou JSON)
Examples
Notation
from jansou.core.notation import parse_mpsz, dump_mpsz, parse_mjai, dump_mjai, parse_136, dump_136
tiles = parse_mpsz("667s 34668m 2357p 2z")
dump_mpsz(tiles) # 34668m2357p667s2z
dump_mjai(tiles) # 6s 6s 7s 3m 4m 6m 6m 8m 2p 3p 5p 7p S
dump_136(tiles) # [92, 93, 96, 8, 12, 20, 21, 28, 40, 44, 53, 60, 112]
Hand
from jansou.core.tiles import Tile, TileKind
from jansou.core.notation import parse_mpsz
from jansou.core.hand import Hand
from jansou.analysis.efficiency import discard_evaluation
from jansou.analysis.shanten import shanten, is_tenpai
from jansou.analysis.waits import waits
# (1) shanten advancement
hand = Hand(parse_mpsz("279m 1569p 1168s 35z 2m"))
shanten(hand) # 3
is_tenpai(hand) # False
options = discard_evaluation(hand) # sorted by shanten advancement and wider acceptance
# in this case, 1p 9p 3z 5z are the best, with the same number of acceptance
best = options[0]
best.discard # 1p
best.shanten # 3
best.total_acceptance # 20
# (2) acceptance
hand = Hand(parse_mpsz("567m 34567p 23489s 2z"))
options = discard_evaluation(hand) # there are multiple options for 1 shanten
best = options[0] # but the best option is 2z with the widest acceptance
best.discard # 2z
best.shanten # 1
best.total_acceptance # 33
# (3) waits
hand = Hand(parse_mpsz("1112345678999m"))
waits(hand) # 1m 2m 3m 4m 5m 6m 7m 8m 9m
Score
from jansou.core.tiles import Tile, TileKind, Wind
from jansou.core.notation import parse_mpsz
from jansou.core.rules import preset
from jansou.core.hand import Hand, Meld, MeldType, CallSource
from jansou.scoring.context import WinContext
from jansou.scoring.score import score
hand = Hand(
parse_mpsz("77m 34p 055s 5p"),
[
Meld(MeldType.PON, parse_mpsz("111m"), Tile(TileKind.M1), CallSource.TOIMEN),
Meld(MeldType.PON, parse_mpsz("777z"), Tile(TileKind.CHUN), CallSource.KAMICHA),
]
)
context = WinContext(
rules=preset("m-league"),
seat_wind=Wind.WEST,
is_tsumo=False,
dora_indicators=[Tile(TileKind.CHUN)],
)
result = score(hand, Tile(TileKind.P5), context)
result.yaku # YAKUHAI_CHUN
result.han # 2
result.fu.total # 40 (20 + 4 + 4 + 4 = 32)
result.payment.total # 2600
shanten(hand) # 0
is_tenpai(hand) # True
waits(hand) # 2p, 5p
Game environment, replay log
from jansou.core.rules import preset
from jansou.game.environment import Environment
from jansou.game.agents import SmartEfficiencyAgent
from jansou.io.from_game import paifu_from_game
from jansou.io.tenhou_json import dump_tenhou_json_url
env = Environment(preset("tenhou"))
agents = [SmartEfficiencyAgent() for i in range(4)]
result = env.run(agents)
result.scores
result.ranking
paifu = paifu_from_game(env)
dump_tenhou_json_url(paifu) # https://tenhou.net/6/#json=...
# can be viewed at https://mjv.snack.studio
Step-by-step play
from jansou.core.rules import preset
from jansou.game.environment import Environment
env = Environment(preset("tenhou"))
game = env.play() # returns generator
# play() make take `observe` function for
# unmasked events
request = next(game) # first decision
while True:
action = __choose(request) # any of request.actions
try:
request = game.send(action)
except StopIteration as stop:
result = stop.value # GameResult
break
Development
uv sync
uv run pytest
uv run pytest --cov
uv run ruff format
uv run ruff check
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
jansou-0.5.0.tar.gz
(182.6 kB
view details)
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
jansou-0.5.0-py3-none-any.whl
(108.4 kB
view details)
File details
Details for the file jansou-0.5.0.tar.gz.
File metadata
- Download URL: jansou-0.5.0.tar.gz
- Upload date:
- Size: 182.6 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.11.28 {"installer":{"name":"uv","version":"0.11.28","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
7c2b68ee523e48a7eb5107a0a415c9ced64cb822dc01100be0245fb7e827f21d
|
|
| MD5 |
6453d1f99f081475690c78fdb9a30aad
|
|
| BLAKE2b-256 |
2a4a2b0cb2a3ae4b8d59b19e2b27e9be01e6ae57f51f4dddd76b51e516926ef4
|
File details
Details for the file jansou-0.5.0-py3-none-any.whl.
File metadata
- Download URL: jansou-0.5.0-py3-none-any.whl
- Upload date:
- Size: 108.4 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.11.28 {"installer":{"name":"uv","version":"0.11.28","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
95da4f02a062af4b5bb9586d657969c7aaf545c56aebcbc55cb19d928fb2c6ce
|
|
| MD5 |
69b8048b929fcf05c9c946e81ebad42d
|
|
| BLAKE2b-256 |
61ea8d16d774f37cad391a18fb5227dfb9e177beb9f5f71c9848dcc7656f001c
|