
Offers tools for analyzing Japanese Mahjong hands, including points calculation, hand improvement analysis, and winning hand evaluation. Provides support for multiple languages via dynamic library bindings.
Japanese Mahjong Utilities
Implemented Features:
App developed based on this library: https://github.com/ssttkkl/mahjong-utils-app
Good news! This component has officially partnered with DeepWiki. Now you can ask AI directly instead of wasting time posting Issues!
Documentation: https://deepwiki.com/ssttkkl/mahjong-utils
Example question: How to calculate all yaku in a winning hand?
This project uses Kotlin/Multiplatform and supports multiple platforms:
For Kotlin/Java: Continue reading below
For Python: We provide Python bindings - see python-lib/README.md
For JavaScript/TypeScript: We provide JS/TS bindings - see js-lib/README.md
Maven:
<dependency>
<groupId>io.github.ssttkkl</groupId>
<artifactId>mahjong-utils</artifactId>
<version>${mahjongUtilsVersion}</version>
</dependency>Gradle:
implementation "io.github.ssttkkl:mahjong-utils:{mahjongUtilsVersion}"Without drawn tile:
val result = shanten(Tile.parseTiles("34568m235p68s"))
val shantenInfo = result.shantenInfo as ShantenWithoutGot
print(shantenInfo.shantenNum) // 2
print(shantenInfo.advance) // [7m, 1p, 4p, 5p, 6s, 8s, 7s, 2p, 3p, 6m, 8m, 3m]With drawn tile:
val result = shanten(Tile.parseTiles("112233p44556s127z"))
val shantenInfo = result.shantenInfo as ShantenWithGot
print(shantenInfo.shantenNum) // 1
print(shantenInfo.discardToAdvance)
/*
{1z=ShantenWithoutGot(shantenNum=1, advance=[3s, 6s, 2z, 7z], advanceNum=13, goodShapeAdvance=[2z, 7z], goodShapeAdvanceNum=6),
2z=ShantenWithoutGot(shantenNum=1, advance=[3s, 6s, 1z, 7z], advanceNum=13, goodShapeAdvance=[1z, 7z], goodShapeAdvanceNum=6),
6s=ShantenWithoutGot(shantenNum=1, advance=[1z, 2z, 7z], advanceNum=9, goodShapeAdvance=[], goodShapeAdvanceNum=0),
7z=ShantenWithoutGot(shantenNum=1, advance=[3s, 6s, 1z, 2z], advanceNum=13, goodShapeAdvance=[1z, 2z], goodShapeAdvanceNum=6),
5s=ShantenWithoutGot(shantenNum=2, advance=[2s, 3s, 4s, 5s, 6s, 1z, 2z, 7z, 7s], advanceNum=28, goodShapeAdvance=null, goodShapeAdvanceNum=null),
3p=ShantenWithoutGot(shantenNum=2, advance=[3p, 3s, 6s, 1z, 2z, 7z], advanceNum=18, goodShapeAdvance=null, goodShapeAdvanceNum=null),
4s=ShantenWithoutGot(shantenNum=2, advance=[3s, 4s, 5s, 6s, 7s, 1z, 2z, 7z], advanceNum=24, goodShapeAdvance=null, goodShapeAdvanceNum=null),
2p=ShantenWithoutGot(shantenNum=2, advance=[2p, 3s, 6s, 1z, 2z, 7z], advanceNum=18, goodShapeAdvance=null, goodShapeAdvanceNum=null),
1p=ShantenWithoutGot(shantenNum=2, advance=[1p, 4p, 3s, 6s, 1z, 2z, 7z], advanceNum=22, goodShapeAdvance=null, goodShapeAdvanceNum=null)}
*/val result = hora(
tiles = Tile.parseTiles("12233466m111z"),
furo = listOf(Furo("789p")),
agari = Tile.get("1z"),
tsumo = true,
dora = 4,
selfWind = Wind.East,
roundWind = Wind.East
)
print(result.yaku) // {SelfWind, RoundWind}
print(result.han) // 6
print(result.hu) // 30
print(result.parentPoint) // ParentPoint(ron=18000, tsumo=6000)
print(result.childPoint) // ChildPoint(ron=12000, tsumoParent=6000, tsumoChild=3000)// Dealer points for 3 han 40 fu
val parentPoint = getParentPointByHanHu(3, 40)
print(parentPoint.ron) // 7700
print(parentPoint.tsumo) // 2600
print(parentPoint.tsumoTotal) // 7800
// Non-dealer points for 3 han 40 fu
val childPoint = getChildPointByHanHu(3, 40)
print(childPoint.ron) // 5200
print(childPoint.tsumoParent) // 2600
print(childPoint.tsumoChild) // 1300
print(childPoint.tsumoTotal) // 5200For cross-language integration, mahjong-utils-entry exposes a unified call method with JSON-formatted parameters and responses.
Ensure JDK11+ is installed. Clone the project and run:
./gradlew :mahjong-utils-entry:linkReleaseSharedNativeOutput files will be in mahjong-utils-entry/build/bin/native/releaseShared.
#include <stdio.h>
#include "libmahjongutils_api.h"
int main(int argc, char** argv) {
libmahjongutils_ExportedSymbols* lib = libmahjongutils_symbols();
const char* params = "{\"tiles\":[\"1s\",\"1s\",\"1s\",\"2s\",\"3s\",\"4s\",\"5s\",\"6s\",\"7s\",\"8s\"]}";
const char* result = lib->kotlin.root.mahjongutils.entry.call("shanten", params);
printf("%s", result);
return 0;
}Output contains three fields: code (status code), msg (error message), and data (result payload).
{"data":{"hand":{"tiles":["1s","1s","1s","2s","3s","4s","5s","6s","7s","8s"],"furo":[],"patterns":[{"type":"RegularHandPattern","k":3,"jyantou":null,"menzenMentsu":["111s","234s","567s"],"furo":[],"tatsu":[],"remaining":["8s"]},{"type":"RegularHandPattern","k":3,"jyantou":null,"menzenMentsu":["111s","234s","678s"],"furo":[],"tatsu":[],"remaining":["5s"]},{"type":"RegularHandPattern","k":3,"jyantou":null,"menzenMentsu":["111s","345s","678s"],"furo":[],"tatsu":[],"remaining":["2s"]},{"type":"RegularHandPattern","k":3,"jyantou":"1s","menzenMentsu":["123s","456s"],"furo":[],"tatsu":["78s"],"remaining":[]},{"type":"RegularHandPattern","k":3,"jyantou":"1s","menzenMentsu":["123s","678s"],"furo":[],"tatsu":["45s"],"remaining":[]},{"type":"RegularHandPattern","k":3,"jyantou":"1s","menzenMentsu":["345s","678s"],"furo":[],"tatsu":["12s"],"remaining":[]}]},"shantenInfo":{"type":"ShantenWithoutGot","shantenNum":0,"advance":["8s","5s","2s","6s","9s","3s"],"advanceNum":19,"goodShapeAdvance":null,"goodShapeAdvanceNum":null,"improvement":{},"improvementNum":0,"goodShapeImprovement":{},"goodShapeImprovementNum":0},"regular":{"hand":{"tiles":["1s","1s","1s","2s","3s","4s","5s","6s","7s","8s"],"furo":[],"patterns":[{"k":3,"jyantou":null,"menzenMentsu":["111s","234s","567s"],"furo":[],"tatsu":[],"remaining":["8s"]},{"k":3,"jyantou":null,"menzenMentsu":["111s","234s","678s"],"furo":[],"tatsu":[],"remaining":["5s"]},{"k":3,"jyantou":null,"menzenMentsu":["111s","345s","678s"],"furo":[],"tatsu":[],"remaining":["2s"]},{"k":3,"jyantou":"1s","menzenMentsu":["123s","456s"],"furo":[],"tatsu":["78s"],"remaining":[]},{"k":3,"jyantou":"1s","menzenMentsu":["123s","678s"],"furo":[],"tatsu":["45s"],"remaining":[]},{"k":3,"jyantou":"1s","menzenMentsu":["345s","678s"],"furo":[],"tatsu":["12s"],"remaining":[]}]},"shantenInfo":{"type":"ShantenWithoutGot","shantenNum":0,"advance":["8s","5s","2s","6s","9s","3s"],"advanceNum":19,"goodShapeAdvance":null,"goodShapeAdvanceNum":null,"improvement":{},"improvementNum":0,"goodShapeImprovement":{},"goodShapeImprovementNum":0}},"chitoi":null,"kokushi":null},"code":200,"msg":""}
For more about Kotlin/Native dynamic libraries: https://kotlinlang.org/docs/native-dynamic-libraries.html#use-generated-headers-from-c
Japanese Mahjong Utilities
Implemented Features:
App developed based on this library: https://github.com/ssttkkl/mahjong-utils-app
Good news! This component has officially partnered with DeepWiki. Now you can ask AI directly instead of wasting time posting Issues!
Documentation: https://deepwiki.com/ssttkkl/mahjong-utils
Example question: How to calculate all yaku in a winning hand?
This project uses Kotlin/Multiplatform and supports multiple platforms:
For Kotlin/Java: Continue reading below
For Python: We provide Python bindings - see python-lib/README.md
For JavaScript/TypeScript: We provide JS/TS bindings - see js-lib/README.md
Maven:
<dependency>
<groupId>io.github.ssttkkl</groupId>
<artifactId>mahjong-utils</artifactId>
<version>${mahjongUtilsVersion}</version>
</dependency>Gradle:
implementation "io.github.ssttkkl:mahjong-utils:{mahjongUtilsVersion}"Without drawn tile:
val result = shanten(Tile.parseTiles("34568m235p68s"))
val shantenInfo = result.shantenInfo as ShantenWithoutGot
print(shantenInfo.shantenNum) // 2
print(shantenInfo.advance) // [7m, 1p, 4p, 5p, 6s, 8s, 7s, 2p, 3p, 6m, 8m, 3m]With drawn tile:
val result = shanten(Tile.parseTiles("112233p44556s127z"))
val shantenInfo = result.shantenInfo as ShantenWithGot
print(shantenInfo.shantenNum) // 1
print(shantenInfo.discardToAdvance)
/*
{1z=ShantenWithoutGot(shantenNum=1, advance=[3s, 6s, 2z, 7z], advanceNum=13, goodShapeAdvance=[2z, 7z], goodShapeAdvanceNum=6),
2z=ShantenWithoutGot(shantenNum=1, advance=[3s, 6s, 1z, 7z], advanceNum=13, goodShapeAdvance=[1z, 7z], goodShapeAdvanceNum=6),
6s=ShantenWithoutGot(shantenNum=1, advance=[1z, 2z, 7z], advanceNum=9, goodShapeAdvance=[], goodShapeAdvanceNum=0),
7z=ShantenWithoutGot(shantenNum=1, advance=[3s, 6s, 1z, 2z], advanceNum=13, goodShapeAdvance=[1z, 2z], goodShapeAdvanceNum=6),
5s=ShantenWithoutGot(shantenNum=2, advance=[2s, 3s, 4s, 5s, 6s, 1z, 2z, 7z, 7s], advanceNum=28, goodShapeAdvance=null, goodShapeAdvanceNum=null),
3p=ShantenWithoutGot(shantenNum=2, advance=[3p, 3s, 6s, 1z, 2z, 7z], advanceNum=18, goodShapeAdvance=null, goodShapeAdvanceNum=null),
4s=ShantenWithoutGot(shantenNum=2, advance=[3s, 4s, 5s, 6s, 7s, 1z, 2z, 7z], advanceNum=24, goodShapeAdvance=null, goodShapeAdvanceNum=null),
2p=ShantenWithoutGot(shantenNum=2, advance=[2p, 3s, 6s, 1z, 2z, 7z], advanceNum=18, goodShapeAdvance=null, goodShapeAdvanceNum=null),
1p=ShantenWithoutGot(shantenNum=2, advance=[1p, 4p, 3s, 6s, 1z, 2z, 7z], advanceNum=22, goodShapeAdvance=null, goodShapeAdvanceNum=null)}
*/val result = hora(
tiles = Tile.parseTiles("12233466m111z"),
furo = listOf(Furo("789p")),
agari = Tile.get("1z"),
tsumo = true,
dora = 4,
selfWind = Wind.East,
roundWind = Wind.East
)
print(result.yaku) // {SelfWind, RoundWind}
print(result.han) // 6
print(result.hu) // 30
print(result.parentPoint) // ParentPoint(ron=18000, tsumo=6000)
print(result.childPoint) // ChildPoint(ron=12000, tsumoParent=6000, tsumoChild=3000)// Dealer points for 3 han 40 fu
val parentPoint = getParentPointByHanHu(3, 40)
print(parentPoint.ron) // 7700
print(parentPoint.tsumo) // 2600
print(parentPoint.tsumoTotal) // 7800
// Non-dealer points for 3 han 40 fu
val childPoint = getChildPointByHanHu(3, 40)
print(childPoint.ron) // 5200
print(childPoint.tsumoParent) // 2600
print(childPoint.tsumoChild) // 1300
print(childPoint.tsumoTotal) // 5200For cross-language integration, mahjong-utils-entry exposes a unified call method with JSON-formatted parameters and responses.
Ensure JDK11+ is installed. Clone the project and run:
./gradlew :mahjong-utils-entry:linkReleaseSharedNativeOutput files will be in mahjong-utils-entry/build/bin/native/releaseShared.
#include <stdio.h>
#include "libmahjongutils_api.h"
int main(int argc, char** argv) {
libmahjongutils_ExportedSymbols* lib = libmahjongutils_symbols();
const char* params = "{\"tiles\":[\"1s\",\"1s\",\"1s\",\"2s\",\"3s\",\"4s\",\"5s\",\"6s\",\"7s\",\"8s\"]}";
const char* result = lib->kotlin.root.mahjongutils.entry.call("shanten", params);
printf("%s", result);
return 0;
}Output contains three fields: code (status code), msg (error message), and data (result payload).
{"data":{"hand":{"tiles":["1s","1s","1s","2s","3s","4s","5s","6s","7s","8s"],"furo":[],"patterns":[{"type":"RegularHandPattern","k":3,"jyantou":null,"menzenMentsu":["111s","234s","567s"],"furo":[],"tatsu":[],"remaining":["8s"]},{"type":"RegularHandPattern","k":3,"jyantou":null,"menzenMentsu":["111s","234s","678s"],"furo":[],"tatsu":[],"remaining":["5s"]},{"type":"RegularHandPattern","k":3,"jyantou":null,"menzenMentsu":["111s","345s","678s"],"furo":[],"tatsu":[],"remaining":["2s"]},{"type":"RegularHandPattern","k":3,"jyantou":"1s","menzenMentsu":["123s","456s"],"furo":[],"tatsu":["78s"],"remaining":[]},{"type":"RegularHandPattern","k":3,"jyantou":"1s","menzenMentsu":["123s","678s"],"furo":[],"tatsu":["45s"],"remaining":[]},{"type":"RegularHandPattern","k":3,"jyantou":"1s","menzenMentsu":["345s","678s"],"furo":[],"tatsu":["12s"],"remaining":[]}]},"shantenInfo":{"type":"ShantenWithoutGot","shantenNum":0,"advance":["8s","5s","2s","6s","9s","3s"],"advanceNum":19,"goodShapeAdvance":null,"goodShapeAdvanceNum":null,"improvement":{},"improvementNum":0,"goodShapeImprovement":{},"goodShapeImprovementNum":0},"regular":{"hand":{"tiles":["1s","1s","1s","2s","3s","4s","5s","6s","7s","8s"],"furo":[],"patterns":[{"k":3,"jyantou":null,"menzenMentsu":["111s","234s","567s"],"furo":[],"tatsu":[],"remaining":["8s"]},{"k":3,"jyantou":null,"menzenMentsu":["111s","234s","678s"],"furo":[],"tatsu":[],"remaining":["5s"]},{"k":3,"jyantou":null,"menzenMentsu":["111s","345s","678s"],"furo":[],"tatsu":[],"remaining":["2s"]},{"k":3,"jyantou":"1s","menzenMentsu":["123s","456s"],"furo":[],"tatsu":["78s"],"remaining":[]},{"k":3,"jyantou":"1s","menzenMentsu":["123s","678s"],"furo":[],"tatsu":["45s"],"remaining":[]},{"k":3,"jyantou":"1s","menzenMentsu":["345s","678s"],"furo":[],"tatsu":["12s"],"remaining":[]}]},"shantenInfo":{"type":"ShantenWithoutGot","shantenNum":0,"advance":["8s","5s","2s","6s","9s","3s"],"advanceNum":19,"goodShapeAdvance":null,"goodShapeAdvanceNum":null,"improvement":{},"improvementNum":0,"goodShapeImprovement":{},"goodShapeImprovementNum":0}},"chitoi":null,"kokushi":null},"code":200,"msg":""}
For more about Kotlin/Native dynamic libraries: https://kotlinlang.org/docs/native-dynamic-libraries.html#use-generated-headers-from-c