High-performance Mahjong winning-hand detection & AI discard algorithm based on lookup tables, supporting multiple wildcard tiles (jokers). One algorithm, three implementations — Java / Go / C++ — kept behavior-identical by shared cross-language tests.
- Win detection: Millisecond-level check for winning hands, supports any number of wildcard tiles
- Waiting-hand calculation: Quickly lists all tiles that complete the current hand
- AI discard: Score-model-driven auto decision for discarding, ponging, and konging
- Lookup table: Offline pre-computation; runtime does hash lookups only — extremely fast
- Full tile coverage: Characters (Wan), Circles (Tong), Bamboo (Tiao), Wind tiles (East/South/West/North), Arrow tiles (Zhong/Fa/Bai)
- Three implementations: Java, Go and C++17 load the same lookup tables under
data/; 2000+ fixed-seed deals are replayed by every CI to pin identical behavior (including bit-exact scores)
java/ Java implementation (Maven project, published to Maven Central)
go/ Go implementation (Go module, behavior-aligned with the Java version)
cpp/ C++17 implementation (CMake, behavior-aligned with the Java version)
data/ Pre-computed lookup tables + the cross-language parity fixture, shared by all implementations
deploy/ Deployment scaffolding for the live demo server (systemd unit + script)
Both implementations load the same table files under data/ and are kept behaviorally in sync by tests:
the algorithm unit tests are ported 1:1 between JUnit, go test and C++ tests, and
data/parity_cases.txt holds 2000+ fixed-seed deals whose
hu/ting/AI results are replayed and asserted by every language
(java/.../ParityFixtureTest.java, go/parity_test.go and cpp/test/parity_test.cpp).
<dependency>
<groupId>com.github.esrrhs</groupId>
<artifactId>majiang_algorithm</artifactId>
<version>1.0.18</version>
</dependency>// Load pre-computed tables
HuTable.load(Files.readAllLines(normalTablePath));
HuTableFeng.load(Files.readAllLines(fengTablePath));
HuTableJian.load(Files.readAllLines(jianTablePath));
// Check if hand is a winning hand
boolean isHu = HuUtil.isHu(cards, gui);
// Query which tiles complete the hand
List<Integer> tingCards = HuUtil.isTing(cards, gui);// Load AI scoring tables
AITable.load(Files.readAllLines(normalTablePath));
AITableFeng.load(Files.readAllLines(fengTablePath));
AITableJian.load(Files.readAllLines(jianTablePath));
// Decide which tile to discard
int card = AIUtil.outAI(cards, gui);
// Decide whether to pong / kong
boolean isPeng = AIUtil.pengAI(cards, gui, pengCard, 0.0d);
boolean isGang = AIUtil.gangAI(cards, gui, gangCard, 0.0d);go get github.com/esrrhs/majiang_algorithm/gopackage main
import (
majiang "github.com/esrrhs/majiang_algorithm/go"
)
func main() {
// Load pre-computed tables (looked up in ., ./data, ../data)
majiang.Load()
cards := majiang.StringToCards("1万,2万,3万,东,东")
gui := majiang.StringToCard("东")
// Check if hand is a winning hand
isHu := majiang.IsHu(cards, gui)
// Query which tiles complete the hand
ting := majiang.IsTing(cards, []int{gui})
// AI discard / pong / kong
out := majiang.OutAI(cards, []int{gui})
isPeng := majiang.PengAI(cards, []int{gui}, pengCard, 0)
isGang := majiang.GangAI(cards, []int{gui}, gangCard, 0)
}Run the Go test suite (from go/, replays the same cases as the Java pipeline):
cd go && go test ./...cd cpp
cmake -S . -B build -DCMAKE_BUILD_TYPE=Release
cmake --build build -j
./build/majiang_tests # or: ctest --test-dir build --output-on-failure#include "majiang/api.h"
#include "majiang/def.h"
#include "majiang/hu_util.h"
#include "majiang/ai_util.h"
// Load pre-computed tables (looked up in ., ./data, ../data)
majiang::Load();
std::vector<int> cards = majiang::StringToCards("1万,2万,3万,东,东");
int gui = majiang::StringToCard("东");
bool isHu = majiang::IsHu(cards, gui); // Check if hand is a winning hand
std::vector<int> ting = majiang::IsTing(cards, {gui}); // Waiting tiles
int out = majiang::OutAI(cards, {gui}); // AI discard
bool isPeng = majiang::PengAI(cards, {gui}, out, 0.0);
bool isGang = majiang::GangAI(cards, {gui}, out, 0.0);To regenerate the lookup tables with either implementation, run from the output directory:
# Java: gen() writes majiang_clien_*.txt / majiang_server_*.txt / majiang.db / majiang_ai_*.txt
# Go: HuGen() writes majiang_clien_*.txt + majiang_server_*.txt, AiGen() writes majiang_ai_*.txt🌐 Live Demo Online: 👉 http://majiang.esrrhs.xyz (now powered by the Go build)
An interactive web platform and algorithm laboratory modeled after Tencent Mahjong:
- 4-Player Mahjong Battle (1 Human vs 3 AI / 4 AI Spectator):
- Green felt table, 3D tiles, and full tile set (Wan, Tong, Tiao, Winds, Dragons).
- Wildcard (gui / laizi) support: Random flip indicator, specified wildcard, or clean hand.
- Draw, Discard, Chow (Chi), Pong (Peng), Kong (Gang), and Winning Hand (Hu).
- Real-time Ready-Hand (Ting) Detection:
- Shows winning tiles and remaining count in the game whenever you are in Ting.
- Hover over hand tiles during discard to preview winning targets if discarded.
- 💡 AI Recommendation:
- One-click best discard recommendation using
AIUtil.outAIwith expected score. - 4-AI auto-play spectator mode with variable speeds (1x ~ 10x).
- One-click best discard recommendation using
- 🧪 Algorithm Playground:
- Test any hand (1-14 tiles) with wildcards.
- Measures
isHu,isTing, andoutAIexecution times in microseconds (µs).
cd java
./mvnw exec:java
# or with custom port:
./mvnw exec:java -Dexec.args="--port=8080"The same web platform also ships as the Go build (single static binary, frontend embedded) — this is what the live demo runs as a systemd service (deploy/gq/):
cd go
go run ./cmd/majiangserver --port=8080 # web server
go run ./cmd/majiangserver --cli # CLI 4-AI simulation benchmarkVisit in your browser: 👉 http://localhost:8080
cd java && ./mvnw exec:java -Dexec.args="--cli"| Document | Content |
|---|---|
| Win Detection Algorithm | Wildcard encoding, table structure, full win-detection & waiting-hand flow |
| AI Algorithm | Hand scoring model, discard / pong / kong decision logic |
| Project | Description |
|---|---|
| texas_algorithm | Texas Hold'em algorithm |
| teenpatti_algorithm | Teen Patti algorithm |