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
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.8.1.tar.gz
(187.8 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.8.1-py3-none-any.whl
(110.5 kB
view details)
File details
Details for the file jansou-0.8.1.tar.gz.
File metadata
- Download URL: jansou-0.8.1.tar.gz
- Upload date:
- Size: 187.8 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 |
32ad1cd3d433aa584ca722c934fe39e083eb0d944d885e95daf6def24e8a2c7f
|
|
| MD5 |
2f24bdbe3eb02ca6865e3608b1c7a2f9
|
|
| BLAKE2b-256 |
857008ffc067192be86029e7dee2da539675df808e8979444a9b466d21b31150
|
File details
Details for the file jansou-0.8.1-py3-none-any.whl.
File metadata
- Download URL: jansou-0.8.1-py3-none-any.whl
- Upload date:
- Size: 110.5 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 |
9ad184fb174c4cef502482b5062ebbaa65509cc28882d4a27c5ca2e1b63f94ff
|
|
| MD5 |
e8c3bbbc5f318cc4dcfbeb57af68aa7a
|
|
| BLAKE2b-256 |
9bd87fc4e0cb10a6bd4ab287f892153a7c4a560f6ed74e5a70df419bb8ce5802
|